Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2662 lines
66 KiB

4 years ago
  1. /******************************************************************************
  2. Copyright (C) Microsoft Corporation 1985-1991. All rights reserved.
  3. Title: aviopen.c - open a AVI file
  4. *****************************************************************************/
  5. #include "graphic.h"
  6. #ifdef _WIN32
  7. #include <wchar.h>
  8. #endif
  9. #ifdef USEAVIFILE
  10. #include <initguid.h>
  11. #include <scode.h>
  12. DEFINE_AVIGUID(IID_IAVIFile, 0x00020020, 0, 0);
  13. DEFINE_AVIGUID(IID_IAVIStream, 0x00020021, 0, 0);
  14. #endif
  15. #define comptypeNONE mmioFOURCC('N','O','N','E')
  16. //
  17. // special error to force using AVIFile to try and open this file.
  18. //
  19. #define AVIERR_NOT_AVIFILE 4242
  20. //
  21. // if this is defined we will always use AVIFILE.DLL, except for
  22. // 1:1 interleaved files.
  23. //
  24. #ifdef USEAVIFILE
  25. #define USE_AVIFILE_FOR_NON_INT
  26. #endif
  27. #ifdef _WIN32
  28. BOOL runningInWow = 0; // Assume we are not in WOW
  29. #endif
  30. /***************************************************************************
  31. * Local function declarations
  32. ***************************************************************************/
  33. STATICFN BOOL NEAR PASCAL InitStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
  34. STATICFN BOOL NEAR PASCAL InitVideoStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
  35. STATICFN BOOL NEAR PASCAL InitAudioStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
  36. STATICFN BOOL NEAR PASCAL InitOtherStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
  37. STATICFN void NEAR PASCAL CloseStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
  38. STATICFN BOOL NEAR PASCAL ParseNewHeader(NPMCIGRAPHIC npMCI);
  39. STATICFN BOOL NEAR PASCAL OpenRiffAVIFile(NPMCIGRAPHIC npMCI, BOOL bForce);
  40. STATICFN BOOL NEAR PASCAL OpenWithAVIFile(NPMCIGRAPHIC npMCI);
  41. STATICFN BOOL NEAR PASCAL OpenInterface(NPMCIGRAPHIC npMCI);
  42. #ifdef USEAVIFILE
  43. STATICFN BOOL NEAR PASCAL OpenAVIFile(NPMCIGRAPHIC npMCI, IAVIFile FAR *pf);
  44. STATICFN BOOL NEAR PASCAL OpenAVIStream(NPMCIGRAPHIC npMCI, int stream, IAVIStream FAR *pf);
  45. #endif
  46. INLINE static BOOL NEAR PASCAL IsRectBogus(RECT *prc);
  47. #ifndef _WIN32
  48. static LONG NEAR PASCAL atol(char *sz);
  49. #endif // !_WIN32
  50. #ifdef _WIN32XXXX
  51. #define GetFileDriveType GetDriveType
  52. #else
  53. static UINT NEAR PASCAL GetFileDriveType(LPTSTR szPath);
  54. #endif
  55. #ifndef _WIN32
  56. SZCODE szOLENLSDLL[] = "OLE2NLS.DLL";
  57. SZCODE szOLENLSAPI[] = "GetUserDefaultLangID";
  58. #endif
  59. //
  60. // due to OLE stupidity, all ole calls must be single-threaded and thus on
  61. // the app thread. so we moved the Open call to the app thread
  62. //
  63. // -- called on app thread -------------------------------------------------
  64. //
  65. /***************************************************************************
  66. *
  67. * @doc INTERNAL MCIAVI
  68. *
  69. * @api BOOL | mciaviOpenFile | Open an AVI file.
  70. * the filename we are to open is passed to npMCI->szFileName.
  71. *
  72. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  73. *
  74. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  75. *
  76. ***************************************************************************/
  77. BOOL FAR PASCAL mciaviOpenFile(NPMCIGRAPHIC npMCI)
  78. {
  79. //
  80. // mciaviOpenFile should not be called with a file open!
  81. //
  82. DPF(("mciaviOpenFile, name = >>%ls<<\n", npMCI->szFilename));
  83. Assert(npMCI->streams == 0);
  84. Assert(npMCI->hmmio == NULL);
  85. Assert(npMCI->hpIndex == NULL);
  86. Assert(!(npMCI->dwFlags & (
  87. MCIAVI_NOTINTERLEAVED |
  88. MCIAVI_ANIMATEPALETTE |
  89. MCIAVI_CANDRAW |
  90. MCIAVI_HASINDEX)));
  91. //
  92. // !!!support open new
  93. //
  94. if (npMCI->szFilename[0] == '\0') {
  95. }
  96. //
  97. // what media is this file coming from, will be important later
  98. // when we play.
  99. //
  100. if (npMCI->szFilename[0] == TEXT('@')) {
  101. npMCI->uDriveType = DRIVE_INTERFACE;
  102. if (IsNTWOW()) {
  103. DPF2(("Rejecting open from WOW because of an interface pointer\n"));
  104. npMCI->dwTaskError=MCIERR_UNSUPPORTED_FUNCTION;
  105. return(FALSE);
  106. // Cannot support a 16 bit interface pointer from 32 bit code
  107. }
  108. }
  109. else
  110. npMCI->uDriveType = GetFileDriveType(npMCI->szFilename);
  111. #ifdef DEBUG
  112. switch (npMCI->uDriveType) {
  113. case DRIVE_CDROM:
  114. DOUT2("Media is a CD-ROM\n");
  115. break;
  116. case DRIVE_REMOTE:
  117. DOUT2("Media is a Network\n");
  118. break;
  119. case DRIVE_FIXED:
  120. DOUT2("Media is a Hard disk\n");
  121. break;
  122. case DRIVE_REMOVABLE:
  123. DOUT2("Media is a floppy disk\n");
  124. break;
  125. case DRIVE_INTERFACE:
  126. DOUT2("Media is OLE COM Interface\n");
  127. break;
  128. default:
  129. DPF(("Unknown Media type %d\n", npMCI->uDriveType));
  130. break;
  131. }
  132. #endif
  133. #ifdef USEAVIFILE
  134. //
  135. // if the "filename" is of the form: '@########' then we assume we
  136. // have been pased a interface pointer of some sort.
  137. //
  138. if (npMCI->szFilename[0] == TEXT('@') &&
  139. OpenInterface(npMCI))
  140. goto DoneOpening;
  141. // !!! This will open even AVI files this way!
  142. if ((npMCI->dwOptionFlags & MCIAVIO_USEAVIFILE) &&
  143. OpenWithAVIFile(npMCI))
  144. goto DoneOpening;
  145. #endif
  146. if (!OpenRiffAVIFile(npMCI, FALSE)) {
  147. #ifdef USEAVIFILE
  148. //
  149. // unable to open RIFF file, if it was because it was
  150. // not a AVI file, then give AVIFile a try.
  151. //
  152. if (npMCI->dwTaskError == AVIERR_NOT_AVIFILE) {
  153. npMCI->dwTaskError = 0;
  154. if (OpenWithAVIFile(npMCI))
  155. goto DoneOpening;
  156. // if this is a riff file that we could open but chose
  157. // to pass to avifile, and avifile is not present, then we need
  158. // to pass it back to OpenRiffAVIFile and force it to try
  159. // and open it properly.
  160. npMCI->dwTaskError = 0;
  161. if (OpenRiffAVIFile(npMCI, TRUE))
  162. goto DoneOpening;
  163. if (npMCI->dwTaskError == AVIERR_NOT_AVIFILE) {
  164. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  165. // We cannot return an AVI specific error as there is
  166. // no way for the UI to get access to the error text.
  167. // Hence we use a generic mci error.
  168. }
  169. }
  170. #endif
  171. goto error;
  172. }
  173. DoneOpening:
  174. npMCI->dwTaskError = 0;
  175. return TRUE;
  176. error:
  177. mciaviCloseFile(npMCI);
  178. if (npMCI->dwTaskError == 0)
  179. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  180. return FALSE;
  181. }
  182. #ifdef USEAVIFILE
  183. /***************************************************************************
  184. *
  185. * @doc INTERNAL MCIAVI
  186. *
  187. * @api BOOL | OpenWithAVIFile | Open an file using AVIFile
  188. *
  189. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  190. *
  191. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  192. *
  193. ***************************************************************************/
  194. STATICFN BOOL NEAR PASCAL OpenWithAVIFile(NPMCIGRAPHIC npMCI)
  195. {
  196. IAVIFile FAR *pf = NULL;
  197. if (!InitAVIFile(npMCI))
  198. return FALSE;
  199. AVIFileOpen(&pf, npMCI->szFilename, MMIO_READ, 0);
  200. if (pf == NULL) {
  201. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  202. return FALSE;
  203. }
  204. if (!OpenAVIFile(npMCI, pf)) {
  205. mciaviCloseFile(npMCI);
  206. pf->lpVtbl->Release(pf);
  207. return FALSE;
  208. }
  209. return TRUE;
  210. }
  211. /***************************************************************************
  212. *
  213. * @doc INTERNAL MCIAVI
  214. *
  215. * @api BOOL | OpenInterface | Open an interface pointer
  216. *
  217. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  218. *
  219. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  220. *
  221. ***************************************************************************/
  222. STATICFN BOOL NEAR PASCAL OpenInterface(NPMCIGRAPHIC npMCI)
  223. {
  224. IUnknown FAR *p;
  225. IAVIFile FAR *pf=NULL;
  226. IAVIStream FAR *ps=NULL;
  227. if (!InitAVIFile(npMCI))
  228. return FALSE;
  229. if (npMCI->szFilename[0] != TEXT('@')) {
  230. DPF1(("Failing to open interface for file %ls\n",npMCI->szFilename));
  231. return FALSE;
  232. }
  233. #ifdef UNICODE
  234. p = (IUnknown FAR *)wcstol(npMCI->szFilename+1, NULL, 10);
  235. #else
  236. p = (IUnknown FAR *)atol(npMCI->szFilename+1);
  237. #endif
  238. #ifdef USE_ISVALIDINTERFACE
  239. if (!IsValidInterface(p))
  240. return FALSE;
  241. #endif
  242. #ifndef _WIN32
  243. //!!!we need to do the PSP stuff? or will the TASK stuff in
  244. //!!!COMPOBJ screw us up?
  245. {
  246. extern void FAR SetPSP(UINT psp);
  247. SetPSP(npMCI->pspParent);
  248. }
  249. #endif
  250. p->lpVtbl->QueryInterface(p, &IID_IAVIFile, (LPVOID FAR *)&pf);
  251. if (pf != NULL)
  252. {
  253. if (OpenAVIFile(npMCI, pf))
  254. return TRUE;
  255. pf->lpVtbl->Release(pf);
  256. pf = NULL; // AVIMakeFileFromStreams need not set pf to NULL
  257. }
  258. p->lpVtbl->QueryInterface(p, &IID_IAVIStream, (LPVOID FAR *)&ps);
  259. if (ps != NULL)
  260. {
  261. AVIMakeFileFromStreams(&pf, 1, &ps);
  262. ps->lpVtbl->Release(ps);
  263. if (pf == NULL)
  264. return FALSE;
  265. if (OpenAVIFile(npMCI, pf))
  266. return TRUE;
  267. pf->lpVtbl->Release(pf);
  268. //return FALSE;
  269. }
  270. return FALSE;
  271. }
  272. /***************************************************************************
  273. *
  274. * @doc INTERNAL MCIAVI
  275. *
  276. * @api BOOL | OpenAVIFile | Open an a AVIFile object
  277. *
  278. * NOTE we do not do call AddRef() we assume we dont need to.
  279. * neither do we Release(). If we fail, our caller will Release(),
  280. *
  281. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  282. *
  283. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  284. * On a successful return npMCI->pf will be loaded with the
  285. * input IAVIFILE interface pointer.
  286. *
  287. ***************************************************************************/
  288. STATICFN BOOL NEAR PASCAL OpenAVIFile(NPMCIGRAPHIC npMCI, IAVIFile FAR *pf)
  289. {
  290. AVIFILEINFOW info;
  291. HRESULT hr;
  292. IAVIStream FAR *ps;
  293. STREAMINFO *psi;
  294. int i;
  295. Assert(npMCI->pf == NULL);
  296. _fmemset(&info, 0, sizeof(info));
  297. hr = pf->lpVtbl->Info(pf, &info, sizeof(info));
  298. if (FAILED(GetScode(hr))) {
  299. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  300. return FALSE;
  301. }
  302. DPF1(("OpenAVIFile: %ls\n\t#Streams=%d, Width=%d Height=%d\n",
  303. info.szFileType, info.dwStreams, info.dwWidth, info.dwHeight));
  304. //
  305. // get rid of bad files
  306. //
  307. if (info.dwStreams == 0 || info.dwStreams > 255 || info.dwLength == 0) {
  308. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  309. return FALSE;
  310. }
  311. //
  312. // save the interface pointer
  313. // make a copy of the VTable, for later use
  314. //
  315. npMCI->pf = pf;
  316. ////npMCI->vt = *pf->lpVtbl;
  317. npMCI->dwFlags |= MCIAVI_HASINDEX;
  318. npMCI->dwMicroSecPerFrame = muldiv32(info.dwScale, 1000000, info.dwRate);
  319. npMCI->lFrames = (LONG)info.dwLength;
  320. npMCI->dwRate = info.dwRate;
  321. npMCI->dwScale = info.dwScale;
  322. npMCI->streams = (int)info.dwStreams;
  323. npMCI->dwBytesPerSec = info.dwMaxBytesPerSec;
  324. npMCI->dwSuggestedBufferSize = info.dwSuggestedBufferSize + 2*sizeof(DWORD);
  325. SetRect(&npMCI->rcMovie,0,0,(int)info.dwWidth,(int)info.dwHeight);
  326. npMCI->paStreamInfo = (STREAMINFO*)
  327. LocalAlloc(LPTR,npMCI->streams * sizeof(STREAMINFO));
  328. if (npMCI->paStreamInfo == NULL) {
  329. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  330. npMCI->pf = NULL;
  331. return FALSE;
  332. }
  333. for (i = 0; i < npMCI->streams; i++) {
  334. ps = NULL;
  335. AVIFileGetStream(pf, &ps, 0, i);
  336. if (ps == NULL) {
  337. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  338. npMCI->pf = NULL;
  339. return FALSE;
  340. }
  341. if (!OpenAVIStream(npMCI, i, ps))
  342. DPF(("Error opening stream %d!\n", i));
  343. if (npMCI->dwTaskError) {
  344. npMCI->pf = NULL;
  345. ps->lpVtbl->Release(ps);
  346. return FALSE;
  347. }
  348. }
  349. //
  350. // compute the key frames every value
  351. //
  352. // do this by finding the key frame average over the first few frames.
  353. //
  354. #define NFRAMES 250
  355. if (psi = npMCI->psiVideo) {
  356. LONG l;
  357. int nKeyFrames=0;
  358. int nFrames = (int) min(npMCI->lFrames, NFRAMES);
  359. for (l=0; l<nFrames; l++) {
  360. if (AVIStreamFindSample(psi->ps, psi->sh.dwStart+l, FIND_PREV|FIND_KEY) == l)
  361. nKeyFrames++;
  362. }
  363. if (nKeyFrames > 1)
  364. npMCI->dwKeyFrameInfo = (DWORD)((nFrames + nKeyFrames/2)/nKeyFrames);
  365. else
  366. npMCI->dwKeyFrameInfo = 0;
  367. }
  368. return TRUE;
  369. }
  370. /***************************************************************************
  371. *
  372. * @doc INTERNAL MCIAVI
  373. *
  374. * @api BOOL | OpenAVIStream | Open an a AVIStream object
  375. *
  376. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  377. *
  378. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  379. *
  380. ***************************************************************************/
  381. STATICFN BOOL NEAR PASCAL OpenAVIStream(NPMCIGRAPHIC npMCI, int stream, IAVIStream FAR *ps)
  382. {
  383. STREAMINFO* psi;
  384. AVISTREAMINFOW info;
  385. HRESULT hr;
  386. _fmemset(&info, 0, sizeof(info));
  387. hr = ps->lpVtbl->Info(ps, &info, sizeof(info));
  388. if (FAILED(GetScode(hr))) {
  389. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  390. return FALSE;
  391. }
  392. DPF(("OpenAVIStream(%d) %4.4hs:%4.4hs %ls\n", stream, (LPSTR)&info.fccType, (LPSTR)&info.fccHandler, info.szName));
  393. //
  394. // init the STREAMINFO from the IAVIStream
  395. //
  396. psi = SI(stream);
  397. psi->ps = ps; // save interface
  398. ////psi->vt = *ps->lpVtbl; // save VTable !!!needed?
  399. psi->sh.fccType = info.fccType;
  400. psi->sh.fccHandler = info.fccHandler;
  401. psi->sh.dwFlags = info.dwFlags;
  402. psi->sh.wPriority = info.wPriority;
  403. psi->sh.wLanguage = info.wLanguage;
  404. psi->sh.dwInitialFrames = 0; // info.dwInitialFrames;
  405. psi->sh.dwScale = info.dwScale;
  406. psi->sh.dwRate = info.dwRate;
  407. psi->sh.dwStart = info.dwStart;
  408. psi->sh.dwLength = info.dwLength;
  409. psi->sh.dwSuggestedBufferSize = info.dwSuggestedBufferSize;
  410. psi->sh.dwQuality = info.dwQuality;
  411. psi->sh.dwSampleSize = info.dwSampleSize;
  412. psi->sh.rcFrame = info.rcFrame;
  413. //
  414. // get the format of the stream.
  415. //
  416. AVIStreamFormatSize(ps, 0, &psi->cbFormat);
  417. psi->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, psi->cbFormat);
  418. if (!psi->lpFormat) {
  419. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  420. return FALSE;
  421. }
  422. AVIStreamReadFormat(psi->ps, 0, psi->lpFormat, &psi->cbFormat);
  423. //
  424. // get the extra data for the stream.
  425. //
  426. AVIStreamReadData(psi->ps,ckidSTREAMHANDLERDATA, NULL, &psi->cbData);
  427. if (psi->cbData > 0) {
  428. psi->lpData = GlobalAllocPtr(GMEM_MOVEABLE, psi->cbData);
  429. if (!psi->lpData) {
  430. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  431. return FALSE;
  432. }
  433. AVIStreamReadData(psi->ps, ckidSTREAMHANDLERDATA, NULL, &psi->cbData);
  434. }
  435. return InitStream(npMCI, psi);
  436. }
  437. /***************************************************************************
  438. *
  439. * @doc INTERNAL MCIAVI
  440. *
  441. * @api BOOL | InitAVIFile | called to RTL to AVIFILE.DLL
  442. *
  443. * @rdesc TRUE means OK, otherwise error
  444. *
  445. ***************************************************************************/
  446. #ifdef _WIN32
  447. SZCODE szAVIFILE[] = TEXT("AVIFIL32.DLL");
  448. #ifdef USE_ISVALIDINTERFACE
  449. SZCODE szCOMPOBJ[] = TEXT("COMPOB32");
  450. #endif
  451. #else
  452. SZCODE szAVIFILE[] = "AVIFILE.DLL";
  453. #ifdef USE_ISVALIDINTERFACE
  454. SZCODE szCOMPOBJ[] = "COMPOBJ";
  455. #endif
  456. #endif
  457. // On NT the entry points will NOT be unicode strings, as there is
  458. // no unicode version of GetProcAddress. BUT SZCODE generate UNICODE...
  459. // on the other hand, the entry points will all be unicode entrypoints 'W'
  460. #ifdef UNICODE
  461. #define UNICODE_APPEND "W"
  462. #else
  463. #define UNICODE_APPEND
  464. #endif
  465. SZCODEA szAVIFileInit[] = "AVIFileInit";
  466. SZCODEA szAVIFileExit[] = "AVIFileExit";
  467. #ifdef USE_ISVALIDINTERFACE
  468. SZCODEA szIsValidInterface[] = "IsValidInterface";
  469. #endif
  470. SZCODEA szAVIMakeFileFromStreams[] = "AVIMakeFileFromStreams";
  471. SZCODEA szAVIStreamBeginStreaming[] = "AVIStreamBeginStreaming";
  472. SZCODEA szAVIStreamEndStreaming[] = "AVIStreamEndStreaming";
  473. SZCODEA szAVIFileOpen[] = "AVIFileOpen" UNICODE_APPEND;
  474. BOOL FAR InitAVIFile(NPMCIGRAPHIC npMCI)
  475. {
  476. UINT u;
  477. if (hdllAVIFILE == (HMODULE)-1)
  478. // We have already tried to load AVIFILE and failed
  479. return FALSE;
  480. if (hdllAVIFILE == NULL) {
  481. u = SetErrorMode(SEM_NOOPENFILEERRORBOX);
  482. #ifdef _WIN32
  483. // The load and free of szAVIFILE is safe as it is protected by
  484. // the npMCI device critical section. This means that open/close
  485. // events will be synchronised.
  486. #endif
  487. hdllAVIFILE = LoadLibrary(szAVIFILE);
  488. SetErrorMode(u);
  489. #ifndef _WIN32
  490. if ((UINT)hdllAVIFILE <= (UINT)HINSTANCE_ERROR)
  491. hdllAVIFILE = NULL;
  492. #endif
  493. if (hdllAVIFILE == NULL) {
  494. hdllAVIFILE = (HMODULE)-1;
  495. DPF(("Failed to load AVIFILE dll - error %d\n", GetLastError()));
  496. return FALSE;
  497. }
  498. #ifdef USE_ISVALIDINTERFACE
  499. hdllCOMPOBJ = GetModuleHandle(szCOMPOBJ);
  500. Assert(hdllCOMPOBJ != NULL);
  501. (FARPROC)XIsValidInterface = GetProcAddress(hdllCOMPOBJ, szIsValidInterface);
  502. Assert(XIsValidInterface != NULL);
  503. #endif
  504. (FARPROC)XAVIFileInit = GetProcAddress(hdllAVIFILE, szAVIFileInit);
  505. (FARPROC)XAVIFileExit = GetProcAddress(hdllAVIFILE, szAVIFileExit);
  506. (FARPROC)XAVIFileOpen = GetProcAddress(hdllAVIFILE, szAVIFileOpen);
  507. (FARPROC)XAVIMakeFileFromStreams = GetProcAddress(hdllAVIFILE, szAVIMakeFileFromStreams);
  508. (FARPROC)XAVIStreamBeginStreaming = GetProcAddress(hdllAVIFILE, szAVIStreamBeginStreaming);
  509. (FARPROC)XAVIStreamEndStreaming = GetProcAddress(hdllAVIFILE, szAVIStreamEndStreaming);
  510. Assert(XAVIFileInit != NULL);
  511. Assert(XAVIFileExit != NULL);
  512. Assert(XAVIFileOpen != NULL);
  513. Assert(XAVIMakeFileFromStreams != NULL);
  514. }
  515. //
  516. // we need to call AVIFileInit() and AVIFileExit() for each task that
  517. // is using AVIFile or things will not work right.
  518. //
  519. if (!(npMCI->dwFlags & MCIAVI_USING_AVIFILE)) {
  520. npMCI->dwFlags |= MCIAVI_USING_AVIFILE;
  521. AVIFileInit(); // Should we check the return code?
  522. uAVIFILE++;
  523. }
  524. return TRUE;
  525. }
  526. // must be called from app thread too!
  527. /***************************************************************************
  528. *
  529. * @doc INTERNAL MCIAVI
  530. *
  531. * @api BOOL | FreeAVIFile | called to un-RTL to AVIFILE.DLL
  532. *
  533. * @rdesc TRUE means OK, otherwise error
  534. *
  535. ***************************************************************************/
  536. BOOL FAR FreeAVIFile(NPMCIGRAPHIC npMCI)
  537. {
  538. if (!(npMCI->dwFlags & MCIAVI_USING_AVIFILE))
  539. return FALSE;
  540. Assert(hdllAVIFILE != (HMODULE)-1 && hdllAVIFILE != NULL);
  541. // free this tasks use of AVIFile
  542. AVIFileExit();
  543. // if no more people using AVIFile let the DLLs go.
  544. Assert(uAVIFILE > 0);
  545. uAVIFILE--;
  546. if (uAVIFILE == 0) {
  547. FreeLibrary(hdllAVIFILE);
  548. hdllAVIFILE = NULL;
  549. #ifdef USE_ISVALIDINTERFACE
  550. hdllCOMPOBJ = NULL;
  551. #endif
  552. }
  553. }
  554. #endif /* USEAVIFILE */
  555. /***************************************************************************
  556. *
  557. * @doc INTERNAL MCIAVI
  558. *
  559. * @api BOOL | OpenRiffAVIFile | Open an RIFF AVI file
  560. *
  561. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  562. *
  563. * @parm BOOL | bForce | if FALSE, return error for files that
  564. * we think should be opened by AVIFILE instead.
  565. *
  566. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  567. *
  568. ***************************************************************************/
  569. STATICFN BOOL NEAR PASCAL OpenRiffAVIFile(NPMCIGRAPHIC npMCI, BOOL bForce)
  570. {
  571. HMMIO hmmio;
  572. HANDLE h = NULL;
  573. BOOL fRet = TRUE;
  574. MMIOINFO mmioInfo;
  575. MMCKINFO ckRIFF;
  576. MMCKINFO ckLIST;
  577. MMCKINFO ckRECORD;
  578. _fmemset(&mmioInfo, 0, sizeof(MMIOINFO));
  579. mmioInfo.htask = (HANDLE)npMCI->hCallingTask;
  580. hmmio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ | MMIO_DENYWRITE);
  581. if (hmmio == NULL)
  582. hmmio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ);
  583. if (!hmmio) {
  584. switch (mmioInfo.wErrorRet) {
  585. case MMIOERR_OUTOFMEMORY:
  586. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  587. break;
  588. case MMIOERR_FILENOTFOUND:
  589. case MMIOERR_CANNOTOPEN:
  590. default:
  591. npMCI->dwTaskError = MCIERR_FILE_NOT_FOUND;
  592. break;
  593. }
  594. fRet = FALSE;
  595. goto exit;
  596. }
  597. npMCI->hmmio = hmmio;
  598. /*
  599. ** Descend into RIFF file
  600. */
  601. if (mmioDescend(hmmio, &ckRIFF, NULL, 0) != 0) {
  602. goto ERROR_BADFILE;
  603. }
  604. /*
  605. * check for a 'QuickTime AVI' file, a QuickTime AVI file is a
  606. * QuickTime public movie with a AVI file in the 'mdat' atom.
  607. */
  608. if (ckRIFF.cksize == mmioFOURCC('m','d','a','t'))
  609. {
  610. DPF(("File is a QuickTime public movie\n"));
  611. /*
  612. * now the 'mdat' atom better be a RIFF/AVI or we cant handle it.
  613. */
  614. if (mmioDescend(hmmio, &ckRIFF, NULL, 0) != 0) {
  615. goto ERROR_BADFILE;
  616. }
  617. }
  618. /* Make sure it's a RIFF file */
  619. if (ckRIFF.ckid != FOURCC_RIFF) {
  620. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  621. goto ERROR_NOT_AVIFILE;
  622. }
  623. /* Otherwise, it should be an AVI file */
  624. if (ckRIFF.fccType != formtypeAVI) {
  625. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  626. goto ERROR_NOT_AVIFILE;
  627. }
  628. /*
  629. ** Descend into header LIST
  630. */
  631. ckLIST.fccType = listtypeAVIHEADER;
  632. if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) != 0) {
  633. goto ERROR_BADFILE;
  634. }
  635. /* Leave space at end of buffer for pad word */
  636. h = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, ckLIST.cksize -
  637. sizeof(DWORD) +
  638. sizeof(DWORD));
  639. if (!h) {
  640. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  641. return FALSE;
  642. }
  643. npMCI->lp = npMCI->lpBuffer = (LPSTR) GlobalLock(h);
  644. DPF(("Reading header list: %lu bytes.\n", ckLIST.cksize - sizeof(DWORD)));
  645. if (mmioRead(hmmio, npMCI->lp, ckLIST.cksize - sizeof(DWORD))
  646. != (LONG) (ckLIST.cksize - sizeof(DWORD))) {
  647. npMCI->dwTaskError = MCIERR_FILE_READ;
  648. goto ERROR_RETURN;
  649. }
  650. #ifdef USE_AVIFILE_FOR_NON_INT
  651. //
  652. // we check here for AVI RIFF files we dont want to handle with our
  653. // built in code, and want to pass on to AVIFILE.DLL
  654. //
  655. // we handle the following files:
  656. //
  657. // interleaved
  658. //
  659. // we pass on the following files to AVIFILE.DLL
  660. //
  661. // non-interleaved
  662. //
  663. // pretty simple right now, just interleaved non-interlaved
  664. // but could get as complex as you want.
  665. //
  666. // if there is a problem with avifile (eg dll not present) we will
  667. // be called with bForce TRUE: this means we should open it if at
  668. // all possible
  669. //
  670. if (!bForce) {
  671. MainAVIHeader FAR * lpHdr;
  672. lpHdr = (MainAVIHeader FAR *)((BYTE FAR *)npMCI->lp + 8);
  673. if (!(lpHdr->dwFlags & AVIF_ISINTERLEAVED) ||
  674. lpHdr->dwInitialFrames == 0) {
  675. DOUT("File is not interleaved, giving it to AVIFILE.DLL\n");
  676. goto ERROR_NOT_AVIFILE;
  677. }
  678. //
  679. // ok now we have a 1:1 interleved file.
  680. //
  681. // always use our code on a CD-ROM, but on other media...
  682. //
  683. switch (npMCI->uDriveType) {
  684. case DRIVE_CDROM:
  685. break;
  686. case DRIVE_REMOTE:
  687. case DRIVE_FIXED:
  688. case DRIVE_REMOVABLE:
  689. break;
  690. default:
  691. break;
  692. }
  693. }
  694. #endif
  695. if (PEEK_DWORD() == ckidAVIMAINHDR) {
  696. if (!ParseNewHeader(npMCI))
  697. goto ERROR_RETURN;
  698. } else {
  699. goto ERROR_BADFILE;
  700. }
  701. /* Ascend out of header LIST */
  702. if (mmioAscend(hmmio, &ckLIST, 0) != 0) {
  703. npMCI->dwTaskError = MCIERR_FILE_READ;
  704. goto ERROR_RETURN;
  705. }
  706. /* Initially, no frame has been drawn */
  707. npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
  708. /*
  709. ** Descend into big 'Movie LIST'
  710. */
  711. ckLIST.fccType = listtypeAVIMOVIE;
  712. if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) != 0) {
  713. goto ERROR_BADFILE;
  714. }
  715. npMCI->dwMovieListOffset = ckLIST.dwDataOffset;
  716. /* Calculate end of 'movi' list, in case we need to read the index */
  717. npMCI->dwBigListEnd = ckLIST.dwDataOffset + ckLIST.cksize +
  718. (ckLIST.cksize & 1);
  719. /*
  720. ** Descend into header of first chunk
  721. */
  722. if (mmioDescend(hmmio, &ckRECORD, &ckLIST, 0) != 0) {
  723. goto ERROR_BADFILE;
  724. }
  725. npMCI->dwFirstRecordType = ckRECORD.ckid;
  726. npMCI->dwFirstRecordSize = ckRECORD.cksize + 2 * sizeof(DWORD);
  727. npMCI->dwFirstRecordPosition = mmioSeek(hmmio, 0, SEEK_CUR);
  728. if (mmioAscend(hmmio, &ckRECORD, 0) != 0) {
  729. npMCI->dwTaskError = MCIERR_FILE_READ;
  730. goto ERROR_RETURN;
  731. }
  732. #ifdef DEBUG
  733. DPF2(("First record (%4.4s) 0x%lx bytes at position 0x%lx.\n",
  734. (LPSTR)&npMCI->dwFirstRecordType,
  735. npMCI->dwFirstRecordSize,
  736. npMCI->dwFirstRecordPosition));
  737. if (npMCI->dwFirstRecordPosition & 0x7ff) {
  738. DPF(("!!\n"));
  739. DPF(("!! This file is not properly aligned to a 2K boundary.\n"));
  740. DPF(("!! It may not play well from CD-ROM.\n"));
  741. DPF(("!!\n"));
  742. }
  743. #endif
  744. exit:
  745. if (!fRet)
  746. mciaviCloseFile(npMCI);
  747. if (h) {
  748. npMCI->lpBuffer = NULL;
  749. npMCI->dwBufferSize = 0L;
  750. GlobalUnlock(h);
  751. GlobalFree(h);
  752. }
  753. return fRet;
  754. ERROR_NOT_AVIFILE:
  755. #ifdef USEAVIFILE
  756. npMCI->dwTaskError = AVIERR_NOT_AVIFILE; // mark as not a AVI file
  757. goto ERROR_RETURN; // we will be called a second time
  758. #endif
  759. ERROR_BADFILE:
  760. npMCI->dwTaskError = MCIERR_INVALID_FILE; // something wrong with the file
  761. ERROR_RETURN:
  762. fRet = FALSE;
  763. goto exit;
  764. }
  765. /***************************************************************************
  766. * @doc INTERNAL MCIAVI
  767. *
  768. * @api BOOL | ParseNewHeader | 'nuf said
  769. *
  770. ***************************************************************************/
  771. STATICFN BOOL NEAR PASCAL ParseNewHeader(NPMCIGRAPHIC npMCI)
  772. {
  773. DWORD dwHeaderSize;
  774. MainAVIHeader FAR * lpHdr;
  775. int stream;
  776. if (GET_DWORD() != ckidAVIMAINHDR) {
  777. goto FileError;
  778. }
  779. dwHeaderSize = GET_DWORD(); /* Skip size */
  780. /* Now, we're pointing at the actual header */
  781. lpHdr = (MainAVIHeader FAR *) npMCI->lp;
  782. npMCI->lFrames = (LONG)lpHdr->dwTotalFrames;
  783. npMCI->dwMicroSecPerFrame = lpHdr->dwMicroSecPerFrame;
  784. npMCI->dwRate = 1000000;
  785. npMCI->dwScale = npMCI->dwMicroSecPerFrame;
  786. /* Reject some bad values */
  787. if (!lpHdr->dwStreams || lpHdr->dwStreams > 255 || !npMCI->lFrames) {
  788. goto FileError;
  789. }
  790. npMCI->streams = (int) lpHdr->dwStreams;
  791. npMCI->dwBytesPerSec = lpHdr->dwMaxBytesPerSec;
  792. npMCI->wEarlyRecords = (UINT) lpHdr->dwInitialFrames;
  793. npMCI->dwSuggestedBufferSize = lpHdr->dwSuggestedBufferSize;
  794. SetRect(&npMCI->rcMovie,0,0,(int)lpHdr->dwWidth,(int)lpHdr->dwHeight);
  795. npMCI->dwFlags |= MCIAVI_HASINDEX;
  796. if (!(lpHdr->dwFlags & AVIF_ISINTERLEAVED)) {
  797. DPF(("File is not interleaved.\n"));
  798. npMCI->dwFlags |= MCIAVI_NOTINTERLEAVED;
  799. }
  800. SKIP_BYTES(dwHeaderSize); /* Skip rest of chunk */
  801. npMCI->paStreamInfo = (STREAMINFO NEAR *)
  802. LocalAlloc(LPTR, npMCI->streams * sizeof(STREAMINFO));
  803. // !!! error check
  804. for (stream = 0; stream < npMCI->streams; stream++) {
  805. AVIStreamHeader FAR * lpStream;
  806. HPSTR hpNextChunk;
  807. STREAMINFO * psi = &npMCI->paStreamInfo[stream];
  808. if (GET_DWORD() != FOURCC_LIST) {
  809. goto FileError;
  810. }
  811. dwHeaderSize = GET_DWORD(); /* Skip size */
  812. hpNextChunk = npMCI->lp + (dwHeaderSize + (dwHeaderSize & 1));
  813. if (GET_DWORD() != listtypeSTREAMHEADER) {
  814. goto FileError;
  815. }
  816. /* Now, we're at the begging of the stream's header chunks. */
  817. if (GET_DWORD() != ckidSTREAMHEADER) {
  818. goto FileError;
  819. }
  820. dwHeaderSize = GET_DWORD(); /* Skip size */
  821. /* Now, we're pointing at the stream header */
  822. lpStream = (AVIStreamHeader FAR *) npMCI->lp;
  823. hmemcpy(&psi->sh, lpStream, min(dwHeaderSize, sizeof(psi->sh)));
  824. //
  825. // reject files with more than one video stream.
  826. //
  827. if (psi->sh.fccType == streamtypeVIDEO &&
  828. npMCI->nVideoStreams >= 1) {
  829. DPF(("File has multiple video streams, giving it to AVIFILE.DLL\n"));
  830. goto DontHandleThisFile;
  831. }
  832. SKIP_BYTES(dwHeaderSize);
  833. /* Read the format */
  834. if (GET_DWORD() != ckidSTREAMFORMAT) {
  835. goto FileError;
  836. }
  837. dwHeaderSize = GET_DWORD(); /* Skip size */
  838. if (dwHeaderSize > 16384L) {
  839. goto FileError;
  840. }
  841. psi->cbFormat = dwHeaderSize;
  842. psi->lpFormat = GlobalAllocPtr(GHND,dwHeaderSize);
  843. if (!psi->lpFormat) {
  844. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  845. return FALSE;
  846. }
  847. hmemcpy(psi->lpFormat, npMCI->lp, dwHeaderSize);
  848. SKIP_BYTES(dwHeaderSize);
  849. if (PEEK_DWORD() == ckidSTREAMHANDLERDATA) {
  850. GET_DWORD();
  851. dwHeaderSize = GET_DWORD(); /* Skip size */
  852. psi->cbData = dwHeaderSize;
  853. psi->lpData = GlobalAllocPtr(GHND,dwHeaderSize);
  854. if (!psi->lpData) {
  855. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  856. return FALSE;
  857. }
  858. hmemcpy(psi->lpData, npMCI->lp, dwHeaderSize);
  859. /* Skip to end of Data chunk */
  860. SKIP_BYTES(dwHeaderSize);
  861. } else {
  862. psi->cbData = 0;
  863. psi->lpData = NULL;
  864. }
  865. InitStream(npMCI, psi);
  866. npMCI->lp = hpNextChunk;
  867. }
  868. if (npMCI->dwTaskError) {
  869. return FALSE;
  870. }
  871. return TRUE;
  872. FileError:
  873. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  874. return FALSE;
  875. DontHandleThisFile:
  876. npMCI->dwTaskError = AVIERR_NOT_AVIFILE;
  877. return FALSE;
  878. }
  879. //
  880. // --- called on worker thread ---------------------------------------------
  881. //
  882. /***************************************************************************
  883. *
  884. * @doc INTERNAL MCIAVI
  885. *
  886. * @api BOOL | OpenFileInit | called after a file is opened to init things
  887. *
  888. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  889. *
  890. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  891. *
  892. ***************************************************************************/
  893. BOOL NEAR PASCAL OpenFileInit(NPMCIGRAPHIC npMCI)
  894. {
  895. int i;
  896. RECT rc;
  897. //
  898. // lets make sure we opened something.
  899. //
  900. if (npMCI->streams == 0)
  901. return FALSE;
  902. if (npMCI->nVideoStreams + npMCI->nAudioStreams + npMCI->nOtherStreams == 0)
  903. return FALSE;
  904. if (npMCI->nVideoStreams == 0)
  905. npMCI->dwFlags &= ~MCIAVI_SHOWVIDEO;
  906. if (npMCI->nAudioStreams == 0)
  907. npMCI->dwFlags &= ~MCIAVI_PLAYAUDIO; // No audio streams means silence
  908. if (IsNTWOW()) {
  909. if (!npMCI->pbiFormat) {
  910. // Although there are valid video streams we do not appear to have
  911. // access to a decompressor that knows what to do with them. Hence
  912. // if we are in NT/WOW we fail the open, the call will return to
  913. // 16 bit land and there might be a codec there that can cope
  914. return(FALSE);
  915. }
  916. }
  917. if (npMCI->nAudioStreams > 1) {
  918. UINT wLang;
  919. int stream;
  920. #ifndef _WIN32
  921. UINT (WINAPI * GetUserDefaultLangID)(void);
  922. UINT u;
  923. HANDLE hdll;
  924. u = SetErrorMode(SEM_NOOPENFILEERRORBOX);
  925. hdll = LoadLibrary(szOLENLSDLL);
  926. SetErrorMode(u);
  927. wLang = 0;
  928. if ((UINT)hdll > (UINT)HINSTANCE_ERROR)
  929. {
  930. if ((FARPROC) GetUserDefaultLangID = GetProcAddress(hdll, szOLENLSAPI)) {
  931. wLang = GetUserDefaultLangID();
  932. }
  933. FreeLibrary(hdll);
  934. }
  935. #else
  936. wLang = GetUserDefaultLangID();
  937. #endif
  938. DPF(("Current language: %x\n", wLang));
  939. if (wLang > 0) {
  940. for (stream = 0; stream < npMCI->streams; stream++) {
  941. if (SH(stream).fccType == streamtypeAUDIO) {
  942. if (!(SH(stream).dwFlags & STREAM_ENABLED))
  943. continue;
  944. if (SH(stream).wLanguage == wLang) {
  945. npMCI->nAudioStream = stream;
  946. npMCI->psiAudio = SI(stream);
  947. break;
  948. }
  949. }
  950. }
  951. }
  952. }
  953. if (npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) {
  954. npMCI->wEarlyRecords = npMCI->wEarlyVideo;
  955. }
  956. else {
  957. npMCI->wEarlyRecords = max(npMCI->wEarlyVideo, npMCI->wEarlyAudio);
  958. }
  959. if (npMCI->wEarlyRecords == 0 &&
  960. !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED)) {
  961. DPF(("Interleaved file with no audio skew?\n"));
  962. npMCI->dwFlags |= MCIAVI_NOTINTERLEAVED;
  963. }
  964. if (npMCI->dwFlags & MCIAVI_ANIMATEPALETTE) {
  965. DPF(("This AVI file has palette changes.\n"));
  966. if (npMCI->nVideoStreams > 1) {
  967. npMCI->dwFlags &= ~MCIAVI_ANIMATEPALETTE;
  968. DPF(("...But we are going to ignore them?\n"));
  969. }
  970. }
  971. //
  972. // this must be set
  973. //
  974. if (npMCI->dwSuggestedBufferSize == 0) {
  975. for (i=0; i<npMCI->streams; i++)
  976. npMCI->dwSuggestedBufferSize =
  977. max(SH(i).dwSuggestedBufferSize,npMCI->dwSuggestedBufferSize);
  978. }
  979. //
  980. // check all fields in the main header
  981. //
  982. if (npMCI->dwScale == 0 ||
  983. npMCI->dwRate == 0) {
  984. }
  985. ////will be set when header parsed
  986. ////npMCI->dwMicroSecPerFrame = muldiv32(npMCI->dwScale, 1000000, npMCI->dwRate);
  987. npMCI->dwPlayMicroSecPerFrame = npMCI->dwMicroSecPerFrame;
  988. #define COMMON_SCALE 10000
  989. //
  990. // convert the rate/scale into something that is normalized to 1000
  991. //
  992. npMCI->dwRate = muldiv32(npMCI->dwRate, COMMON_SCALE, npMCI->dwScale);
  993. npMCI->dwScale = COMMON_SCALE;
  994. //
  995. // walk all streams and fix them up.
  996. //
  997. for (i=0; i<npMCI->streams; i++) {
  998. STREAMINFO *psi = SI(i);
  999. LONG lStart;
  1000. LONG lEnd;
  1001. //
  1002. // convert the rate/scale into something that is normalized to 1000
  1003. //
  1004. psi->sh.dwRate = muldiv32(psi->sh.dwRate, COMMON_SCALE, psi->sh.dwScale);
  1005. psi->sh.dwScale = COMMON_SCALE;
  1006. //
  1007. // trim any streams that hang over the movie.
  1008. //
  1009. lStart = MovieToStream(psi, 0);
  1010. lEnd = MovieToStream(psi, npMCI->lFrames);
  1011. if ((LONG)(psi->sh.dwStart + psi->sh.dwLength) > lEnd) {
  1012. DPF(("Stream #%d is too long, was %ld now %ld\n", i,
  1013. psi->sh.dwLength, lEnd - psi->sh.dwStart));
  1014. psi->sh.dwLength = lEnd - psi->sh.dwStart;
  1015. }
  1016. }
  1017. //
  1018. // If this is WOW we had better check that there is an audio codec
  1019. // available. If not, we let the 16 bit side open the video.
  1020. //
  1021. if (IsNTWOW()) {
  1022. if (SetUpAudio(npMCI, FALSE)) return FALSE;
  1023. }
  1024. //
  1025. // fix up the movie rect
  1026. //
  1027. if (IsRectEmpty(&npMCI->rcMovie)) {
  1028. DPF2(("Movie rect is empty\n"));
  1029. SetRectEmpty(&rc);
  1030. for (i=0; i<npMCI->streams; i++) {
  1031. UnionRect(&rc,&rc,&SH(i).rcFrame);
  1032. }
  1033. npMCI->rcMovie = rc;
  1034. DPF2(("Movie rect was empty, now [%d, %d, %d, %d]\n", rc));
  1035. } else {
  1036. rc = npMCI->rcMovie;
  1037. }
  1038. // check we can access the interface ptr - the main reason for this
  1039. // is that if we have pasted this interface ptr, we cannot access it on
  1040. // the worker thread (which is where we are now). Clean up the error handling
  1041. // by actually detecting this case.
  1042. if (npMCI->pf) {
  1043. IAVIStream * ps;
  1044. long l;
  1045. if ((l = AVIFileGetStream(npMCI->pf, &ps, 0, 0)) != 0) {
  1046. DPF(("avifile err %d", l));
  1047. npMCI->dwTaskError = l;
  1048. return FALSE;
  1049. } else {
  1050. if (ps->lpVtbl->FindSample(ps, 0, FIND_FROM_START|FIND_NEXT|FIND_ANY) < 0) {
  1051. npMCI->dwTaskError = MCIERR_DEVICE_NOT_READY;
  1052. ps->lpVtbl->Release(ps);
  1053. return FALSE;
  1054. }
  1055. ps->lpVtbl->Release(ps);
  1056. }
  1057. }
  1058. //
  1059. // always read the index, so we can skip frames even on CD!
  1060. //
  1061. ReadIndex(npMCI);
  1062. DPF(("Key frames are every (on average): %ld frames (%ld ms)\n",npMCI->dwKeyFrameInfo, MovieToTime(npMCI->dwKeyFrameInfo)));
  1063. // force things to happen, in case we're re-loading
  1064. SetRectEmpty(&npMCI->rcSource);
  1065. SetRectEmpty(&npMCI->rcDest);
  1066. /* this will call DrawDibBegin() ... */
  1067. if (TryPutRect(npMCI, MCI_DGV_PUT_SOURCE, &rc, &npMCI->dwTaskError)) {
  1068. return FALSE;
  1069. }
  1070. /*
  1071. * also set the dest rect. This should be done
  1072. * by the WM_SIZE message sent during SetWindowToDefaultSize.
  1073. * On NT, the WM_SIZE message is not sent synchronously since it
  1074. * is an inter-thread sendmessage (the winproc is on the original thread
  1075. * whereas we are currently running on the avi thread). The winproc
  1076. * thread may well not get the WM_SIZE message until much too late, so
  1077. * set the dest rect here. Note: don't use ResetDestRect since that
  1078. * also relies on the window size, which is not set yet.
  1079. */
  1080. /* double frame size of destination if zoom by 2 */
  1081. if (npMCI->dwOptionFlags & MCIAVIO_ZOOMBY2)
  1082. SetRect(&rc, 0, 0, rc.right*2, rc.bottom*2);
  1083. if (TryPutRect(npMCI, MCI_DGV_PUT_DESTINATION, &rc, &npMCI->dwTaskError)) {
  1084. return FALSE;
  1085. }
  1086. if (npMCI->dwTaskError) {
  1087. return FALSE;
  1088. }
  1089. //
  1090. // size the window and things.
  1091. //
  1092. SetWindowToDefaultSize(npMCI, FALSE);
  1093. DrawBegin(npMCI, NULL);
  1094. return TRUE;
  1095. }
  1096. /***************************************************************************
  1097. *
  1098. * @doc INTERNAL MCIAVI
  1099. *
  1100. * @api BOOL | mciaviCloseFile | Close an AVI file.
  1101. *
  1102. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1103. *
  1104. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  1105. *
  1106. ***************************************************************************/
  1107. BOOL FAR PASCAL mciaviCloseFile (NPMCIGRAPHIC npMCI)
  1108. {
  1109. if (!npMCI)
  1110. return FALSE;
  1111. if (npMCI->lpMMIOBuffer) {
  1112. GlobalFreePtr(npMCI->lpMMIOBuffer);
  1113. npMCI->lpMMIOBuffer = NULL;
  1114. }
  1115. npMCI->hicDraw = NULL;
  1116. if (npMCI->hicDrawDefault) {
  1117. if (npMCI->hicDrawDefault != (HIC) -1)
  1118. ICClose(npMCI->hicDrawDefault);
  1119. npMCI->hicDrawDefault = NULL;
  1120. }
  1121. if (npMCI->hicDrawFull) {
  1122. if (npMCI->hicDrawFull != (HIC) -1)
  1123. ICClose(npMCI->hicDrawFull);
  1124. npMCI->hicDrawFull = NULL;
  1125. }
  1126. if (npMCI->hicDecompress) {
  1127. // !!! What if we never began it?
  1128. ICDecompressEnd(npMCI->hicDecompress);
  1129. ICClose(npMCI->hicDecompress);
  1130. npMCI->hicDecompress = NULL;
  1131. }
  1132. if (npMCI->hicInternal) {
  1133. ICClose(npMCI->hicInternal);
  1134. npMCI->hicInternal = NULL;
  1135. }
  1136. if (npMCI->hicInternalFull) {
  1137. ICClose(npMCI->hicInternalFull);
  1138. npMCI->hicInternalFull = NULL;
  1139. }
  1140. if (npMCI->hmmio) {
  1141. mmioClose(npMCI->hmmio, 0);
  1142. npMCI->hmmio = NULL;
  1143. }
  1144. if (npMCI->hmmioAudio) {
  1145. mmioClose(npMCI->hmmioAudio, 0);
  1146. npMCI->hmmioAudio = NULL;
  1147. }
  1148. if (npMCI->pWF) {
  1149. LocalFree((HANDLE) npMCI->pWF);
  1150. npMCI->pWF = NULL;
  1151. }
  1152. if (npMCI->pbiFormat) {
  1153. GlobalFreePtr(npMCI->pbiFormat);
  1154. npMCI->pbiFormat = NULL;
  1155. }
  1156. // if (npMCI->hpal) {
  1157. // DeleteObject(npMCI->hpal);
  1158. // npMCI->hpal = NULL;
  1159. // }
  1160. if (npMCI->hpDecompress) {
  1161. GlobalFreePtr(npMCI->hpDecompress);
  1162. npMCI->hpDecompress = NULL;
  1163. }
  1164. if (npMCI->hpIndex) {
  1165. GlobalFreePtr(npMCI->hpIndex);
  1166. npMCI->hpIndex = NULL;
  1167. }
  1168. if (npMCI->hpFrameIndex) {
  1169. GlobalUnlock(npMCI->hgFrameIndex);
  1170. GlobalFree(npMCI->hgFrameIndex);
  1171. npMCI->hpFrameIndex = NULL;
  1172. npMCI->hgFrameIndex = 0;
  1173. }
  1174. if (npMCI->pVolumeTable) {
  1175. LocalFree((HLOCAL)npMCI->pVolumeTable);
  1176. npMCI->pVolumeTable = NULL;
  1177. }
  1178. #ifdef USEAVIFILE
  1179. if (npMCI->pf) {
  1180. AVIFileClose(npMCI->pf);
  1181. npMCI->pf = NULL;
  1182. }
  1183. #endif
  1184. if (npMCI->paStreamInfo) {
  1185. int i;
  1186. for (i = 0; i < npMCI->streams; i++) {
  1187. CloseStream(npMCI, &npMCI->paStreamInfo[i]);
  1188. }
  1189. LocalFree((HLOCAL)npMCI->paStreamInfo);
  1190. npMCI->paStreamInfo = NULL;
  1191. }
  1192. npMCI->streams = 0;
  1193. npMCI->nAudioStreams = 0;
  1194. npMCI->nVideoStreams = 0;
  1195. npMCI->nErrorStreams = 0;
  1196. npMCI->nOtherStreams = 0;
  1197. npMCI->wEarlyVideo = 0;
  1198. npMCI->wEarlyAudio = 0;
  1199. npMCI->wEarlyRecords = 0;
  1200. //!!! I bet we need to clear more
  1201. npMCI->dwFlags &= ~(MCIAVI_NOTINTERLEAVED |
  1202. MCIAVI_ANIMATEPALETTE |
  1203. MCIAVI_CANDRAW |
  1204. MCIAVI_HASINDEX);
  1205. return TRUE;
  1206. }
  1207. /***************************************************************************
  1208. *
  1209. * @doc INTERNAL MCIAVI
  1210. *
  1211. * @api BOOL | CloseStream | Close an StreamAVI file.
  1212. *
  1213. ***************************************************************************/
  1214. STATICFN void NEAR PASCAL CloseStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1215. {
  1216. psi->dwFlags &= ~STREAM_ENABLED;
  1217. ////psi->sh.fccType = 0;
  1218. ////psi->sh.fccHandler = 0;
  1219. if (psi->lpFormat) {
  1220. GlobalFreePtr(psi->lpFormat);
  1221. psi->lpFormat = NULL;
  1222. }
  1223. if (psi->lpData) {
  1224. GlobalFreePtr(psi->lpData);
  1225. psi->lpData = NULL;
  1226. }
  1227. if (psi->hicDraw) {
  1228. ICClose(psi->hicDraw);
  1229. psi->hicDraw = NULL;
  1230. }
  1231. #ifdef USEAVIFILE
  1232. if (psi->ps) {
  1233. AVIStreamClose(psi->ps);
  1234. psi->ps = NULL;
  1235. }
  1236. #endif
  1237. }
  1238. /***************************************************************************
  1239. *
  1240. * @doc INTERNAL MCIAVI
  1241. *
  1242. * @api BOOL | InitStream | initialize a stream
  1243. *
  1244. ***************************************************************************/
  1245. STATICFN BOOL NEAR PASCAL InitStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1246. {
  1247. BOOL f;
  1248. //
  1249. // set flags
  1250. //
  1251. if (psi->sh.dwFlags & AVISF_VIDEO_PALCHANGES)
  1252. psi->dwFlags |= STREAM_PALCHANGES;
  1253. psi->lStart = (LONG)psi->sh.dwStart;
  1254. psi->lEnd = (LONG)psi->sh.dwStart + psi->sh.dwLength;
  1255. if (psi->sh.fccType == streamtypeVIDEO &&
  1256. !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED))
  1257. psi->lStart -= (LONG)psi->sh.dwInitialFrames;
  1258. switch(psi->sh.fccType) {
  1259. case streamtypeVIDEO:
  1260. f = InitVideoStream(npMCI, psi);
  1261. break;
  1262. case streamtypeAUDIO:
  1263. f = InitAudioStream(npMCI, psi);
  1264. break;
  1265. default:
  1266. f = InitOtherStream(npMCI, psi);
  1267. break;
  1268. }
  1269. if (!f) {
  1270. psi->dwFlags |= STREAM_ERROR;
  1271. npMCI->nErrorStreams++;
  1272. CloseStream(npMCI, psi);
  1273. }
  1274. //
  1275. // disable the stream if the file header says to
  1276. //
  1277. if (psi->sh.dwFlags & AVISF_DISABLED) {
  1278. psi->dwFlags &= ~STREAM_ENABLED;
  1279. }
  1280. return f;
  1281. }
  1282. /***************************************************************************
  1283. *
  1284. * @doc INTERNAL MCIAVI
  1285. *
  1286. * @api BOOL | InitVideoStream | initialize a video stream
  1287. *
  1288. ***************************************************************************/
  1289. STATICFN BOOL NEAR PASCAL InitVideoStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1290. {
  1291. LPBITMAPINFOHEADER lpbi;
  1292. int stream = psi - npMCI->paStreamInfo;
  1293. npMCI->wEarlyVideo = (UINT)psi->sh.dwInitialFrames;
  1294. if (psi->sh.dwFlags & AVISF_VIDEO_PALCHANGES) {
  1295. //!!! is this right.
  1296. npMCI->dwFlags |= MCIAVI_ANIMATEPALETTE;
  1297. }
  1298. if (IsRectBogus(&psi->sh.rcFrame)) {
  1299. DPF(("BOGUS Stream rectangle [%d %d %d %d]\n", psi->sh.rcFrame));
  1300. SetRectEmpty(&psi->sh.rcFrame);
  1301. }
  1302. // In case the rectangle is totally wrong, chop it down to size....
  1303. // !!! What if the user _wants_ a zero-size RECT?
  1304. IntersectRect(&psi->sh.rcFrame, &psi->sh.rcFrame, &npMCI->rcMovie);
  1305. if (IsRectEmpty(&psi->sh.rcFrame)) {
  1306. DPF2(("Video stream rect is empty, correcting\n"));
  1307. SetRect(&psi->sh.rcFrame, 0, 0,
  1308. (int)((LPBITMAPINFOHEADER)psi->lpFormat)->biWidth,
  1309. (int)((LPBITMAPINFOHEADER)psi->lpFormat)->biHeight);
  1310. }
  1311. //
  1312. // make sure the biCompression is right for RLE files.
  1313. //
  1314. lpbi = (LPBITMAPINFOHEADER)psi->lpFormat;
  1315. if (psi->sh.fccHandler == 0) {
  1316. if (lpbi->biCompression == 0)
  1317. psi->sh.fccHandler = comptypeDIB;
  1318. if (lpbi->biCompression == BI_RLE8 && lpbi->biBitCount == 8)
  1319. psi->sh.fccHandler = comptypeRLE;
  1320. if (lpbi->biCompression > 256)
  1321. psi->sh.fccHandler = lpbi->biCompression;
  1322. }
  1323. if (lpbi->biCompression <= BI_RLE8 && lpbi->biBitCount == 8) {
  1324. if (psi->sh.fccHandler == comptypeRLE0 ||
  1325. psi->sh.fccHandler == comptypeRLE)
  1326. lpbi->biCompression = BI_RLE8;
  1327. // Assuming a DIB handler has RGB data will blow up files that have RLE data.
  1328. // Unfortunately, VidEdit writes out stupid files like this.
  1329. // if (psi->sh.fccHandler == comptypeDIB)
  1330. // lpbi->biCompression = BI_RGB;
  1331. }
  1332. //
  1333. // make sure the color table is set to the right size
  1334. //
  1335. if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
  1336. lpbi->biClrUsed = (1 << (int)lpbi->biBitCount);
  1337. //
  1338. // try to open draw handler
  1339. //
  1340. if (psi->sh.fccHandler) {
  1341. psi->hicDraw = ICDrawOpen(psi->sh.fccType,psi->sh.fccHandler,psi->lpFormat);
  1342. if (psi->hicDraw)
  1343. DPF(("Opened draw handler %4.4hs:%4.4hs (Stream %d)\n", (LPSTR)&psi->sh.fccType,(LPSTR)&psi->sh.fccHandler, stream));
  1344. else
  1345. DPF(("Failed to open draw handler for %4.4hs:%4.4hs (Stream %d)\n", (LPSTR)&psi->sh.fccType,(LPSTR)&psi->sh.fccHandler, stream));
  1346. }
  1347. //
  1348. // one video stream is the master, he controls the palette etc
  1349. // for lack of a better default the first valid (i.e. one we can
  1350. // handle) video stream will
  1351. // become the master.
  1352. //
  1353. if (npMCI->pbiFormat == NULL) {
  1354. npMCI->nVideoStream = stream;
  1355. npMCI->psiVideo = psi;
  1356. npMCI->pbiFormat = (LPBITMAPINFOHEADER)
  1357. GlobalAllocPtr(GMEM_MOVEABLE, psi->cbFormat);
  1358. if (!npMCI->pbiFormat) {
  1359. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  1360. return FALSE;
  1361. }
  1362. //
  1363. // copy the entire format over
  1364. //
  1365. hmemcpy(npMCI->pbiFormat,psi->lpFormat,psi->cbFormat);
  1366. npMCI->bih = *npMCI->pbiFormat;
  1367. npMCI->bih.biSize = sizeof(BITMAPINFOHEADER);
  1368. npMCI->bih.biCompression = BI_RGB;
  1369. if (npMCI->bih.biClrUsed) {
  1370. /* save the original colors. */
  1371. hmemcpy(npMCI->argb, (LPBYTE)npMCI->pbiFormat + npMCI->pbiFormat->biSize,
  1372. (int)npMCI->bih.biClrUsed * sizeof(RGBQUAD));
  1373. hmemcpy(npMCI->argbOriginal, (LPSTR) npMCI->pbiFormat + npMCI->pbiFormat->biSize,
  1374. (int)npMCI->bih.biClrUsed * sizeof(RGBQUAD));
  1375. }
  1376. //
  1377. // now open the decompressor, try fastdecompress if it will do it.
  1378. //
  1379. npMCI->hicDecompress = ICLocate(ICTYPE_VIDEO,psi->sh.fccHandler,
  1380. psi->lpFormat,NULL,ICMODE_FASTDECOMPRESS);
  1381. // fast decompress may not be supported
  1382. if (npMCI->hicDecompress == NULL) {
  1383. npMCI->hicDecompress = ICDecompressOpen(ICTYPE_VIDEO,
  1384. psi->sh.fccHandler,psi->lpFormat,NULL);
  1385. }
  1386. //
  1387. // set any state data.
  1388. //
  1389. if (npMCI->hicDecompress && psi->cbData) {
  1390. ICSetState(npMCI->hicDecompress, psi->lpData, psi->cbData);
  1391. }
  1392. if (psi->hicDraw == NULL && npMCI->hicDecompress == NULL &&
  1393. psi->sh.fccHandler != comptypeRLE0 &&
  1394. psi->sh.fccHandler != comptypeNONE &&
  1395. psi->sh.fccHandler != comptypeDIB &&
  1396. psi->sh.fccHandler != comptypeRLE &&
  1397. psi->sh.fccHandler != 0) {
  1398. DPF(("Unable to open compressor '%4.4hs'!!!\n", (LPSTR) &psi->sh.fccHandler));
  1399. npMCI->nVideoStream = -1;
  1400. npMCI->psiVideo = NULL;
  1401. GlobalFreePtr(npMCI->pbiFormat);
  1402. npMCI->pbiFormat = NULL;
  1403. //
  1404. // we would like to return a custom error but by the time the
  1405. // user (application) comes to retrieve the error text MCI will
  1406. // have unloaded us (because the open failed) and therefore
  1407. // will not be able to get our specific error text.
  1408. // Hence we return a bogus generic error.
  1409. //
  1410. #ifdef _WIN32
  1411. SetLastError(MCIERR_AVI_NOCOMPRESSOR);
  1412. #endif
  1413. if (npMCI->streams == 1) // this is the only stream
  1414. npMCI->dwTaskError = MMSYSERR_NODRIVER; // MCIERR_AVI_NOCOMPRESSOR;
  1415. return FALSE; // cant load this video stream
  1416. }
  1417. }
  1418. else {
  1419. //
  1420. // this is not the default video stream find a draw handler that
  1421. // can deal with the stream.
  1422. //
  1423. //
  1424. // try VIDS.DRAW
  1425. //
  1426. // if that fails open a draw handler not-specific to the format
  1427. //
  1428. if (psi->hicDraw == NULL) {
  1429. psi->hicDraw = ICOpen(psi->sh.fccType,FOURCC_AVIDraw,ICMODE_DRAW);
  1430. if (psi->hicDraw)
  1431. DOUT("Opened draw handler VIDS.DRAW\n");
  1432. if (psi->hicDraw && ICDrawQuery(psi->hicDraw,psi->lpFormat) != ICERR_OK) {
  1433. DOUT("Closing VIDS.DRAW because it cant handle this format");
  1434. ICClose(psi->hicDraw);
  1435. psi->hicDraw = NULL;
  1436. }
  1437. }
  1438. //
  1439. // if that fails open our internal handler.
  1440. //
  1441. if (psi->hicDraw == NULL) {
  1442. psi->hicDraw = ICOpenFunction(psi->sh.fccType,
  1443. FOURCC_AVIDraw,ICMODE_DRAW,(FARPROC)ICAVIDrawProc);
  1444. if (psi->hicDraw)
  1445. DOUT("Opened Internal draw handler\n");
  1446. }
  1447. }
  1448. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1449. psi->dwFlags |= STREAM_VIDEO; // is a video stream
  1450. psi->dwFlags |= STREAM_ENABLED;
  1451. npMCI->nVideoStreams++;
  1452. return TRUE;
  1453. }
  1454. /***************************************************************************
  1455. *
  1456. * @doc INTERNAL MCIAVI
  1457. *
  1458. * @api BOOL | InitAudioStream | initialize a audio stream
  1459. *
  1460. ***************************************************************************/
  1461. STATICFN BOOL NEAR PASCAL InitAudioStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1462. {
  1463. int stream = psi - npMCI->paStreamInfo;
  1464. LPWAVEFORMAT pwf;
  1465. npMCI->wEarlyAudio = (UINT)psi->sh.dwInitialFrames;
  1466. pwf = (LPWAVEFORMAT)psi->lpFormat;
  1467. if (pwf->nChannels == 0 || pwf->nSamplesPerSec == 0) {
  1468. return FALSE;
  1469. }
  1470. if (pwf->wFormatTag == WAVE_FORMAT_PCM) {
  1471. pwf->nBlockAlign = pwf->nChannels *
  1472. ((((LPPCMWAVEFORMAT)pwf)->wBitsPerSample + 7) / 8);
  1473. pwf->nAvgBytesPerSec = pwf->nBlockAlign * pwf->nSamplesPerSec;
  1474. }
  1475. psi->sh.dwSampleSize = pwf->nBlockAlign;
  1476. psi->dwFlags |= STREAM_AUDIO; // audio stream
  1477. psi->dwFlags |= STREAM_ENABLED; // enabled by default.
  1478. //
  1479. // make sure dwRate and dwScale are right
  1480. // dwRate/dwScale should be blocks/sec
  1481. //
  1482. Assert(muldiv32(pwf->nAvgBytesPerSec,1000,pwf->nBlockAlign) ==
  1483. muldiv32(psi->sh.dwRate, 1000, psi->sh.dwScale));
  1484. //
  1485. // just to be safe set these ourself to the right value.
  1486. //
  1487. psi->sh.dwRate = pwf->nAvgBytesPerSec;
  1488. psi->sh.dwScale = pwf->nBlockAlign;
  1489. //
  1490. // only one audio stream can be active at once
  1491. // for lack of a better default the first audio stream will
  1492. // become the active one.
  1493. //
  1494. if (npMCI->nAudioStreams == 0) {
  1495. npMCI->nAudioStream = stream;
  1496. npMCI->psiAudio = psi;
  1497. }
  1498. npMCI->nAudioStreams++;
  1499. return TRUE;
  1500. }
  1501. /***************************************************************************
  1502. *
  1503. * @doc INTERNAL MCIAVI
  1504. *
  1505. * @api BOOL | InitOtherStream | initialize a random stream
  1506. *
  1507. ***************************************************************************/
  1508. STATICFN BOOL NEAR PASCAL InitOtherStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1509. {
  1510. int stream = psi - npMCI->paStreamInfo;
  1511. /* Open the specified video compressor */
  1512. psi->hicDraw = ICDrawOpen(psi->sh.fccType,psi->sh.fccHandler,psi->lpFormat);
  1513. if (psi->hicDraw == NULL) {
  1514. DPF(("Unable to play stream!\n"));
  1515. return FALSE;
  1516. }
  1517. if (psi->cbData > 0) {
  1518. ICSetState(psi->hicDraw, psi->lpData, psi->cbData);
  1519. }
  1520. psi->dwFlags |= STREAM_ENABLED;
  1521. ////psi->dwFlags |= STREAM_OTHER;
  1522. npMCI->nOtherStreams++;
  1523. return TRUE;
  1524. }
  1525. /***************************************************************************
  1526. *
  1527. * @doc INTERNAL MCIAVI
  1528. *
  1529. * @api BOOL | CleanIndex | This function cleans up the index loaded by
  1530. * ReadIndex() it does the following when cleaning up the index:
  1531. *
  1532. * converts all offsets to be absolute
  1533. *
  1534. * converts "alpha" format index's into new format.
  1535. *
  1536. * computes the max buffer size needed to read this file.
  1537. *
  1538. ***************************************************************************/
  1539. INLINE static BOOL NEAR CleanIndex(NPMCIGRAPHIC npMCI)
  1540. {
  1541. LONG lScan;
  1542. AVIINDEXENTRY FAR * px;
  1543. AVIINDEXENTRY FAR * pxRec=NULL;
  1544. DWORD lIndexAdjust;
  1545. Assert(npMCI->hpIndex != NULL);
  1546. px = (AVIINDEXENTRY FAR *)npMCI->hpIndex;
  1547. #ifdef ALPHAFILES
  1548. if (npMCI->dwFlags & MCIAVI_USINGALPHAFORMAT)
  1549. lIndexAdjust = 0;
  1550. else
  1551. #endif
  1552. if (// (avihdr.dwFlags & AVIF_MUSTUSEINDEX) ||
  1553. (px->dwChunkOffset < 100))
  1554. lIndexAdjust = npMCI->dwMovieListOffset;
  1555. else
  1556. lIndexAdjust = (npMCI->dwMovieListOffset + sizeof(DWORD)) -
  1557. px->dwChunkOffset;
  1558. //!!! only compute this for the video stream! (or interleaved...)
  1559. npMCI->dwSuggestedBufferSize = 0; // lets get this exactly right
  1560. DPF(("Adjusting index by %ld bytes....\n", lIndexAdjust));
  1561. /* Can we do anything to see if the index is valid? */
  1562. for (lScan = 0; lScan < (LONG)npMCI->macIndex;
  1563. lScan++, ++((AVIINDEXENTRY _huge *)px)) {
  1564. DWORD ckid;
  1565. //
  1566. // adjust the offset to be absolute
  1567. //
  1568. px->dwChunkOffset += lIndexAdjust;
  1569. // get ckid
  1570. ckid = px->ckid;
  1571. //
  1572. // make sure the buffer size is right, ignore audio chunks because
  1573. // they are either in a 'rec' or we will be reading them into
  1574. // internal buffers not the main buffer.365
  1575. //
  1576. if (((npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) ||
  1577. ckid == listtypeAVIRECORD) &&
  1578. TWOCCFromFOURCC(ckid) != cktypeWAVEbytes) {
  1579. if (px->dwChunkLength + 8 > npMCI->dwSuggestedBufferSize)
  1580. npMCI->dwSuggestedBufferSize = px->dwChunkLength + 12;
  1581. }
  1582. #ifdef ALPHAFILES
  1583. //
  1584. // convert a "old" index to a new index
  1585. //
  1586. if (npMCI->dwFlags & MCIAVI_USINGALPHAFORMAT) {
  1587. switch(ckid) {
  1588. case ckidDIBbits:
  1589. px->dwFlags |= AVIIF_KEYFRAME;
  1590. px->ckid = MAKEAVICKID(cktypeDIBbits, 0);
  1591. break;
  1592. case ckidDIBcompressed:
  1593. px->ckid = MAKEAVICKID(cktypeDIBcompressed, 0);
  1594. break;
  1595. case ckidDIBhalfframe:
  1596. px->ckid = MAKEAVICKID(cktypeDIBhalf, 0);
  1597. break;
  1598. case ckidPALchange:
  1599. px->ckid = MAKEAVICKID(cktypePALchange, 0);
  1600. break;
  1601. case ckidWAVEbytes:
  1602. px->ckid = MAKEAVICKID(cktypeWAVEbytes, 1);
  1603. break;
  1604. case ckidWAVEsilence:
  1605. px->ckid = MAKEAVICKID(cktypeWAVEsilence, 1);
  1606. break;
  1607. case ckidAVIPADDING:
  1608. case ckidOLDPADDING:
  1609. px->ckid = ckidAVIPADDING;
  1610. break;
  1611. }
  1612. ckid = px->ckid;
  1613. }
  1614. #endif
  1615. //
  1616. // do special things with the video stream.
  1617. //
  1618. if (StreamFromFOURCC(ckid) == (UINT)npMCI->nVideoStream) {
  1619. //
  1620. // fix up bogus index's by adding any missing AVIIF_KEYFRAME
  1621. // bits. ie this only applies for silly RLE files.
  1622. //
  1623. if (TWOCCFromFOURCC(ckid) == cktypeDIBbits &&
  1624. VIDFMT(npMCI->nVideoStream)->biCompression <= BI_RLE8)
  1625. px->dwFlags |= AVIIF_KEYFRAME;
  1626. //
  1627. // for video streams, make sure the palette changes are marked
  1628. // as a no time chunk
  1629. //
  1630. if (TWOCCFromFOURCC(ckid) == cktypePALchange)
  1631. px->dwFlags |= AVIIF_NOTIME/*|AVIIF_PALCHANGE*/;
  1632. //
  1633. // make sure the 'REC ' list has the right flags.
  1634. //
  1635. if (pxRec) {
  1636. if ((px->dwFlags & AVIIF_KEYFRAME) !=
  1637. (pxRec->dwFlags & AVIIF_KEYFRAME)) {
  1638. // Record list does not have correct flags
  1639. pxRec->dwFlags &= ~AVIIF_KEYFRAME;
  1640. pxRec->dwFlags |= (px->dwFlags & AVIIF_KEYFRAME);
  1641. }
  1642. }
  1643. }
  1644. if (ckid == listtypeAVIRECORD) {
  1645. pxRec = px;
  1646. if (npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) {
  1647. DPF(("Non interleaved file with a 'REC ' in it?\n"));
  1648. npMCI->wEarlyRecords = max(npMCI->wEarlyVideo, npMCI->wEarlyAudio);
  1649. if (npMCI->wEarlyRecords > 0) {
  1650. DPF(("Interlaved file with bad header\n"));
  1651. npMCI->dwFlags &= ~MCIAVI_NOTINTERLEAVED;
  1652. }
  1653. }
  1654. }
  1655. }
  1656. return TRUE;
  1657. }
  1658. /***************************************************************************
  1659. *
  1660. * @doc INTERNAL MCIAVI
  1661. *
  1662. * @api BOOL | MakeFrameIndex | makes the frame index
  1663. *
  1664. * the frame index is a array of AVIFRAMEINDEX entries, one for each
  1665. * frame in the movie. using the frame index we can easily find
  1666. * a given frame, along with it's keyframe and palette.
  1667. *
  1668. ***************************************************************************/
  1669. static BOOL NEAR MakeFrameIndex(NPMCIGRAPHIC npMCI)
  1670. {
  1671. LONG nFrames;
  1672. LONG iFrameStart;
  1673. LONG iFrame;
  1674. LONG iKeyFrame;
  1675. LONG nKeyFrames;
  1676. LONG iScan;
  1677. LONG iNewIndex;
  1678. LONG iPalette;
  1679. BOOL fInterleaved;
  1680. DWORD ckid;
  1681. STREAMINFO *psi;
  1682. AVIINDEXENTRY _huge * pNewIndex;
  1683. AVIINDEXENTRY _huge * pIndexEntry;
  1684. AVIFRAMEINDEX _huge * pFrameIndex;
  1685. if (npMCI->nVideoStreams == 0)
  1686. return TRUE;
  1687. if (npMCI->hpFrameIndex != NULL)
  1688. return TRUE;
  1689. psi = npMCI->psiVideo;
  1690. Assert(psi != NULL);
  1691. fInterleaved = !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED);
  1692. if (fInterleaved &&
  1693. muldiv32(npMCI->dwRate, 1000, npMCI->dwScale) !=
  1694. muldiv32(psi->sh.dwRate, 1000, psi->sh.dwScale)) {
  1695. //
  1696. // master video stream should match the movie rate!
  1697. //
  1698. AssertSz(FALSE, "Video stream differnet rate than movie");
  1699. npMCI->dwRate = psi->sh.dwRate;
  1700. npMCI->dwScale = psi->sh.dwScale;
  1701. }
  1702. if (fInterleaved)
  1703. iFrameStart = -(LONG)npMCI->wEarlyRecords;
  1704. else
  1705. iFrameStart = -(LONG)npMCI->wEarlyVideo;
  1706. nFrames = npMCI->lFrames - iFrameStart;
  1707. // hpFrameIndex is modified to point partway into the block.
  1708. // therefore we need to remember the handle of the block so we
  1709. // can free it correctly (esp on NT)
  1710. if ( (npMCI->hgFrameIndex = GlobalAlloc(GMEM_SHARE|GHND, (DWORD)(nFrames+1) * sizeof(AVIFRAMEINDEX)))
  1711. && (npMCI->hpFrameIndex = GlobalLock(npMCI->hgFrameIndex))) {
  1712. } else {
  1713. // We failed to allocate, or we failed to lock down. Clean up
  1714. // and return an error.
  1715. if (npMCI->hgFrameIndex) {
  1716. GlobalFree(npMCI->hgFrameIndex);
  1717. npMCI->hgFrameIndex = 0;
  1718. }
  1719. DPF(("Couldn't allocate memory for frame index!\n"));
  1720. return FALSE;
  1721. }
  1722. //
  1723. // do this so we can just index the array with the frame number
  1724. // (positive or neg)
  1725. //
  1726. npMCI->hpFrameIndex += (-iFrameStart);
  1727. pFrameIndex = npMCI->hpFrameIndex;
  1728. iFrame = iFrameStart;
  1729. iKeyFrame = -(LONG)npMCI->wEarlyVideo; // iFrameStart;
  1730. iNewIndex = 0;
  1731. iPalette = -1; // first palette
  1732. nKeyFrames= 0;
  1733. #ifdef USEAVIFILE
  1734. if (npMCI->pf) {
  1735. PAVISTREAM ps = SI(npMCI->nVideoStream)->ps;
  1736. for (iFrame = 0; iFrame < npMCI->lFrames; iFrame++) {
  1737. LONG iKey;
  1738. iKey = AVIStreamFindSample(ps,iFrame,FIND_PREV|FIND_KEY);
  1739. iPalette = AVIStreamFindSample(ps,iFrame,FIND_PREV|FIND_FORMAT);
  1740. if (iKey != -1)
  1741. iKeyFrame = iKey;
  1742. if (iPalette == -1)
  1743. iPalette = 0;
  1744. pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
  1745. pFrameIndex[iFrame].iNextKey = 0;
  1746. pFrameIndex[iFrame].iPalette = (WORD)iPalette;
  1747. pFrameIndex[iFrame].dwOffset = 0;
  1748. pFrameIndex[iFrame].dwLength = 0;
  1749. Assert(iPalette <= 0xFFFF);
  1750. if (iFrame - iKeyFrame > 0xFFFF) {
  1751. //!!! we need to set a flag!
  1752. //!!! we need to throw out the index!
  1753. AssertSz(FALSE, "File has too few key frames");
  1754. pFrameIndex[iFrame].iPrevKey = 0;
  1755. }
  1756. }
  1757. goto ack;
  1758. }
  1759. #endif
  1760. Assert(npMCI->hpIndex != NULL);
  1761. Assert(npMCI->macIndex != 0L);
  1762. pNewIndex = npMCI->hpIndex;
  1763. pIndexEntry = npMCI->hpIndex;
  1764. for (iScan = 0; iScan < (LONG)npMCI->macIndex; iScan++, pIndexEntry++) {
  1765. ckid = pIndexEntry->ckid;
  1766. //
  1767. // check for palette changes.
  1768. //
  1769. if (StreamFromFOURCC(ckid) == (UINT)npMCI->nVideoStream &&
  1770. TWOCCFromFOURCC(ckid) == cktypePALchange) {
  1771. iPalette = iNewIndex;
  1772. pNewIndex[iNewIndex++] = *pIndexEntry;
  1773. if (fInterleaved)
  1774. pFrameIndex[iFrame-1].iPalette = (WORD)iPalette;
  1775. }
  1776. //
  1777. // remove the video stream from the master index
  1778. //
  1779. if ((ckid != listtypeAVIRECORD) &&
  1780. (StreamFromFOURCC(ckid) != (UINT)npMCI->nVideoStream)) {
  1781. pNewIndex[iNewIndex++] = *pIndexEntry;
  1782. }
  1783. //
  1784. // in interleaved files a "frame" happens every list record
  1785. //
  1786. // in non-interleaved files a "frame" happens every piece of
  1787. // data in the video stream (except no time chunks)
  1788. //
  1789. if (fInterleaved) {
  1790. if (ckid != listtypeAVIRECORD)
  1791. continue;
  1792. } else {
  1793. if ((StreamFromFOURCC(ckid) != (UINT)npMCI->nVideoStream) ||
  1794. (pIndexEntry->dwFlags & AVIIF_NOTIME))
  1795. continue;
  1796. }
  1797. AssertSz(iFrame < npMCI->lFrames,"Too many frames in index!");
  1798. if (iFrame >= npMCI->lFrames) {
  1799. break;
  1800. }
  1801. if (pIndexEntry->dwFlags & AVIIF_KEYFRAME) {
  1802. iKeyFrame = iFrame;
  1803. nKeyFrames++;
  1804. }
  1805. pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
  1806. pFrameIndex[iFrame].iNextKey = 0;
  1807. pFrameIndex[iFrame].iPalette = (WORD)iPalette;
  1808. pFrameIndex[iFrame].dwOffset = pIndexEntry->dwChunkOffset;
  1809. pFrameIndex[iFrame].dwLength = pIndexEntry->dwChunkLength;
  1810. if (fInterleaved)
  1811. pFrameIndex[iFrame].dwOffset += 3 * sizeof(DWORD);
  1812. Assert(iPalette <= 0xFFFF);
  1813. if (iFrame - iKeyFrame > 0xFFFF) {
  1814. //!!! we need to set a flag!
  1815. //!!! we need to throw out the index!
  1816. AssertSz(FALSE, "File has too few key frames");
  1817. pFrameIndex[iFrame].iPrevKey = 0;
  1818. }
  1819. iFrame++;
  1820. }
  1821. ack:
  1822. //
  1823. // iFrame better equal npMCI->lFrames
  1824. //
  1825. Assert(iFrame == npMCI->lFrames);
  1826. if (iFrame < npMCI->lFrames)
  1827. npMCI->lFrames = iFrame;
  1828. //
  1829. // make a "dummy" last frame
  1830. //
  1831. pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
  1832. pFrameIndex[iFrame].iNextKey = 0;
  1833. pFrameIndex[iFrame].iPalette = (WORD)iPalette;
  1834. pFrameIndex[iFrame].dwOffset = 0;
  1835. pFrameIndex[iFrame].dwLength = 0;
  1836. //
  1837. // compute the key frames every value
  1838. //
  1839. if (nKeyFrames) {
  1840. if (nKeyFrames > 1)
  1841. npMCI->dwKeyFrameInfo = (DWORD)((nFrames + nKeyFrames/2)/nKeyFrames);
  1842. else
  1843. npMCI->dwKeyFrameInfo = 0;
  1844. }
  1845. //
  1846. // now go through the index, and fix all the iNextKey fields
  1847. //
  1848. pFrameIndex = npMCI->hpFrameIndex;
  1849. ////iKeyFrame = npMCI->lFrames; //!!! what should this be set to? zero?
  1850. for (iFrame = npMCI->lFrames; iFrame>=iFrameStart; iFrame--)
  1851. {
  1852. if (pFrameIndex[iFrame].iPrevKey == 0)
  1853. iKeyFrame = iFrame;
  1854. if (iKeyFrame >= iFrame)
  1855. pFrameIndex[iFrame].iNextKey = (UINT)(iKeyFrame - iFrame);
  1856. else
  1857. pFrameIndex[iFrame].iNextKey = 0xFFFF; // way far away
  1858. if (iKeyFrame - iFrame > 0xFFFF) {
  1859. //!!! we need to set a flag!
  1860. //!!! we need to throw out the index!
  1861. AssertSz(FALSE, "File has too few key frames");
  1862. pFrameIndex[iFrame].iNextKey = 0;
  1863. }
  1864. }
  1865. //
  1866. // we dont need the index, if we are using AVIFile or
  1867. // we have a interleaved file. when the file is interleaved
  1868. // we never do random access (except for palette changes)
  1869. //
  1870. // !!!this is not true, we need the index iff we have a audio only
  1871. // file or we play a interleaved file real slow.
  1872. //
  1873. if (npMCI->pf /* ||
  1874. (fInterleaved && !(npMCI->dwFlags & MCIAVI_ANIMATEPALETTE))*/ ) {
  1875. DOUT("The Master index must go!\n");
  1876. iNewIndex = 0;
  1877. }
  1878. //
  1879. // now re-alloc the master index down to size.
  1880. //
  1881. // !!! do we even need the master index anymore, for interleaved files?
  1882. //
  1883. DPF(("Master index was %ld entries now %ld\n",npMCI->macIndex, iNewIndex));
  1884. npMCI->macIndex = iNewIndex;
  1885. if (iNewIndex > 0) {
  1886. npMCI->hpIndex = (AVIINDEXENTRY _huge *)
  1887. GlobalReAllocPtr(npMCI->hpIndex,
  1888. (LONG)iNewIndex * sizeof(AVIINDEXENTRY),
  1889. GMEM_MOVEABLE | GMEM_SHARE);
  1890. Assert(npMCI->hpIndex != NULL);
  1891. }
  1892. else {
  1893. if (npMCI->hpIndex)
  1894. GlobalFreePtr(npMCI->hpIndex);
  1895. npMCI->hpIndex = NULL;
  1896. }
  1897. return TRUE;
  1898. }
  1899. /***************************************************************************
  1900. *
  1901. * @doc INTERNAL MCIAVI
  1902. *
  1903. * @api BOOL | ReadIndex | Read the index into npMCI->hpIndex. Should
  1904. * only be called if the HASINDEX flag is set.
  1905. *
  1906. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data
  1907. *
  1908. * @rdesc TRUE means no errors, false means unable to read index.
  1909. *
  1910. ***************************************************************************/
  1911. BOOL FAR PASCAL ReadIndex(NPMCIGRAPHIC npMCI)
  1912. {
  1913. MMCKINFO ck;
  1914. DWORD dwOldPos;
  1915. if (npMCI->hpIndex || npMCI->hpFrameIndex)
  1916. return TRUE;
  1917. if (!(npMCI->dwFlags & MCIAVI_HASINDEX))
  1918. return FALSE;
  1919. if (npMCI->pf) {
  1920. MakeFrameIndex(npMCI);
  1921. return TRUE;
  1922. }
  1923. #if 0
  1924. if (GetCurrentTask() != npMCI->hTask) {
  1925. /* this function is called (from GraphicStatus) when
  1926. * possibly playing - so we have to suspend play while we read
  1927. * the index.
  1928. */
  1929. // won't work with the current multi-thread design
  1930. // TEMPORARYSTATE ts;
  1931. // if (StopTemporarily(npMCI, &ts) == 0) {
  1932. // mciaviTaskMessage(npMCI, TASKREADINDEX);
  1933. // RestartAgain(npMCI, &ts);
  1934. // return (npMCI->hpIndex != NULL);
  1935. // }
  1936. return(FALSE);
  1937. }
  1938. #else
  1939. if (GetCurrentTask() != npMCI->hTask)
  1940. return FALSE;
  1941. #endif
  1942. dwOldPos = mmioSeek(npMCI->hmmio, 0, SEEK_CUR);
  1943. DPF(("Reading index: starting from %lx\n", npMCI->dwBigListEnd));
  1944. if (mmioSeek(npMCI->hmmio, npMCI->dwBigListEnd, SEEK_SET) == -1) {
  1945. IndexReadError:
  1946. DPF(("Error reading index!\n"));
  1947. npMCI->dwFlags &= ~(MCIAVI_HASINDEX);
  1948. mmioSeek(npMCI->hmmio, dwOldPos, SEEK_SET);
  1949. return FALSE;
  1950. }
  1951. ck.ckid = ckidAVINEWINDEX;
  1952. if (mmioDescend(npMCI->hmmio, &ck, NULL, MMIO_FINDCHUNK) != 0) {
  1953. goto IndexReadError;
  1954. }
  1955. /* A zero-size index isn't much good. */
  1956. if (ck.cksize == 0)
  1957. goto IndexReadError;
  1958. npMCI->macIndex = ck.cksize / sizeof(AVIINDEXENTRY);
  1959. npMCI->hpIndex = (AVIINDEXENTRY _huge *)
  1960. GlobalAllocPtr(GMEM_SHARE | GMEM_MOVEABLE, ck.cksize);
  1961. if (!npMCI->hpIndex) {
  1962. DPF(("Insufficient memory to read index.\n"));
  1963. goto IndexReadError;
  1964. }
  1965. #ifndef _WIN32
  1966. Assert(OFFSETOF(npMCI->hpIndex) == 0);
  1967. #endif
  1968. if (mmioRead(npMCI->hmmio, (HPSTR) npMCI->hpIndex, ck.cksize) != (LONG) ck.cksize) {
  1969. Assert(0);
  1970. goto IndexReadError;
  1971. }
  1972. CleanIndex(npMCI);
  1973. MakeFrameIndex(npMCI);
  1974. ////should we do this for audio? remove video data?
  1975. ////MakeStreamIndex(npMCI, ???);
  1976. mmioSeek(npMCI->hmmio, dwOldPos, SEEK_SET);
  1977. return TRUE;
  1978. }
  1979. /***************************************************************************
  1980. * @doc INTERNAL MCIAVI
  1981. *
  1982. * @api BOOL | IsRectBogus | 'nuf said
  1983. *
  1984. ***************************************************************************/
  1985. INLINE static BOOL NEAR PASCAL IsRectBogus(RECT* prc)
  1986. {
  1987. if (prc->right - prc->left <= 0 ||
  1988. prc->bottom - prc->top <= 0 ||
  1989. prc->bottom <= 0 ||
  1990. prc->right <= 0)
  1991. return TRUE;
  1992. else
  1993. return FALSE;
  1994. }
  1995. #ifndef _WIN32 // _WIN32 has one!
  1996. /***************************************************************************
  1997. *
  1998. * @doc INTERNAL MCIAVI
  1999. *
  2000. * @api LONG | atol | local version of atol
  2001. *
  2002. ***************************************************************************/
  2003. static LONG NEAR PASCAL atol(char *sz)
  2004. {
  2005. LONG l = 0;
  2006. while (*sz && *sz >= '0' && *sz <= '9')
  2007. l = l*10 + *sz++ - '0';
  2008. return l;
  2009. }
  2010. #endif
  2011. #ifndef _WIN32
  2012. /*--------------------------------------------------------------------------
  2013. *
  2014. * IsCDROMDrive() -
  2015. *
  2016. * Purpose: Return non-zero if a RAM drive
  2017. *
  2018. * wDrive drive index (0=A, 1=B, ...)
  2019. *
  2020. * return TRUE/FALSE
  2021. *-------------------------------------------------------------------------*/
  2022. #pragma optimize("", off)
  2023. static BOOL NEAR PASCAL IsCDROMDrive(UINT wDrive)
  2024. {
  2025. BOOL f;
  2026. _asm {
  2027. mov ax, 1500h /* first test for presence of MSCDEX */
  2028. xor bx, bx
  2029. int 2fh
  2030. mov ax, bx /* MSCDEX is not there if bx is still zero */
  2031. or ax, ax /* ...so return FALSE from this function */
  2032. jz no_mscdex
  2033. mov ax, 150bh /* MSCDEX driver check API */
  2034. mov cx, wDrive /* ...cx is drive index */
  2035. int 2fh
  2036. no_mscdex:
  2037. mov f,ax
  2038. }
  2039. return f;
  2040. }
  2041. #pragma optimize("", on)
  2042. /***************************************************************************
  2043. *
  2044. * @doc INTERNAL MCIAVI
  2045. *
  2046. * @api BOOL | IsNetFile | is the passed file on a network drive?
  2047. *
  2048. ***************************************************************************/
  2049. static BOOL NEAR PASCAL IsNetFile(LPTSTR szFile)
  2050. {
  2051. OFSTRUCT of;
  2052. if (OpenFile(szFile, &of, OF_PARSE) == -1)
  2053. return FALSE;
  2054. AnsiUpper(of.szPathName);
  2055. if (of.szPathName[0] == '\\' && of.szPathName[1] == '\\')
  2056. return TRUE;
  2057. if (of.szPathName[0] == '/' && of.szPathName[1] == '/')
  2058. return TRUE;
  2059. if (of.szPathName[1] == ':' &&
  2060. GetDriveType(of.szPathName[0] - 'A') == DRIVE_REMOTE &&
  2061. !IsCDROMDrive(of.szPathName[0] - 'A'))
  2062. return TRUE;
  2063. return FALSE;
  2064. }
  2065. /***************************************************************************
  2066. *
  2067. * @doc INTERNAL MCIAVI
  2068. *
  2069. * @api BOOL | IsCDROMFile | is the passed file on a CD-ROM drive?
  2070. *
  2071. ***************************************************************************/
  2072. static BOOL NEAR PASCAL IsCDROMFile(LPTSTR szFile)
  2073. {
  2074. OFSTRUCT of;
  2075. if (OpenFile(szFile, &of, OF_PARSE) == -1)
  2076. return FALSE;
  2077. AnsiUpper(of.szPathName);
  2078. if (of.szPathName[0] == '\\' && of.szPathName[1] == '\\')
  2079. return FALSE;
  2080. if (of.szPathName[0] == '/' && of.szPathName[1] == '/')
  2081. return FALSE;
  2082. if (of.szPathName[1] == ':' &&
  2083. GetDriveType(of.szPathName[0] - 'A') == DRIVE_REMOTE &&
  2084. IsCDROMDrive(of.szPathName[0] - 'A'))
  2085. return TRUE;
  2086. return FALSE;
  2087. }
  2088. /***************************************************************************
  2089. *
  2090. * @doc INTERNAL MCIAVI
  2091. *
  2092. * @api UINT | GetFileDriveType | return drive type given a file
  2093. *
  2094. * DRIVE_CDROM
  2095. * DRIVE_REMOTE
  2096. * DRIVE_FIXED
  2097. * DRIVE_REMOVABLE
  2098. *
  2099. ***************************************************************************/
  2100. static UINT NEAR PASCAL GetFileDriveType(LPSTR szPath)
  2101. {
  2102. if (IsCDROMFile(szPath))
  2103. return DRIVE_CDROM;
  2104. if (IsNetFile(szPath))
  2105. return DRIVE_REMOTE;
  2106. if (szPath[1] == ':')
  2107. return GetDriveType(szPath[0] - 'A');
  2108. return 0;
  2109. }
  2110. #else // _WIN32
  2111. /*
  2112. * We cannot pass the call directly to GetDriveType as we have a full
  2113. * pathname - and that gets rejected by GetFileDriveType. Using a
  2114. * static variable is ok - the open call is serialised. The assumption
  2115. * is that this routine is ONLY called with complete path information.
  2116. */
  2117. TCHAR DriveType[4] = TEXT(".:\\");
  2118. static UINT NEAR PASCAL GetFileDriveType(LPTSTR szPath)
  2119. {
  2120. Assert(szPath != NULL);
  2121. if (*szPath == TEXT('\\')) {
  2122. Assert(szPath[1] == TEXT('\\'));
  2123. return(DRIVE_REMOTE);
  2124. }
  2125. if (szPath[1] == TEXT(':')) {
  2126. DriveType[0] = szPath[0];
  2127. return GetDriveType(DriveType);
  2128. }
  2129. return 0;
  2130. }
  2131. #endif