Leaked source code of windows server 2003
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.

2664 lines
69 KiB

  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, 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 mess 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. return TRUE;
  554. }
  555. #endif /* USEAVIFILE */
  556. /***************************************************************************
  557. *
  558. * @doc INTERNAL MCIAVI
  559. *
  560. * @api BOOL | OpenRiffAVIFile | Open an RIFF AVI file
  561. *
  562. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  563. *
  564. * @parm BOOL | bForce | if FALSE, return error for files that
  565. * we think should be opened by AVIFILE instead.
  566. *
  567. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  568. *
  569. ***************************************************************************/
  570. STATICFN BOOL NEAR PASCAL OpenRiffAVIFile(NPMCIGRAPHIC npMCI, BOOL bForce)
  571. {
  572. HMMIO hmmio;
  573. HANDLE h = NULL;
  574. BOOL fRet = TRUE;
  575. MMIOINFO mmioInfo;
  576. MMCKINFO ckRIFF;
  577. MMCKINFO ckLIST;
  578. MMCKINFO ckRECORD;
  579. _fmemset(&mmioInfo, 0, sizeof(MMIOINFO));
  580. mmioInfo.htask = (HANDLE)npMCI->hCallingTask;
  581. hmmio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ | MMIO_DENYWRITE);
  582. if (hmmio == NULL)
  583. hmmio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ);
  584. if (!hmmio) {
  585. switch (mmioInfo.wErrorRet) {
  586. case MMIOERR_OUTOFMEMORY:
  587. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  588. break;
  589. case MMIOERR_FILENOTFOUND:
  590. case MMIOERR_CANNOTOPEN:
  591. default:
  592. npMCI->dwTaskError = MCIERR_FILE_NOT_FOUND;
  593. break;
  594. }
  595. fRet = FALSE;
  596. goto exit;
  597. }
  598. npMCI->hmmio = hmmio;
  599. /*
  600. ** Descend into RIFF file
  601. */
  602. if (mmioDescend(hmmio, &ckRIFF, NULL, 0) != 0) {
  603. goto ERROR_BADFILE;
  604. }
  605. /*
  606. * check for a 'QuickTime AVI' file, a QuickTime AVI file is a
  607. * QuickTime public movie with a AVI file in the 'mdat' atom.
  608. */
  609. if (ckRIFF.cksize == mmioFOURCC('m','d','a','t'))
  610. {
  611. DPF(("File is a QuickTime public movie\n"));
  612. /*
  613. * now the 'mdat' atom better be a RIFF/AVI or we cant handle it.
  614. */
  615. if (mmioDescend(hmmio, &ckRIFF, NULL, 0) != 0) {
  616. goto ERROR_BADFILE;
  617. }
  618. }
  619. /* Make sure it's a RIFF file */
  620. if (ckRIFF.ckid != FOURCC_RIFF) {
  621. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  622. goto ERROR_NOT_AVIFILE;
  623. }
  624. /* Otherwise, it should be an AVI file */
  625. if (ckRIFF.fccType != formtypeAVI) {
  626. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  627. goto ERROR_NOT_AVIFILE;
  628. }
  629. /*
  630. ** Descend into header LIST
  631. */
  632. ckLIST.fccType = listtypeAVIHEADER;
  633. if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) != 0) {
  634. goto ERROR_BADFILE;
  635. }
  636. /* Leave space at end of buffer for pad word */
  637. h = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, ckLIST.cksize -
  638. sizeof(DWORD) +
  639. sizeof(DWORD));
  640. if (!h) {
  641. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  642. return FALSE;
  643. }
  644. npMCI->lp = npMCI->lpBuffer = (LPSTR) GlobalLock(h);
  645. DPF(("Reading header list: %lu bytes.\n", ckLIST.cksize - sizeof(DWORD)));
  646. if (mmioRead(hmmio, npMCI->lp, ckLIST.cksize - sizeof(DWORD))
  647. != (LONG) (ckLIST.cksize - sizeof(DWORD))) {
  648. npMCI->dwTaskError = MCIERR_FILE_READ;
  649. goto ERROR_RETURN;
  650. }
  651. #ifdef USE_AVIFILE_FOR_NON_INT
  652. //
  653. // we check here for AVI RIFF files we dont want to handle with our
  654. // built in code, and want to pass on to AVIFILE.DLL
  655. //
  656. // we handle the following files:
  657. //
  658. // interleaved
  659. //
  660. // we pass on the following files to AVIFILE.DLL
  661. //
  662. // non-interleaved
  663. //
  664. // pretty simple right now, just interleaved non-interlaved
  665. // but could get as complex as you want.
  666. //
  667. // if there is a problem with avifile (eg dll not present) we will
  668. // be called with bForce TRUE: this means we should open it if at
  669. // all possible
  670. //
  671. if (!bForce) {
  672. MainAVIHeader FAR * lpHdr;
  673. lpHdr = (MainAVIHeader FAR *)((BYTE FAR *)npMCI->lp + 8);
  674. if (!(lpHdr->dwFlags & AVIF_ISINTERLEAVED) ||
  675. lpHdr->dwInitialFrames == 0) {
  676. DOUT("File is not interleaved, giving it to AVIFILE.DLL\n");
  677. goto ERROR_NOT_AVIFILE;
  678. }
  679. //
  680. // ok now we have a 1:1 interleved file.
  681. //
  682. // always use our code on a CD-ROM, but on other media...
  683. //
  684. switch (npMCI->uDriveType) {
  685. case DRIVE_CDROM:
  686. break;
  687. case DRIVE_REMOTE:
  688. case DRIVE_FIXED:
  689. case DRIVE_REMOVABLE:
  690. break;
  691. default:
  692. break;
  693. }
  694. }
  695. #endif
  696. if (PEEK_DWORD() == ckidAVIMAINHDR) {
  697. if (!ParseNewHeader(npMCI))
  698. goto ERROR_RETURN;
  699. } else {
  700. goto ERROR_BADFILE;
  701. }
  702. /* Ascend out of header LIST */
  703. if (mmioAscend(hmmio, &ckLIST, 0) != 0) {
  704. npMCI->dwTaskError = MCIERR_FILE_READ;
  705. goto ERROR_RETURN;
  706. }
  707. /* Initially, no frame has been drawn */
  708. npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
  709. /*
  710. ** Descend into big 'Movie LIST'
  711. */
  712. ckLIST.fccType = listtypeAVIMOVIE;
  713. if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) != 0) {
  714. goto ERROR_BADFILE;
  715. }
  716. npMCI->dwMovieListOffset = ckLIST.dwDataOffset;
  717. /* Calculate end of 'movi' list, in case we need to read the index */
  718. npMCI->dwBigListEnd = ckLIST.dwDataOffset + ckLIST.cksize +
  719. (ckLIST.cksize & 1);
  720. /*
  721. ** Descend into header of first chunk
  722. */
  723. if (mmioDescend(hmmio, &ckRECORD, &ckLIST, 0) != 0) {
  724. goto ERROR_BADFILE;
  725. }
  726. npMCI->dwFirstRecordType = ckRECORD.ckid;
  727. npMCI->dwFirstRecordSize = ckRECORD.cksize + 2 * sizeof(DWORD);
  728. npMCI->dwFirstRecordPosition = mmioSeek(hmmio, 0, SEEK_CUR);
  729. if (mmioAscend(hmmio, &ckRECORD, 0) != 0) {
  730. npMCI->dwTaskError = MCIERR_FILE_READ;
  731. goto ERROR_RETURN;
  732. }
  733. #ifdef DEBUG
  734. DPF2(("First record (%4.4s) 0x%lx bytes at position 0x%lx.\n",
  735. (LPSTR)&npMCI->dwFirstRecordType,
  736. npMCI->dwFirstRecordSize,
  737. npMCI->dwFirstRecordPosition));
  738. if (npMCI->dwFirstRecordPosition & 0x7ff) {
  739. DPF(("!!\n"));
  740. DPF(("!! This file is not properly aligned to a 2K boundary.\n"));
  741. DPF(("!! It may not play well from CD-ROM.\n"));
  742. DPF(("!!\n"));
  743. }
  744. #endif
  745. exit:
  746. if (!fRet)
  747. mciaviCloseFile(npMCI);
  748. if (h) {
  749. npMCI->lpBuffer = NULL;
  750. npMCI->dwBufferSize = 0L;
  751. GlobalUnlock(h);
  752. GlobalFree(h);
  753. }
  754. return fRet;
  755. ERROR_NOT_AVIFILE:
  756. #ifdef USEAVIFILE
  757. npMCI->dwTaskError = AVIERR_NOT_AVIFILE; // mark as not a AVI file
  758. goto ERROR_RETURN; // we will be called a second time
  759. #endif
  760. ERROR_BADFILE:
  761. npMCI->dwTaskError = MCIERR_INVALID_FILE; // something wrong with the file
  762. ERROR_RETURN:
  763. fRet = FALSE;
  764. goto exit;
  765. }
  766. /***************************************************************************
  767. * @doc INTERNAL MCIAVI
  768. *
  769. * @api BOOL | ParseNewHeader | 'nuf said
  770. *
  771. ***************************************************************************/
  772. STATICFN BOOL NEAR PASCAL ParseNewHeader(NPMCIGRAPHIC npMCI)
  773. {
  774. DWORD dwHeaderSize;
  775. MainAVIHeader FAR * lpHdr;
  776. int stream;
  777. if (GET_DWORD() != ckidAVIMAINHDR) {
  778. goto FileError;
  779. }
  780. dwHeaderSize = GET_DWORD(); /* Skip size */
  781. /* Now, we're pointing at the actual header */
  782. lpHdr = (MainAVIHeader FAR *) npMCI->lp;
  783. npMCI->lFrames = (LONG)lpHdr->dwTotalFrames;
  784. npMCI->dwMicroSecPerFrame = lpHdr->dwMicroSecPerFrame;
  785. npMCI->dwRate = 1000000;
  786. npMCI->dwScale = npMCI->dwMicroSecPerFrame;
  787. /* Reject some bad values */
  788. if (!lpHdr->dwStreams || lpHdr->dwStreams > 255 || !npMCI->lFrames) {
  789. goto FileError;
  790. }
  791. npMCI->streams = (int) lpHdr->dwStreams;
  792. npMCI->dwBytesPerSec = lpHdr->dwMaxBytesPerSec;
  793. npMCI->wEarlyRecords = (UINT) lpHdr->dwInitialFrames;
  794. npMCI->dwSuggestedBufferSize = lpHdr->dwSuggestedBufferSize;
  795. SetRect(&npMCI->rcMovie,0,0,(int)lpHdr->dwWidth,(int)lpHdr->dwHeight);
  796. npMCI->dwFlags |= MCIAVI_HASINDEX;
  797. if (!(lpHdr->dwFlags & AVIF_ISINTERLEAVED)) {
  798. DPF(("File is not interleaved.\n"));
  799. npMCI->dwFlags |= MCIAVI_NOTINTERLEAVED;
  800. }
  801. SKIP_BYTES(dwHeaderSize); /* Skip rest of chunk */
  802. npMCI->paStreamInfo = (STREAMINFO NEAR *)
  803. LocalAlloc(LPTR, npMCI->streams * sizeof(STREAMINFO));
  804. // !!! error check
  805. for (stream = 0; stream < npMCI->streams; stream++) {
  806. AVIStreamHeader FAR * lpStream;
  807. HPSTR hpNextChunk;
  808. STREAMINFO * psi = &npMCI->paStreamInfo[stream];
  809. if (GET_DWORD() != FOURCC_LIST) {
  810. goto FileError;
  811. }
  812. dwHeaderSize = GET_DWORD(); /* Skip size */
  813. hpNextChunk = npMCI->lp + (dwHeaderSize + (dwHeaderSize & 1));
  814. if (GET_DWORD() != listtypeSTREAMHEADER) {
  815. goto FileError;
  816. }
  817. /* Now, we're at the begging of the stream's header chunks. */
  818. if (GET_DWORD() != ckidSTREAMHEADER) {
  819. goto FileError;
  820. }
  821. dwHeaderSize = GET_DWORD(); /* Skip size */
  822. /* Now, we're pointing at the stream header */
  823. lpStream = (AVIStreamHeader FAR *) npMCI->lp;
  824. hmemcpy(&psi->sh, lpStream, min(dwHeaderSize, sizeof(psi->sh)));
  825. //
  826. // reject files with more than one video stream.
  827. //
  828. if (psi->sh.fccType == streamtypeVIDEO &&
  829. npMCI->nVideoStreams >= 1) {
  830. DPF(("File has multiple video streams, giving it to AVIFILE.DLL\n"));
  831. goto DontHandleThisFile;
  832. }
  833. SKIP_BYTES(dwHeaderSize);
  834. /* Read the format */
  835. if (GET_DWORD() != ckidSTREAMFORMAT) {
  836. goto FileError;
  837. }
  838. dwHeaderSize = GET_DWORD(); /* Skip size */
  839. if (dwHeaderSize > 16384L) {
  840. goto FileError;
  841. }
  842. psi->cbFormat = dwHeaderSize;
  843. psi->lpFormat = GlobalAllocPtr(GHND,dwHeaderSize);
  844. if (!psi->lpFormat) {
  845. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  846. return FALSE;
  847. }
  848. hmemcpy(psi->lpFormat, npMCI->lp, dwHeaderSize);
  849. SKIP_BYTES(dwHeaderSize);
  850. if (PEEK_DWORD() == ckidSTREAMHANDLERDATA) {
  851. GET_DWORD();
  852. dwHeaderSize = GET_DWORD(); /* Skip size */
  853. psi->cbData = dwHeaderSize;
  854. psi->lpData = GlobalAllocPtr(GHND,dwHeaderSize);
  855. if (!psi->lpData) {
  856. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  857. return FALSE;
  858. }
  859. hmemcpy(psi->lpData, npMCI->lp, dwHeaderSize);
  860. /* Skip to end of Data chunk */
  861. SKIP_BYTES(dwHeaderSize);
  862. } else {
  863. psi->cbData = 0;
  864. psi->lpData = NULL;
  865. }
  866. InitStream(npMCI, psi);
  867. npMCI->lp = hpNextChunk;
  868. }
  869. if (npMCI->dwTaskError) {
  870. return FALSE;
  871. }
  872. return TRUE;
  873. FileError:
  874. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  875. return FALSE;
  876. DontHandleThisFile:
  877. npMCI->dwTaskError = AVIERR_NOT_AVIFILE;
  878. return FALSE;
  879. }
  880. //
  881. // --- called on worker thread ---------------------------------------------
  882. //
  883. /***************************************************************************
  884. *
  885. * @doc INTERNAL MCIAVI
  886. *
  887. * @api BOOL | OpenFileInit | called after a file is opened to init things
  888. *
  889. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  890. *
  891. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  892. *
  893. ***************************************************************************/
  894. BOOL NEAR PASCAL OpenFileInit(NPMCIGRAPHIC npMCI)
  895. {
  896. int i;
  897. RECT rc;
  898. //
  899. // lets make sure we opened something.
  900. //
  901. if (npMCI->streams == 0)
  902. return FALSE;
  903. if (npMCI->nVideoStreams + npMCI->nAudioStreams + npMCI->nOtherStreams == 0)
  904. return FALSE;
  905. if (npMCI->nVideoStreams == 0)
  906. npMCI->dwFlags &= ~MCIAVI_SHOWVIDEO;
  907. if (npMCI->nAudioStreams == 0)
  908. npMCI->dwFlags &= ~MCIAVI_PLAYAUDIO; // No audio streams means silence
  909. if (IsNTWOW()) {
  910. if (!npMCI->pbiFormat) {
  911. // Although there are valid video streams we do not appear to have
  912. // access to a decompressor that knows what to do with them. Hence
  913. // if we are in NT/WOW we fail the open, the call will return to
  914. // 16 bit land and there might be a codec there that can cope
  915. return(FALSE);
  916. }
  917. }
  918. if (npMCI->nAudioStreams > 1) {
  919. UINT wLang;
  920. int stream;
  921. #ifndef _WIN32
  922. UINT (WINAPI * GetUserDefaultLangID)(void);
  923. UINT u;
  924. HANDLE hdll;
  925. u = SetErrorMode(SEM_NOOPENFILEERRORBOX);
  926. hdll = LoadLibrary(szOLENLSDLL);
  927. SetErrorMode(u);
  928. wLang = 0;
  929. if ((UINT)hdll > (UINT)HINSTANCE_ERROR)
  930. {
  931. if ((FARPROC) GetUserDefaultLangID = GetProcAddress(hdll, szOLENLSAPI)) {
  932. wLang = GetUserDefaultLangID();
  933. }
  934. FreeLibrary(hdll);
  935. }
  936. #else
  937. wLang = GetUserDefaultLangID();
  938. #endif
  939. DPF(("Current language: %x\n", wLang));
  940. if (wLang > 0) {
  941. for (stream = 0; stream < npMCI->streams; stream++) {
  942. if (SH(stream).fccType == streamtypeAUDIO) {
  943. if (!(SH(stream).dwFlags & STREAM_ENABLED))
  944. continue;
  945. if (SH(stream).wLanguage == wLang) {
  946. npMCI->nAudioStream = stream;
  947. npMCI->psiAudio = SI(stream);
  948. break;
  949. }
  950. }
  951. }
  952. }
  953. }
  954. if (npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) {
  955. npMCI->wEarlyRecords = npMCI->wEarlyVideo;
  956. }
  957. else {
  958. npMCI->wEarlyRecords = max(npMCI->wEarlyVideo, npMCI->wEarlyAudio);
  959. }
  960. if (npMCI->wEarlyRecords == 0 &&
  961. !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED)) {
  962. DPF(("Interleaved file with no audio skew?\n"));
  963. npMCI->dwFlags |= MCIAVI_NOTINTERLEAVED;
  964. }
  965. if (npMCI->dwFlags & MCIAVI_ANIMATEPALETTE) {
  966. DPF(("This AVI file has palette changes.\n"));
  967. if (npMCI->nVideoStreams > 1) {
  968. npMCI->dwFlags &= ~MCIAVI_ANIMATEPALETTE;
  969. DPF(("...But we are going to ignore them?\n"));
  970. }
  971. }
  972. //
  973. // this must be set
  974. //
  975. if (npMCI->dwSuggestedBufferSize == 0) {
  976. for (i=0; i<npMCI->streams; i++)
  977. npMCI->dwSuggestedBufferSize =
  978. max(SH(i).dwSuggestedBufferSize,npMCI->dwSuggestedBufferSize);
  979. }
  980. //
  981. // check all fields in the main header
  982. //
  983. if (npMCI->dwScale == 0 ||
  984. npMCI->dwRate == 0) {
  985. }
  986. ////will be set when header parsed
  987. ////npMCI->dwMicroSecPerFrame = muldiv32(npMCI->dwScale, 1000000, npMCI->dwRate);
  988. npMCI->dwPlayMicroSecPerFrame = npMCI->dwMicroSecPerFrame;
  989. #define COMMON_SCALE 10000
  990. //
  991. // convert the rate/scale into something that is normalized to 1000
  992. //
  993. npMCI->dwRate = muldiv32(npMCI->dwRate, COMMON_SCALE, npMCI->dwScale);
  994. npMCI->dwScale = COMMON_SCALE;
  995. //
  996. // walk all streams and fix them up.
  997. //
  998. for (i=0; i<npMCI->streams; i++) {
  999. STREAMINFO *psi = SI(i);
  1000. LONG lStart;
  1001. LONG lEnd;
  1002. //
  1003. // convert the rate/scale into something that is normalized to 1000
  1004. //
  1005. psi->sh.dwRate = muldiv32(psi->sh.dwRate, COMMON_SCALE, psi->sh.dwScale);
  1006. psi->sh.dwScale = COMMON_SCALE;
  1007. //
  1008. // trim any streams that hang over the movie.
  1009. //
  1010. lStart = MovieToStream(psi, 0);
  1011. lEnd = MovieToStream(psi, npMCI->lFrames);
  1012. if ((LONG)(psi->sh.dwStart + psi->sh.dwLength) > lEnd) {
  1013. DPF(("Stream #%d is too long, was %ld now %ld\n", i,
  1014. psi->sh.dwLength, lEnd - psi->sh.dwStart));
  1015. psi->sh.dwLength = lEnd - psi->sh.dwStart;
  1016. }
  1017. }
  1018. //
  1019. // If this is WOW we had better check that there is an audio codec
  1020. // available. If not, we let the 16 bit side open the video.
  1021. //
  1022. if (IsNTWOW()) {
  1023. if (SetUpAudio(npMCI, FALSE)) return FALSE;
  1024. }
  1025. //
  1026. // fix up the movie rect
  1027. //
  1028. if (IsRectEmpty(&npMCI->rcMovie)) {
  1029. DPF2(("Movie rect is empty\n"));
  1030. SetRectEmpty(&rc);
  1031. for (i=0; i<npMCI->streams; i++) {
  1032. UnionRect(&rc,&rc,&SH(i).rcFrame);
  1033. }
  1034. npMCI->rcMovie = rc;
  1035. DPF2(("Movie rect was empty, now [%d, %d, %d, %d]\n", rc));
  1036. } else {
  1037. rc = npMCI->rcMovie;
  1038. }
  1039. // check we can access the interface ptr - the main reason for this
  1040. // is that if we have pasted this interface ptr, we cannot access it on
  1041. // the worker thread (which is where we are now). Clean up the error handling
  1042. // by actually detecting this case.
  1043. if (npMCI->pf) {
  1044. IAVIStream * ps;
  1045. long l;
  1046. if ((l = AVIFileGetStream(npMCI->pf, &ps, 0, 0)) != 0) {
  1047. DPF(("avifile err %d", l));
  1048. npMCI->dwTaskError = l;
  1049. return FALSE;
  1050. } else {
  1051. if (ps->lpVtbl->FindSample(ps, 0, FIND_FROM_START|FIND_NEXT|FIND_ANY) < 0) {
  1052. npMCI->dwTaskError = MCIERR_DEVICE_NOT_READY;
  1053. ps->lpVtbl->Release(ps);
  1054. return FALSE;
  1055. }
  1056. ps->lpVtbl->Release(ps);
  1057. }
  1058. }
  1059. //
  1060. // always read the index, so we can skip frames even on CD!
  1061. //
  1062. ReadIndex(npMCI);
  1063. DPF(("Key frames are every (on average): %ld frames (%ld ms)\n",npMCI->dwKeyFrameInfo, MovieToTime(npMCI->dwKeyFrameInfo)));
  1064. // force things to happen, in case we're re-loading
  1065. SetRectEmpty(&npMCI->rcSource);
  1066. SetRectEmpty(&npMCI->rcDest);
  1067. /* this will call DrawDibBegin() ... */
  1068. if (TryPutRect(npMCI, MCI_DGV_PUT_SOURCE, &rc, &npMCI->dwTaskError)) {
  1069. return FALSE;
  1070. }
  1071. /*
  1072. * also set the dest rect. This should be done
  1073. * by the WM_SIZE message sent during SetWindowToDefaultSize.
  1074. * On NT, the WM_SIZE message is not sent synchronously since it
  1075. * is an inter-thread sendmessage (the winproc is on the original thread
  1076. * whereas we are currently running on the avi thread). The winproc
  1077. * thread may well not get the WM_SIZE message until much too late, so
  1078. * set the dest rect here. Note: don't use ResetDestRect since that
  1079. * also relies on the window size, which is not set yet.
  1080. */
  1081. /* double frame size of destination if zoom by 2 */
  1082. if (npMCI->dwOptionFlags & MCIAVIO_ZOOMBY2)
  1083. SetRect(&rc, 0, 0, rc.right*2, rc.bottom*2);
  1084. if (TryPutRect(npMCI, MCI_DGV_PUT_DESTINATION, &rc, &npMCI->dwTaskError)) {
  1085. return FALSE;
  1086. }
  1087. if (npMCI->dwTaskError) {
  1088. return FALSE;
  1089. }
  1090. //
  1091. // size the window and things.
  1092. //
  1093. SetWindowToDefaultSize(npMCI, FALSE);
  1094. DrawBegin(npMCI, NULL);
  1095. return TRUE;
  1096. }
  1097. /***************************************************************************
  1098. *
  1099. * @doc INTERNAL MCIAVI
  1100. *
  1101. * @api BOOL | mciaviCloseFile | Close an AVI file.
  1102. *
  1103. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  1104. *
  1105. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  1106. *
  1107. ***************************************************************************/
  1108. BOOL FAR PASCAL mciaviCloseFile (NPMCIGRAPHIC npMCI)
  1109. {
  1110. if (!npMCI)
  1111. return FALSE;
  1112. if (npMCI->lpMMIOBuffer) {
  1113. GlobalFreePtr(npMCI->lpMMIOBuffer);
  1114. npMCI->lpMMIOBuffer = NULL;
  1115. }
  1116. npMCI->hicDraw = NULL;
  1117. if (npMCI->hicDrawDefault) {
  1118. if (npMCI->hicDrawDefault != (HIC) -1)
  1119. ICClose(npMCI->hicDrawDefault);
  1120. npMCI->hicDrawDefault = NULL;
  1121. }
  1122. if (npMCI->hicDrawFull) {
  1123. if (npMCI->hicDrawFull != (HIC) -1)
  1124. ICClose(npMCI->hicDrawFull);
  1125. npMCI->hicDrawFull = NULL;
  1126. }
  1127. if (npMCI->hicDecompress) {
  1128. // !!! What if we never began it?
  1129. ICDecompressEnd(npMCI->hicDecompress);
  1130. ICClose(npMCI->hicDecompress);
  1131. npMCI->hicDecompress = NULL;
  1132. }
  1133. if (npMCI->hicInternal) {
  1134. ICClose(npMCI->hicInternal);
  1135. npMCI->hicInternal = NULL;
  1136. }
  1137. if (npMCI->hicInternalFull) {
  1138. ICClose(npMCI->hicInternalFull);
  1139. npMCI->hicInternalFull = NULL;
  1140. }
  1141. if (npMCI->hmmio) {
  1142. mmioClose(npMCI->hmmio, 0);
  1143. npMCI->hmmio = NULL;
  1144. }
  1145. if (npMCI->hmmioAudio) {
  1146. mmioClose(npMCI->hmmioAudio, 0);
  1147. npMCI->hmmioAudio = NULL;
  1148. }
  1149. if (npMCI->pWF) {
  1150. LocalFree((HANDLE) npMCI->pWF);
  1151. npMCI->pWF = NULL;
  1152. }
  1153. if (npMCI->pbiFormat) {
  1154. GlobalFreePtr(npMCI->pbiFormat);
  1155. npMCI->pbiFormat = NULL;
  1156. }
  1157. // if (npMCI->hpal) {
  1158. // DeleteObject(npMCI->hpal);
  1159. // npMCI->hpal = NULL;
  1160. // }
  1161. if (npMCI->hpDecompress) {
  1162. GlobalFreePtr(npMCI->hpDecompress);
  1163. npMCI->hpDecompress = NULL;
  1164. }
  1165. if (npMCI->hpIndex) {
  1166. GlobalFreePtr(npMCI->hpIndex);
  1167. npMCI->hpIndex = NULL;
  1168. }
  1169. if (npMCI->hpFrameIndex) {
  1170. GlobalUnlock(npMCI->hgFrameIndex);
  1171. GlobalFree(npMCI->hgFrameIndex);
  1172. npMCI->hpFrameIndex = NULL;
  1173. npMCI->hgFrameIndex = 0;
  1174. }
  1175. if (npMCI->pVolumeTable) {
  1176. LocalFree((HLOCAL)npMCI->pVolumeTable);
  1177. npMCI->pVolumeTable = NULL;
  1178. }
  1179. #ifdef USEAVIFILE
  1180. if (npMCI->pf) {
  1181. AVIFileClose(npMCI->pf);
  1182. npMCI->pf = NULL;
  1183. }
  1184. #endif
  1185. if (npMCI->paStreamInfo) {
  1186. int i;
  1187. for (i = 0; i < npMCI->streams; i++) {
  1188. CloseStream(npMCI, &npMCI->paStreamInfo[i]);
  1189. }
  1190. LocalFree((HLOCAL)npMCI->paStreamInfo);
  1191. npMCI->paStreamInfo = NULL;
  1192. }
  1193. npMCI->streams = 0;
  1194. npMCI->nAudioStreams = 0;
  1195. npMCI->nVideoStreams = 0;
  1196. npMCI->nErrorStreams = 0;
  1197. npMCI->nOtherStreams = 0;
  1198. npMCI->wEarlyVideo = 0;
  1199. npMCI->wEarlyAudio = 0;
  1200. npMCI->wEarlyRecords = 0;
  1201. //!!! I bet we need to clear more
  1202. npMCI->dwFlags &= ~(MCIAVI_NOTINTERLEAVED |
  1203. MCIAVI_ANIMATEPALETTE |
  1204. MCIAVI_CANDRAW |
  1205. MCIAVI_HASINDEX);
  1206. return TRUE;
  1207. }
  1208. /***************************************************************************
  1209. *
  1210. * @doc INTERNAL MCIAVI
  1211. *
  1212. * @api BOOL | CloseStream | Close an StreamAVI file.
  1213. *
  1214. ***************************************************************************/
  1215. STATICFN void NEAR PASCAL CloseStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1216. {
  1217. psi->dwFlags &= ~STREAM_ENABLED;
  1218. ////psi->sh.fccType = 0;
  1219. ////psi->sh.fccHandler = 0;
  1220. if (psi->lpFormat) {
  1221. GlobalFreePtr(psi->lpFormat);
  1222. psi->lpFormat = NULL;
  1223. }
  1224. if (psi->lpData) {
  1225. GlobalFreePtr(psi->lpData);
  1226. psi->lpData = NULL;
  1227. }
  1228. if (psi->hicDraw) {
  1229. ICClose(psi->hicDraw);
  1230. psi->hicDraw = NULL;
  1231. }
  1232. #ifdef USEAVIFILE
  1233. if (psi->ps) {
  1234. AVIStreamClose(psi->ps);
  1235. psi->ps = NULL;
  1236. }
  1237. #endif
  1238. }
  1239. /***************************************************************************
  1240. *
  1241. * @doc INTERNAL MCIAVI
  1242. *
  1243. * @api BOOL | InitStream | initialize a stream
  1244. *
  1245. ***************************************************************************/
  1246. STATICFN BOOL NEAR PASCAL InitStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1247. {
  1248. BOOL f;
  1249. //
  1250. // set flags
  1251. //
  1252. if (psi->sh.dwFlags & AVISF_VIDEO_PALCHANGES)
  1253. psi->dwFlags |= STREAM_PALCHANGES;
  1254. psi->lStart = (LONG)psi->sh.dwStart;
  1255. psi->lEnd = (LONG)psi->sh.dwStart + psi->sh.dwLength;
  1256. if (psi->sh.fccType == streamtypeVIDEO &&
  1257. !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED))
  1258. psi->lStart -= (LONG)psi->sh.dwInitialFrames;
  1259. switch(psi->sh.fccType) {
  1260. case streamtypeVIDEO:
  1261. f = InitVideoStream(npMCI, psi);
  1262. break;
  1263. case streamtypeAUDIO:
  1264. f = InitAudioStream(npMCI, psi);
  1265. break;
  1266. default:
  1267. f = InitOtherStream(npMCI, psi);
  1268. break;
  1269. }
  1270. if (!f) {
  1271. psi->dwFlags |= STREAM_ERROR;
  1272. npMCI->nErrorStreams++;
  1273. CloseStream(npMCI, psi);
  1274. }
  1275. //
  1276. // disable the stream if the file header says to
  1277. //
  1278. if (psi->sh.dwFlags & AVISF_DISABLED) {
  1279. psi->dwFlags &= ~STREAM_ENABLED;
  1280. }
  1281. return f;
  1282. }
  1283. /***************************************************************************
  1284. *
  1285. * @doc INTERNAL MCIAVI
  1286. *
  1287. * @api BOOL | InitVideoStream | initialize a video stream
  1288. *
  1289. ***************************************************************************/
  1290. STATICFN BOOL NEAR PASCAL InitVideoStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1291. {
  1292. LPBITMAPINFOHEADER lpbi;
  1293. int stream = (int) (INT_PTR) (psi - npMCI->paStreamInfo);
  1294. npMCI->wEarlyVideo = (UINT)psi->sh.dwInitialFrames;
  1295. if (psi->sh.dwFlags & AVISF_VIDEO_PALCHANGES) {
  1296. //!!! is this right.
  1297. npMCI->dwFlags |= MCIAVI_ANIMATEPALETTE;
  1298. }
  1299. if (IsRectBogus(&psi->sh.rcFrame)) {
  1300. DPF(("BOGUS Stream rectangle [%d %d %d %d]\n", psi->sh.rcFrame));
  1301. SetRectEmpty(&psi->sh.rcFrame);
  1302. }
  1303. // In case the rectangle is totally wrong, chop it down to size....
  1304. // !!! What if the user _wants_ a zero-size RECT?
  1305. IntersectRect(&psi->sh.rcFrame, &psi->sh.rcFrame, &npMCI->rcMovie);
  1306. if (IsRectEmpty(&psi->sh.rcFrame)) {
  1307. DPF2(("Video stream rect is empty, correcting\n"));
  1308. SetRect(&psi->sh.rcFrame, 0, 0,
  1309. (int)((LPBITMAPINFOHEADER)psi->lpFormat)->biWidth,
  1310. (int)((LPBITMAPINFOHEADER)psi->lpFormat)->biHeight);
  1311. }
  1312. //
  1313. // make sure the biCompression is right for RLE files.
  1314. //
  1315. lpbi = (LPBITMAPINFOHEADER)psi->lpFormat;
  1316. if (psi->sh.fccHandler == 0) {
  1317. if (lpbi->biCompression == 0)
  1318. psi->sh.fccHandler = comptypeDIB;
  1319. if (lpbi->biCompression == BI_RLE8 && lpbi->biBitCount == 8)
  1320. psi->sh.fccHandler = comptypeRLE;
  1321. if (lpbi->biCompression > 256)
  1322. psi->sh.fccHandler = lpbi->biCompression;
  1323. }
  1324. if (lpbi->biCompression <= BI_RLE8 && lpbi->biBitCount == 8) {
  1325. if (psi->sh.fccHandler == comptypeRLE0 ||
  1326. psi->sh.fccHandler == comptypeRLE)
  1327. lpbi->biCompression = BI_RLE8;
  1328. // Assuming a DIB handler has RGB data will blow up files that have RLE data.
  1329. // Unfortunately, VidEdit writes out files like this.
  1330. // if (psi->sh.fccHandler == comptypeDIB)
  1331. // lpbi->biCompression = BI_RGB;
  1332. }
  1333. //
  1334. // make sure the color table is set to the right size
  1335. //
  1336. if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
  1337. lpbi->biClrUsed = (1 << (int)lpbi->biBitCount);
  1338. //
  1339. // try to open draw handler
  1340. //
  1341. if (psi->sh.fccHandler) {
  1342. psi->hicDraw = ICDrawOpen(psi->sh.fccType,psi->sh.fccHandler,psi->lpFormat);
  1343. if (psi->hicDraw)
  1344. DPF(("Opened draw handler %4.4hs:%4.4hs (Stream %d)\n", (LPSTR)&psi->sh.fccType,(LPSTR)&psi->sh.fccHandler, stream));
  1345. else
  1346. DPF(("Failed to open draw handler for %4.4hs:%4.4hs (Stream %d)\n", (LPSTR)&psi->sh.fccType,(LPSTR)&psi->sh.fccHandler, stream));
  1347. }
  1348. //
  1349. // one video stream is the master, he controls the palette etc
  1350. // for lack of a better default the first valid (i.e. one we can
  1351. // handle) video stream will
  1352. // become the master.
  1353. //
  1354. if (npMCI->pbiFormat == NULL) {
  1355. npMCI->nVideoStream = stream;
  1356. npMCI->psiVideo = psi;
  1357. npMCI->pbiFormat = (LPBITMAPINFOHEADER)
  1358. GlobalAllocPtr(GMEM_MOVEABLE, psi->cbFormat);
  1359. if (!npMCI->pbiFormat) {
  1360. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  1361. return FALSE;
  1362. }
  1363. //
  1364. // copy the entire format over
  1365. //
  1366. hmemcpy(npMCI->pbiFormat,psi->lpFormat,psi->cbFormat);
  1367. npMCI->bih = *npMCI->pbiFormat;
  1368. npMCI->bih.biSize = sizeof(BITMAPINFOHEADER);
  1369. npMCI->bih.biCompression = BI_RGB;
  1370. if (npMCI->bih.biClrUsed) {
  1371. /* save the original colors. */
  1372. hmemcpy(npMCI->argb, (LPBYTE)npMCI->pbiFormat + npMCI->pbiFormat->biSize,
  1373. (int)npMCI->bih.biClrUsed * sizeof(RGBQUAD));
  1374. hmemcpy(npMCI->argbOriginal, (LPSTR) npMCI->pbiFormat + npMCI->pbiFormat->biSize,
  1375. (int)npMCI->bih.biClrUsed * sizeof(RGBQUAD));
  1376. }
  1377. //
  1378. // now open the decompressor, try fastdecompress if it will do it.
  1379. //
  1380. npMCI->hicDecompress = ICLocate(ICTYPE_VIDEO,psi->sh.fccHandler,
  1381. psi->lpFormat,NULL,ICMODE_FASTDECOMPRESS);
  1382. // fast decompress may not be supported
  1383. if (npMCI->hicDecompress == NULL) {
  1384. npMCI->hicDecompress = ICDecompressOpen(ICTYPE_VIDEO,
  1385. psi->sh.fccHandler,psi->lpFormat,NULL);
  1386. }
  1387. //
  1388. // set any state data.
  1389. //
  1390. if (npMCI->hicDecompress && psi->cbData) {
  1391. ICSetState(npMCI->hicDecompress, psi->lpData, psi->cbData);
  1392. }
  1393. if (psi->hicDraw == NULL && npMCI->hicDecompress == NULL &&
  1394. psi->sh.fccHandler != comptypeRLE0 &&
  1395. psi->sh.fccHandler != comptypeNONE &&
  1396. psi->sh.fccHandler != comptypeDIB &&
  1397. psi->sh.fccHandler != comptypeRLE &&
  1398. psi->sh.fccHandler != 0) {
  1399. DPF(("Unable to open compressor '%4.4hs'!!!\n", (LPSTR) &psi->sh.fccHandler));
  1400. npMCI->nVideoStream = -1;
  1401. npMCI->psiVideo = NULL;
  1402. GlobalFreePtr(npMCI->pbiFormat);
  1403. npMCI->pbiFormat = NULL;
  1404. //
  1405. // we would like to return a custom error but by the time the
  1406. // user (application) comes to retrieve the error text MCI will
  1407. // have unloaded us (because the open failed) and therefore
  1408. // will not be able to get our specific error text.
  1409. // Hence we return a bogus generic error.
  1410. //
  1411. #ifdef _WIN32
  1412. SetLastError(MCIERR_AVI_NOCOMPRESSOR);
  1413. #endif
  1414. if (npMCI->streams == 1) // this is the only stream
  1415. npMCI->dwTaskError = MMSYSERR_NODRIVER; // MCIERR_AVI_NOCOMPRESSOR;
  1416. return FALSE; // cant load this video stream
  1417. }
  1418. }
  1419. else {
  1420. //
  1421. // this is not the default video stream find a draw handler that
  1422. // can deal with the stream.
  1423. //
  1424. //
  1425. // try VIDS.DRAW
  1426. //
  1427. // if that fails open a draw handler not-specific to the format
  1428. //
  1429. if (psi->hicDraw == NULL) {
  1430. psi->hicDraw = ICOpen(psi->sh.fccType,FOURCC_AVIDraw,ICMODE_DRAW);
  1431. if (psi->hicDraw)
  1432. DOUT("Opened draw handler VIDS.DRAW\n");
  1433. if (psi->hicDraw && ICDrawQuery(psi->hicDraw,psi->lpFormat) != ICERR_OK) {
  1434. DOUT("Closing VIDS.DRAW because it cant handle this format");
  1435. ICClose(psi->hicDraw);
  1436. psi->hicDraw = NULL;
  1437. }
  1438. }
  1439. //
  1440. // if that fails open our internal handler.
  1441. //
  1442. if (psi->hicDraw == NULL) {
  1443. psi->hicDraw = ICOpenFunction(psi->sh.fccType,
  1444. FOURCC_AVIDraw,ICMODE_DRAW,(FARPROC)ICAVIDrawProc);
  1445. if (psi->hicDraw)
  1446. DOUT("Opened Internal draw handler\n");
  1447. }
  1448. }
  1449. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1450. psi->dwFlags |= STREAM_VIDEO; // is a video stream
  1451. psi->dwFlags |= STREAM_ENABLED;
  1452. npMCI->nVideoStreams++;
  1453. return TRUE;
  1454. }
  1455. /***************************************************************************
  1456. *
  1457. * @doc INTERNAL MCIAVI
  1458. *
  1459. * @api BOOL | InitAudioStream | initialize a audio stream
  1460. *
  1461. ***************************************************************************/
  1462. STATICFN BOOL NEAR PASCAL InitAudioStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1463. {
  1464. int stream = (int) (INT_PTR) (psi - npMCI->paStreamInfo);
  1465. LPWAVEFORMAT pwf;
  1466. npMCI->wEarlyAudio = (UINT)psi->sh.dwInitialFrames;
  1467. pwf = (LPWAVEFORMAT)psi->lpFormat;
  1468. if (pwf->nChannels == 0 || pwf->nSamplesPerSec == 0) {
  1469. return FALSE;
  1470. }
  1471. if (pwf->wFormatTag == WAVE_FORMAT_PCM) {
  1472. pwf->nBlockAlign = pwf->nChannels *
  1473. ((((LPPCMWAVEFORMAT)pwf)->wBitsPerSample + 7) / 8);
  1474. pwf->nAvgBytesPerSec = pwf->nBlockAlign * pwf->nSamplesPerSec;
  1475. }
  1476. psi->sh.dwSampleSize = pwf->nBlockAlign;
  1477. psi->dwFlags |= STREAM_AUDIO; // audio stream
  1478. psi->dwFlags |= STREAM_ENABLED; // enabled by default.
  1479. //
  1480. // make sure dwRate and dwScale are right
  1481. // dwRate/dwScale should be blocks/sec
  1482. //
  1483. Assert(muldiv32(pwf->nAvgBytesPerSec,1000,pwf->nBlockAlign) ==
  1484. muldiv32(psi->sh.dwRate, 1000, psi->sh.dwScale));
  1485. //
  1486. // just to be safe set these ourself to the right value.
  1487. //
  1488. psi->sh.dwRate = pwf->nAvgBytesPerSec;
  1489. psi->sh.dwScale = pwf->nBlockAlign;
  1490. //
  1491. // only one audio stream can be active at once
  1492. // for lack of a better default the first audio stream will
  1493. // become the active one.
  1494. //
  1495. if (npMCI->nAudioStreams == 0) {
  1496. npMCI->nAudioStream = stream;
  1497. npMCI->psiAudio = psi;
  1498. }
  1499. npMCI->nAudioStreams++;
  1500. return TRUE;
  1501. }
  1502. /***************************************************************************
  1503. *
  1504. * @doc INTERNAL MCIAVI
  1505. *
  1506. * @api BOOL | InitOtherStream | initialize a random stream
  1507. *
  1508. ***************************************************************************/
  1509. STATICFN BOOL NEAR PASCAL InitOtherStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1510. {
  1511. int stream = (int) (INT_PTR) (psi - npMCI->paStreamInfo);
  1512. /* Open the specified video compressor */
  1513. psi->hicDraw = ICDrawOpen(psi->sh.fccType,psi->sh.fccHandler,psi->lpFormat);
  1514. if (psi->hicDraw == NULL) {
  1515. DPF(("Unable to play stream!\n"));
  1516. return FALSE;
  1517. }
  1518. if (psi->cbData > 0) {
  1519. ICSetState(psi->hicDraw, psi->lpData, psi->cbData);
  1520. }
  1521. psi->dwFlags |= STREAM_ENABLED;
  1522. ////psi->dwFlags |= STREAM_OTHER;
  1523. npMCI->nOtherStreams++;
  1524. return TRUE;
  1525. }
  1526. /***************************************************************************
  1527. *
  1528. * @doc INTERNAL MCIAVI
  1529. *
  1530. * @api BOOL | CleanIndex | This function cleans up the index loaded by
  1531. * ReadIndex() it does the following when cleaning up the index:
  1532. *
  1533. * converts all offsets to be absolute
  1534. *
  1535. * converts "alpha" format index's into new format.
  1536. *
  1537. * computes the max buffer size needed to read this file.
  1538. *
  1539. ***************************************************************************/
  1540. INLINE static BOOL NEAR CleanIndex(NPMCIGRAPHIC npMCI)
  1541. {
  1542. LONG lScan;
  1543. AVIINDEXENTRY FAR * px;
  1544. AVIINDEXENTRY FAR * pxRec=NULL;
  1545. DWORD lIndexAdjust;
  1546. Assert(npMCI->hpIndex != NULL);
  1547. px = (AVIINDEXENTRY FAR *)npMCI->hpIndex;
  1548. #ifdef ALPHAFILES
  1549. if (npMCI->dwFlags & MCIAVI_USINGALPHAFORMAT)
  1550. lIndexAdjust = 0;
  1551. else
  1552. #endif
  1553. if (// (avihdr.dwFlags & AVIF_MUSTUSEINDEX) ||
  1554. (px->dwChunkOffset < 100))
  1555. lIndexAdjust = npMCI->dwMovieListOffset;
  1556. else
  1557. lIndexAdjust = (npMCI->dwMovieListOffset + sizeof(DWORD)) -
  1558. px->dwChunkOffset;
  1559. //!!! only compute this for the video stream! (or interleaved...)
  1560. npMCI->dwSuggestedBufferSize = 0; // lets get this exactly right
  1561. DPF(("Adjusting index by %ld bytes....\n", lIndexAdjust));
  1562. /* Can we do anything to see if the index is valid? */
  1563. for (lScan = 0; lScan < (LONG)npMCI->macIndex;
  1564. lScan++, ++((AVIINDEXENTRY _huge *)px)) {
  1565. DWORD ckid;
  1566. //
  1567. // adjust the offset to be absolute
  1568. //
  1569. px->dwChunkOffset += lIndexAdjust;
  1570. // get ckid
  1571. ckid = px->ckid;
  1572. //
  1573. // make sure the buffer size is right, ignore audio chunks because
  1574. // they are either in a 'rec' or we will be reading them into
  1575. // internal buffers not the main buffer.365
  1576. //
  1577. if (((npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) ||
  1578. ckid == listtypeAVIRECORD) &&
  1579. TWOCCFromFOURCC(ckid) != cktypeWAVEbytes) {
  1580. if (px->dwChunkLength + 8 > npMCI->dwSuggestedBufferSize)
  1581. npMCI->dwSuggestedBufferSize = px->dwChunkLength + 12;
  1582. }
  1583. #ifdef ALPHAFILES
  1584. //
  1585. // convert a "old" index to a new index
  1586. //
  1587. if (npMCI->dwFlags & MCIAVI_USINGALPHAFORMAT) {
  1588. switch(ckid) {
  1589. case ckidDIBbits:
  1590. px->dwFlags |= AVIIF_KEYFRAME;
  1591. px->ckid = MAKEAVICKID(cktypeDIBbits, 0);
  1592. break;
  1593. case ckidDIBcompressed:
  1594. px->ckid = MAKEAVICKID(cktypeDIBcompressed, 0);
  1595. break;
  1596. case ckidDIBhalfframe:
  1597. px->ckid = MAKEAVICKID(cktypeDIBhalf, 0);
  1598. break;
  1599. case ckidPALchange:
  1600. px->ckid = MAKEAVICKID(cktypePALchange, 0);
  1601. break;
  1602. case ckidWAVEbytes:
  1603. px->ckid = MAKEAVICKID(cktypeWAVEbytes, 1);
  1604. break;
  1605. case ckidWAVEsilence:
  1606. px->ckid = MAKEAVICKID(cktypeWAVEsilence, 1);
  1607. break;
  1608. case ckidAVIPADDING:
  1609. case ckidOLDPADDING:
  1610. px->ckid = ckidAVIPADDING;
  1611. break;
  1612. }
  1613. ckid = px->ckid;
  1614. }
  1615. #endif
  1616. //
  1617. // do special things with the video stream.
  1618. //
  1619. if (StreamFromFOURCC(ckid) == (UINT)npMCI->nVideoStream) {
  1620. //
  1621. // fix up bogus index's by adding any missing AVIIF_KEYFRAME
  1622. // bits. ie this only applies for RLE files.
  1623. //
  1624. if (TWOCCFromFOURCC(ckid) == cktypeDIBbits &&
  1625. VIDFMT(npMCI->nVideoStream)->biCompression <= BI_RLE8)
  1626. px->dwFlags |= AVIIF_KEYFRAME;
  1627. //
  1628. // for video streams, make sure the palette changes are marked
  1629. // as a no time chunk
  1630. //
  1631. if (TWOCCFromFOURCC(ckid) == cktypePALchange)
  1632. px->dwFlags |= AVIIF_NOTIME/*|AVIIF_PALCHANGE*/;
  1633. //
  1634. // make sure the 'REC ' list has the right flags.
  1635. //
  1636. if (pxRec) {
  1637. if ((px->dwFlags & AVIIF_KEYFRAME) !=
  1638. (pxRec->dwFlags & AVIIF_KEYFRAME)) {
  1639. // Record list does not have correct flags
  1640. pxRec->dwFlags &= ~AVIIF_KEYFRAME;
  1641. pxRec->dwFlags |= (px->dwFlags & AVIIF_KEYFRAME);
  1642. }
  1643. }
  1644. }
  1645. if (ckid == listtypeAVIRECORD) {
  1646. pxRec = px;
  1647. if (npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) {
  1648. DPF(("Non interleaved file with a 'REC ' in it?\n"));
  1649. npMCI->wEarlyRecords = max(npMCI->wEarlyVideo, npMCI->wEarlyAudio);
  1650. if (npMCI->wEarlyRecords > 0) {
  1651. DPF(("Interlaved file with bad header\n"));
  1652. npMCI->dwFlags &= ~MCIAVI_NOTINTERLEAVED;
  1653. }
  1654. }
  1655. }
  1656. }
  1657. return TRUE;
  1658. }
  1659. /***************************************************************************
  1660. *
  1661. * @doc INTERNAL MCIAVI
  1662. *
  1663. * @api BOOL | MakeFrameIndex | makes the frame index
  1664. *
  1665. * the frame index is a array of AVIFRAMEINDEX entries, one for each
  1666. * frame in the movie. using the frame index we can easily find
  1667. * a given frame, along with it's keyframe and palette.
  1668. *
  1669. ***************************************************************************/
  1670. static BOOL NEAR MakeFrameIndex(NPMCIGRAPHIC npMCI)
  1671. {
  1672. LONG nFrames;
  1673. LONG iFrameStart;
  1674. LONG iFrame;
  1675. LONG iKeyFrame;
  1676. LONG nKeyFrames;
  1677. LONG iScan;
  1678. LONG iNewIndex;
  1679. LONG iPalette;
  1680. BOOL fInterleaved;
  1681. DWORD ckid;
  1682. STREAMINFO *psi;
  1683. AVIINDEXENTRY _huge * pNewIndex;
  1684. AVIINDEXENTRY _huge * pIndexEntry;
  1685. AVIFRAMEINDEX _huge * pFrameIndex;
  1686. if (npMCI->nVideoStreams == 0)
  1687. return TRUE;
  1688. if (npMCI->hpFrameIndex != NULL)
  1689. return TRUE;
  1690. psi = npMCI->psiVideo;
  1691. Assert(psi != NULL);
  1692. fInterleaved = !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED);
  1693. if (fInterleaved &&
  1694. muldiv32(npMCI->dwRate, 1000, npMCI->dwScale) !=
  1695. muldiv32(psi->sh.dwRate, 1000, psi->sh.dwScale)) {
  1696. //
  1697. // master video stream should match the movie rate!
  1698. //
  1699. AssertSz(FALSE, "Video stream differnet rate than movie");
  1700. npMCI->dwRate = psi->sh.dwRate;
  1701. npMCI->dwScale = psi->sh.dwScale;
  1702. }
  1703. if (fInterleaved)
  1704. iFrameStart = -(LONG)npMCI->wEarlyRecords;
  1705. else
  1706. iFrameStart = -(LONG)npMCI->wEarlyVideo;
  1707. nFrames = npMCI->lFrames - iFrameStart;
  1708. // hpFrameIndex is modified to point partway into the block.
  1709. // therefore we need to remember the handle of the block so we
  1710. // can free it correctly (esp on NT)
  1711. if ( (npMCI->hgFrameIndex = GlobalAlloc(GMEM_SHARE|GHND, (DWORD)(nFrames+1) * sizeof(AVIFRAMEINDEX)))
  1712. && (npMCI->hpFrameIndex = GlobalLock(npMCI->hgFrameIndex))) {
  1713. } else {
  1714. // We failed to allocate, or we failed to lock down. Clean up
  1715. // and return an error.
  1716. if (npMCI->hgFrameIndex) {
  1717. GlobalFree(npMCI->hgFrameIndex);
  1718. npMCI->hgFrameIndex = 0;
  1719. }
  1720. DPF(("Couldn't allocate memory for frame index!\n"));
  1721. return FALSE;
  1722. }
  1723. //
  1724. // do this so we can just index the array with the frame number
  1725. // (positive or neg)
  1726. //
  1727. npMCI->hpFrameIndex += (-iFrameStart);
  1728. pFrameIndex = npMCI->hpFrameIndex;
  1729. iFrame = iFrameStart;
  1730. iKeyFrame = -(LONG)npMCI->wEarlyVideo; // iFrameStart;
  1731. iNewIndex = 0;
  1732. iPalette = -1; // first palette
  1733. nKeyFrames= 0;
  1734. #ifdef USEAVIFILE
  1735. if (npMCI->pf) {
  1736. PAVISTREAM ps = SI(npMCI->nVideoStream)->ps;
  1737. for (iFrame = 0; iFrame < npMCI->lFrames; iFrame++) {
  1738. LONG iKey;
  1739. iKey = AVIStreamFindSample(ps,iFrame,FIND_PREV|FIND_KEY);
  1740. iPalette = AVIStreamFindSample(ps,iFrame,FIND_PREV|FIND_FORMAT);
  1741. if (iKey != -1)
  1742. iKeyFrame = iKey;
  1743. if (iPalette == -1)
  1744. iPalette = 0;
  1745. pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
  1746. pFrameIndex[iFrame].iNextKey = 0;
  1747. pFrameIndex[iFrame].iPalette = (WORD)iPalette;
  1748. pFrameIndex[iFrame].dwOffset = 0;
  1749. pFrameIndex[iFrame].dwLength = 0;
  1750. Assert(iPalette <= 0xFFFF);
  1751. if (iFrame - iKeyFrame > 0xFFFF) {
  1752. //!!! we need to set a flag!
  1753. //!!! we need to throw out the index!
  1754. AssertSz(FALSE, "File has too few key frames");
  1755. pFrameIndex[iFrame].iPrevKey = 0;
  1756. }
  1757. }
  1758. goto ack;
  1759. }
  1760. #endif
  1761. Assert(npMCI->hpIndex != NULL);
  1762. Assert(npMCI->macIndex != 0L);
  1763. pNewIndex = npMCI->hpIndex;
  1764. pIndexEntry = npMCI->hpIndex;
  1765. for (iScan = 0; iScan < (LONG)npMCI->macIndex; iScan++, pIndexEntry++) {
  1766. ckid = pIndexEntry->ckid;
  1767. //
  1768. // check for palette changes.
  1769. //
  1770. if (StreamFromFOURCC(ckid) == (UINT)npMCI->nVideoStream &&
  1771. TWOCCFromFOURCC(ckid) == cktypePALchange) {
  1772. iPalette = iNewIndex;
  1773. pNewIndex[iNewIndex++] = *pIndexEntry;
  1774. if (fInterleaved)
  1775. pFrameIndex[iFrame-1].iPalette = (WORD)iPalette;
  1776. }
  1777. //
  1778. // remove the video stream from the master index
  1779. //
  1780. if ((ckid != listtypeAVIRECORD) &&
  1781. (StreamFromFOURCC(ckid) != (UINT)npMCI->nVideoStream)) {
  1782. pNewIndex[iNewIndex++] = *pIndexEntry;
  1783. }
  1784. //
  1785. // in interleaved files a "frame" happens every list record
  1786. //
  1787. // in non-interleaved files a "frame" happens every piece of
  1788. // data in the video stream (except no time chunks)
  1789. //
  1790. if (fInterleaved) {
  1791. if (ckid != listtypeAVIRECORD)
  1792. continue;
  1793. } else {
  1794. if ((StreamFromFOURCC(ckid) != (UINT)npMCI->nVideoStream) ||
  1795. (pIndexEntry->dwFlags & AVIIF_NOTIME))
  1796. continue;
  1797. }
  1798. AssertSz(iFrame < npMCI->lFrames,"Too many frames in index!");
  1799. if (iFrame >= npMCI->lFrames) {
  1800. break;
  1801. }
  1802. if (pIndexEntry->dwFlags & AVIIF_KEYFRAME) {
  1803. iKeyFrame = iFrame;
  1804. nKeyFrames++;
  1805. }
  1806. pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
  1807. pFrameIndex[iFrame].iNextKey = 0;
  1808. pFrameIndex[iFrame].iPalette = (WORD)iPalette;
  1809. pFrameIndex[iFrame].dwOffset = pIndexEntry->dwChunkOffset;
  1810. pFrameIndex[iFrame].dwLength = pIndexEntry->dwChunkLength;
  1811. if (fInterleaved)
  1812. pFrameIndex[iFrame].dwOffset += 3 * sizeof(DWORD);
  1813. Assert(iPalette <= 0xFFFF);
  1814. if (iFrame - iKeyFrame > 0xFFFF) {
  1815. //!!! we need to set a flag!
  1816. //!!! we need to throw out the index!
  1817. AssertSz(FALSE, "File has too few key frames");
  1818. pFrameIndex[iFrame].iPrevKey = 0;
  1819. }
  1820. iFrame++;
  1821. }
  1822. ack:
  1823. //
  1824. // iFrame better equal npMCI->lFrames
  1825. //
  1826. Assert(iFrame == npMCI->lFrames);
  1827. if (iFrame < npMCI->lFrames)
  1828. npMCI->lFrames = iFrame;
  1829. //
  1830. // make a "dummy" last frame
  1831. //
  1832. pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
  1833. pFrameIndex[iFrame].iNextKey = 0;
  1834. pFrameIndex[iFrame].iPalette = (WORD)iPalette;
  1835. pFrameIndex[iFrame].dwOffset = 0;
  1836. pFrameIndex[iFrame].dwLength = 0;
  1837. //
  1838. // compute the key frames every value
  1839. //
  1840. if (nKeyFrames) {
  1841. if (nKeyFrames > 1)
  1842. npMCI->dwKeyFrameInfo = (DWORD)((nFrames + nKeyFrames/2)/nKeyFrames);
  1843. else
  1844. npMCI->dwKeyFrameInfo = 0;
  1845. }
  1846. //
  1847. // now go through the index, and fix all the iNextKey fields
  1848. //
  1849. pFrameIndex = npMCI->hpFrameIndex;
  1850. ////iKeyFrame = npMCI->lFrames; //!!! what should this be set to? zero?
  1851. for (iFrame = npMCI->lFrames; iFrame>=iFrameStart; iFrame--)
  1852. {
  1853. if (pFrameIndex[iFrame].iPrevKey == 0)
  1854. iKeyFrame = iFrame;
  1855. if (iKeyFrame >= iFrame)
  1856. pFrameIndex[iFrame].iNextKey = (UINT)(iKeyFrame - iFrame);
  1857. else
  1858. pFrameIndex[iFrame].iNextKey = 0xFFFF; // way far away
  1859. if (iKeyFrame - iFrame > 0xFFFF) {
  1860. //!!! we need to set a flag!
  1861. //!!! we need to throw out the index!
  1862. AssertSz(FALSE, "File has too few key frames");
  1863. pFrameIndex[iFrame].iNextKey = 0;
  1864. }
  1865. }
  1866. //
  1867. // we dont need the index, if we are using AVIFile or
  1868. // we have a interleaved file. when the file is interleaved
  1869. // we never do random access (except for palette changes)
  1870. //
  1871. // !!!this is not true, we need the index iff we have a audio only
  1872. // file or we play a interleaved file real slow.
  1873. //
  1874. if (npMCI->pf /* ||
  1875. (fInterleaved && !(npMCI->dwFlags & MCIAVI_ANIMATEPALETTE))*/ ) {
  1876. DOUT("The Master index must go!\n");
  1877. iNewIndex = 0;
  1878. }
  1879. //
  1880. // now re-alloc the master index down to size.
  1881. //
  1882. // !!! do we even need the master index anymore, for interleaved files?
  1883. //
  1884. DPF(("Master index was %ld entries now %ld\n",npMCI->macIndex, iNewIndex));
  1885. npMCI->macIndex = iNewIndex;
  1886. if (iNewIndex > 0) {
  1887. npMCI->hpIndex = (AVIINDEXENTRY _huge *)
  1888. GlobalReAllocPtr(npMCI->hpIndex,
  1889. (LONG)iNewIndex * sizeof(AVIINDEXENTRY),
  1890. GMEM_MOVEABLE | GMEM_SHARE);
  1891. Assert(npMCI->hpIndex != NULL);
  1892. }
  1893. else {
  1894. if (npMCI->hpIndex)
  1895. GlobalFreePtr(npMCI->hpIndex);
  1896. npMCI->hpIndex = NULL;
  1897. }
  1898. return TRUE;
  1899. }
  1900. /***************************************************************************
  1901. *
  1902. * @doc INTERNAL MCIAVI
  1903. *
  1904. * @api BOOL | ReadIndex | Read the index into npMCI->hpIndex. Should
  1905. * only be called if the HASINDEX flag is set.
  1906. *
  1907. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data
  1908. *
  1909. * @rdesc TRUE means no errors, false means unable to read index.
  1910. *
  1911. ***************************************************************************/
  1912. BOOL FAR PASCAL ReadIndex(NPMCIGRAPHIC npMCI)
  1913. {
  1914. MMCKINFO ck;
  1915. DWORD dwOldPos;
  1916. if (npMCI->hpIndex || npMCI->hpFrameIndex)
  1917. return TRUE;
  1918. if (!(npMCI->dwFlags & MCIAVI_HASINDEX))
  1919. return FALSE;
  1920. if (npMCI->pf) {
  1921. MakeFrameIndex(npMCI);
  1922. return TRUE;
  1923. }
  1924. #if 0
  1925. if (GetCurrentTask() != npMCI->hTask) {
  1926. /* this function is called (from GraphicStatus) when
  1927. * possibly playing - so we have to suspend play while we read
  1928. * the index.
  1929. */
  1930. // won't work with the current multi-thread design
  1931. // TEMPORARYSTATE ts;
  1932. // if (StopTemporarily(npMCI, &ts) == 0) {
  1933. // mciaviTaskMessage(npMCI, TASKREADINDEX);
  1934. // RestartAgain(npMCI, &ts);
  1935. // return (npMCI->hpIndex != NULL);
  1936. // }
  1937. return(FALSE);
  1938. }
  1939. #else
  1940. if (GetCurrentTask() != npMCI->hTask)
  1941. return FALSE;
  1942. #endif
  1943. dwOldPos = mmioSeek(npMCI->hmmio, 0, SEEK_CUR);
  1944. DPF(("Reading index: starting from %lx\n", npMCI->dwBigListEnd));
  1945. if (mmioSeek(npMCI->hmmio, npMCI->dwBigListEnd, SEEK_SET) == -1) {
  1946. IndexReadError:
  1947. DPF(("Error reading index!\n"));
  1948. npMCI->dwFlags &= ~(MCIAVI_HASINDEX);
  1949. mmioSeek(npMCI->hmmio, dwOldPos, SEEK_SET);
  1950. return FALSE;
  1951. }
  1952. ck.ckid = ckidAVINEWINDEX;
  1953. if (mmioDescend(npMCI->hmmio, &ck, NULL, MMIO_FINDCHUNK) != 0) {
  1954. goto IndexReadError;
  1955. }
  1956. /* A zero-size index isn't much good. */
  1957. if (ck.cksize == 0)
  1958. goto IndexReadError;
  1959. npMCI->macIndex = ck.cksize / sizeof(AVIINDEXENTRY);
  1960. npMCI->hpIndex = (AVIINDEXENTRY _huge *)
  1961. GlobalAllocPtr(GMEM_SHARE | GMEM_MOVEABLE, ck.cksize);
  1962. if (!npMCI->hpIndex) {
  1963. DPF(("Insufficient memory to read index.\n"));
  1964. goto IndexReadError;
  1965. }
  1966. #ifndef _WIN32
  1967. Assert(OFFSETOF(npMCI->hpIndex) == 0);
  1968. #endif
  1969. if (mmioRead(npMCI->hmmio, (HPSTR) npMCI->hpIndex, ck.cksize) != (LONG) ck.cksize) {
  1970. Assert(0);
  1971. goto IndexReadError;
  1972. }
  1973. CleanIndex(npMCI);
  1974. MakeFrameIndex(npMCI);
  1975. ////should we do this for audio? remove video data?
  1976. ////MakeStreamIndex(npMCI, ???);
  1977. mmioSeek(npMCI->hmmio, dwOldPos, SEEK_SET);
  1978. return TRUE;
  1979. }
  1980. /***************************************************************************
  1981. * @doc INTERNAL MCIAVI
  1982. *
  1983. * @api BOOL | IsRectBogus | 'nuf said
  1984. *
  1985. ***************************************************************************/
  1986. INLINE static BOOL NEAR PASCAL IsRectBogus(RECT* prc)
  1987. {
  1988. if (prc->right - prc->left <= 0 ||
  1989. prc->bottom - prc->top <= 0 ||
  1990. prc->bottom <= 0 ||
  1991. prc->right <= 0)
  1992. return TRUE;
  1993. else
  1994. return FALSE;
  1995. }
  1996. #ifndef _WIN32 // _WIN32 has one!
  1997. /***************************************************************************
  1998. *
  1999. * @doc INTERNAL MCIAVI
  2000. *
  2001. * @api LONG | atol | local version of atol
  2002. *
  2003. ***************************************************************************/
  2004. static LONG NEAR PASCAL atol(char *sz)
  2005. {
  2006. LONG l = 0;
  2007. while (*sz && *sz >= '0' && *sz <= '9')
  2008. l = l*10 + *sz++ - '0';
  2009. return l;
  2010. }
  2011. #endif
  2012. #ifndef _WIN32
  2013. /*--------------------------------------------------------------------------
  2014. *
  2015. * IsCDROMDrive() -
  2016. *
  2017. * Purpose: Return non-zero if a RAM drive
  2018. *
  2019. * wDrive drive index (0=A, 1=B, ...)
  2020. *
  2021. * return TRUE/FALSE
  2022. *-------------------------------------------------------------------------*/
  2023. #pragma optimize("", off)
  2024. static BOOL NEAR PASCAL IsCDROMDrive(UINT wDrive)
  2025. {
  2026. BOOL f;
  2027. _asm {
  2028. mov ax, 1500h /* first test for presence of MSCDEX */
  2029. xor bx, bx
  2030. int 2fh
  2031. mov ax, bx /* MSCDEX is not there if bx is still zero */
  2032. or ax, ax /* ...so return FALSE from this function */
  2033. jz no_mscdex
  2034. mov ax, 150bh /* MSCDEX driver check API */
  2035. mov cx, wDrive /* ...cx is drive index */
  2036. int 2fh
  2037. no_mscdex:
  2038. mov f,ax
  2039. }
  2040. return f;
  2041. }
  2042. #pragma optimize("", on)
  2043. /***************************************************************************
  2044. *
  2045. * @doc INTERNAL MCIAVI
  2046. *
  2047. * @api BOOL | IsNetFile | is the passed file on a network drive?
  2048. *
  2049. ***************************************************************************/
  2050. static BOOL NEAR PASCAL IsNetFile(LPTSTR szFile)
  2051. {
  2052. OFSTRUCT of;
  2053. if (OpenFile(szFile, &of, OF_PARSE) == -1)
  2054. return FALSE;
  2055. AnsiUpper(of.szPathName);
  2056. if (of.szPathName[0] == '\\' && of.szPathName[1] == '\\')
  2057. return TRUE;
  2058. if (of.szPathName[0] == '/' && of.szPathName[1] == '/')
  2059. return TRUE;
  2060. if (of.szPathName[1] == ':' &&
  2061. GetDriveType(of.szPathName[0] - 'A') == DRIVE_REMOTE &&
  2062. !IsCDROMDrive(of.szPathName[0] - 'A'))
  2063. return TRUE;
  2064. return FALSE;
  2065. }
  2066. /***************************************************************************
  2067. *
  2068. * @doc INTERNAL MCIAVI
  2069. *
  2070. * @api BOOL | IsCDROMFile | is the passed file on a CD-ROM drive?
  2071. *
  2072. ***************************************************************************/
  2073. static BOOL NEAR PASCAL IsCDROMFile(LPTSTR szFile)
  2074. {
  2075. OFSTRUCT of;
  2076. if (OpenFile(szFile, &of, OF_PARSE) == -1)
  2077. return FALSE;
  2078. AnsiUpper(of.szPathName);
  2079. if (of.szPathName[0] == '\\' && of.szPathName[1] == '\\')
  2080. return FALSE;
  2081. if (of.szPathName[0] == '/' && of.szPathName[1] == '/')
  2082. return FALSE;
  2083. if (of.szPathName[1] == ':' &&
  2084. GetDriveType(of.szPathName[0] - 'A') == DRIVE_REMOTE &&
  2085. IsCDROMDrive(of.szPathName[0] - 'A'))
  2086. return TRUE;
  2087. return FALSE;
  2088. }
  2089. /***************************************************************************
  2090. *
  2091. * @doc INTERNAL MCIAVI
  2092. *
  2093. * @api UINT | GetFileDriveType | return drive type given a file
  2094. *
  2095. * DRIVE_CDROM
  2096. * DRIVE_REMOTE
  2097. * DRIVE_FIXED
  2098. * DRIVE_REMOVABLE
  2099. *
  2100. ***************************************************************************/
  2101. static UINT NEAR PASCAL GetFileDriveType(LPSTR szPath)
  2102. {
  2103. if (IsCDROMFile(szPath))
  2104. return DRIVE_CDROM;
  2105. if (IsNetFile(szPath))
  2106. return DRIVE_REMOTE;
  2107. if (szPath[1] == ':')
  2108. return GetDriveType(szPath[0] - 'A');
  2109. return 0;
  2110. }
  2111. #else // _WIN32
  2112. /*
  2113. * We cannot pass the call directly to GetDriveType as we have a full
  2114. * pathname - and that gets rejected by GetFileDriveType. Using a
  2115. * static variable is ok - the open call is serialised. The assumption
  2116. * is that this routine is ONLY called with complete path information.
  2117. */
  2118. TCHAR DriveType[4] = TEXT(".:\\");
  2119. static UINT NEAR PASCAL GetFileDriveType(LPTSTR szPath)
  2120. {
  2121. Assert(szPath != NULL);
  2122. if (*szPath == TEXT('\\')) {
  2123. Assert(szPath[1] == TEXT('\\'));
  2124. return(DRIVE_REMOTE);
  2125. }
  2126. if (szPath[1] == TEXT(':')) {
  2127. DriveType[0] = szPath[0];
  2128. return GetDriveType(DriveType);
  2129. }
  2130. return 0;
  2131. }
  2132. #endif