DSEQF.CPP
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:37k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /****************************************************************************
  2.  *
  3.  *  DSEQF.CPP
  4.  *
  5.  *  routines for reading DIB sequences
  6.  *
  7.  ***************************************************************************/
  8. /**************************************************************************
  9.  *
  10.  *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  11.  *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  12.  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  13.  *  PURPOSE.
  14.  *
  15.  *  Copyright (C) 1992 - 1997 Microsoft Corporation.  All Rights Reserved.
  16.  *
  17.  **************************************************************************/
  18. #define INC_OLE2
  19. #include <windows.h>
  20. #include <windowsx.h>
  21. #include <mmsystem.h>
  22. #include <string.h>
  23. #include <stdlib.h>
  24. #include <malloc.h>
  25. #include <ctype.h>
  26. #include <vfw.h>
  27. #include "handler.h"
  28. #include "handler.rc"
  29. static DWORD NEAR PASCAL dseqParseFileName(
  30.         LPSTR lpszFileName,
  31.         LPSTR lpszTemplate,
  32.         DWORD FAR * lpdwMaxValue);
  33. #ifdef DEBUG
  34.         static void CDECL dprintf(LPSTR, ...);
  35.         #define DPF dprintf
  36. #else
  37.         #define DPF ; / ## /
  38. #endif
  39. ///////////////////////////////////////////////////////////////////////////
  40. ///////////////////////////////////////////////////////////////////////////
  41. ///////////////////////////////////////////////////////////////////////////
  42. /*      -       -       -       -       -       -       -       -       */
  43. UINT    uUseCount;
  44. BOOL    fLocked;
  45. /*      -       -       -       -       -       -       -       -       */
  46. //
  47. // External function called by the Class Factory to create an instance of
  48. // the DIB sequence reader/writer
  49. //
  50. HRESULT CAVIFile::Create(
  51. IUnknown FAR*   pUnknownOuter,
  52. const IID FAR&  riid,
  53. void FAR* FAR*  ppv)
  54. {
  55.         IUnknown FAR*   pUnknown;
  56.         CAVIFile FAR*   pAVIFile;
  57.         HRESULT hresult;
  58.         pAVIFile = new FAR CAVIFile(pUnknownOuter, &pUnknown);
  59.         if (!pAVIFile)
  60.                 return ResultFromScode(E_OUTOFMEMORY);
  61.         hresult = pUnknown->QueryInterface(riid, ppv);
  62.         if (FAILED(GetScode(hresult)))
  63.                 delete pAVIFile;
  64.         return hresult;
  65. }
  66. /*      -       -       -       -       -       -       -       -       */
  67. //
  68. // Random C++ stuff: constructors & such...
  69. //
  70. CAVIFile::CAVIFile(
  71. IUnknown FAR*   pUnknownOuter,
  72. IUnknown FAR* FAR*      ppUnknown) :
  73.         m_Unknown(this),
  74.         m_AVIFile(this),
  75.         m_Persist(this),
  76. #ifdef CUSTOMMARSHAL
  77.         m_Marshal(this),
  78. #endif
  79.         m_AVIStream(this)
  80. {
  81.         if (pUnknownOuter)
  82.                 m_pUnknownOuter = pUnknownOuter;
  83.         else
  84.                 m_pUnknownOuter = &m_Unknown;
  85.         *ppUnknown = &m_Unknown;
  86. }
  87. /*      -       -       -       -       -       -       -       -       */
  88. CAVIFile::CUnknownImpl::CUnknownImpl(
  89. CAVIFile FAR*   pAVIFile)
  90. {
  91.         m_pAVIFile = pAVIFile;
  92.         m_refs = 0;
  93. }
  94. /*      -       -       -       -       -       -       -       -       */
  95. //
  96. // This QueryInterface function allows a caller to move between the various
  97. // interfaces the object presents
  98. //
  99. STDMETHODIMP CAVIFile::CUnknownImpl::QueryInterface(
  100. const IID FAR&  iid,
  101. void FAR* FAR*  ppv)
  102. {
  103.         if (iid == IID_IUnknown)
  104.                 *ppv = &m_pAVIFile->m_Unknown;
  105.         else if (iid == IID_IAVIFile)
  106.                 *ppv = &m_pAVIFile->m_AVIFile;
  107.         else if (iid == IID_IAVIStream)
  108.                 *ppv = &m_pAVIFile->m_AVIStream;
  109.         else if (iid == IID_IPersistFile)
  110.                 *ppv = &m_pAVIFile->m_Persist;
  111. #ifdef CUSTOMMARSHAL
  112.         else if (iid == IID_IMarshal)
  113.                 *ppv = &m_pAVIFile->m_Marshal;
  114. #endif
  115.         else
  116.                 return ResultFromScode(E_NOINTERFACE);
  117.         AddRef();
  118.         return NULL;
  119. }
  120. /*      -       -       -       -       -       -       -       -       */
  121. STDMETHODIMP_(ULONG) CAVIFile::CUnknownImpl::AddRef()
  122. {
  123.         uUseCount++;
  124.         return ++m_refs;
  125. }
  126. /*      -       -       -       -       -       -       -       -       */
  127. //
  128. // All calls to AddRef, Release, QueryInterface for the file or stream
  129. // functions are redirected to the Unknown implementation...
  130. //
  131. CAVIFile::CAVIFileImpl::CAVIFileImpl(
  132. CAVIFile FAR*   pAVIFile)
  133. {
  134.         m_pAVIFile = pAVIFile;
  135. }
  136. /*      -       -       -       -       -       -       -       -       */
  137. CAVIFile::CAVIFileImpl::~CAVIFileImpl()
  138. {
  139. }
  140. /*      -       -       -       -       -       -       -       -       */
  141. STDMETHODIMP CAVIFile::CAVIFileImpl::QueryInterface(
  142. const IID FAR&  iid,
  143. void FAR* FAR*  ppv)
  144. {
  145.         return m_pAVIFile->m_pUnknownOuter->QueryInterface(iid, ppv);
  146. }
  147. /*      -       -       -       -       -       -       -       -       */
  148. STDMETHODIMP_(ULONG) CAVIFile::CAVIFileImpl::AddRef()
  149. {
  150.         return m_pAVIFile->m_pUnknownOuter->AddRef();
  151. }
  152. /*      -       -       -       -       -       -       -       -       */
  153. STDMETHODIMP_(ULONG) CAVIFile::CAVIFileImpl::Release()
  154. {
  155.         return m_pAVIFile->m_pUnknownOuter->Release();
  156. }
  157. /*      -       -       -       -       -       -       -       -       */
  158. CAVIFile::CAVIStreamImpl::CAVIStreamImpl(
  159. CAVIFile FAR*   pAVIFile)
  160. {
  161.         m_pAVIFile = pAVIFile;
  162. }
  163. /*      -       -       -       -       -       -       -       -       */
  164. CAVIFile::CAVIStreamImpl::~CAVIStreamImpl()
  165. {
  166. }
  167. /*      -       -       -       -       -       -       -       -       */
  168. STDMETHODIMP CAVIFile::CAVIStreamImpl::QueryInterface(
  169. const IID FAR&  iid,
  170. void FAR* FAR*  ppv)
  171. {
  172.         return m_pAVIFile->m_pUnknownOuter->QueryInterface(iid, ppv);
  173. }
  174. /*      -       -       -       -       -       -       -       -       */
  175. STDMETHODIMP_(ULONG) CAVIFile::CAVIStreamImpl::AddRef()
  176. {
  177.         return m_pAVIFile->m_pUnknownOuter->AddRef();
  178. }
  179. /*      -       -       -       -       -       -       -       -       */
  180. STDMETHODIMP_(ULONG) CAVIFile::CAVIStreamImpl::Release()
  181. {
  182.         return m_pAVIFile->m_pUnknownOuter->Release();
  183. }
  184. // --- IPersistFile implementation --------------------------------------
  185. CAVIFile::CPersistFileImpl::CPersistFileImpl(CAVIFile FAR* pAVIFile)
  186. {
  187.     m_pAVIFile = pAVIFile;
  188. }
  189. STDMETHODIMP
  190. CAVIFile::CPersistFileImpl::QueryInterface(REFIID riid, LPVOID FAR* ppv)
  191. {
  192.         return m_pAVIFile->m_pUnknownOuter->QueryInterface(riid, ppv);
  193. }
  194. STDMETHODIMP_(ULONG)
  195. CAVIFile::CPersistFileImpl::AddRef()
  196. {
  197.         return m_pAVIFile->m_pUnknownOuter->AddRef();
  198. }
  199. STDMETHODIMP_(ULONG)
  200. CAVIFile::CPersistFileImpl::Release()
  201. {
  202.         return m_pAVIFile->m_pUnknownOuter->Release();
  203. }
  204. // *** IPersist methods ***
  205. STDMETHODIMP
  206. CAVIFile::CPersistFileImpl::GetClassID (LPCLSID lpClassID)
  207. {
  208.         *lpClassID = CLSID_DIBSEQFileReader;
  209.         return NOERROR;
  210. }
  211. // *** IPersistFile methods ***
  212. STDMETHODIMP
  213. CAVIFile::CPersistFileImpl::IsDirty ()
  214. {
  215.     if (m_pAVIFile->fDirty) {
  216.         return NOERROR;
  217.     } else {
  218.         return ResultFromScode(S_FALSE);
  219.     }
  220. }
  221. /*      -       -       -       -       -       -       -       -       */
  222. //
  223. // This function takes the name of the first file in a DIB sequence, and
  224. // returns a printf() specifier which can be used to create the names in
  225. // the sequence, along with minimum and maximum values that can be used.
  226. //
  227. //
  228. // Examples:
  229. //  lpszFileName = "FOO0047.DIB"
  230. //       -> lpszTemplate = "FOO%04d.DIB", dwMaxValue = 9999, return = 47
  231. //
  232. //  lpszFileName = "TEST01.DIB"
  233. //       -> lpszTemplate = "TEST%01d.DIB", dwMaxValue = 9, return = 1
  234. //
  235. //  lpszFileName = "TEST1.DIB"
  236. //       -> lpszTemplate = "TEST%d.DIB", dwMaxValue = 9999, return = 1
  237. //
  238. //  lpszFileName = "SINGLE.DIB"
  239. //       -> lpszTemplate = "SINGLE.DIB", dwMaxValue = 0, return = 0
  240. //
  241. static DWORD NEAR PASCAL dseqParseFileName(
  242. LPSTR lpszFileName,
  243. LPSTR lpszTemplate,
  244. DWORD FAR * lpdwMaxValue)
  245. {
  246.         char    achTemp[_MAX_PATH];
  247.         DWORD   dwFirst;
  248.         WORD    wFieldWidth;
  249.         DWORD   dwMult;
  250.         BOOL    fLeadingZero = FALSE;
  251.         LPSTR   lp;
  252.         LPSTR   lp2;
  253.         LPSTR   lpExt;
  254.         /* Find end of string */
  255.         lp2 = lpszFileName;
  256.         lp = achTemp;
  257.         while (*lp2)
  258.         *lp++ = *lp2++;
  259.         *lp = '';
  260.         /* Make lp2 point at last character of base filename (w/o extension) */
  261.         /* Make lpExt point at the extension (without the dot) */
  262.         for (lp2 = lp; *lp2 != '.'; lp2--)
  263.         {
  264.                 lpExt = lp2;
  265.                 if ((lp2 == achTemp) || (*lp2 == '\')
  266.                         || (*lp2 == ':') || (*lp2 == '!'))
  267.                 {
  268.                         /* There is no extension */
  269.                         lp2 = lp;
  270.                         lpExt = lp;
  271.                         break;
  272.                 }
  273.         }
  274.         lp2--;
  275.         // Count the number of numeric characters here....
  276.         dwFirst = 0;
  277.         wFieldWidth = 0;
  278.         dwMult = 1;
  279.         while (lp2 >= achTemp && (*lp2 >= '0') && (*lp2 <= '9')) {
  280.         fLeadingZero = (*lp2 == '0');
  281.         dwFirst += dwMult * (*(lp2--) - '0');
  282.         dwMult *= 10;
  283.         wFieldWidth++;
  284.         }
  285.         *lpdwMaxValue = dwMult - 1;
  286.         lp2++;
  287.         *lp2 = '';
  288.         // Make the format specifier....
  289.         if (wFieldWidth) {
  290.         if (fLeadingZero) {
  291.                 wsprintf((LPSTR) lpszTemplate,"%s%%0%ulu.%s",
  292.                         (LPSTR) achTemp, wFieldWidth,(LPSTR) lpExt);
  293.         } else {
  294.                 wsprintf((LPSTR) lpszTemplate,"%s%%lu.%s",
  295.                         (LPSTR) achTemp, (LPSTR) lpExt);
  296.                 *lpdwMaxValue = 999999L;
  297.                 // !!! This should really be based on the number of
  298.                 // characters left after the base name....
  299.         }
  300.         } else
  301.         wsprintf((LPSTR) lpszTemplate,"%s.%s",
  302.                         (LPSTR) achTemp, (LPSTR) lpExt);
  303.         DPF("First = %lu, Width = %u, Template = '%s'n",dwFirst, wFieldWidth, lpszTemplate);
  304.         return dwFirst;
  305. }
  306. /*      -       -       -       -       -       -       -       -       */
  307. #define SLASH(c) ((c) == '/' || (c) == '\')
  308. /*--------------------------------------------------------------+
  309. | FileName  - return a pointer to the filename part of szPath   |
  310. |             with no preceding path.                           |
  311. +--------------------------------------------------------------*/
  312. LPSTR FAR FileName(
  313. LPCSTR lszPath)
  314. {
  315.         LPCSTR  lszCur;
  316.         for (lszCur = lszPath + lstrlen(lszPath); lszCur > lszPath && !SLASH(*lszCur) && *lszCur != ':';)
  317.                 lszCur = AnsiPrev(lszPath, lszCur);
  318.         if (lszCur == lszPath)
  319.                 return (LPSTR)lszCur;
  320.         else
  321.                 return (LPSTR)(lszCur + 1);
  322. }
  323. /*      -       -       -       -       -       -       -       -       */
  324. //
  325. // "Open" a DIB sequence, by parsing the filename and counting the number
  326. // of frames actually present....
  327. //
  328. STDMETHODIMP
  329. CAVIFile::CPersistFileImpl::Load (LPCOLESTR szFile, DWORD mode)
  330. {
  331.         CAVIFile FAR    *p = m_pAVIFile;
  332.         UINT            ui;
  333.         char            ach[80];
  334.         char            szFileA[MAX_PATH];
  335.         p->mode = mode;
  336.         //
  337.         // Parse the filename
  338.         //
  339.         wsprintf(szFileA, "%ls", szFile);
  340.         p->dwFirstFrame = dseqParseFileName(szFileA,
  341.                                 p->achFilenameTemplate,
  342.                                 &p->dwMaxValue);
  343.         //
  344.         // Initialize the variables that keep track of what frame is cached
  345.         //
  346.         p->lCurFrame = -1;
  347.         p->lpFrame = NULL;
  348.         p->cbFrame = 0;
  349.         p->cbFrameBuffer = 0;
  350.         p->lpFormat = NULL;
  351.         p->cbFormat = 0;
  352.         p->cbFormatBuffer = 0;
  353.         //
  354.         // Build a stream header....
  355.         //
  356.         p->sinfo.fccType = streamtypeVIDEO;
  357.         p->sinfo.fccHandler = 0;
  358.         p->sinfo.dwFlags = 0;
  359.         p->sinfo.wPriority = 0;
  360.         p->sinfo.wLanguage = 0;
  361.         p->sinfo.dwInitialFrames = 0;
  362.         p->sinfo.dwScale = 1;
  363.         p->sinfo.dwRate = 15;
  364.         p->sinfo.dwStart = 0;
  365.         p->sinfo.dwLength = 0;
  366.         p->sinfo.dwSuggestedBufferSize = 0;
  367.         p->sinfo.dwSampleSize = 0;
  368.         LoadString(ghModule, IDS_STREAMNAME, ach, sizeof(ach));
  369.         {
  370.             char TempFileName[80];
  371.             char TempName[80];
  372.             wsprintf(TempFileName, "%ls", szFile);
  373.             wsprintf(TempName, ach, FileName(TempFileName));
  374.             wsprintfW(p->sinfo.szName, L"%hs", TempName);
  375.         }
  376.         //
  377.         // ... and a file header.
  378.         //
  379.         _fmemset(&p->finfo, 0, sizeof(p->finfo));
  380.         p->finfo.dwRate = 15;
  381.         p->finfo.dwScale = 1;
  382.         p->finfo.dwStreams = 1;
  383.         p->finfo.dwWidth = 0;
  384.         p->finfo.dwHeight = 0;
  385.         LoadString(ghModule, IDS_FILETYPE,
  386.                 p->finfo.szFileType,
  387.                 sizeof(p->finfo.szFileType));
  388.         p->finfo.dwCaps = AVIFILECAPS_CANREAD |
  389.                                 AVIFILECAPS_CANWRITE |
  390.                                 AVIFILECAPS_ALLKEYFRAMES;
  391.         if (mode & OF_CREATE) {
  392.         //
  393.         // They're creating a "new" sequence
  394.         //
  395.         p->fStreamPresent = FALSE;
  396.         } else {
  397.         char            ach[_MAX_PATH];
  398.         OFSTRUCT        of;
  399.         DWORD           dwFrame;
  400.         HRESULT         hr;
  401.         //
  402.         // They're opening an existing sequence, so we have to actually
  403.         // count how many files are present
  404.         //
  405.         p->fStreamPresent = TRUE;
  406.         ui = SetErrorMode(SEM_NOOPENFILEERRORBOX);
  407.         for (dwFrame = 0; TRUE; dwFrame++) {
  408.                 if (dwFrame > p->dwMaxValue)
  409.                 break;
  410.                 wsprintf(ach,p->achFilenameTemplate, dwFrame + p->dwFirstFrame);
  411.                 // DPF("DIBSEQ: Checking frame %lu from '%s'n",dwFrame,(LPSTR) ach);
  412. /****************************************************************************/
  413. /* DOS share has a bug.  If the file we're testing for existence is open    */
  414. /* already by someone else, we have to give it the same flag for SHARE as   */
  415. /* the other person is using.  So we have to try both on and off.  Only one */
  416. /* of these will return TRUE but if one of them does, the file exists.  Also*/
  417. /* we have to turn off the system model error box for share violations.     */
  418. /****************************************************************************/
  419.                 if (OpenFile((LPSTR)ach, &of, OF_EXIST) == HFILE_ERROR &&
  420.                         OpenFile((LPSTR)ach, &of, OF_EXIST | OF_SHARE_DENY_NONE) ==
  421.                         HFILE_ERROR)
  422.                 break;
  423.         }
  424.         SetErrorMode(ui);
  425.         if (dwFrame == 0)
  426.                 goto error;
  427.         //
  428.         // Fix up the length in the header structures
  429.         //
  430.         p->sinfo.dwLength = dwFrame;
  431.         p->finfo.dwLength = dwFrame;
  432.         //
  433.         // Load the first frame, so we'll be ready...
  434.         //
  435.         hr = p->LoadFrame(0);
  436.         if (FAILED(GetScode(hr)))
  437.                 return hr;
  438.         p->finfo.dwSuggestedBufferSize = p->cbFrame;
  439.         p->sinfo.dwSuggestedBufferSize = p->cbFrame;
  440.         p->finfo.dwWidth = ((LPBITMAPINFOHEADER) p->lpFormat)->biWidth;
  441.         p->finfo.dwHeight = ((LPBITMAPINFOHEADER) p->lpFormat)->biHeight;
  442.         p->sinfo.dwFlags = AVISTREAMINFO_FORMATCHANGES;
  443.         SetRect(&p->sinfo.rcFrame,
  444.                 0, 0, (int) p->finfo.dwWidth, (int) p->finfo.dwHeight);
  445.         }
  446.         //
  447.         // all done return success.
  448.         //
  449.         return ResultFromScode(0); // success
  450. error:
  451.         return ResultFromScode(AVIERR_FILEREAD);
  452. }
  453. STDMETHODIMP
  454. CAVIFile::CPersistFileImpl::Save (LPCOLESTR lpszFileName, BOOL fRemember)
  455. {
  456.         return ResultFromScode(AVIERR_UNSUPPORTED);
  457. }
  458. STDMETHODIMP
  459. CAVIFile::CPersistFileImpl::SaveCompleted (LPCOLESTR lpszFileName)
  460. {
  461.         return NOERROR;
  462. }
  463. STDMETHODIMP
  464. CAVIFile::CPersistFileImpl::GetCurFile (LPOLESTR FAR * lplpszFileName)
  465. {
  466.         return ResultFromScode(AVIERR_UNSUPPORTED);
  467. }
  468. // -------------------- IAVIFile Implementation-----------------------
  469. //
  470. // The GetStream method returns an interface pointer to the video stream,
  471. // assuming one exists.
  472. //
  473. STDMETHODIMP CAVIFile::CAVIFileImpl::GetStream(
  474. PAVISTREAM FAR * ppavi,
  475. DWORD fccType,
  476. LONG lParam)
  477. {
  478.         CAVIFile FAR *  p = m_pAVIFile;
  479.         int             iStreamWant;
  480.         iStreamWant = (int)lParam;
  481.         if (!p->fStreamPresent)
  482.         return ResultFromScode(-1);
  483.         // We only support one stream
  484.         if (lParam != 0)
  485.                 return ResultFromScode(-1);
  486.         // We only support a video stream
  487.         if (fccType && fccType != streamtypeVIDEO)
  488.         return ResultFromScode(-1);
  489.         //
  490.         // Be sure to keep the reference count up to date...
  491.         //
  492.         AddRef();
  493.         *ppavi = (PAVISTREAM) &(p->m_AVIStream);
  494.         return ResultFromScode(AVIERR_OK);
  495. }
  496. //
  497. // If they opened the file with the OF_CREATE flag, they will use this
  498. // method to create the video stream.
  499. //
  500. STDMETHODIMP CAVIFile::CAVIFileImpl::CreateStream(
  501. PAVISTREAM FAR *ppstream,
  502. AVISTREAMINFOW FAR *psi
  503. )
  504. {
  505.         CAVIFile FAR *  p = m_pAVIFile;
  506.         // If the stream was already there, we fail.
  507.         if (p->fStreamPresent)
  508.         return ResultFromScode(AVIERR_UNSUPPORTED);
  509.         p->sinfo = *psi;
  510.         p->sinfo.dwLength = 0;
  511.         *ppstream = (PAVISTREAM) &(p->m_AVIStream);
  512.         p->fStreamPresent = TRUE;
  513.         // Keep the reference count correct
  514.         AddRef();
  515.         return ResultFromScode(AVIERR_OK);
  516. }
  517. STDMETHODIMP CAVIFile::CAVIFileImpl::WriteData(
  518. DWORD ckid,
  519. LPVOID lpData,
  520. LONG cbData)
  521. {
  522.         CAVIFile FAR *  p = m_pAVIFile;
  523.         return ResultFromScode(AVIERR_UNSUPPORTED);
  524. }
  525. STDMETHODIMP CAVIFile::CAVIFileImpl::ReadData(
  526. DWORD ckid,
  527. LPVOID lpData,
  528. LONG FAR *lpcbData)
  529. {
  530.         CAVIFile FAR *  p = m_pAVIFile;
  531.         return ResultFromScode(AVIERR_UNSUPPORTED);
  532. }
  533. STDMETHODIMP CAVIFile::CAVIFileImpl::EndRecord(void)
  534. {
  535.         return ResultFromScode(AVIERR_OK);
  536. }
  537. STDMETHODIMP CAVIFile::CAVIFileImpl::Info(AVIFILEINFOW FAR * pfi, LONG lSize)
  538. {
  539.         CAVIFile FAR *  p = m_pAVIFile;
  540.         hmemcpy(pfi, &p->finfo, min(lSize,sizeof(p->finfo)));
  541.         return 0;
  542. }
  543. STDMETHODIMP CAVIFile::CAVIStreamImpl::Create(
  544. LONG lParam1,
  545. LONG lParam2)
  546. {
  547.         return ResultFromScode(AVIERR_UNSUPPORTED);
  548. }
  549. //
  550. // Returns where the last key frame before the given frame is.
  551. //
  552. // For now, we assume each DIB is a key frame.
  553. //
  554. STDMETHODIMP_(LONG) CAVIFile::CAVIStreamImpl::FindSample(
  555. LONG lPos,
  556. LONG lFlags)
  557. {
  558.         CAVIFile FAR * p = m_pAVIFile;
  559.         // some minimal error checking....
  560.         if (lPos < 0 || lPos >= (LONG) p->sinfo.dwLength)
  561.         return -1;
  562.         // !!! Can we really assume every frame is non-empty and a key frame?
  563.         // !!! Who knows where format changes are? Let's assume everywhere!
  564.         return lPos;
  565. }
  566. #define WIDTHBYTES(i)           ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
  567. #define DIBWIDTHBYTES(bi)       (int)WIDTHBYTES((int)(bi).biWidth * (int)(bi).biBitCount)
  568. #define BFT_BITMAP              0x4d42   /* 'BM' */
  569. //
  570. // Helper function to load a given frame into our cache.
  571. //
  572. // This is where the actual work is done; all other functions just return
  573. // the current format or frame out of the cache.
  574. //
  575. HRESULT NEAR PASCAL CAVIFile::LoadFrame(
  576. LONG lPos)
  577. {
  578.         char            ach[_MAX_PATH];
  579.         HMMIO           hmmio;
  580.         BITMAPFILEHEADER        bfh;
  581.         BITMAPINFOHEADER        bih;
  582.         SCODE           sc = 0;
  583.         UINT            ui;
  584.         //
  585.         // Check if we've already loaded this frame...
  586.         //
  587.         if (lPos == lCurFrame)
  588.         return 0;
  589.         //
  590.         // Build the filename by printing using our template
  591.         //
  592.         wsprintf(ach, achFilenameTemplate, dwFirstFrame + lPos);
  593.         // No system error box, please.
  594.         ui = SetErrorMode(SEM_NOOPENFILEERRORBOX);
  595.         //
  596.         // Go try to read the frame... Because of SHARE we have to try
  597.         // opening it two different ways.
  598.         //
  599.         hmmio = mmioOpen(ach, NULL, MMIO_READ | OF_SHARE_DENY_WRITE);
  600.         if (!hmmio) {
  601.                 hmmio = mmioOpen(ach, NULL, MMIO_READ);
  602.         if (!hmmio)
  603.                 return ResultFromScode(AVIERR_FILEOPEN);
  604.         }
  605.         SetErrorMode(ui);
  606.         //
  607.         // Read the BitmapFileHeader...
  608.         //
  609.         if (mmioRead(hmmio, (LPSTR) &bfh, sizeof(bfh)) != sizeof(bfh)) {
  610.         sc = AVIERR_FILEREAD;
  611.         goto error;
  612.         }
  613.         if (bfh.bfType != BFT_BITMAP) {
  614.         sc = AVIERR_BADFORMAT;
  615.         goto error;
  616.         }
  617.         //
  618.         // Read the BitmapInfoHeader...
  619.         //
  620.         if (mmioRead(hmmio, (LPSTR) &bih, sizeof(bih)) != sizeof(bih)) {
  621.         sc = AVIERR_FILEREAD;
  622.         goto error;
  623.         }
  624.         if (bih.biSize < sizeof(bih)) {
  625.         sc = AVIERR_BADFORMAT;
  626.         goto error;
  627.         }
  628.         // Check that the width and height match....
  629.         if ((finfo.dwWidth && finfo.dwWidth != (DWORD) bih.biWidth) ||
  630.         (finfo.dwHeight && finfo.dwHeight != (DWORD) bih.biHeight)) {
  631.         sc = AVIERR_BADFORMAT;
  632.         goto error;
  633.         }
  634.         // Fix up some fields in the header...
  635.         if (bih.biSizeImage == 0) {
  636.         bih.biSizeImage = DIBWIDTHBYTES(bih) * bih.biHeight;
  637.         }
  638.         if (bih.biClrUsed == 0 && bih.biBitCount <= 8 && bih.biCompression <= BI_RLE8)
  639.         bih.biClrUsed = 1 << bih.biBitCount;
  640.         cbFormat = bih.biSize + bih.biClrUsed * sizeof(RGBQUAD);
  641.         // Allocate space for the format
  642.         if (cbFormat > cbFormatBuffer) {
  643.         if (lpFormat) {
  644.                 GlobalFreePtr(lpFormat);
  645.                 lpFormat = 0;
  646.                 cbFormatBuffer = 0;
  647.         }
  648.         lpFormat = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_DDESHARE, cbFormat);
  649.         if (!lpFormat) {
  650.                 sc = AVIERR_MEMORY;
  651.                 goto error;
  652.         }
  653.         cbFormatBuffer = cbFormat;
  654.         }
  655.         *((LPBITMAPINFOHEADER) lpFormat) = bih;
  656.         // If the format is bigger than a BITMAPINFOHEADER, read the rest....
  657.         if (cbFormat > sizeof(bih)) {
  658.         if (mmioRead(hmmio, (LPSTR) lpFormat + sizeof(bih),
  659.                 cbFormat - (LONG)sizeof(bih))
  660.                 != cbFormat - (LONG)sizeof(bih))
  661.         {
  662.                 sc = AVIERR_FILEREAD;
  663.                 goto error;
  664.         }
  665.         }
  666.         //
  667.         // Allocate enough space to read the frame in...
  668.         //
  669.         if (bih.biSizeImage > (DWORD) cbFrameBuffer) {
  670.         if (lpFrame) {
  671.                 GlobalFreePtr(lpFrame);
  672.                 lpFrame = 0;
  673.                 cbFrameBuffer = 0;
  674.         }
  675.         lpFrame = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_DDESHARE, bih.biSizeImage);
  676.         if (!lpFrame) {
  677.                 sc = AVIERR_MEMORY;
  678.                 goto error;
  679.         }
  680.         cbFrameBuffer = bih.biSizeImage;
  681.         }
  682.         cbFrame = bih.biSizeImage;
  683.         //
  684.         // and actually read the frame....
  685.         //
  686.         if (mmioRead(hmmio, (LPSTR) lpFrame, cbFrame) != cbFrame) {
  687.         sc = AVIERR_FILEREAD;
  688.         goto error;
  689.         }
  690.         lCurFrame = lPos;
  691. error:
  692.         mmioClose(hmmio, 0);
  693.         return ResultFromScode(sc);
  694. }
  695. //
  696. // The ReadFormat method returns the format of the specified frame....
  697. //
  698. STDMETHODIMP CAVIFile::CAVIStreamImpl::ReadFormat(
  699. LONG lPos,
  700. LPVOID lpFormat,
  701. LONG FAR *lpcbFormat)
  702. {
  703.         CAVIFile FAR * p = m_pAVIFile;
  704.         HRESULT hr;
  705.         //
  706.         // Try to get the correct frame
  707.         //
  708.         hr = p->LoadFrame(lPos);
  709.         if (hr != 0)
  710.         return hr;
  711.         // No buffer to fill in, this means return the size needed.
  712.         if (lpFormat == NULL || *lpcbFormat == 0) {
  713.                 *lpcbFormat = p->cbFormat;
  714.         return 0;
  715.         }
  716.         //
  717.         // and return as much of the format as will fit.
  718.         //
  719.         hmemcpy(lpFormat, p->lpFormat, min(*lpcbFormat, p->cbFormat));
  720.         *lpcbFormat = p->cbFormat;
  721.         return 0;
  722. }
  723. ///////////////////////////////////////////////////////////////////////////
  724. ///////////////////////////////////////////////////////////////////////////
  725. STDMETHODIMP CAVIFile::CAVIStreamImpl::Info(AVISTREAMINFOW FAR * psi, LONG lSize)
  726. {
  727.         CAVIFile FAR * p = m_pAVIFile;
  728.         hmemcpy(psi,&p->sinfo, min(lSize,sizeof(p->sinfo)));
  729. //      return sizeof(p->sinfo);
  730.         return 0;
  731. }
  732. STDMETHODIMP_(ULONG) CAVIFile::CUnknownImpl::Release()
  733. {
  734.         CAVIFile FAR * p = m_pAVIFile;
  735.         uUseCount--;
  736.         if (!--m_refs) {
  737.         LONG lRet = AVIERR_OK;
  738.         if (p->fDirty) {
  739.         }
  740.         goto success;
  741.         success:
  742.         if (p->lpFormat)
  743.                 GlobalFreePtr(p->lpFormat);
  744.         p->lpFormat = NULL;
  745.         p->cbFormat = 0;
  746.         if (p->lpFrame)
  747.                 GlobalFreePtr(p->lpFrame);
  748.         p->lpFrame = NULL;
  749.         p->cbFrame = 0;
  750.         delete this;
  751.         return 0;
  752.         }
  753.         return m_refs;
  754. }
  755. ///////////////////////////////////////////////////////////////////////////
  756. ///////////////////////////////////////////////////////////////////////////
  757. STDMETHODIMP CAVIFile::CAVIStreamImpl::Read(
  758. LONG            lStart,
  759. LONG            lSamples,
  760. LPVOID          lpBuffer,
  761. LONG            cbBuffer,
  762. LONG FAR *      plBytes,
  763. LONG FAR *      plSamples)
  764. {
  765.         CAVIFile FAR *  p = m_pAVIFile;
  766.         HRESULT     hr;
  767.         if (lStart < 0 || lStart >= (LONG) p->sinfo.dwLength) {
  768.         if (plBytes)
  769.             *plBytes = 0;
  770.         if (plSamples)
  771.             *plSamples = 0;
  772.         return 0;
  773.         }
  774.         // We always read one frame at a time...
  775.         lSamples = 1;
  776.         // Load it into the cache....
  777.         hr = p->LoadFrame(lStart);
  778.         if (hr != 0)
  779.         return hr;
  780.         //
  781.         // a NULL buffer means return the size buffer needed to read
  782.         // the given sample.
  783.         //
  784.         if (lpBuffer == NULL || cbBuffer == 0) {
  785.         if (plBytes)
  786.             *plBytes =  p->cbFrame;
  787.         if (plSamples)
  788.             *plSamples = lSamples;
  789.         return 0;
  790.         }
  791.         //
  792.         // They didn't give us enough space for the frame, so complain
  793.         //
  794.         if (cbBuffer < p->cbFrame) {
  795.         if (plBytes)
  796.             *plBytes = p->cbFrame;
  797.         return ResultFromScode(AVIERR_BUFFERTOOSMALL);
  798.         }
  799.         //
  800.         // Copy the frame into the caller's buffer
  801.         //
  802.         hmemcpy(lpBuffer, p->lpFrame, p->cbFrame);
  803.         //
  804.         // success return number of bytes and number of samples read
  805.         //
  806.         if (plBytes)
  807.                 *plBytes = p->cbFrame;
  808.         if (plSamples)
  809.                 *plSamples = lSamples;
  810.         return ResultFromScode(AVIERR_OK);
  811. }
  812. STDMETHODIMP CAVIFile::CAVIStreamImpl::SetFormat(
  813. LONG lPos,
  814. LPVOID lpFormat,
  815. LONG cbFormat)
  816. {
  817.         CAVIFile FAR * p = m_pAVIFile;
  818.         // Keep track of the format....
  819.         p->cbFormat = cbFormat;
  820.         p->lpFormat = (LPVOID) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_DDESHARE, cbFormat);
  821.         if (p->lpFormat == NULL)
  822.         return ResultFromScode(AVIERR_MEMORY);
  823.         hmemcpy(p->lpFormat, lpFormat, cbFormat);
  824.         p->finfo.dwWidth = ((LPBITMAPINFOHEADER) p->lpFormat)->biWidth;
  825.         p->finfo.dwHeight = ((LPBITMAPINFOHEADER) p->lpFormat)->biHeight;
  826.         SetRect(&p->sinfo.rcFrame,
  827.             0, 0, (int) p->finfo.dwWidth, (int) p->finfo.dwHeight);
  828.         return 0L;
  829. }
  830. //
  831. // Helper function to save a single frame
  832. //
  833. HRESULT NEAR PASCAL CAVIFile::WriteFrame(
  834. LONG lPos,
  835. LPVOID lp,
  836. LONG cb)
  837. {
  838.         char            ach[_MAX_PATH];
  839.         HMMIO           hmmio;
  840.         BITMAPFILEHEADER        bfh;
  841.         //
  842.         // If they're overwriting the cached frame, invalidate the cache
  843.         //
  844.         if (lPos == lCurFrame)
  845.         lCurFrame = -1;
  846.         //
  847.         // Build the filename to write to
  848.         //
  849.         wsprintf(ach, achFilenameTemplate, dwFirstFrame + lPos);
  850.         // and write it.
  851.         hmmio = mmioOpen(ach, NULL, MMIO_WRITE | MMIO_CREATE | OF_SHARE_EXCLUSIVE);
  852.         if (!hmmio)
  853.         return ResultFromScode(AVIERR_FILEOPEN);
  854.         //
  855.         // Write the BitmapFileHeader
  856.         //
  857.         bfh.bfType = BFT_BITMAP;
  858.         bfh.bfOffBits = sizeof(bfh) + cbFormat;
  859.         bfh.bfSize = bfh.bfOffBits + cb;
  860.         if (mmioWrite(hmmio, (LPSTR) &bfh, sizeof(bfh)) != sizeof(bfh)) {
  861. error:
  862.         mmioClose(hmmio, 0);
  863.         return ResultFromScode(AVIERR_FILEWRITE);
  864.         }
  865.         ((LPBITMAPINFOHEADER) lpFormat)->biSizeImage = cb;
  866.         //
  867.         // Write the DIB format
  868.         //
  869.         if (mmioWrite(hmmio, (LPSTR) lpFormat, cbFormat) != cbFormat)
  870.         goto error;
  871.         //
  872.         // Write the data
  873.         //
  874.         if (mmioWrite(hmmio, (LPSTR) lp, cb) != cb)
  875.         goto error;
  876.         //
  877.         // Flush things so that we can be sure everything is written out
  878.         //
  879.         if (mmioFlush(hmmio, 0) != 0)
  880.         goto error;
  881.         mmioClose(hmmio, 0);
  882.         return 0;
  883. }
  884. STDMETHODIMP CAVIFile::CAVIStreamImpl::Write(
  885. LONG lStart,
  886. LONG lSamples,
  887. LPVOID lpData,
  888. LONG cbData,
  889. DWORD dwFlags,
  890. LONG FAR *plSampWritten,
  891. LONG FAR *plBytesWritten)
  892. {
  893.         CAVIFile FAR *  p = m_pAVIFile;
  894.         HRESULT     hr;
  895.         if ((p->mode & (OF_WRITE | OF_READWRITE)) == 0)
  896.         return ResultFromScode(AVIERR_READONLY);
  897.         if (p->lpFormat == NULL)
  898.         return ResultFromScode(AVIERR_UNSUPPORTED);
  899.         // < 0 means "at end"
  900.         if (lStart < 0)
  901.         lStart = p->sinfo.dwStart + p->sinfo.dwLength;
  902.         if (lStart > (LONG) (p->sinfo.dwStart + p->sinfo.dwLength))
  903.         return ResultFromScode(AVIERR_BADPARAM);
  904.         // !!! Die if we've reached the limit of our numbers....
  905.         if ((DWORD) lStart + p->dwFirstFrame > p->dwMaxValue)
  906.         return ResultFromScode(AVIERR_FILEWRITE);
  907.         if (lSamples != 1)
  908.         return ResultFromScode(AVIERR_BADPARAM);
  909.         // only allow key frames!
  910.         if (!(dwFlags & AVIIF_KEYFRAME)) {
  911.         DPF("Tried to write a non-key frame to a DIB sequence!n");
  912.         return ResultFromScode(AVIERR_UNSUPPORTED);
  913.         }
  914.         hr = p->WriteFrame(lStart, lpData, cbData);
  915.         if (hr != AVIERR_OK)
  916.         return hr;
  917.         p->fDirty = TRUE;
  918.         p->sinfo.dwLength =
  919.                 max((LONG) p->sinfo.dwLength,
  920.                                   lStart + lSamples);
  921.         p->finfo.dwLength = p->sinfo.dwLength;
  922.         p->finfo.dwSuggestedBufferSize =
  923.                         max(p->finfo.dwSuggestedBufferSize, (DWORD) cbData);
  924.         p->sinfo.dwSuggestedBufferSize =
  925.                         p->finfo.dwSuggestedBufferSize;
  926.         if (plSampWritten)
  927.         *plSampWritten = lSamples;
  928.         if (plBytesWritten)
  929.         *plBytesWritten = cbData;
  930.         return ResultFromScode(AVIERR_OK);
  931. }
  932. // these both are for saving. we don't support saving
  933. STDMETHODIMP
  934. CAVIFile::CAVIFileImpl::DeleteStream(DWORD fccType, LONG lParam)
  935. {
  936.     return ResultFromScode(E_FAIL);
  937. }
  938. STDMETHODIMP
  939. CAVIFile::CAVIStreamImpl::SetInfo(
  940.                 AVISTREAMINFOW FAR * lpInfo,
  941.                 LONG cbInfo
  942. )
  943. {
  944.     return ResultFromScode(E_FAIL);
  945. }
  946. STDMETHODIMP CAVIFile::CAVIStreamImpl::Delete(
  947. LONG lStart,
  948. LONG lSamples)
  949. {
  950.         CAVIFile FAR * p = m_pAVIFile;
  951.         return ResultFromScode(AVIERR_UNSUPPORTED);
  952. }
  953. // Should these just map to Read/WriteData? !!!
  954. STDMETHODIMP CAVIFile::CAVIStreamImpl::ReadData(
  955. DWORD fcc,
  956. LPVOID lp,
  957. LONG FAR *lpcb)
  958. {
  959.         CAVIFile FAR * p = m_pAVIFile;
  960.         return ResultFromScode(AVIERR_UNSUPPORTED);
  961. }
  962. STDMETHODIMP CAVIFile::CAVIStreamImpl::WriteData(
  963. DWORD fcc,
  964. LPVOID lp,
  965. LONG cb)
  966. {
  967.         CAVIFile FAR * p = m_pAVIFile;
  968.         return ResultFromScode(AVIERR_UNSUPPORTED);
  969. }
  970. #ifdef CUSTOMMARSHAL
  971. // The code below supports custom marshalling.
  972. // !!! Need good explanation here!
  973. CAVIFile::CMarshalImpl::CMarshalImpl(
  974. CAVIFile FAR*   pAVIFile)
  975. {
  976.         m_pAVIFile = pAVIFile;
  977. }
  978. /*      -       -       -       -       -       -       -       -       */
  979. STDMETHODIMP CAVIFile::CMarshalImpl::QueryInterface(
  980. const IID FAR&  iid,
  981. void FAR* FAR*  ppv)
  982. {
  983.         return m_pAVIFile->m_pUnknownOuter->QueryInterface(iid, ppv);
  984. }
  985. /*      -       -       -       -       -       -       -       -       */
  986. STDMETHODIMP_(ULONG) CAVIFile::CMarshalImpl::AddRef()
  987. {
  988.         return m_pAVIFile->m_pUnknownOuter->AddRef();
  989. }
  990. /*      -       -       -       -       -       -       -       -       */
  991. STDMETHODIMP_(ULONG) CAVIFile::CMarshalImpl::Release()
  992. {
  993.         return m_pAVIFile->m_pUnknownOuter->Release();
  994. }
  995. // *** IMarshal methods ***
  996. STDMETHODIMP CAVIFile::CMarshalImpl::GetUnmarshalClass (
  997. THIS_ REFIID riid, LPVOID pv,
  998. DWORD dwDestContext, LPVOID pvDestContext,
  999. DWORD mshlflags, LPCLSID pCid)
  1000. {
  1001.         HRESULT hr = NOERROR;
  1002.         DPF("UnMarshalClass calledn");
  1003.         if (dwDestContext != MSHCTX_LOCAL) {
  1004.         LPMARSHAL       pMarshal;
  1005.         DPF("Marshal context is %lu: delegating...n", dwDestContext);
  1006.         hr = CoGetStandardMarshal(riid, NULL, dwDestContext, pvDestContext, mshlflags,
  1007.                         &pMarshal);
  1008.         if (hr != NOERROR)
  1009.                 return hr;
  1010.         hr = pMarshal->GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext,
  1011.                         mshlflags, pCid);
  1012.         pMarshal->Release();
  1013.         return hr;
  1014.         }
  1015.         *pCid = CLSID_AVISimpleUnMarshal;
  1016.         return hr;
  1017. }
  1018. STDMETHODIMP CAVIFile::CMarshalImpl::GetMarshalSizeMax (
  1019. THIS_ REFIID riid, LPVOID pv,
  1020. DWORD dwDestContext, LPVOID pvDestContext,
  1021. DWORD mshlflags, LPDWORD pSize)
  1022. {
  1023.         HRESULT hr = NOERROR;
  1024.         IUnknown FAR * pUnk = &m_pAVIFile->m_Unknown;
  1025.         if (dwDestContext != MSHCTX_LOCAL) {
  1026.         LPMARSHAL       pMarshal;
  1027.         hr = CoGetStandardMarshal(riid, NULL, dwDestContext, pvDestContext, mshlflags,
  1028.                 &pMarshal);
  1029.         if (hr != NOERROR)
  1030.                 return hr;
  1031.         hr = pMarshal->GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext,
  1032.                         mshlflags, pSize);
  1033.         pMarshal->Release();
  1034.         return hr;
  1035.         }
  1036.         *pSize = sizeof(pUnk);
  1037.         return hr;
  1038. }
  1039. STDMETHODIMP CAVIFile::CMarshalImpl::MarshalInterface (
  1040. THIS_ LPSTREAM pStm, REFIID riid,
  1041. LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext,
  1042. DWORD mshlflags)
  1043. {
  1044.         HRESULT hr = NOERROR;
  1045.         IUnknown FAR * pUnk = &m_pAVIFile->m_Unknown;
  1046.         DPF("MarshalInterface calledn");
  1047.         if (dwDestContext != MSHCTX_LOCAL) {
  1048.         LPMARSHAL pMarshal;
  1049.         DPF("Marshal context is %lu: delegating...n", dwDestContext);
  1050.         hr = CoGetStandardMarshal(riid, NULL, dwDestContext, pvDestContext, mshlflags,
  1051.                 &pMarshal);
  1052.         if (hr != NOERROR)
  1053.                 return hr;
  1054.         hr = pMarshal->MarshalInterface(pStm, riid, pv, dwDestContext, pvDestContext,
  1055.                         mshlflags);
  1056.         pMarshal->Release();
  1057.         return hr;
  1058.         }
  1059.         if ((riid != IID_IAVIStream && riid != IID_IAVIFile && riid != IID_IUnknown))
  1060.                 return ResultFromScode(E_INVALIDARG);
  1061.         if ((hr = pStm->Write(&pUnk, sizeof(pUnk), NULL)) == NOERROR)
  1062.         AddRef();
  1063.         DPF("Returns %lxn", (DWORD) (LPVOID) hr);
  1064.         return hr;
  1065. }
  1066. STDMETHODIMP CAVIFile::CMarshalImpl::UnmarshalInterface (
  1067. THIS_ LPSTREAM pStm, REFIID riid,
  1068. LPVOID FAR* ppv)
  1069. {
  1070.         HRESULT hr = ResultFromScode(E_FAIL);
  1071.         DPF("UnMarshalInterface called!!!n");
  1072.         return hr;
  1073. }
  1074. STDMETHODIMP CAVIFile::CMarshalImpl::ReleaseMarshalData (
  1075. THIS_ LPSTREAM pStm)
  1076. {
  1077.         HRESULT hr = NOERROR;
  1078.         IUnknown FAR * pUnk;
  1079.         hr = pStm->Read(&pUnk,sizeof(pUnk),NULL);
  1080.         if (hr == NOERROR)
  1081.         pUnk->Release();
  1082.         return hr;
  1083. }
  1084. STDMETHODIMP CAVIFile::CMarshalImpl::DisconnectObject (
  1085. THIS_ DWORD dwReserved)
  1086. {
  1087.         HRESULT hr = NOERROR;
  1088.         return hr;
  1089. }
  1090. #endif
  1091. /*****************************************************************************
  1092.  *
  1093.  * dprintf() is called by the DPF macro if DEBUG is defined at compile time.
  1094.  *
  1095.  * The messages will be send to COM1: like any debug message. To
  1096.  * enable debug output, add the following to WIN.INI :
  1097.  *
  1098.  * [debug]
  1099.  * ICSAMPLE=1
  1100.  *
  1101.  ****************************************************************************/
  1102. #ifdef DEBUG
  1103. #define MODNAME "DSEQFILE"
  1104. static BOOL fDebug = -1;
  1105. static void cdecl dprintf(
  1106. LPSTR szFormat, ...)
  1107. {
  1108.         char ach[128];
  1109.         va_list va;
  1110.         if (fDebug == -1)
  1111.                 fDebug = GetProfileIntA("Debug",MODNAME, FALSE);
  1112.         if (!fDebug)
  1113.                 return;
  1114.         va_start(va, szFormat);
  1115.         if (szFormat[0] == '!')
  1116.                 ach[0]=0, szFormat++;
  1117.         else
  1118.                 lstrcpyA(ach, MODNAME ": ");
  1119.         wvsprintfA(ach+lstrlenA(ach),szFormat,(LPSTR)va);
  1120.         va_end(va);
  1121. //      lstrcatA(ach, "rrn");
  1122.         OutputDebugStringA(ach);
  1123. }
  1124. #endif