Source code of Windows XP (NT5)
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.

2680 lines
94 KiB

  1. /* Copyright (c) 1991-1999 Microsoft Corporation */
  2. /*-------------------------------------------------------------------*\
  3. *
  4. * mmio.c
  5. *
  6. * Basic MMIO functions.
  7. *
  8. \*-------------------------------------------------------------------*/
  9. /*--------------------------------------------------------------------*/
  10. /* Revision history:
  11. * LaurieGr: Jan 92 Ported from win16. Source tree fork, not common code.
  12. * StephenE: Apr 92 Enabled UNICODE.
  13. */
  14. /*--------------------------------------------------------------------*/
  15. /*--------------------------------------------------------------------*/
  16. /* Implementation notes:
  17. *
  18. * An HMMIO is in fact a PMMIO i.e. a pointer to a MMIOINFO.
  19. * This causes the code to be littered with casts.
  20. * Whoever exported MMIOINFO should learn about encapsulation and
  21. * all that stuff. sigh.
  22. *
  23. * The "current disk offset" is the disk offset (i.e. the location
  24. * in the disk file) that the next MMIOM_READ or MMIOM_WRITE will
  25. * read from or write to. The I/O procedure maintains the
  26. * <lDiskOffset> field of the file's MMIO structure so that
  27. * <lDiskOffset> is equal to the current disk offset.
  28. *
  29. * The "current buffered offset" is the disk offset that the next
  30. * mmioRead() or mmioWrite() call would read from or write to.
  31. * The current buffered offset is defined as
  32. *
  33. * <lBufOffset> + (<pchNext> - <pchBuffer>)
  34. *
  35. * since <lBufOffset> is the disk offset of the start of the buffer
  36. * and <pchNext> corresponds to the current buffered offset.
  37. *
  38. * If the file is unbuffered, then <pchBuffer>, <pchNext>,
  39. * <pchEndRead> and <pchEndWrite> will always be NULL, and
  40. * <lBufOffset> will always be considered the "current buffered
  41. * offset", i.e. mmioRead() and mmioWrite() will read/write
  42. * at this offset.
  43. *
  44. *
  45. * Except right at the beginning of mmioOpen(), the MMIO_ALLOCBUF
  46. * flag is set if and only if the pchBuffer field points to a block
  47. * of global memory that MMIO has allocated.
  48. */
  49. /*--------------------------------------------------------------------*/
  50. #include "winmmi.h"
  51. #include "mmioi.h"
  52. /*--------------------------------------------------------------------*\
  53. * Local function prototypes
  54. \*--------------------------------------------------------------------*/
  55. static void NEAR PASCAL SetIOProc( LPCWSTR szFileName, LPMMIOINFO lpmmio);
  56. static LPMMIOPROC NEAR PASCAL RemoveIOProc(FOURCC fccIOProc, HANDLE htask);
  57. static LONG NEAR PASCAL mmioDiskIO(PMMIO pmmio, UINT uMsg, LPSTR pch, LONG cch);
  58. static UINT NEAR PASCAL mmioExpandMemFile(PMMIO pmmio, LONG lExpand);
  59. static LPMMIOPROC mmioInternalInstallIOProc( FOURCC fccIOProc,
  60. LPMMIOPROC pIOProc,
  61. DWORD dwFlags);
  62. /*--------------------------------------------------------------------*/
  63. /* The I/O procedure map is a linked list of IOProcMapEntry structures.
  64. * The head of the list, <gIOProcMapHead> is a pointer node to the last
  65. * entry registered. The first few elements of the list are the predefined
  66. * global IO procedures below -- these all have <hTask> equal to NULL so
  67. * that no task can unregister them.
  68. *
  69. */
  70. typedef struct IOProcMapEntryTag
  71. {
  72. FOURCC fccIOProc; // ID of installed I/O procedure
  73. LPMMIOPROC pIOProc; // I/O procedure address
  74. HANDLE hTask; // task that called mmioRegisterIOProc()
  75. struct IOProcMapEntryTag *pNext; // pointer to next IOProc entry
  76. } IOProcMapEntry, *pIOProcMapEntry;
  77. // MMIOPROC is defined in the public MMSYSTEM.H
  78. // typedef LONG (APIENTRY MMIOPROC)(LPSTR lpmmioinfo, UINT uMsg, LONG lParam1, LONG lParam2);
  79. MMIOPROC mmioDOSIOProc, mmioMEMIOProc; // standard I/O procedures
  80. static IOProcMapEntry gIOProcMaps[] = {
  81. { FOURCC_DOS, mmioDOSIOProc, NULL, &gIOProcMaps[1] },
  82. { FOURCC_MEM, mmioMEMIOProc, NULL, NULL }
  83. };
  84. //
  85. // Global head of list
  86. //
  87. static pIOProcMapEntry gIOProcMapHead = gIOProcMaps;
  88. #ifdef DUMPIOPROCLIST
  89. /* debug dump of ioproclist */
  90. static void DumpIOProcList(void)
  91. { pIOProcMapEntry pph;
  92. dprintf(("gIOProcMapHead= %8x\n",gIOProcMapHead ));
  93. for (pph = gIOProcMapHead;pph ;pph=pph->pNext)
  94. { dprintf(( "fourcc=%c%c%c%c pioproc=%8x hTask=%8x\n"
  95. , pph->fccIOProc/16777216
  96. , (pph->fccIOProc/65536)%256
  97. , (pph->fccIOProc/256)%256
  98. , (pph->fccIOProc)%256
  99. , pph->pIOProc
  100. , pph->hTask
  101. ));
  102. }
  103. } /* DumpIOProcList */
  104. #endif
  105. /* Call the IOProc in the info structure and return the result.
  106. Take due account of whether it is a 16 or 32 bit IOProc.
  107. */
  108. static LRESULT IOProc(LPMMIOINFO lpmmioinfo, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
  109. {
  110. /* just pass the call on */
  111. return ((LPMMIOPROC)(lpmmioinfo->pIOProc)) ((LPSTR)lpmmioinfo, uMsg, lParam1, lParam2);
  112. } /* IOProc */
  113. /*--------------------------------------------------------------------*/
  114. /* @doc INTERNAL
  115. @func LPMMIOPROC | FindIOProc | This function locates the IOProcMapEntry
  116. for a previously installed IO procedure .
  117. */
  118. /*--------------------------------------------------------------------*/
  119. static pIOProcMapEntry
  120. FindIOProc(FOURCC fccIOProc, HANDLE htask)
  121. {
  122. IOProcMapEntry *pEnt; // an entry in linked list
  123. /* walk through the linked list, first looking for an entry with
  124. * identifier <fccIOProc> that was added by the current task, then
  125. * looking for global entries.
  126. */
  127. for (pEnt = gIOProcMapHead; pEnt; pEnt = pEnt->pNext)
  128. if ((pEnt->fccIOProc == fccIOProc) && (pEnt->hTask == htask))
  129. return pEnt;
  130. for (pEnt = gIOProcMapHead; pEnt; pEnt = pEnt->pNext)
  131. if ( (pEnt->fccIOProc == fccIOProc)
  132. // ?? && (pEnt->hTask ==NULL) ??
  133. )
  134. return pEnt;
  135. return NULL;
  136. }
  137. /*--------------------------------------------------------------------*/
  138. /* @doc INTERNAL
  139. @func LPMMIOPROC | RemoveIOProc | This function removes previously installed
  140. IO procedure.
  141. */
  142. /*--------------------------------------------------------------------*/
  143. static LPMMIOPROC PASCAL NEAR
  144. RemoveIOProc(FOURCC fccIOProc, HANDLE htask)
  145. {
  146. IOProcMapEntry *pEnt; // an entry in linked list
  147. IOProcMapEntry *pEntPrev; // the entry before <pEnt>
  148. /* walk through the linked list, looking for an entry with
  149. * identifier <fccIOProc> that was added by the current task
  150. */
  151. for ( pEntPrev = NULL, pEnt = gIOProcMapHead
  152. ; pEnt
  153. ; pEntPrev = pEnt, pEnt = pEnt->pNext
  154. )
  155. if ((pEnt->fccIOProc == fccIOProc) && (pEnt->hTask == htask)) {
  156. LPMMIOPROC pIOProc;
  157. pIOProc = pEnt->pIOProc;
  158. if (pEntPrev)
  159. pEntPrev->pNext = pEnt->pNext;
  160. else
  161. gIOProcMapHead = pEnt->pNext;
  162. FreeHandle((HMMIO) pEnt);
  163. return pIOProc;
  164. }
  165. return NULL;
  166. }
  167. /*--------------------------------------------------------------------*/
  168. /* @doc INTERNAL
  169. @func void | SetIOProc | This function sets the physical IO procedure
  170. based on either the file name or the parameters within the
  171. <p lpmmioinfo> structure passed.
  172. @parm LPCWSTR | szFilename | Specifies a pointer to a string
  173. containing the filename of the file to open. If no I/O procedure is
  174. @parm LPMMIOINFO | lpmmioinfo | Specifies a pointer to an
  175. <t MMIOINFO> structure containing extra parameters used by
  176. <f SetIOProc> in determining the IO procedure to use. The
  177. <e MMIOINFO.pIOProc> element is set to the procedure found.
  178. @rdesc Nothing.
  179. */
  180. /*--------------------------------------------------------------------*/
  181. static void NEAR PASCAL
  182. SetIOProc( LPCWSTR szFileName, LPMMIOINFO lpmmio)
  183. {
  184. IOProcMapEntry *pEnt; // the entry in linked list
  185. /* If the IOProc is not given, see if the file name implies that
  186. * <szFileName> is either a RIFF compound file or some kind of
  187. * other registered storage system -- look for the last CFSEPCHAR in
  188. * the name, e.g. '+' in "foo.bnd+bar.hlp+blorg.dib", and figure
  189. * that the IOProc ID is the extension of the compound file name,
  190. * e.g. the extension of "foo.bnd+bar.hlp", i.e. 'HLP '.
  191. *
  192. * Alternatively, if <szFileName> is NULL, then assume that
  193. * <lpmmio->adwInfo[0]> is a DOS file handle.
  194. */
  195. if (lpmmio->pIOProc == NULL)
  196. {
  197. if (lpmmio->fccIOProc == 0)
  198. {
  199. if (szFileName != NULL)
  200. {
  201. LPWSTR pch;
  202. /* see if <szFileName> contains CFSEPCHAR */
  203. if ((pch = wcsrchr(szFileName, CFSEPCHAR)) != 0)
  204. {
  205. /* find the extension that precedes CFSEPCHAR,
  206. * e.g. "hlp" in "foo.bnd+bar.hlp+blorg.dib"
  207. */
  208. while ( (pch > szFileName)
  209. && (*pch != '.')
  210. && (*pch != ':')
  211. && (*pch != '\\')
  212. )
  213. pch--;
  214. if (*pch == '.')
  215. {
  216. WCHAR aszFour[sizeof(FOURCC)+1];
  217. int i;
  218. for (i = 0, pch++; i < sizeof(FOURCC); i++)
  219. if (*pch == CFSEPCHAR)
  220. aszFour[i] = (WCHAR)0;
  221. else
  222. aszFour[i] = *pch++;
  223. aszFour[sizeof(FOURCC)] = (WCHAR)0;
  224. lpmmio->fccIOProc
  225. = mmioStringToFOURCCW(aszFour, MMIO_TOUPPER);
  226. }
  227. }
  228. }
  229. /* if the caller didn't specify an IOProc, and the code above
  230. * didn't determine an IOProc ID, then the default is the DOS
  231. * IOProc.
  232. */
  233. if (lpmmio->fccIOProc == 0)
  234. lpmmio->fccIOProc = FOURCC_DOS;
  235. }
  236. /* unless an IOProc address is specified explicitly, look up the
  237. * IOProc in the global IOProc ID-to-address table -- the default
  238. * is 'DOS' since we'll assume that custom storage system I/O
  239. * procedures would have been installed
  240. */
  241. pEnt = FindIOProc( lpmmio->fccIOProc
  242. , lpmmio->htask
  243. ? lpmmio->htask
  244. : GetCurrentTask()
  245. );
  246. if (pEnt && pEnt->pIOProc) {
  247. lpmmio->pIOProc = pEnt -> pIOProc;
  248. }
  249. else {
  250. lpmmio->pIOProc = mmioDOSIOProc;
  251. lpmmio->dwReserved1 = 0;
  252. }
  253. }
  254. }
  255. /*--------------------------------------------------------------------*/
  256. /* @doc INTERNAL
  257. @func void | mmioCleanupIOProcs | removes from the linked list entries
  258. installed with the given task handle
  259. @parm HANDLE | hTask | Specifies the task to clean up for
  260. @rdesc Nothing.
  261. @comm This will only be called to clean up a WOW task.
  262. */
  263. /*--------------------------------------------------------------------*/
  264. void mmioCleanupIOProcs(HANDLE hTask)
  265. {
  266. IOProcMapEntry *pEnt;
  267. IOProcMapEntry *pEntPrev;
  268. for (pEntPrev = NULL, pEnt = gIOProcMapHead; pEnt;) {
  269. if (pEnt->hTask == hTask) {
  270. dprintf1(("MMIOPROC handle (%04X) not closed.", pEnt));
  271. if (pEntPrev) {
  272. pEntPrev->pNext = pEnt->pNext;
  273. FreeHandle((HMMIO)pEnt);
  274. pEnt = pEntPrev->pNext;
  275. } else {
  276. gIOProcMapHead = pEnt->pNext;
  277. FreeHandle((HMMIO)pEnt);
  278. pEnt = gIOProcMapHead;
  279. }
  280. } else {
  281. pEntPrev = pEnt;
  282. pEnt = pEnt->pNext;
  283. }
  284. }
  285. }
  286. /*--------------------------------------------------------------------*/
  287. /* @doc EXTERNAL
  288. @api UINT | mmioRename | This function renames the specified file.
  289. @parm LPCTSTR | szFilename | Specifies a pointer to a string
  290. containing the filename of the file to rename.
  291. @parm LPCTSTR | szNewFileName | Specifies a pointer to a string
  292. containing the new filename.
  293. @parm LPMMIOINFO | lpmmioinfo | Specifies a pointer to an
  294. <t MMIOINFO> structure containing extra parameters used by
  295. <f mmioRename>.
  296. If <p lpmmioinfo> is not NULL, all unused fields of the
  297. <t MMIOINFO> structure it references must be set to zero, including the
  298. reserved fields.
  299. @parm DWORD | dwRenameFlags | Specifies option flags for the rename
  300. operation. This should be set to zero.
  301. @rdesc The return value is zero if the file was renamed. Otherwise, the
  302. return value is an error code returned from <f mmioRename> or from the I/O
  303. procedure.
  304. */
  305. /*--------------------------------------------------------------------*/
  306. UINT APIENTRY
  307. mmioRenameW( LPCWSTR szFileName
  308. , LPCWSTR szNewFileName
  309. , LPCMMIOINFO lpmmioinfo
  310. , DWORD fdwRename
  311. )
  312. {
  313. MMIOINFO mmioinfo;
  314. ZeroMemory( &mmioinfo, sizeof( MMIOINFO ) );
  315. V_RPOINTER0(lpmmioinfo, sizeof(MMIOINFO), MMSYSERR_INVALPARAM);
  316. if (lpmmioinfo) {
  317. V_CALLBACK0((FARPROC)lpmmioinfo->pIOProc, MMSYSERR_INVALPARAM);
  318. mmioinfo = *lpmmioinfo;
  319. }
  320. SetIOProc(szFileName, &mmioinfo);
  321. if ( (mmioinfo.dwFlags & MMIO_UNICODEPROC )
  322. || (mmioinfo.pIOProc == mmioDOSIOProc ) // or the DOS file IO Proc
  323. || (mmioinfo.pIOProc == mmioMEMIOProc ) ) { // or a memory file IO Proc
  324. /*------------------------------------------------------------*\
  325. * We have an unicode IO Proc so use the given file names
  326. * without any conversion.
  327. \*------------------------------------------------------------*/
  328. return (UINT)IOProc( &mmioinfo, MMIOM_RENAME,
  329. (LPARAM)szFileName, (LPARAM)szNewFileName );
  330. } else {
  331. UINT uiRc;
  332. LPSTR pAsciiFileName; // Ascii version of szFileName
  333. LPSTR pAsciiNewFileName; // Ascii version of szNewFileName
  334. /*------------------------------------------------------------*\
  335. * We have an ascii IO Proc so convert the given file names
  336. * into ascii.
  337. \*------------------------------------------------------------*/
  338. pAsciiFileName = AllocAsciiStr( szFileName );
  339. if ( pAsciiFileName == (LPSTR)NULL ) {
  340. return MMIOERR_OUTOFMEMORY;
  341. }
  342. pAsciiNewFileName = AllocAsciiStr( szNewFileName );
  343. if ( pAsciiNewFileName == (LPSTR)NULL ) {
  344. FreeAsciiStr( pAsciiFileName );
  345. return MMIOERR_OUTOFMEMORY;
  346. }
  347. uiRc = (UINT)IOProc( &mmioinfo,
  348. MMIOM_RENAME,
  349. (LPARAM)pAsciiFileName,
  350. (LPARAM)pAsciiNewFileName );
  351. FreeAsciiStr( pAsciiFileName );
  352. FreeAsciiStr( pAsciiNewFileName );
  353. return uiRc;
  354. }
  355. }
  356. UINT APIENTRY
  357. mmioRenameA( LPCSTR szFileName
  358. , LPCSTR szNewFileName
  359. , LPCMMIOINFO lpmmioinfo
  360. , DWORD fdwRename
  361. )
  362. {
  363. MMIOINFO mmioinfo;
  364. LPWSTR pUnicodeFileName;
  365. LPWSTR pUnicodeNewFileName;
  366. UINT uiRc;
  367. ZeroMemory( &mmioinfo, sizeof( MMIOINFO ) );
  368. V_RPOINTER0(lpmmioinfo, sizeof(MMIOINFO), MMSYSERR_INVALPARAM);
  369. if (lpmmioinfo) {
  370. V_CALLBACK0((FARPROC)lpmmioinfo->pIOProc, MMSYSERR_INVALPARAM);
  371. mmioinfo = *lpmmioinfo;
  372. }
  373. /*----------------------------------------------------------------*\
  374. * SetIOProc only works with unicode strings, therefore we always
  375. * have to convert szFileName to unicode, so:
  376. * Allocate some storage to hold the unicode version of szFileName.
  377. * Do the acsii to unicode conversion .
  378. * Call SetIOProc
  379. \*----------------------------------------------------------------*/
  380. pUnicodeFileName = AllocUnicodeStr( szFileName );
  381. if ( pUnicodeFileName == (LPWSTR)NULL ) {
  382. return MMIOERR_OUTOFMEMORY;
  383. }
  384. SetIOProc( pUnicodeFileName, &mmioinfo );
  385. if ( (mmioinfo.dwFlags & MMIO_UNICODEPROC )
  386. || (mmioinfo.pIOProc == mmioDOSIOProc ) // or the DOS file IO Proc
  387. || (mmioinfo.pIOProc == mmioMEMIOProc ) ) { // or a memory file IO Proc
  388. /*------------------------------------------------------------*\
  389. * We have a unicode IO Proc, this means that we have to
  390. * convert szNewFileName to unicode too.
  391. \*------------------------------------------------------------*/
  392. pUnicodeNewFileName = AllocUnicodeStr( szNewFileName );
  393. if ( pUnicodeNewFileName == (LPWSTR)NULL ) {
  394. FreeUnicodeStr( pUnicodeFileName );
  395. return MMIOERR_OUTOFMEMORY;
  396. }
  397. uiRc = (UINT)IOProc( &mmioinfo,
  398. MMIOM_RENAME,
  399. (LPARAM)pUnicodeFileName,
  400. (LPARAM)pUnicodeNewFileName );
  401. FreeUnicodeStr( pUnicodeNewFileName );
  402. } else {
  403. /*------------------------------------------------------------*\
  404. * We have an ascii IO Proc so use the given file names
  405. * without any conversion.
  406. \*------------------------------------------------------------*/
  407. uiRc = (UINT)IOProc( &mmioinfo, MMIOM_RENAME,
  408. (LPARAM)szFileName, (LPARAM)szNewFileName);
  409. }
  410. FreeUnicodeStr( pUnicodeFileName );
  411. return uiRc;
  412. }
  413. /*--------------------------------------------------------------------*/
  414. /* @doc EXTERNAL
  415. @api HMMIO | mmioOpen | This function opens a file for unbuffered
  416. or buffered I/O. The file can be a DOS file, a memory file, or an
  417. element of a custom storage system.
  418. @parm LPTSTR | szFilename | Specifies a pointer to a string
  419. containing the filename of the file to open. If no I/O procedure is
  420. specified to open the file, then the filename determines how the file
  421. is opened, as follows:
  422. -- If the filename does not contain "+", then it is assumed
  423. to be the name of a DOS file.
  424. -- If the filename is of the form "foo.ext+bar", then the
  425. extension "EXT " is assumed to identify an installed I/O procedure
  426. which is called to perform I/O on the file (see <f mmioInstallIOProc>).
  427. -- If the filename is NULL and no I/O procedure is given, then
  428. <e MMIOINFO.adwInfo[0]> is assumed to be the DOS file handle
  429. of a currently open file.
  430. The filename should not be longer than 128 bytes, including the
  431. terminating NULL.
  432. When opening a memory file, set <p szFilename> to NULL.
  433. @parm LPMMIOINFO | lpmmioinfo | Specifies a pointer to an
  434. <t MMIOINFO> structure containing extra parameters used by
  435. <f mmioOpen>. Unless you are opening a memory file, specifying the
  436. size of a buffer for buffered I/O, or specifying an uninstalled I/O
  437. procedure to open a file, this parameter should be NULL.
  438. If <p lpmmioinfo> is not NULL, all unused fields of the
  439. <t MMIOINFO> structure it references must be set to zero, including the
  440. reserved fields.
  441. @parm DWORD | dwOpenFlags | Specifies option flags for the open
  442. operation. The MMIO_READ, MMIO_WRITE, and MMIO_READWRITE flags are
  443. mutually exclusive--only one should be specified. The MMIO_COMPAT,
  444. MMIO_EXCLUSIVE, MMIO_DENYWRITE, MMIO_DENYREAD, and MMIO_DENYNONE flags
  445. are DOS file-sharing flags, and can only be used after the DOS
  446. command SHARE has been executed.
  447. @flag MMIO_READ | Opens the file for reading only. This is the
  448. default, if MMIO_WRITE and MMIO_READWRITE are not specified.
  449. @flag MMIO_WRITE | Opens the file for writing. You should not
  450. read from a file opened in this mode.
  451. @flag MMIO_READWRITE | Opens the file for both reading and writing.
  452. @flag MMIO_CREATE | Creates a new file.
  453. If the file already exists, it is truncated to zero length.
  454. For memory files, MMIO_CREATE indicates the end of the file
  455. is initially at the start of the buffer.
  456. @flag MMIO_DELETE | Deletes a file. If this flag is specified,
  457. <p szFilename> should not be NULL. The return
  458. value will be TRUE (cast to HMMIO) if the file was deleted
  459. successfully, FALSE otherwise. Do not call <f mmioClose>
  460. for a file that has been deleted. If this flag is specified,
  461. all other file opening flags are ignored.
  462. @flag MMIO_PARSE | Creates a fully qualified filename from the path
  463. specified in <p szFileName>. The fully qualified filename is
  464. placed back into <p szFileName>. The return value
  465. will be TRUE (cast to HMMIO) if the qualification was
  466. successful, FALSE otherwise. The file is not opened, and the function
  467. does not return a valid MMIO file handle, so do not attempt to
  468. close the file. If this flag is specified, all other file
  469. opening flags are ignored.
  470. @flag MMIO_EXIST | Determines whether the specified file exists
  471. and creates a fully qualified filename from the path
  472. specified in <p szFileName>. The fully qualified filename is
  473. placed back into <p szFileName>. The return value
  474. will be TRUE (cast to HMMIO) if the qualification was
  475. successful and the file exists, FALSE otherwise. The file is
  476. not opened, and the function does not return a valid MMIO file
  477. handle, so do not attempt to close the file.
  478. @flag MMIO_ALLOCBUF | Opens a file for buffered I/O.
  479. To allocate a buffer larger or smaller than the default
  480. buffer size (8K), set the <e MMIOINFO.cchBuffer> field of the
  481. <t MMIOINFO> structure to the desired buffer size. If
  482. <e MMIOINFO.cchBuffer> is zero, then the default buffer size
  483. is used. If you are providing your own I/O buffer, then the
  484. MMIO_ALLOCBUF flag should not be used.
  485. @flag MMIO_COMPAT | Opens the file with compatibility mode,
  486. allowing any process on a given machine to open the file
  487. any number of times. <f mmioOpen> fails if the file has
  488. been opened with any of the other sharing modes.
  489. @flag MMIO_EXCLUSIVE | Opens the file with exclusive mode,
  490. denying other processes both read and write access to the file.
  491. <f mmioOpen> fails if the file has been opened in any other
  492. mode for read or write access, even by the current process.
  493. @flag MMIO_DENYWRITE | Opens the file and denies other
  494. processes write access to the file. <f mmioOpen> fails
  495. if the file has been opened in compatibility or for write
  496. access by any other process.
  497. @flag MMIO_DENYREAD | Opens the file and denies other
  498. processes read access to the file. <f mmioOpen> fails if the
  499. file has been opened in compatibility mode or for read access
  500. by any other process.
  501. @flag MMIO_DENYNONE | Opens the file without denying other
  502. processes read or write access to the file. <f mmioOpen>
  503. fails if the file has been opened in compatibility mode
  504. by any other process.
  505. @flag MMIO_GETTEMP | Creates a temporary filename, optionally
  506. using the parameters passed in <p szFileName> to determine
  507. the temporary name. For example, you can specify "C:F" to
  508. create a temporary file residing on drive C, starting with
  509. letter "F". The resulting filename is placed in the buffer
  510. pointed to by <p szFileName>. The return value will be TRUE
  511. (cast to HMMIO) if the temporary filename was created successfully,
  512. FALSE otherwise. The file is
  513. not opened, and the function does not return a valid MMIO file
  514. handle, so do not attempt to close the file.
  515. This flag overrides all other flags.
  516. @rdesc The return value is a handle to the opened file. This handle
  517. is not a DOS file handle--do not use it with any file I/O functions
  518. other than MMIO functions.
  519. If the file cannot be opened, the return value is NULL. If
  520. <p lpmmioinfo> is not NULL, then its <e MMIOINFO.wErrorRet> field
  521. will contain extended error information returned by the I/O
  522. procedure.
  523. @comm If <p lpmmioinfo> references an <t MMIOINFO> structure, set
  524. up the fields as described below. All unused fields must be set to
  525. zero, including reserved fields.
  526. -- To request that a file be opened with an installed I/O
  527. procedure, set the <e MMIOINFO.fccIOProc> field
  528. to the four-character code of the I/O procedure,
  529. and set the <e MMIOINFO.pIOProc> field to NULL.
  530. -- To request that a file be opened with an uninstalled I/O procedure,
  531. set the <e MMIOINFO.pIOProc> field to
  532. point to the I/O procedure, and set <e MMIOINFO.fccIOProc> to NULL.
  533. -- To request that <f mmioOpen> determine which I/O procedure to use
  534. to open the file based on the filename contained in <p szFilename>,
  535. set both <e MMIOINFO.fccIOProc> and <e MMIOINFO.pIOProc> to NULL.
  536. This is the default behavior if no <t MMIOINFO> structure is specified.
  537. -- To open a memory file using an internally allocated and managed
  538. buffer, set the <e MMIOINFO.pchBuffer> field to NULL,
  539. <e MMIOINFO.fccIOProc> to FOURCC_MEM,
  540. <e MMIOINFO.cchBuffer> to the initial size of the buffer, and
  541. <e MMIOINFO.adwInfo[0]> to the incremental expansion size of the
  542. buffer. This memory file will automatically be expanded in increments of
  543. <e MMIOINFO.adwInfo[0]> bytes when necessary. Specify the MMIO_CREATE
  544. flag for the <p dwOpenFlags> parameter to initially set the end of
  545. the file to be the beginning of the buffer.
  546. -- To open a memory file using a caller-supplied buffer, set
  547. the <e MMIOINFO.pchBuffer> field to point to the memory buffer,
  548. <e MMIOINFO.fccIOProc> to FOURCC_MEM,
  549. <e MMIOINFO.cchBuffer> to the size of the buffer, and
  550. <e MMIOINFO.adwInfo[0]> to the incremental expansion size of the
  551. buffer. The expansion size in <e MMIOINFO.adwInfo[0]> should only
  552. be non-zero if <e MMIOINFO.pchBuffer> is a pointer obtained by calling
  553. <f GlobalAlloc> and <f GlobalLock>, since <f GlobalReAlloc> will be called to
  554. expand the buffer. In particular, if <e MMIOINFO.pchBuffer> points to a
  555. local or global array, a block of memory in the local heap, or a block
  556. of memory allocated by <f GlobalDosAlloc>, <e MMIOINFO.adwInfo[0]> must
  557. be zero.
  558. Specify the MMIO_CREATE flag for the <p dwOpenFlags> parameter to
  559. initially set the end of the file to be the beginning of the buffer;
  560. otherwise, the entire block of memory will be considered readable.
  561. -- To use a currently open DOS file handle with MMIO, set the
  562. <e MMIOINFO.fccIOProc> field to FOURCC_DOS,
  563. <e MMIOINFO.pchBuffer> to NULL, and <e MMIOINFO.adwInfo[0]> to the
  564. DOS file handle. Note that offsets within the file will be relative to
  565. the beginning of the file, and will not depend on the DOS file position
  566. at the time <f mmioOpen> is called; the initial MMIO offset will be the same
  567. as the DOS offset when <f mmioOpen> is called.
  568. Later, to close the MMIO file handle without closing the DOS
  569. file handle, pass the MMIO_FHOPEN flag to <f mmioClose>.
  570. You must call <f mmioClose> to close a file opened with <f mmioOpen>.
  571. Open files are not automatically closed when an application exits.
  572. @xref mmioClose
  573. */
  574. /* these are the changes to mmioOpen() to support compound files... */
  575. /* @doc CFDOC
  576. @api HMMIO | mmioOpen | ...The file can be a DOS file, a memory file,
  577. an element of a RIFF compound file...
  578. @parm LPTSTR | szFilename | ...
  579. -- If <p szFilename> is of the form "foo+bar", then <f mmioOpen>
  580. opens the compound file element named "bar" that is stored inside
  581. the RIFF compound file named "foo".
  582. -- If <p szFilename> is of the form "foo.ext+bar", then the
  583. extension "ext" is assumed to identify the installed I/O procedure
  584. (see <f mmioInstallIOProc>). The extension "bnd", and any extensions
  585. that have not been installed, are assumed to refer to a RIFF compound
  586. file.
  587. @parm LPMMIOINFO | lpmmioinfo | ...
  588. @parm DWORD | dwOpenFlags | ...
  589. @rdesc ...
  590. @comm ...
  591. The following I/O procedure identifiers (type FOURCC) are predefined:
  592. ...
  593. FOURCC_BND: <p szFilename> is assumed to be the name of
  594. a RIFF compound file element, and <p adwInfo[0]> should
  595. contain the HMMCF of the compound file. Alternatively,
  596. <p szFilename> can include the name of the compound file
  597. (e.g. "foo.bnd+bar.dib" as described above), and <p adwInfo[0]>
  598. should be NULL, to automatically open the compound file.
  599. ...
  600. The easy way to open an element of a RIFF compound file: just
  601. include the name of the compound file in <p szFilename> preceded
  602. by a "+" as described above. For example, opening
  603. "c:\data\bar.bnd+blorg.dib" opens the compound file element
  604. named "blorg.dib" in the compound file "c:\data\bar.bnd".
  605. <p lpmmioinfo> can be null in this case -- set <p dwOpenFlags>
  606. as described above. You can use this same method to open an
  607. element of a custom storage system, if the file extension of the
  608. compound file ("bnd" in the above example) corresponds to an
  609. installed I/O procedure -- see <f mmioInstallIOProc> for details.
  610. To open an element of a RIFF compound file that was opened using
  611. <f mmioCFAccess> or <f mmioCFOpen>: set <p szFilename>
  612. to be the name of the compound file element; set <p fccIOProc>
  613. to FOURCC_BND; set <p adwInfo[0]> to the HMMCF of the open compound
  614. file; set <p dwOpenFlags> and <p cchBuffer> as described above;
  615. set all other fields of <p lpmmioinfo> to zero.
  616. ...
  617. */
  618. /*--------------------------------------------------------------------*/
  619. HMMIO APIENTRY
  620. mmioOpenW( LPWSTR szFileName, LPMMIOINFO lpmmioinfo, DWORD dwOpenFlags )
  621. {
  622. PMMIO pmmio; // MMIO status block
  623. LPSTR hpBuffer;
  624. UINT w; // an MMRESULT or a LRESULT from an IOPROC
  625. V_FLAGS(dwOpenFlags, MMIO_OPEN_VALID, mmioOpen, NULL);
  626. V_WPOINTER0(lpmmioinfo, sizeof(MMIOINFO), NULL);
  627. if (lpmmioinfo) {
  628. lpmmioinfo->wErrorRet = 0;
  629. V_CALLBACK0((FARPROC)lpmmioinfo->pIOProc, NULL);
  630. }
  631. /* allocate MMIO status information block */
  632. if ( (pmmio = (PMMIO)(NewHandle(TYPE_MMIO, NULL, sizeof(MMIOINFO)))) == NULL)
  633. {
  634. if (lpmmioinfo) {
  635. lpmmioinfo->wErrorRet = MMIOERR_OUTOFMEMORY;
  636. }
  637. return NULL;
  638. }
  639. // Implicitly acquired by NewHandle()
  640. ReleaseHandleListResource();
  641. /*----------------------------------------------------------------*\
  642. * NewHandle does not zero the allocated storage so we had better do
  643. * it now.
  644. \*----------------------------------------------------------------*/
  645. ZeroMemory( pmmio, sizeof(MMIOINFO) );
  646. /* if user supplied <lpmmioinfo>, copy it to <pmmio> */
  647. if (lpmmioinfo != NULL) {
  648. *pmmio = *lpmmioinfo;
  649. }
  650. /* <dwOpenFlags> always takes precedence over contents of <pmmio> */
  651. pmmio->dwFlags = dwOpenFlags;
  652. pmmio->hmmio = ((HMMIO)pmmio);
  653. /* MMIO_ALLOCBUF in the flags means that the user wants a buffer
  654. * allocated for buffered I/O, but after this point it means that
  655. * a buffer *was* allocated, so turn off the flag until the buffer
  656. * is actually allocated (which is done by mmioSetBuffer() below)
  657. */
  658. if (pmmio->dwFlags & MMIO_ALLOCBUF)
  659. {
  660. /* if a buffer size is not specified, use the default */
  661. if (pmmio->cchBuffer == 0) {
  662. pmmio->cchBuffer = MMIO_DEFAULTBUFFER;
  663. }
  664. pmmio->dwFlags &= ~MMIO_ALLOCBUF;
  665. }
  666. /* Set the pIOProc function as determined by the file name or the
  667. * parameters in the pmmio structure.
  668. */
  669. SetIOProc(szFileName, pmmio);
  670. /* The pmmio structure hasn't been set up for buffering, so we must
  671. * explicitly make sure that pchBuffer is NULL.
  672. */
  673. hpBuffer = pmmio->pchBuffer;
  674. pmmio->pchBuffer = NULL;
  675. /* set up buffered I/O however the user requested it */
  676. w = mmioSetBuffer(((HMMIO)pmmio), hpBuffer, pmmio->cchBuffer, 0);
  677. if (w)
  678. {
  679. if (lpmmioinfo) {
  680. lpmmioinfo->wErrorRet = w;
  681. }
  682. FreeHandle(((HMMIO)pmmio));
  683. return NULL;
  684. }
  685. if ( (pmmio->dwFlags & MMIO_UNICODEPROC) // a Unicode IO Proc
  686. || (pmmio->pIOProc == mmioDOSIOProc ) // or the DOS file IO Proc
  687. || (pmmio->pIOProc == mmioMEMIOProc ) ) { // or a memory file IO Proc
  688. /* let the I/O procedure open/delete/qualify the file */
  689. w = (UINT)IOProc( pmmio, MMIOM_OPEN, (LPARAM)szFileName, 0L );
  690. } else {
  691. if (NULL == szFileName) {
  692. w = (UINT)IOProc( pmmio,
  693. MMIOM_OPEN,
  694. (LPARAM)NULL,
  695. 0L );
  696. } else {
  697. LPSTR lpAsciiFileName; // ascii version of szFileName
  698. /*------------------------------------------------------------*\
  699. * We have an ascii IO Proc so convert the given file name
  700. * into ascii.
  701. \*------------------------------------------------------------*/
  702. lpAsciiFileName = AllocAsciiStr( szFileName );
  703. if ( lpAsciiFileName == (LPSTR)NULL ) {
  704. if (lpmmioinfo) {
  705. lpmmioinfo->wErrorRet = MMIOERR_OUTOFMEMORY;
  706. }
  707. FreeHandle( (HMMIO)pmmio );
  708. return NULL;
  709. }
  710. /*------------------------------------------------------------*\
  711. * Call the IO proc and then free the allocated unicode
  712. * filename storage.
  713. \*------------------------------------------------------------*/
  714. w = (UINT)IOProc( pmmio,
  715. MMIOM_OPEN,
  716. (LPARAM)lpAsciiFileName,
  717. 0L );
  718. FreeAsciiStr( lpAsciiFileName );
  719. }
  720. }
  721. /* If this is non-zero, return it to the user */
  722. if (w != 0)
  723. {
  724. if (lpmmioinfo != NULL) {
  725. lpmmioinfo->wErrorRet = w;
  726. }
  727. FreeHandle(((HMMIO)pmmio));
  728. return NULL;
  729. }
  730. if (pmmio->dwFlags & (MMIO_DELETE| MMIO_PARSE| MMIO_EXIST| MMIO_GETTEMP))
  731. {
  732. /* if the file is being deleted/parsed/name gotten, exit
  733. * QUICKLY because the file handle (or whatever) in <pmmio>
  734. * is not valid.
  735. */
  736. mmioSetBuffer(((HMMIO)pmmio), NULL, 0L, 0);
  737. FreeHandle(((HMMIO)pmmio));
  738. return (HMMIO) TRUE;
  739. }
  740. /* the initial "current buffered offset" will be equal to the initial
  741. * "current disk offset"
  742. */
  743. pmmio->lBufOffset = pmmio->lDiskOffset;
  744. return ((HMMIO)pmmio);
  745. }
  746. HMMIO APIENTRY
  747. mmioOpenA( LPSTR szFileName, LPMMIOINFO lpmmioinfo, DWORD dwOpenFlags )
  748. {
  749. PMMIO pmmio; // MMIO status block
  750. LPSTR hpBuffer;
  751. UINT w; // an MMRESULT or a LRESULT from an IOPROC
  752. LPWSTR lpUnicodeName; // Unicode version of szFileName
  753. WCHAR UnicodeBuffer[ MAX_PATH ];
  754. V_FLAGS(dwOpenFlags, MMIO_OPEN_VALID, mmioOpen, NULL);
  755. V_WPOINTER0(lpmmioinfo, sizeof(MMIOINFO), NULL);
  756. if (lpmmioinfo) {
  757. lpmmioinfo->wErrorRet = 0;
  758. V_CALLBACK0((FARPROC)lpmmioinfo->pIOProc, NULL);
  759. }
  760. /*----------------------------------------------------------------*\
  761. * Don't convert szFilename if it does not point to anything
  762. \*----------------------------------------------------------------*/
  763. if ( szFileName != (LPSTR)NULL ) {
  764. /*----------------------------------------------------------------*\
  765. * Convert the Ascii szFileName to Unicode
  766. \*----------------------------------------------------------------*/
  767. AsciiStrToUnicodeStr( (PBYTE)UnicodeBuffer,
  768. (PBYTE)UnicodeBuffer + (MAX_PATH * sizeof(WCHAR)),
  769. szFileName );
  770. lpUnicodeName = UnicodeBuffer;
  771. } else {
  772. lpUnicodeName = (LPWSTR)NULL;
  773. }
  774. /* allocate MMIO status information block */
  775. if ( (pmmio = (PMMIO)(NewHandle(TYPE_MMIO, NULL, sizeof(MMIOINFO)))) == NULL)
  776. {
  777. if (lpmmioinfo) {
  778. lpmmioinfo->wErrorRet = MMIOERR_OUTOFMEMORY;
  779. }
  780. return NULL;
  781. }
  782. // Implicitly acquired by NewHandle()
  783. ReleaseHandleListResource();
  784. /*----------------------------------------------------------------*\
  785. * NewHandle does not zero the allocated storage so we had better do
  786. * it now.
  787. \*----------------------------------------------------------------*/
  788. ZeroMemory( pmmio, sizeof(MMIOINFO) );
  789. /* if user supplied <lpmmioinfo>, copy it to <pmmio> */
  790. if (lpmmioinfo != NULL) {
  791. *pmmio = *lpmmioinfo;
  792. }
  793. /* <dwOpenFlags> always takes precedence over contents of <pmmio> */
  794. pmmio->dwFlags = dwOpenFlags;
  795. pmmio->hmmio = ((HMMIO)pmmio);
  796. /* MMIO_ALLOCBUF in the flags means that the user wants a buffer
  797. * allocated for buffered I/O, but after this point it means that
  798. * a buffer *was* allocated, so turn off the flag until the buffer
  799. * is actually allocated (which is done by mmioSetBuffer() below)
  800. */
  801. if (pmmio->dwFlags & MMIO_ALLOCBUF)
  802. {
  803. /* if a buffer size is not specified, use the default */
  804. if (pmmio->cchBuffer == 0) {
  805. pmmio->cchBuffer = MMIO_DEFAULTBUFFER;
  806. }
  807. pmmio->dwFlags &= ~MMIO_ALLOCBUF;
  808. }
  809. /* Set the pIOProc function as determined by the file name or the
  810. * parameters in the pmmio structure.
  811. */
  812. SetIOProc( lpUnicodeName, pmmio );
  813. /* The pmmio structure hasn't been set up for buffering, so we must
  814. * explicitly make sure that pchBuffer is NULL.
  815. */
  816. hpBuffer = pmmio->pchBuffer;
  817. pmmio->pchBuffer = NULL;
  818. /* set up buffered I/O however the user requested it */
  819. w = mmioSetBuffer(((HMMIO)pmmio), hpBuffer, pmmio->cchBuffer, 0);
  820. if (w)
  821. {
  822. if (lpmmioinfo) {
  823. lpmmioinfo->wErrorRet = w;
  824. }
  825. FreeHandle(((HMMIO)pmmio));
  826. return NULL;
  827. }
  828. if ( (pmmio->dwFlags & MMIO_UNICODEPROC) // a Unicode IO Proc
  829. || (pmmio->pIOProc == mmioDOSIOProc) // or the DOS file IO Proc
  830. || (pmmio->pIOProc == mmioMEMIOProc) ) { // or a memory file IO Proc
  831. /* let the I/O procedure open/delete/qualify the file */
  832. w = (UINT)IOProc( pmmio, MMIOM_OPEN,
  833. (LPARAM)lpUnicodeName, 0L );
  834. /*------------------------------------------------------------*\
  835. * If we have a DOS IO proc and the user specified the
  836. * parse option and we did not get any errors from the IO proc
  837. * call we convert the returned parsed path string from Unicode
  838. * back into Ansi and copy this value into szFileName.
  839. \*------------------------------------------------------------*/
  840. if ( w == 0
  841. && (pmmio->pIOProc == mmioDOSIOProc)
  842. && ((dwOpenFlags & MMIO_PARSE) || (dwOpenFlags & MMIO_GETTEMP)) ) {
  843. BYTE ansiPath[ MAX_PATH ];
  844. UnicodeStrToAsciiStr( ansiPath,
  845. ansiPath + MAX_PATH,
  846. lpUnicodeName );
  847. strcpy( (LPSTR)szFileName, (LPCSTR)ansiPath );
  848. }
  849. } else {
  850. w = (UINT)IOProc( pmmio, MMIOM_OPEN, (LPARAM)szFileName, 0L );
  851. }
  852. /* If this is non-zero, return it to the user */
  853. if (w != 0)
  854. {
  855. if (lpmmioinfo != NULL) {
  856. lpmmioinfo->wErrorRet = w;
  857. }
  858. FreeHandle(((HMMIO)pmmio));
  859. return NULL;
  860. }
  861. if (pmmio->dwFlags & (MMIO_DELETE| MMIO_PARSE| MMIO_EXIST| MMIO_GETTEMP))
  862. {
  863. /* if the file is being deleted/parsed/name gotten, exit
  864. * QUICKLY because the file handle (or whatever) in <pmmio>
  865. * is not valid.
  866. */
  867. mmioSetBuffer(((HMMIO)pmmio), NULL, 0L, 0);
  868. FreeHandle(((HMMIO)pmmio));
  869. return (HMMIO) TRUE;
  870. }
  871. /* the initial "current buffered offset" will be equal to the initial
  872. * "current disk offset"
  873. */
  874. pmmio->lBufOffset = pmmio->lDiskOffset;
  875. return ((HMMIO)pmmio);
  876. }
  877. /*--------------------------------------------------------------------*/
  878. /* @doc EXTERNAL
  879. @api MMRESULT | mmioClose | This function closes a file opened with
  880. <f mmioOpen>.
  881. @parm HMMIO | hmmio | Specifies the file handle of the file to
  882. close.
  883. @parm UINT | uFlags | Specifies options for the close operation.
  884. @flag MMIO_FHOPEN | If the file was opened by passing the DOS
  885. file handle of an already-opened file to <f mmioOpen>, then
  886. using this flag tells <f mmioClose> to close the MMIO file
  887. handle, but not the DOS file handle. (This is done by the
  888. I/O Proc).
  889. @rdesc The return value is zero if the function is successful.
  890. Otherwise, the return value is an error code, either from
  891. <f mmioFlush> or from the I/O procedure. The error code can be
  892. one of the following codes:
  893. @flag MMIOERR_CANNOTWRITE | The contents of the buffer could
  894. not be written to disk.
  895. @flag MMIOERR_CANNOTCLOSE | There was a DOS file system error when
  896. the I/O Proc attempted to close the DOS file.
  897. @xref mmioOpen mmioFlush
  898. */
  899. /*--------------------------------------------------------------------*/
  900. MMRESULT APIENTRY
  901. mmioClose(HMMIO hmmio, UINT uFlags)
  902. {
  903. UINT w; /* either an LRESULT from an IOProc or an MMRESULT */
  904. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  905. if ((w = mmioFlush(hmmio, 0)) != 0)
  906. return w;
  907. w = (UINT)IOProc( (PMMIO)hmmio, MMIOM_CLOSE, (LPARAM)(DWORD) uFlags, (LPARAM) 0);
  908. if (w != 0) return w;
  909. /* free the buffer if necessary */
  910. mmioSetBuffer(hmmio, NULL, 0L, 0);
  911. FreeHandle(hmmio);
  912. return 0;
  913. }
  914. /*--------------------------------------------------------------------*/
  915. /* @doc EXTERNAL
  916. @api LRESULT | mmioRead | This function reads a specified number of
  917. bytes from a file opened with <f mmioOpen>.
  918. @parm HMMIO | hmmio | Specifies the file handle of the file to be
  919. read.
  920. @parm LPSTR | pch | Specifies a pointer to a buffer to contain
  921. the data read from the file.
  922. @parm LONG | cch | Specifies the number of bytes to read from the
  923. file.
  924. @rdesc The return value is the number of bytes actually read. If the
  925. end of the file has been reached and no more bytes can be read, the
  926. return value is zero. If there is an error reading from the file, the
  927. return value is -1.
  928. @comm On 16 bit windows pch is a huge pointer. On 32 bit windows there is no
  929. distinction between huge pointers and long pointers.
  930. @xref mmioWrite
  931. */
  932. /*--------------------------------------------------------------------*/
  933. LONG APIENTRY
  934. mmioRead(HMMIO hmmio, LPSTR pch, LONG cch)
  935. {
  936. LONG lTotalBytesRead = 0L; // total no. bytes read
  937. LONG lBytes; // no. bytes that can be read
  938. PMMIO pmmio=(PMMIO)hmmio; //local copy hmmio - avoid casting, simplify debug
  939. V_HANDLE(hmmio, TYPE_MMIO, -1);
  940. V_WPOINTER(pch, cch, -1);
  941. for(;;)
  942. {
  943. /* calculate the number of bytes that can be read */
  944. lBytes = (LONG)(pmmio->pchEndRead - pmmio->pchNext);
  945. /* can only read at most <cch> bytes from buffer */
  946. if (lBytes > cch)
  947. lBytes = cch;
  948. if (lBytes > 0)
  949. {
  950. /* this is where some performance improvements can
  951. * be made, especially for small reads...?
  952. */
  953. CopyMemory(pch, pmmio->pchNext, lBytes);
  954. pmmio->pchNext += lBytes;
  955. pch += lBytes;
  956. cch -= lBytes;
  957. lTotalBytesRead += lBytes;
  958. }
  959. /* cannot do MMIOM_READ from memory files */
  960. if (pmmio->fccIOProc == FOURCC_MEM)
  961. return lTotalBytesRead;
  962. if (cch == 0) // no more to read?
  963. return lTotalBytesRead;
  964. /* we need to read beyond this buffer; if we have at least
  965. * another bufferful to read, just call the I/O procedure
  966. */
  967. if (cch > pmmio->cchBuffer)
  968. break;
  969. /* read the next bufferful and loop around */
  970. if (mmioAdvance(hmmio, NULL, MMIO_READ) != 0)
  971. return -1;
  972. /* if mmioAdvance() couldn't read any more data, we must be
  973. * at the end of the file
  974. */
  975. if (pmmio->pchNext == pmmio->pchEndRead)
  976. return lTotalBytesRead;
  977. }
  978. /* flush and empty the I/O buffer and manipulate <lBufOffset>
  979. * directly to change the current file position
  980. */
  981. if (mmioFlush(hmmio, MMIO_EMPTYBUF) != 0)
  982. return -1;
  983. /* call the I/O procedure to do the rest of the reading */
  984. lBytes = mmioDiskIO(pmmio, MMIOM_READ, pch, cch);
  985. pmmio->lBufOffset = pmmio->lDiskOffset;
  986. return (lBytes == -1L) ? -1L : lTotalBytesRead + lBytes;
  987. }
  988. /*--------------------------------------------------------------------*/
  989. /* @doc EXTERNAL
  990. @api LRESULT | mmioWrite | This function writes a specified number of
  991. bytes to a file opened with <f mmioOpen>.
  992. @parm HMMIO | hmmio | Specifies the file handle of the file.
  993. @parm LPSTR | pch | Specifies a pointer to the buffer to be
  994. written to the file.
  995. @parm LONG | cch | Specifies the number of bytes to write to the
  996. file.
  997. @rdesc The return value is the number of bytes actually written. If
  998. there is an error writing to the file, the return value is -1.
  999. @comm The current file position is incremented by the number of
  1000. bytes written. On 16 bit windows pch is a huge pointer.
  1001. On 32 bit windows there is no distinction between huge pointers
  1002. and long pointers.
  1003. @xref mmioRead
  1004. */
  1005. /*--------------------------------------------------------------------*/
  1006. LONG APIENTRY
  1007. mmioWrite(HMMIO hmmio, LPCSTR pch, LONG cch)
  1008. {
  1009. LONG lTotalBytesWritten = 0L; // total no. bytes written
  1010. LONG lBytes; // no. bytes that can be written
  1011. // "pch" is LPCSTR which is correct, but
  1012. // we pass it to a polymorphic routine
  1013. // which needs LPSTR.
  1014. V_HANDLE(hmmio, TYPE_MMIO, -1);
  1015. V_RPOINTER(pch, cch, -1);
  1016. for(;;)
  1017. {
  1018. /* calculate the number of bytes that can be written */
  1019. lBytes = (LONG)(((PMMIO)hmmio)->pchEndWrite - ((PMMIO)hmmio)->pchNext);
  1020. if ((cch > lBytes) && (((PMMIO)hmmio)->fccIOProc == FOURCC_MEM))
  1021. {
  1022. /* this is a memory file -- expand it */
  1023. if (mmioExpandMemFile(((PMMIO)hmmio), cch - lBytes) != 0)
  1024. return -1; // cannot expand
  1025. lBytes = (LONG)(((PMMIO)hmmio)->pchEndWrite - ((PMMIO)hmmio)->pchNext);
  1026. }
  1027. /* can only write at most <cch> bytes into the buffer */
  1028. if (lBytes > cch)
  1029. lBytes = cch;
  1030. /* this is where some performance improvements can
  1031. * be made, especially for small writes... should
  1032. * special-case cases when segment boundaries are
  1033. * not crossed (or maybe hmemcpy() should do that)
  1034. */
  1035. if (lBytes > 0)
  1036. {
  1037. CopyMemory(((PMMIO)hmmio)->pchNext, pch, lBytes);
  1038. ((PMMIO)hmmio)->dwFlags |= MMIO_DIRTY;
  1039. ((PMMIO)hmmio)->pchNext += lBytes;
  1040. pch += lBytes;
  1041. cch -= lBytes;
  1042. lTotalBytesWritten += lBytes;
  1043. }
  1044. /* validate <pchEndRead>, i.e. re-enforce the invariant that
  1045. * <pchEndRead> points past the last valid byte in the buffer
  1046. */
  1047. if (((PMMIO)hmmio)->pchEndRead < ((PMMIO)hmmio)->pchNext)
  1048. ((PMMIO)hmmio)->pchEndRead = ((PMMIO)hmmio)->pchNext;
  1049. if (cch == 0) // no more to write?
  1050. return lTotalBytesWritten;
  1051. /* we need to read beyond this buffer; if we have at least
  1052. * another bufferful to read, just call the I/O procedure
  1053. */
  1054. if (cch > ((PMMIO)hmmio)->cchBuffer)
  1055. break;
  1056. /* write this buffer (if needed) and read the next
  1057. * bufferful (if needed)
  1058. */
  1059. if (mmioAdvance(hmmio, NULL, MMIO_WRITE) != 0)
  1060. return -1;
  1061. }
  1062. /* we should never need to do MMIOM_WRITE with memory files */
  1063. /* flush and empty the I/O buffer and manipulate <lBufOffset>
  1064. * directly to change the current file position
  1065. */
  1066. if (mmioFlush(hmmio, MMIO_EMPTYBUF) != 0)
  1067. return -1;
  1068. /* call the I/O procedure to do the rest of the writing
  1069. * mmioDiskIO is a polymorphic routine, hence we need to cast
  1070. * our LPCSTR input pointer to LPSTR.
  1071. */
  1072. lBytes = mmioDiskIO(((PMMIO)hmmio), MMIOM_WRITE, (LPSTR)pch, cch);
  1073. ((PMMIO)hmmio)->lBufOffset = ((PMMIO)hmmio)->lDiskOffset;
  1074. return (lBytes == -1L) ? -1L : lTotalBytesWritten + lBytes;
  1075. }
  1076. /*--------------------------------------------------------------------*/
  1077. /* @doc EXTERNAL
  1078. @api LRESULT | mmioSeek | This function changes the current file
  1079. position in a file opened with <f mmioOpen>. The current file
  1080. position is the location in the file where data is read or written.
  1081. @parm HMMIO | hmmio | Specifies the file handle of the file to seek
  1082. in.
  1083. @parm LONG | lOffset | Specifies an offset to change the file position.
  1084. @parm int | iOrigin | Specifies how the offset specified by
  1085. <p lOffset> is interpreted. Contains one of the following flags:
  1086. @flag SEEK_SET | Seeks to <p lOffset> bytes from the beginning
  1087. of the file.
  1088. @flag SEEK_CUR | Seeks to <p lOffset> bytes from the current
  1089. file position.
  1090. @flag SEEK_END | Seeks to <p lOffset> bytes from the end
  1091. of the file.
  1092. @rdesc The return value is the new file position in bytes, relative
  1093. to the beginning of the file. If there is an error, the return value
  1094. is -1.
  1095. @comm Seeking to an invalid location in the file, such as past the
  1096. end of the file, may cause <f mmioSeek> to not return an error,
  1097. but may cause subsequent I/O operations on the file to fail.
  1098. To locate the end of a file, call <f mmioSeek> with <p lOffset>
  1099. set to zero and <p iOrigin> set to SEEK_END.
  1100. */
  1101. /*--------------------------------------------------------------------*/
  1102. LONG APIENTRY
  1103. mmioSeek(HMMIO hmmio, LONG lOffset, int iOrigin)
  1104. {
  1105. LONG lCurOffset; // disk offset of <pchNext>
  1106. LONG lEndBufOffset; // disk offset of end of buffer
  1107. LONG lNewOffset; // new disk offset
  1108. V_HANDLE(hmmio, TYPE_MMIO, -1);
  1109. /* careful! all this buffer pointer manipulation is fine, but keep
  1110. * in mind that buffering may be disabled (in which case <pchEndRead>
  1111. * and <pchBuffer> will both be NULL, so the buffer will appear to
  1112. * be zero bytes in size)
  1113. */
  1114. /* <((PMMIO)hmmio)->lBufOffset> is the disk offset of the start of the
  1115. * start of the buffer; determine <lCurOffset>, the offset of <pchNext>,
  1116. * and <lEndBufOffset>, the offset of the end of the valid part
  1117. * of the buffer
  1118. */
  1119. lCurOffset = (LONG)(((PMMIO)hmmio)->lBufOffset +
  1120. (((PMMIO)hmmio)->pchNext - ((PMMIO)hmmio)->pchBuffer));
  1121. lEndBufOffset = (LONG)(((PMMIO)hmmio)->lBufOffset +
  1122. (((PMMIO)hmmio)->pchEndRead - ((PMMIO)hmmio)->pchBuffer));
  1123. /* determine <lNewOffset>, the offset to seek to */
  1124. switch (iOrigin)
  1125. {
  1126. case SEEK_SET: // seek relative to start of file
  1127. lNewOffset = lOffset;
  1128. break;
  1129. case SEEK_CUR: // seek relative to current location
  1130. lNewOffset = lCurOffset + lOffset;
  1131. break;
  1132. case SEEK_END: // seek relative to end of file
  1133. if (((PMMIO)hmmio)->fccIOProc == FOURCC_MEM)
  1134. lNewOffset = lEndBufOffset - lOffset;
  1135. else
  1136. {
  1137. LONG lEndFileOffset;
  1138. /* find out where the end of the file is */
  1139. lEndFileOffset
  1140. = (LONG)IOProc( (PMMIO)hmmio, MMIOM_SEEK, (LPARAM) 0, (LPARAM) SEEK_END);
  1141. if (lEndFileOffset == -1)
  1142. return -1;
  1143. /* Check that we don't have buffered data not yet written */
  1144. if (lEndBufOffset > lEndFileOffset) {
  1145. lEndFileOffset = lEndBufOffset;
  1146. }
  1147. lNewOffset = lEndFileOffset - lOffset;
  1148. }
  1149. break;
  1150. default: lNewOffset = 0;
  1151. {
  1152. dprintf(( "Invalid seek type %d\n",iOrigin));
  1153. WinAssert(FALSE);
  1154. }
  1155. }
  1156. if ( (lNewOffset >= ((PMMIO)hmmio)->lBufOffset)
  1157. && (lNewOffset <= lEndBufOffset)
  1158. )
  1159. {
  1160. /* seeking within the valid part of the buffer
  1161. * (possibly including seeking to <lEndBufOffset>)
  1162. */
  1163. ((PMMIO)hmmio)->pchNext = ((PMMIO)hmmio)->pchBuffer +
  1164. (lNewOffset - ((PMMIO)hmmio)->lBufOffset);
  1165. }
  1166. else
  1167. {
  1168. /* seeking outside the buffer */
  1169. if (((PMMIO)hmmio)->fccIOProc == FOURCC_MEM)
  1170. return -1; // can't seek outside mem. file buffer
  1171. if (mmioFlush(hmmio, 0) != 0)
  1172. return -1;
  1173. /* the current "buffered file position" (same as <lDiskOffset>
  1174. * for unbuffered files) equals <lBufOffset> +
  1175. * (<pchNext> - <pchBuffer>); we'll move the current buffered
  1176. * file position (and empty the buffer, since it becomes
  1177. * invalid when <lBufOffset> changes) as follows...
  1178. */
  1179. ((PMMIO)hmmio)->lBufOffset = lNewOffset;
  1180. ((PMMIO)hmmio)->pchNext
  1181. = ((PMMIO)hmmio)->pchEndRead
  1182. = ((PMMIO)hmmio)->pchBuffer;
  1183. /* don't need to actually seek right now, since the next
  1184. * MMIOM_READ or MMIOM_WRITE will have to seek anyway
  1185. */
  1186. }
  1187. return lNewOffset;
  1188. }
  1189. /*--------------------------------------------------------------------*/
  1190. /* @doc EXTERNAL
  1191. @api MMRESULT | mmioGetInfo | This function retrieves information
  1192. about a file opened with <f mmioOpen>. This information allows the
  1193. caller to directly access the I/O buffer, if the file is opened
  1194. for buffered I/O.
  1195. @parm HMMIO | hmmio | Specifies the file handle of the file.
  1196. @parm LPMMIOINFO | lpmmioinfo | Specifies a pointer to a
  1197. caller-allocated <t MMIOINFO> structure that <f mmioGetInfo>
  1198. fills with information about the file. See the <t MMIOINFO> structure
  1199. and the <f mmioOpen> function for information about the fields in
  1200. this structure.
  1201. @parm UINT | uFlags | Is not used and should be set to zero.
  1202. @rdesc The return value is zero if the function is successful.
  1203. @comm To directly access the I/O buffer of a file opened for
  1204. buffered I/O, use the following fields of the <t MMIOINFO> structure
  1205. filled by <f mmioGetInfo>:
  1206. -- The <e MMIOINFO.pchNext> field points to the next byte in the
  1207. buffer that can be read or written. When you read or write, increment
  1208. <e MMIOINFO.pchNext> by the number of bytes read or written.
  1209. -- The <e MMIOINFO.pchEndRead> field points to one byte past the
  1210. last valid byte in the buffer that can be read.
  1211. -- The <e MMIOINFO.pchEndWrite> field points to one byte past the
  1212. last location in the buffer that can be written.
  1213. Once you read or write to the buffer and modify
  1214. <e MMIOINFO.pchNext>, do not call any MMIO function except
  1215. <f mmioAdvance> until you call <f mmioSetInfo>. Call <f mmioSetInfo>
  1216. when you are finished directly accessing the buffer.
  1217. When you reach the end of the buffer specified by
  1218. <e MMIOINFO.pchEndRead> or <e MMIOINFO.pchEndWrite>, call
  1219. <f mmioAdvance> to fill the buffer from the disk, or write
  1220. the buffer to the disk. The <f mmioAdvance> function
  1221. will update the <e MMIOINFO.pchNext>, <e MMIOINFO.pchEndRead>, and
  1222. <e MMIOINFO.pchEndWrite> fields in the <t MMIOINFO> structure for the
  1223. file.
  1224. Before calling <f mmioAdvance> or <f mmioSetInfo> to flush a
  1225. buffer to disk, set the MMIO_DIRTY flag in the <e MMIOINFO.dwFlags>
  1226. field of the <t MMIOINFO> structure for the file. Otherwise, the
  1227. buffer will not get written to disk.
  1228. Do not decrement <e MMIOINFO.pchNext> or modify any fields in the
  1229. <t MMIOINFO> structure other than <e MMIOINFO.pchNext> and
  1230. <e MMIOINFO.dwFlags>. Do not set any flags in <e MMIOINFO.dwFlags>
  1231. except MMIO_DIRTY.
  1232. @xref mmioSetInfo MMIOINFO
  1233. */
  1234. /*--------------------------------------------------------------------*/
  1235. MMRESULT APIENTRY
  1236. mmioGetInfo(HMMIO hmmio, LPMMIOINFO lpmmioinfo, UINT uFlags)
  1237. {
  1238. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  1239. V_WPOINTER(lpmmioinfo, sizeof(MMIOINFO), MMSYSERR_INVALPARAM);
  1240. *lpmmioinfo = *((PMMIO)hmmio);
  1241. return 0;
  1242. }
  1243. /*--------------------------------------------------------------------*/
  1244. /* @doc EXTERNAL
  1245. @api MMRESULT | mmioSetInfo | This function updates the information
  1246. retrieved by <f mmioGetInfo> about a file opened with <f mmioOpen>.
  1247. Use this function to terminate direct buffer access of a file opened
  1248. for buffered I/O.
  1249. @parm HMMIO | hmmio | Specifies the file handle of the file.
  1250. @parm LPMMIOINFO | lpmmioinfo | Specifies a pointer to an
  1251. <t MMIOINFO> structure filled with information with
  1252. <f mmioGetInfo>.
  1253. @parm UINT | uFlags | Is not used and should be set to zero.
  1254. @rdesc The return value is zero if the function is successful.
  1255. @comm If you have written to the file I/O buffer, set the
  1256. MMIO_DIRTY flag in the <e MMIOINFO.dwFlags> field of the <t MMIOINFO>
  1257. structure before calling <f mmioSetInfo> to terminate direct buffer
  1258. access. Otherwise, the buffer will not get flushed to disk.
  1259. @xref mmioGetInfo MMIOINFO
  1260. */
  1261. /*--------------------------------------------------------------------*/
  1262. MMRESULT APIENTRY
  1263. mmioSetInfo(HMMIO hmmio, LPCMMIOINFO lpmmioinfo, UINT fuInfo)
  1264. {
  1265. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  1266. V_RPOINTER(lpmmioinfo, sizeof(MMIOINFO), MMSYSERR_INVALPARAM);
  1267. V_RPOINTER0( lpmmioinfo->pchBuffer
  1268. , lpmmioinfo->cchBuffer
  1269. , MMSYSERR_INVALPARAM
  1270. );
  1271. V_CALLBACK((FARPROC)lpmmioinfo->pIOProc, MMSYSERR_INVALPARAM);
  1272. /* copy the relevant information from <lpmmioinfo> back into <hmmio> */
  1273. *((PMMIO)hmmio) = *lpmmioinfo;
  1274. /* validate <pchEndRead>, i.e. re-enforce the invariant that
  1275. * <pchEndRead> points past the last valid byte in the buffer
  1276. */
  1277. if (((PMMIO)hmmio)->pchEndRead < ((PMMIO)hmmio)->pchNext)
  1278. ((PMMIO)hmmio)->pchEndRead = ((PMMIO)hmmio)->pchNext;
  1279. return 0;
  1280. }
  1281. /*--------------------------------------------------------------------*/
  1282. /* @doc EXTERNAL
  1283. @api MMRESULT | mmioSetBuffer | This function enables or disables
  1284. buffered I/O, or changes the buffer or buffer size for a file opened
  1285. with <f mmioOpen>.
  1286. @parm HMMIO | hmmio | Specifies the file handle of the file.
  1287. @parm LPSTR | pchBuffer | Specifies a pointer to a
  1288. caller-supplied buffer to use for buffered I/O. If NULL,
  1289. <f mmioSetBuffer> allocates an internal buffer for buffered I/O.
  1290. @parm LONG | cchBuffer | Specifies the size of the caller-supplied
  1291. buffer, or the size of the buffer for <f mmioSetBuffer> to allocate.
  1292. @parm UINT | fuInfo | Is not used and should be set to zero.
  1293. @rdesc The return value is zero if the function is successful.
  1294. Otherwise, the return value specifies an error code. If an error
  1295. occurs, the file handle remains valid. The error code can be one
  1296. of the following codes:
  1297. @flag MMIOERR_CANNOTWRITE | The contents of the old buffer could
  1298. not be written to disk, so the operation was aborted.
  1299. @flag MMIOERR_OUTOFMEMORY | The new buffer could not be allocated,
  1300. probably due to a lack of available memory.
  1301. @comm To enable buffering using an internal buffer, set
  1302. <p pchBuffer> to NULL and <p cchBuffer> to the desired buffer size.
  1303. To supply your own buffer, set <p pchBuffer> to point to the buffer,
  1304. and set <p cchBuffer> to the size of the buffer.
  1305. To disable buffered I/O, set <p pchBuffer> to NULL and
  1306. <p cchBuffer> to zero.
  1307. If buffered I/O is already enabled using an internal buffer, you
  1308. can reallocate the buffer to a different size by setting
  1309. <p pchBuffer> to NULL and <p cchBuffer> to the new buffer size. The
  1310. contents of the buffer may be changed after resizing.
  1311. */
  1312. /*--------------------------------------------------------------------*/
  1313. MMRESULT APIENTRY
  1314. mmioSetBuffer( HMMIO hmmio
  1315. , LPSTR pchBuffer
  1316. , LONG cchBuffer
  1317. , UINT uFlags
  1318. )
  1319. {
  1320. MMRESULT mmr;
  1321. HANDLE hMem;
  1322. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  1323. // Validate the buffer - for READ/WRITE as appropriate
  1324. if (((PMMIO)hmmio)->dwFlags & MMIO_WRITE) {
  1325. V_WPOINTER0(pchBuffer, cchBuffer, MMSYSERR_INVALPARAM);
  1326. } else {
  1327. V_RPOINTER0(pchBuffer, cchBuffer, MMSYSERR_INVALPARAM);
  1328. }
  1329. if ((((PMMIO)hmmio)->dwFlags & MMIO_ALLOCBUF) &&
  1330. (pchBuffer == NULL) && (cchBuffer > 0))
  1331. {
  1332. /* grow or shrink buffer in-place */
  1333. LPSTR pch;
  1334. LONG lDeltaNext;
  1335. LONG lDeltaEndRead;
  1336. /* Since the ALLOCBUF flag is set, we must have a buffer */
  1337. /* write the buffer to disk, but don't empty it */
  1338. if ((mmr = mmioFlush(hmmio, 0)) != 0)
  1339. return mmr;
  1340. for(;;)
  1341. {
  1342. /* remember where <pchNext> and <pchEndRead> are
  1343. * in the buffer
  1344. */
  1345. lDeltaNext = (LONG)(((PMMIO)hmmio)->pchNext - ((PMMIO)hmmio)->pchBuffer);
  1346. lDeltaEndRead
  1347. = (LONG)(((PMMIO)hmmio)->pchEndRead - ((PMMIO)hmmio)->pchBuffer);
  1348. if (cchBuffer >= lDeltaNext)
  1349. break;
  1350. /* caller wants to truncate the part of the buffer
  1351. * that contains <pchNext> -- handle this by
  1352. * emptying the buffer, recalculating <lDeltaNext>
  1353. * and <lDeltaEndRead>, and continuing below
  1354. */
  1355. if ((mmr = mmioFlush(hmmio, MMIO_EMPTYBUF)) != 0)
  1356. return mmr;
  1357. }
  1358. /* reallocate buffer */
  1359. {
  1360. HANDLE hTemp;
  1361. hTemp = GlobalHandle( ((PMMIO)hmmio)->pchBuffer );
  1362. GlobalUnlock( hTemp );
  1363. hMem = GlobalReAlloc( hTemp
  1364. , cchBuffer
  1365. , GMEM_MOVEABLE
  1366. );
  1367. pch = GlobalLock(hMem);
  1368. dprintf2(("mmioSetBuffer reallocated ptr %8x, handle %8x, to ptr %8x (handle %8x)\n",
  1369. ((PMMIO)hmmio)->pchBuffer, hTemp, pch, hMem));
  1370. }
  1371. /* If we cannot allocate the new buffer, exit with no
  1372. * harm done.
  1373. */
  1374. if (pch == NULL)
  1375. return MMIOERR_OUTOFMEMORY; // out of memory
  1376. /* transfer pointers to new buffer */
  1377. ((PMMIO)hmmio)->cchBuffer = cchBuffer;
  1378. ((PMMIO)hmmio)->pchBuffer = pch;
  1379. ((PMMIO)hmmio)->pchNext = pch + lDeltaNext;
  1380. ((PMMIO)hmmio)->pchEndRead = pch + lDeltaEndRead;
  1381. /* <pchEndWrite> always points to the end of the buf. */
  1382. ((PMMIO)hmmio)->pchEndWrite = ((PMMIO)hmmio)->pchBuffer + cchBuffer;
  1383. /* check if the reallocation truncated valid data */
  1384. if (lDeltaEndRead > cchBuffer)
  1385. ((PMMIO)hmmio)->pchEndRead = ((PMMIO)hmmio)->pchEndWrite;
  1386. return 0;
  1387. }
  1388. /* write the buffer to disk and stop using the buffer */
  1389. if ((mmr = mmioFlush(hmmio, MMIO_EMPTYBUF)) != 0)
  1390. return mmr;
  1391. if (((PMMIO)hmmio)->dwFlags & MMIO_ALLOCBUF)
  1392. {
  1393. hMem = GlobalHandle( ((PMMIO)hmmio)->pchBuffer);
  1394. GlobalUnlock( hMem );
  1395. GlobalFree( hMem );
  1396. ((PMMIO)hmmio)->dwFlags &= ~MMIO_ALLOCBUF;
  1397. }
  1398. /* Initially, no error. */
  1399. mmr = 0;
  1400. if ((pchBuffer == NULL) && (cchBuffer > 0))
  1401. {
  1402. hMem = GlobalAlloc(GMEM_MOVEABLE, cchBuffer);
  1403. if (hMem)
  1404. pchBuffer = GlobalLock(hMem);
  1405. //else pchBuffer = NULL;
  1406. /* If there is an error, change the file to be un-buffered
  1407. * and return an error code. The file is still valid.
  1408. * (Just for a little extra security.)
  1409. */
  1410. if (pchBuffer == NULL)
  1411. { mmr = MMIOERR_OUTOFMEMORY;
  1412. cchBuffer = 0L;
  1413. }
  1414. else
  1415. ((PMMIO)hmmio)->dwFlags |= MMIO_ALLOCBUF;
  1416. }
  1417. /* invariant: <pchEndRead> points past the end of the "valid" portion
  1418. * of the buffer, and <pchEndWrite> points past the last byte that
  1419. * can be written into; <pchNext> points to the next byte to read
  1420. * or write; <lBufOffset> is the current disk offset of the start
  1421. * of the buffer, and it will not change
  1422. */
  1423. ((PMMIO)hmmio)->pchBuffer = pchBuffer;
  1424. ((PMMIO)hmmio)->cchBuffer = cchBuffer;
  1425. ((PMMIO)hmmio)->pchNext
  1426. = ((PMMIO)hmmio)->pchEndRead = ((PMMIO)hmmio)->pchBuffer;
  1427. ((PMMIO)hmmio)->pchEndWrite = ((PMMIO)hmmio)->pchBuffer + cchBuffer;
  1428. return mmr;
  1429. }
  1430. /*--------------------------------------------------------------------*/
  1431. /* @doc EXTERNAL
  1432. @api MMRESULT | mmioFlush | This function writes the I/O buffer of a
  1433. file to disk, if the I/O buffer has been written to.
  1434. @parm HMMIO | hmmio | Specifies the file handle of a file opened
  1435. with <f mmioOpen>.
  1436. @parm UINT | uFlags | Is not used and should be set to zero.
  1437. @rdesc The return value is zero if the function is successful.
  1438. Otherwise, the return value specifies an error code. The error
  1439. code can be one of the following codes:
  1440. @flag MMIOERR_CANNOTWRITE | The contents of the buffer could
  1441. not be written to disk.
  1442. @comm Closing a file with <f mmioClose> will automatically flush
  1443. its buffer.
  1444. If there is insufficient disk space to write the
  1445. buffer, <f mmioFlush> will fail, even if the preceding <f mmioWrite>
  1446. calls were successful.
  1447. */
  1448. /*--------------------------------------------------------------------*/
  1449. MMRESULT APIENTRY
  1450. mmioFlush(HMMIO hmmio, UINT uFlags)
  1451. {
  1452. LONG lBytesAsk; // no. bytes to write
  1453. LONG lBytesWritten; // no. bytes actually written
  1454. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  1455. if ( ( ((PMMIO)hmmio)->fccIOProc
  1456. == FOURCC_MEM
  1457. )
  1458. || ( ((PMMIO)hmmio)->pchBuffer == NULL )
  1459. )
  1460. return 0; // cannot flush memory files
  1461. /* if the file is unbuffered then the dirty flag should not be set */
  1462. if (((PMMIO)hmmio)->dwFlags & MMIO_DIRTY)
  1463. {
  1464. /* figure out how many bytes need to be flushed */
  1465. lBytesAsk = (LONG)(((PMMIO)hmmio)->pchEndRead - ((PMMIO)hmmio)->pchBuffer);
  1466. /* write the buffer to disk */
  1467. lBytesWritten = mmioDiskIO(((PMMIO)hmmio), MMIOM_WRITEFLUSH,
  1468. ((PMMIO)hmmio)->pchBuffer, lBytesAsk);
  1469. if (lBytesWritten != lBytesAsk)
  1470. return MMIOERR_CANNOTWRITE;
  1471. ((PMMIO)hmmio)->dwFlags &= ~MMIO_DIRTY; // buffer is clean now
  1472. }
  1473. if (uFlags & MMIO_EMPTYBUF)
  1474. {
  1475. /* empty the I/O buffer, and update <lBufOffset> to reflect
  1476. * what the current file position is
  1477. */
  1478. ((PMMIO)hmmio)->lBufOffset
  1479. += (LONG)((((PMMIO)hmmio)->pchNext - ((PMMIO)hmmio)->pchBuffer));
  1480. ((PMMIO)hmmio)->pchNext
  1481. = ((PMMIO)hmmio)->pchEndRead = ((PMMIO)hmmio)->pchBuffer;
  1482. }
  1483. return 0;
  1484. }
  1485. /*--------------------------------------------------------------------*/
  1486. /* @doc EXTERNAL
  1487. @api MMRESULT | mmioAdvance | This function advances the I/O buffer of
  1488. a file set up for direct I/O buffer access with <f mmioGetInfo>. If
  1489. the file is opened for reading, the I/O buffer is filled from the
  1490. disk. If the file is opened for writing and the MMIO_DIRTY flag is
  1491. set in the <e MMIOINFO.dwFlags> field of the <t MMIOINFO> structure,
  1492. the buffer is written to disk. The <e MMIOINFO.pchNext>,
  1493. <e MMIOINFO.pchEndRead>, and <e MMIOINFO.pchEndWrite> fields of the
  1494. <t MMIOINFO> structure are updated to reflect the new state of
  1495. the I/O buffer.
  1496. @parm HMMIO | hmmio | Specifies the file handle for a file opened
  1497. with <f mmioOpen>.
  1498. @parm LPMMIOINFO | lpmmioinfo | Optionally specifies a pointer to the
  1499. <t MMIOINFO> structure obtained with <f mmioGetInfo>, which is used to
  1500. set the current file information, then updated after the buffer is
  1501. advanced.
  1502. @parm UINT | uFlags | Specifies options for the operation.
  1503. Contains exactly one of the following two flags:
  1504. @flag MMIO_READ | The buffer is filled from the file.
  1505. @flag MMIO_WRITE | The buffer is written to the file.
  1506. @rdesc The return value is zero if the operation is successful.
  1507. Otherwise, the return value specifies an error code. The error
  1508. code can be one of the following codes:
  1509. @flag MMIOERR_CANNOTWRITE | The contents of the buffer could
  1510. not be written to disk.
  1511. @flag MMIOERR_CANNOTREAD | An error occurred while re-filling
  1512. the buffer.
  1513. @flag MMIOERR_UNBUFFERED | The specified file is not opened
  1514. for buffered I/O.
  1515. @flag MMIOERR_CANNOTEXPAND | The specified memory file cannot
  1516. be expanded, probably because the <e MMIOINFO.adwInfo[0]> field
  1517. was set to zero in the initial call to <f mmioOpen>.
  1518. @flag MMIOERR_OUTOFMEMORY | There was not enough memory to expand
  1519. a memory file for further writing.
  1520. @comm If the specified file is opened for writing or for both
  1521. reading and writing, the I/O buffer will be flushed to disk before
  1522. the next buffer is read. If the I/O buffer cannot be written to disk
  1523. because the disk is full, then <f mmioAdvance> will return
  1524. MMIOERR_CANNOTWRITE.
  1525. If the specified file is only open for writing, the MMIO_WRITE
  1526. flag must be specified.
  1527. If you have written to the I/O buffer, you must set the MMIO_DIRTY
  1528. flag in the <e MMIOINFO.dwFlags> field of the <t MMIOINFO> structure
  1529. before calling <f mmioAdvance>. Otherwise, the buffer will not be
  1530. written to disk.
  1531. If the end of file is reached, <f mmioAdvance> will still return
  1532. success, even though no more data can be read. Thus, to check for
  1533. the end of the file, it is necessary to see if the
  1534. <e MMIOINFO.pchNext> and <e MMIOINFO.pchEndRead> fields of the
  1535. <t MMIOINFO> structure are equal after calling <f mmioAdvance>.
  1536. @xref mmioGetInfo MMIOINFO
  1537. */
  1538. /*--------------------------------------------------------------------*/
  1539. MMRESULT APIENTRY
  1540. mmioAdvance(HMMIO hmmio, LPMMIOINFO lpmmioinfo, UINT uFlags)
  1541. {
  1542. LONG lBytesRead; // bytes actually read
  1543. UINT w;
  1544. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  1545. if (((PMMIO)hmmio)->pchBuffer == NULL)
  1546. return MMIOERR_UNBUFFERED;
  1547. if (lpmmioinfo != NULL) {
  1548. V_WPOINTER(lpmmioinfo, sizeof(MMIOINFO), MMSYSERR_INVALPARAM);
  1549. mmioSetInfo(hmmio, lpmmioinfo, 0);
  1550. }
  1551. if (((PMMIO)hmmio)->fccIOProc == FOURCC_MEM)
  1552. {
  1553. /* this is a memory file:
  1554. * -- if the caller is reading, cannot advance
  1555. * -- if the caller is writing, then advance by expanding
  1556. * the buffer (if possible) if the there is less than
  1557. * <adwInfo[0]> bytes left in the buffer
  1558. */
  1559. if (!(uFlags & MMIO_WRITE))
  1560. return MMIOERR_CANNOTREAD;
  1561. if ( (DWORD)(((PMMIO)hmmio)->pchEndWrite - ((PMMIO)hmmio)->pchNext)
  1562. >= ((PMMIO)hmmio)->adwInfo[0]
  1563. )
  1564. return MMIOERR_CANNOTEXPAND;
  1565. if ((w = mmioExpandMemFile(((PMMIO)hmmio), 1L)) != 0)
  1566. return w; // out of memory, or whatever
  1567. goto GETINFO_AND_EXIT;
  1568. }
  1569. /* empty the I/O buffer, which will effectively advance the
  1570. * buffer by (<pchNext> - <pchBuffer>) bytes
  1571. */
  1572. if ((w = mmioFlush(hmmio, MMIO_EMPTYBUF)) != 0)
  1573. return w;
  1574. /* if MMIO_WRITE bit is not set in uFlags, fill the buffer */
  1575. if (!(uFlags & MMIO_WRITE))
  1576. {
  1577. /* read the next bufferful from the file */
  1578. lBytesRead = mmioDiskIO(((PMMIO)hmmio), MMIOM_READ,
  1579. ((PMMIO)hmmio)->pchBuffer, ((PMMIO)hmmio)->cchBuffer);
  1580. if (lBytesRead == -1)
  1581. return MMIOERR_CANNOTREAD;
  1582. /* reading zero bytes should not be treated as an error
  1583. * condition -- e.g. open a new file R+W and call
  1584. * mmioAdvance(), and MMIOM_READ will return zero bytes
  1585. * because the file started off empty
  1586. */
  1587. ((PMMIO)hmmio)->pchEndRead += lBytesRead;
  1588. }
  1589. GETINFO_AND_EXIT:
  1590. /* copy <hmmio> back to <lpmmioinfo> if <lpmmioinfo> is provided */
  1591. if (lpmmioinfo != NULL)
  1592. mmioGetInfo(hmmio, lpmmioinfo, 0);
  1593. return 0;
  1594. }
  1595. /*--------------------------------------------------------------------*/
  1596. /* @doc EXTERNAL
  1597. @api FOURCC | mmioStringToFOURCC | This function converts a
  1598. null-terminated string to a four-character code.
  1599. @parm LPCTSTR | sz | Specifies a pointer to a null-terminated
  1600. string to a four-character code.
  1601. @parm UINT | uFlags | Specifies options for the conversion:
  1602. @flag MMIO_TOUPPER | Converts all characters to uppercase.
  1603. @rdesc The return value is the four character code created from the
  1604. given string.
  1605. @comm This function does not check to see if the string referenced
  1606. by <p sz> follows any conventions regarding which characters to
  1607. include in a four-character code. The string is
  1608. simply copied to a four-character code and padded with blanks or
  1609. truncated to four characters if required.
  1610. @xref mmioFOURCC
  1611. */
  1612. /*--------------------------------------------------------------------*/
  1613. FOURCC APIENTRY
  1614. mmioStringToFOURCCW( LPCWSTR sz, UINT uFlags )
  1615. {
  1616. FOURCC fcc;
  1617. PBYTE pByte; // ascii version of szFileName
  1618. ULONG cbDst; // character count of szFileName
  1619. // V_STRING(sz, -1, 0);
  1620. /*------------------------------------------------------------*\
  1621. * Convert the given unicode string into ascii and then call
  1622. * the ascii version of mmioStringToFOURCCW
  1623. \*------------------------------------------------------------*/
  1624. cbDst = (wcslen( sz ) * sizeof(WCHAR)) + sizeof(WCHAR);
  1625. pByte = HeapAlloc( hHeap, 0, cbDst );
  1626. if ( pByte == (PBYTE)NULL ) {
  1627. return (FOURCC)(DWORD_PTR)NULL;
  1628. }
  1629. UnicodeStrToAsciiStr( pByte, pByte + cbDst, sz );
  1630. fcc = mmioStringToFOURCCA( (LPSTR)pByte, uFlags );
  1631. HeapFree( hHeap, 0, pByte );
  1632. return (FOURCC)fcc;
  1633. }
  1634. FOURCC APIENTRY
  1635. mmioStringToFOURCCA( LPCSTR sz, UINT uFlags )
  1636. {
  1637. FOURCC fcc;
  1638. LPSTR pch = (LPSTR) &fcc;
  1639. int i;
  1640. V_STRING(sz, (DWORD)-1, 0);
  1641. for (i = sizeof(FOURCC) - 1; i >= 0; i--)
  1642. {
  1643. if (!*sz)
  1644. *pch = ' '; /* and don't increment sz beyond the terminating NULL! */
  1645. else {
  1646. *pch = *sz;
  1647. if (uFlags & MMIO_TOUPPER)
  1648. //#ifdef DBCS // we don't allow DBCS string. This is enough for us.
  1649. *pch = (char)(WORD)PtrToUlong(AnsiUpper((LPSTR)(DWORD_PTR)((ULONG)*pch & 0xff)));
  1650. //#else
  1651. // *pch = (char)(WORD)(LONG)AnsiUpper((LPSTR)(LONG)*pch);
  1652. //#endif
  1653. sz++;
  1654. }
  1655. pch++;
  1656. }
  1657. return fcc;
  1658. }
  1659. /*--------------------------------------------------------------------*/
  1660. /* @doc EXTERNAL
  1661. @api LPMMIOPROC | mmioInstallIOProc | This function installs or
  1662. removes a custom I/O procedure. It will also locate an installed I/O
  1663. procedure, given its corresponding four-character code.
  1664. @parm FOURCC | fccIOProc | Specifies a four-character code
  1665. identifying the I/O procedure to install, remove, or locate. All
  1666. characters in this four-character code should be uppercase characters.
  1667. @parm LPMMIOPROC | pIOProc | Specifies the address of the I/O
  1668. procedure to install. To remove or locate an I/O procedure, set this
  1669. parameter to NULL.
  1670. @parm DWORD | dwFlags | Specifies one of the following flags
  1671. indicating whether the I/O procedure is being installed, removed, or
  1672. located:
  1673. @flag MMIO_INSTALLPROC | Installs the specified I/O procedure.
  1674. @flag MMIO_GLOBALPROC | This flag is a modifier to the install flag,
  1675. and indicates the I/O procedure should be installed for global
  1676. use. This flag is ignored on removal or find.
  1677. @flag MMIO_REMOVEPROC | Removes the specified I/O procedure.
  1678. @flag MMIO_FINDPROC | Searches for the specified I/O procedure.
  1679. @rdesc The return value is the address of the I/O procedure
  1680. installed, removed, or located. If there is an error, the return value
  1681. is NULL.
  1682. @comm If the I/O procedure resides in the application, use
  1683. <f MakeProcInstance> for compatibility with 16 bit windows
  1684. to get a procedure-instance address and specify
  1685. this address for <p pIOProc>. You don't need to get a procedure-instance
  1686. address if the I/O procedure resides in a DLL.
  1687. @cb LONG FAR PASCAL | IOProc | <f IOProc> is a placeholder for the
  1688. application-supplied function name. The actual name must be exported
  1689. by including it in a EXPORTS statement in the application's
  1690. module-definitions file.
  1691. @parm LPSTR | lpmmioinfo | Specifies a pointer to an
  1692. <t MMIOINFO> structure containing information about the open
  1693. file. The I/O procedure must maintain the <e MMIOINFO.lDiskOffset>
  1694. field in this structure to indicate the file offset to the
  1695. next read or write location. The I/O procedure can use the
  1696. <e MMIOINFO.adwInfo[]> field to store state information. The
  1697. I/O procedure should not modify any other fields of the
  1698. <t MMIOINFO> structure.
  1699. @parm UINT | wMsg | Specifies a message indicating the
  1700. requested I/O operation. Messages that can be received include
  1701. <m MMIOM_OPEN>, <m MMIOM_CLOSE>, <m MMIOM_READ>, <m MMIOM_WRITE>,
  1702. and <m MMIOM_SEEK>.
  1703. @parm LONG | lParam1 | Specifies a parameter for the message.
  1704. @parm LONG | lParam2 | Specifies a parameter for the message.
  1705. @rdesc The return value depends on the message specified by
  1706. <p wMsg>. If the I/O procedure does not recognize a message, it should
  1707. return zero.
  1708. @comm The four-character code specified by the
  1709. <e MMIOINFO.fccIOProc> field in the <t MMIOINFO> structure
  1710. associated with a file identifies a filename extension for a custom
  1711. storage system. When an application calls <f mmioOpen> with a
  1712. filename such as "foo.xyz!bar", the I/O procedure associated with the
  1713. four-character code "XYZ " is called to open the "bar" element of the
  1714. file "foo.xyz".
  1715. The <f mmioInstallIOProc> function maintains a separate list of
  1716. installed I/O procedures for each Windows application. Therefore,
  1717. different applications can use the same I/O procedure identifier for
  1718. different I/O procedures without conflict. Installing an I/O procedure
  1719. globally however enables any process to use the procedure.
  1720. If an application calls <f mmioInstallIOProc> more than once to
  1721. register the same I/O procedure, then it must call
  1722. <f mmioInstallIOProc> to remove the procedure once for each time it
  1723. installed the procedure.
  1724. <f mmioInstallIOProc> will not prevent an application from
  1725. installing two different I/O procedures with the same identifier, or
  1726. installing an I/O procedure with one of the predefined identifiers
  1727. ("DOS ", "MEM "). The most recently installed procedure
  1728. takes precedence, and the most recently installed procedure is the
  1729. first one to get removed.
  1730. When searching for a specified I/O procedure, local procedures are
  1731. searched first, then global procedures.
  1732. @xref mmioOpen
  1733. */
  1734. /*--------------------------------------------------------------------*/
  1735. LPMMIOPROC APIENTRY
  1736. mmioInstallIOProcW(FOURCC fccIOProc, LPMMIOPROC pIOProc, DWORD dwFlags)
  1737. {
  1738. V_FLAGS(dwFlags, MMIO_VALIDPROC, mmioInstallIOProc, NULL);
  1739. dwFlags |= MMIO_UNICODEPROC;
  1740. return mmioInternalInstallIOProc( fccIOProc, pIOProc, dwFlags);
  1741. }
  1742. LPMMIOPROC APIENTRY
  1743. mmioInstallIOProcA(FOURCC fccIOProc, LPMMIOPROC pIOProc, DWORD dwFlags)
  1744. {
  1745. V_FLAGS(dwFlags, MMIO_VALIDPROC, mmioInstallIOProc, NULL);
  1746. dwFlags &= ~MMIO_UNICODEPROC;
  1747. return mmioInternalInstallIOProc( fccIOProc, pIOProc, dwFlags);
  1748. }
  1749. static LPMMIOPROC mmioInternalInstallIOProc(
  1750. FOURCC fccIOProc, // I/O Proc 4 char id
  1751. LPMMIOPROC pIOProc, // pointer to any I/O proc to install
  1752. DWORD dwFlags // flags from caller
  1753. )
  1754. {
  1755. IOProcMapEntry *pEnt; // an entry in linked list
  1756. HANDLE hTaskCurrent; // current Windows task handle
  1757. #ifdef DUMPIOPROCLIST
  1758. // dprintf(("initial I/O proc list\n"));
  1759. // DumpIOProcList();
  1760. #endif
  1761. if (fccIOProc == 0L)
  1762. return NULL;
  1763. hTaskCurrent = GetCurrentTask();
  1764. if (dwFlags & MMIO_INSTALLPROC)
  1765. {
  1766. /* install I/O procedure -- always add at the beginning of
  1767. * the list, so it overrides any other I/O procedures
  1768. * with the same identifier installed by the same task
  1769. */
  1770. V_CALLBACK((FARPROC)pIOProc, NULL);
  1771. if ((pEnt = (IOProcMapEntry NEAR *)
  1772. NewHandle(TYPE_MMIO, NULL, sizeof(IOProcMapEntry))) == NULL)
  1773. return NULL; // out of memory
  1774. // Implicitly acquired by NewHandle()
  1775. ReleaseHandleListResource();
  1776. pEnt->fccIOProc = fccIOProc;
  1777. pEnt->pIOProc = pIOProc;
  1778. pEnt->hTask = hTaskCurrent;
  1779. pEnt->pNext = gIOProcMapHead;
  1780. gIOProcMapHead = pEnt;
  1781. #ifdef DUMPIOPROCLIST
  1782. // dprintf(("I/O proc list after addition"));
  1783. // DumpIOProcList();
  1784. #endif
  1785. return pIOProc;
  1786. }
  1787. if (!pIOProc)
  1788. if (dwFlags & MMIO_REMOVEPROC)
  1789. return RemoveIOProc(fccIOProc, hTaskCurrent);
  1790. else if (dwFlags & MMIO_FINDPROC)
  1791. {
  1792. pEnt = FindIOProc(fccIOProc, hTaskCurrent);
  1793. return ( pEnt==NULL
  1794. ? NULL
  1795. : pEnt->pIOProc
  1796. );
  1797. }
  1798. return NULL; // couldn't find requested I/O procedure
  1799. }
  1800. /*--------------------------------------------------------------------*/
  1801. /* @doc EXTERNAL
  1802. @api LRESULT | mmioSendMessage | This function sends a message to the
  1803. I/O procedure associated with the specified file.
  1804. @parm HMMIO | hmmio | Specifies the file handle for a file opened
  1805. with <f mmioOpen>.
  1806. @parm UINT | wMsg | Specifies the message to send to the I/O procedure.
  1807. @parm LONG | lParam1 | Specifies a parameter for the message.
  1808. @parm LONG | lParam2 | Specifies a parameter for the message.
  1809. @rdesc The return value depends on the message. If the I/O procedure
  1810. does not recognize the message, the return value is zero.
  1811. @comm Use this function to send custom user-defined messages. Do
  1812. not use it to send the <m MMIOM_OPEN>, <m MMIOM_CLOSE>,
  1813. <m MMIOM_READ>, <m MMIOM_WRITE>, <m MMIOM_WRITEFLUSH>, or
  1814. <m MMIOM_SEEK> messages. Define
  1815. custom messages to be greater than or equal to the MMIOM_USER constant.
  1816. @xref mmioInstallIOProc
  1817. */
  1818. /*--------------------------------------------------------------------*/
  1819. LRESULT APIENTRY
  1820. mmioSendMessage(HMMIO hmmio, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
  1821. {
  1822. V_HANDLE(hmmio, TYPE_MMIO, (LRESULT)0);
  1823. return IOProc( (PMMIO)hmmio, uMsg, lParam1, lParam2);
  1824. }
  1825. /*--------------------------------------------------------------------*/
  1826. /* @doc INTERNAL
  1827. @api LONG | mmioDiskIO | Perform an unbuffered read or write.
  1828. Do not assume where the current disk offset <p lDiskOffset> will be.
  1829. @parm PMMIO | pmmio | The open file handle returned by <f mmioOpen>.
  1830. @parm UINT | wMsg | MMIOM_READ if <f mmioDiskIO> should read from the disk,
  1831. or MMIOM_WRITE if <f mmioDiskIO> should write to the disk,
  1832. or MMIOM_WRITEFLUSH if <f mmioDiskIO> should flush all pending I/O.
  1833. @parm LPSTR | pch | The buffer to read into or write from.
  1834. @parm LONG | cch | The number of bytes to read or write.
  1835. <f mmioDiskIO> changes the disk offset to be <p lBufOffset>
  1836. and then performs an MMIOM_READ or MMIOM_WRITE operation as
  1837. specified by <p wMsg>, <p pch>, and <p cch>.
  1838. Note that if the I/O buffer is not empty at this point, this
  1839. function may not do what you expect.
  1840. Do not call this function for memory files.
  1841. */
  1842. /*--------------------------------------------------------------------*/
  1843. static LONG NEAR PASCAL
  1844. mmioDiskIO(PMMIO pmmio, UINT uMsg, LPSTR pch, LONG cch)
  1845. {
  1846. if (pmmio->lDiskOffset != pmmio->lBufOffset)
  1847. {
  1848. if (IOProc( pmmio
  1849. , MMIOM_SEEK
  1850. , (LONG) pmmio->lBufOffset
  1851. , (LONG) SEEK_SET
  1852. )
  1853. == -1
  1854. )
  1855. return -1;
  1856. }
  1857. return (LONG)IOProc( pmmio, uMsg, (LPARAM) pch, (LPARAM) cch);
  1858. }
  1859. /*--------------------------------------------------------------------*/
  1860. /* @doc INTERNAL
  1861. @api UINT | mmioExpandMemFile | Assuming that <p pmmio> is a memory file,
  1862. expand it by <p lExpand> bytes or <p adwInfo[0]> bytes, whichever
  1863. is larger. Do not disturb the contents of the buffer or change
  1864. the current file position.
  1865. @parm PMMIO | pmmio | The open file handle returned by <f mmioOpen>.
  1866. @parm LONG | lExpand | The minimum number of bytes to expand the buffer by.
  1867. @rdesc If the function succeeds, zero is returned. If the function fails,
  1868. an error code is returned. In particular, MMIOERR_OUTOFMEMORY is
  1869. returned if memory reallocation failed.
  1870. @comm Only call this function for memory files.
  1871. */
  1872. /*--------------------------------------------------------------------*/
  1873. static UINT NEAR PASCAL
  1874. mmioExpandMemFile(PMMIO pmmio, LONG lExpand)
  1875. {
  1876. MMIOMEMINFO * pInfo = (MMIOMEMINFO *) pmmio->adwInfo;
  1877. DWORD dwFlagsTemp;
  1878. UINT w;
  1879. /* make sure buffer can be expanded */
  1880. /* Note: we used to check ALLOC_BUF here, we don't now. */
  1881. if (pInfo->lExpand == 0)
  1882. return MMIOERR_CANNOTEXPAND; // cannot grow file
  1883. /* how much should the buffer be expanded by? */
  1884. if (lExpand < pInfo->lExpand)
  1885. lExpand = pInfo->lExpand;
  1886. dwFlagsTemp = pmmio->dwFlags;
  1887. pmmio->dwFlags |= MMIO_ALLOCBUF;
  1888. w = mmioSetBuffer(((HMMIO)pmmio), NULL,
  1889. pmmio->cchBuffer + lExpand, 0);
  1890. pmmio->dwFlags = dwFlagsTemp;
  1891. return w;
  1892. }
  1893. /*--------------------------------------------------------------------*/
  1894. /* @doc INTERNAL
  1895. @api LRESULT | mmioDOSIOProc | The 'DOS' I/O procedure, which handles I/O
  1896. on ordinary DOS files.
  1897. @parm LPSTR | lpmmioinfo | A pointer to an MMIOINFO block that
  1898. contains information about the open file.
  1899. @parm UINT | uMsg | The message that the I/O procedure is being
  1900. asked to execute.
  1901. @parm LONG | lParam1 | Specifies additional message information.
  1902. @parm LONG | lParam2 | Specifies additional message information.
  1903. @rdesc Return value depends on <p wMsg>.
  1904. */
  1905. /*--------------------------------------------------------------------*/
  1906. LRESULT
  1907. mmioDOSIOProc(LPSTR lpmmioStr, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
  1908. {
  1909. PMMIO pmmio = (PMMIO)lpmmioStr; // only in DLL!
  1910. MMIODOSINFO *pInfo = (MMIODOSINFO *)pmmio->adwInfo;
  1911. LONG lResult;
  1912. LPWSTR szFilePart;
  1913. WCHAR szPath[ MAX_PATH ];
  1914. switch (uMsg) {
  1915. case MMIOM_OPEN:
  1916. /*
  1917. * The extra info parameter optionally contains a
  1918. * sequence number to pass.
  1919. */
  1920. if ( pmmio->dwFlags & MMIO_GETTEMP )
  1921. {
  1922. V_RPOINTER((LPSTR)lParam1, 4, (LRESULT) MMSYSERR_INVALPARAM);
  1923. if ( GetTempPathW( MAX_PATH, szPath ) == 0 ) {
  1924. wcscpy( szPath, (LPCWSTR)L"." );
  1925. }
  1926. return GetTempFileNameW( szPath, (LPCWSTR)L"sje",
  1927. (WORD)pmmio->adwInfo[0], (LPWSTR)lParam1 )
  1928. ? (LRESULT)0
  1929. : (LRESULT)MMIOERR_FILENOTFOUND;
  1930. }
  1931. /*------------------------------------------------------------*\
  1932. * <lParam1> is either a file name or NULL; if it is
  1933. * NULL, then <adwInfo[0]>, which is actually <pInfo->fh>,
  1934. * should already contain an open DOS file handle.
  1935. *
  1936. * Does lParam1 point to a file name ?
  1937. *
  1938. * if so then either:
  1939. *
  1940. * delete the file,
  1941. * check the existance of the file,
  1942. * parse the file name, or
  1943. * open the file name
  1944. *
  1945. \*------------------------------------------------------------*/
  1946. if ( lParam1 != 0 ) {
  1947. if ( pmmio->dwFlags & MMIO_DELETE ) {
  1948. return DeleteFileW( (LPWSTR)lParam1 )
  1949. ? (LRESULT)0
  1950. : (LRESULT)MMIOERR_FILENOTFOUND;
  1951. }
  1952. if ( pmmio->dwFlags & MMIO_EXIST ) {
  1953. if ( !(pmmio->dwFlags & MMIO_CREATE) ) {
  1954. #ifdef LATER
  1955. I think this should be using SearchPath (with lpszPath==lParam1)
  1956. as the definition of MMIO_EXIST states that a fully qualified
  1957. filename is returned. OR tweak the flags to turn MMIO_PARSE ON
  1958. and execute the next section.
  1959. #endif
  1960. if ( GetFileAttributesW( (LPWSTR)lParam1 ) == -1 ) {
  1961. return (LRESULT)MMIOERR_FILENOTFOUND;
  1962. }
  1963. return (LRESULT)0;
  1964. }
  1965. }
  1966. if ( pmmio->dwFlags & MMIO_PARSE ) {
  1967. if ( GetFullPathNameW((LPWSTR)lParam1,
  1968. MAX_PATH,
  1969. szPath,
  1970. &szFilePart ) == 0 ) {
  1971. return (LRESULT)MMIOERR_FILENOTFOUND;
  1972. }
  1973. wcscpy( (LPWSTR)lParam1, szPath );
  1974. return (LRESULT) 0;
  1975. }
  1976. {
  1977. DWORD dwAccess = 0;
  1978. DWORD dwSharedMode = 0;
  1979. DWORD dwCreate = 0;
  1980. DWORD dwFlags = FILE_ATTRIBUTE_NORMAL;
  1981. /*----------------------------------------------------*\
  1982. * Look at the access flags
  1983. \*----------------------------------------------------*/
  1984. if ( pmmio->dwFlags & MMIO_WRITE ) {
  1985. dwAccess = GENERIC_WRITE;
  1986. } else {
  1987. dwAccess = GENERIC_READ;
  1988. }
  1989. if ( pmmio->dwFlags & MMIO_READWRITE ) {
  1990. dwAccess |= (GENERIC_WRITE | GENERIC_READ);
  1991. }
  1992. /*----------------------------------------------------*\
  1993. * Set dwSharedMode from the share flags
  1994. \*----------------------------------------------------*/
  1995. { /* owing to some crappy design in WIN3.1, the share flags are
  1996. * exclusive = 10
  1997. * deny write = 20
  1998. * deny read = 30
  1999. * deny none = 40
  2000. * so deny read looks like exclusive + deny write. Sigh.
  2001. * 00 is taken as being DENYNONE (probably correct)
  2002. * So is 50, 60 and 70 (which is probably bogus).
  2003. * As we need to support the DOS flags for WOW, we need this
  2004. * code somewhere, so might as well leave the flag definitions
  2005. * as they are. First pull out all the share mode bits.
  2006. */
  2007. DWORD dwShare = MMIO_DENYWRITE | MMIO_DENYREAD
  2008. | MMIO_DENYNONE | MMIO_EXCLUSIVE;
  2009. dwShare &= pmmio->dwFlags;
  2010. switch (dwShare)
  2011. { case MMIO_DENYWRITE:
  2012. dwSharedMode = FILE_SHARE_READ;
  2013. break;
  2014. case MMIO_DENYREAD:
  2015. dwSharedMode = FILE_SHARE_WRITE;
  2016. break;
  2017. case MMIO_EXCLUSIVE:
  2018. dwSharedMode = 0;
  2019. break;
  2020. case MMIO_DENYNONE:
  2021. default:
  2022. dwSharedMode = FILE_SHARE_WRITE | FILE_SHARE_READ;
  2023. break;
  2024. #ifdef later
  2025. Generate an error for invalid flags?
  2026. #endif
  2027. }
  2028. }
  2029. /*----------------------------------------------------*\
  2030. * Look at the create flags
  2031. \*----------------------------------------------------*/
  2032. if ( (pmmio->dwFlags) & MMIO_CREATE) {
  2033. dwCreate = CREATE_ALWAYS;
  2034. wcscpy( szPath, (LPWSTR)lParam1 );
  2035. } else {
  2036. dwCreate = OPEN_EXISTING;
  2037. if ( SearchPathW( NULL, (LPWSTR)lParam1,
  2038. NULL,
  2039. (MAX_PATH - 1),
  2040. szPath, &szFilePart ) == 0 ) {
  2041. return (LRESULT)MMIOERR_FILENOTFOUND;
  2042. }
  2043. }
  2044. pInfo->fh = (int)(DWORD_PTR)CreateFileW( szPath,
  2045. dwAccess,
  2046. dwSharedMode,
  2047. NULL,
  2048. dwCreate,
  2049. dwFlags | FILE_FLAG_SEQUENTIAL_SCAN,
  2050. NULL );
  2051. if ( pInfo->fh == (int)-1 ) {
  2052. return (LRESULT)MMIOERR_FILENOTFOUND;
  2053. }
  2054. if ( pmmio->dwFlags & MMIO_EXIST ) {
  2055. CloseHandle( (HANDLE)(UINT_PTR)pInfo->fh );
  2056. return (LRESULT)0;
  2057. }
  2058. }
  2059. }
  2060. /* check the current file offset */
  2061. pmmio->lDiskOffset = _llseek(pInfo->fh, 0L, SEEK_CUR);
  2062. return (LRESULT)0;
  2063. case MMIOM_CLOSE:
  2064. /* MMIO_FHOPEN flag means keep the DOS file handle open */
  2065. if ( !((DWORD)lParam1 & MMIO_FHOPEN)
  2066. && (_lclose(pInfo->fh) == HFILE_ERROR) ) {
  2067. return (LRESULT) MMIOERR_CANNOTCLOSE;
  2068. }
  2069. return (LRESULT) 0;
  2070. case MMIOM_READ:
  2071. lResult = _lread(pInfo->fh, (LPVOID)lParam1, (LONG)lParam2);
  2072. if (lResult != -1L) {
  2073. pmmio->lDiskOffset += lResult;
  2074. }
  2075. return (LRESULT) lResult;
  2076. case MMIOM_WRITE:
  2077. case MMIOM_WRITEFLUSH:
  2078. lResult = _lwrite(pInfo->fh, (LPVOID)lParam1, (LONG)lParam2);
  2079. if (lResult != -1L) {
  2080. pmmio->lDiskOffset += lResult;
  2081. }
  2082. #ifdef DOSCANFLUSH
  2083. if (uMsg == MMIOM_WRITEFLUSH)
  2084. {
  2085. /* Issue hardware flush command */
  2086. }
  2087. #endif
  2088. return (LRESULT) lResult;
  2089. case MMIOM_SEEK:
  2090. lResult = _llseek(pInfo->fh, (LONG)lParam1, (int)(LONG)lParam2);
  2091. if (lResult != -1L) {
  2092. pmmio->lDiskOffset = lResult;
  2093. }
  2094. return (LRESULT) lResult;
  2095. case MMIOM_RENAME:
  2096. if (!MoveFileW((LPWSTR)lParam1, (LPWSTR)lParam2)) {
  2097. return (LRESULT) MMIOERR_FILENOTFOUND;
  2098. /* ??? There are other errors too? e.g. target exists? */
  2099. }
  2100. break;
  2101. }
  2102. return (LRESULT) 0;
  2103. }
  2104. /*--------------------------------------------------------------------*/
  2105. /* @doc INTERNAL
  2106. @api LRESULT | mmioMEMIOProc | The 'MEM' I/O procedure, which handles I/O
  2107. on memory files.
  2108. @parm LPSTR | lpmmioinfo | A pointer to an MMIOINFO block that
  2109. contains information about the open file.
  2110. @parm UINT | uMsg | The message that the I/O procedure is being
  2111. asked to execute.
  2112. @parm LONG | lParam1 | Specifies additional message information.
  2113. @parm LONG | lParam2 | Specifies additional message information.
  2114. @rdesc Return value depends on <p uMsg>.
  2115. */
  2116. /*--------------------------------------------------------------------*/
  2117. LRESULT
  2118. mmioMEMIOProc(LPSTR lpmmioStr, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
  2119. {
  2120. PMMIO pmmio = (PMMIO) lpmmioStr; // only in DLL!
  2121. switch (uMsg)
  2122. {
  2123. case MMIOM_OPEN:
  2124. if ( pmmio->dwFlags
  2125. & ~(MMIO_CREATE
  2126. | MMIO_READWRITE
  2127. | MMIO_WRITE
  2128. | MMIO_EXCLUSIVE
  2129. | MMIO_DENYWRITE
  2130. | MMIO_DENYREAD
  2131. | MMIO_DENYNONE
  2132. | MMIO_ALLOCBUF
  2133. )
  2134. )
  2135. return (LRESULT) MMSYSERR_INVALFLAG;
  2136. /* all the data in the buffer is valid */
  2137. if (!(pmmio->dwFlags & MMIO_CREATE))
  2138. pmmio->pchEndRead = pmmio->pchEndWrite;
  2139. return (LRESULT) 0;
  2140. case MMIOM_CLOSE:
  2141. /* nothing special to do on close */
  2142. return (LRESULT) 0;
  2143. case MMIOM_READ:
  2144. case MMIOM_WRITE:
  2145. case MMIOM_WRITEFLUSH:
  2146. case MMIOM_SEEK:
  2147. return (LRESULT) -1;
  2148. }
  2149. return (LRESULT) 0;
  2150. }