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.

1961 lines
63 KiB

  1. /* mmio.c
  2. *
  3. * Basic MMIO functions.
  4. *
  5. * Implementation notes:
  6. *
  7. * The "current disk offset" is the disk offset (i.e. the location
  8. * in the disk file) that the next MMIOM_READ or MMIOM_WRITE will
  9. * read from or write to. The I/O procedure maintains the
  10. * <lDiskOffset> field of the file's MMIO structure so that
  11. * <lDiskOffset> is equal to the current disk offset.
  12. *
  13. * The "current buffered offset" is the disk offset that the next
  14. * mmioRead() or mmioWrite() call would read from or write to.
  15. * The current buffered offset is defined as
  16. *
  17. * <lBufOffset> + (<pchNext> - <pchBuffer>)
  18. *
  19. * since <lBufOffset> is the disk offset of the start of the buffer
  20. * and <pchNext> corresponds to the current buffered offset.
  21. *
  22. * If the file is unbuffered, then <pchBuffer>, <pchNext>,
  23. * <pchEndRead> and <pchEndWrite> will always be NULL, and
  24. * <lBufOffset> will always be considered the "current buffered
  25. * offset", i.e. mmioRead() and mmioWrite() will read/write
  26. * at this offset.
  27. *
  28. *
  29. * Except right at the beginning of mmioOpen(), the MMIO_ALLOCBUF
  30. * flag is set if and only if the pchBuffer field points to a block
  31. * of global memory that MMIO has allocated.
  32. */
  33. #include <windows.h>
  34. #include "mmsystem.h"
  35. #include "mmioi.h"
  36. #include "mmsysi.h"
  37. /* The I/O procedure map is a linked list of IOProcMPEntry structures.
  38. * The head of the list, <gIOProcMapHead> is a pointer node to the last
  39. * entry registered. The first few elements of the list are the predefined
  40. * global IO procedures below -- these all have <hTask> equal to NULL so
  41. * that no task can unregister them.
  42. */
  43. static LRESULT CALLBACK mmioDOSIOProc(LPSTR, UINT, LPARAM, LPARAM);
  44. static LRESULT CALLBACK mmioMEMIOProc(LPSTR, UINT, LPARAM, LPARAM);
  45. static IOProcMapEntry gIOProcMaps[] = {
  46. FOURCC_DOS, mmioDOSIOProc, NULL, STATICIOPROC, &gIOProcMaps[1],
  47. FOURCC_MEM, mmioMEMIOProc, NULL, STATICIOPROC, NULL,
  48. };
  49. IOProcMapEntry NEAR * gIOProcMapHead = gIOProcMaps;
  50. /* private prototypes */
  51. static LONG NEAR PASCAL mmioDiskIO(PMMIO pmmio, UINT wMsg, HPSTR pch, LONG cch);
  52. static UINT NEAR PASCAL mmioExpandMemFile(PMMIO pmmio, LONG lExpand);
  53. /* @doc INTERNAL
  54. @func LPMMIOPROC | FindIOProc | This function locates previously installed
  55. IO procedure.
  56. */
  57. static LPMMIOPROC PASCAL NEAR
  58. FindIOProc(FOURCC fccIOProc, HTASK htask)
  59. {
  60. IOProcMapEntry *pEnt; // an entry in linked list
  61. /* walk through the linked list, first looking for an entry with
  62. * identifier <fccIOProc> that was added by the current task, then
  63. * looking for global entries.
  64. */
  65. for (pEnt = gIOProcMapHead; pEnt; pEnt = pEnt->pNext)
  66. if ((pEnt->fccIOProc == fccIOProc) && (pEnt->hTask == htask))
  67. return pEnt->pIOProc;
  68. for (pEnt = gIOProcMapHead; pEnt; pEnt = pEnt->pNext)
  69. if (!pEnt->hTask && (pEnt->fccIOProc == fccIOProc))
  70. return pEnt->pIOProc;
  71. return NULL;
  72. }
  73. /* @doc INTERNAL
  74. @func LPMMIOPROC | RemoveIOProc | This function removes previously installed
  75. IO procedure.
  76. */
  77. static LPMMIOPROC PASCAL NEAR
  78. RemoveIOProc(FOURCC fccIOProc, HTASK htask)
  79. {
  80. IOProcMapEntry *pEnt; // an entry in linked list
  81. IOProcMapEntry *pEntPrev; // the entry before <pEnt>
  82. /* walk through the linked list, looking for an entry with
  83. * identifier <fccIOProc> that was added by the current task
  84. */
  85. for (pEntPrev = NULL, pEnt = gIOProcMapHead; pEnt; pEntPrev = pEnt, pEnt = pEnt->pNext)
  86. if ((pEnt->fccIOProc == fccIOProc) && (pEnt->hTask == htask)) {
  87. LPMMIOPROC pIOProc;
  88. if (pEnt->wFlags & STATICIOPROC)
  89. return NULL;
  90. pIOProc = pEnt->pIOProc;
  91. if (pEntPrev)
  92. pEntPrev->pNext = pEnt->pNext;
  93. else
  94. gIOProcMapHead = pEnt->pNext;
  95. FreeHandle((HMMIO) pEnt);
  96. return pIOProc;
  97. }
  98. return NULL;
  99. }
  100. /* @doc INTERNAL
  101. @func void | SetIOProc | This function sets the physical IO procedure
  102. based on either the file name or the parameters within the
  103. <p lpmmioinfo> structure passed.
  104. @parm LPCSTR | szFilename | Specifies a far pointer to a string
  105. containing the filename of the file to open. If no I/O procedure is
  106. @parm LPMMIOINFO | lpmmioinfo | Specifies a far pointer to an
  107. <t MMIOINFO> structure containing extra parameters used by
  108. <f SetIOProc> in determining the IO procedure to use. The
  109. <e MMIOINFO.pIOProc> element is set to the procedure found.
  110. @rdesc Nothing.
  111. */
  112. static void NEAR PASCAL
  113. SetIOProc(LPCSTR szFileName, LPMMIOINFO lpmmio)
  114. {
  115. /* If the IOProc is not given, see if the file name implies that
  116. * <szFileName> is either a RIFF compound file or some kind of
  117. * other registered storage system -- look for the last CFSEPCHAR in
  118. * the name, e.g. '+' in "foo.bnd+bar.hlp+blorg.dib", and figure
  119. * that the IOProc ID is the extension of the compound file name,
  120. * e.g. the extension of "foo.bnd+bar.hlp", i.e. 'HLP '.
  121. *
  122. * Alternatively, if <szFileName> is NULL, then assume that
  123. * <lpmmio->adwInfo[0]> is a DOS file handle.
  124. */
  125. if (lpmmio->pIOProc == NULL)
  126. {
  127. if (lpmmio->fccIOProc == NULL)
  128. {
  129. if (szFileName != NULL)
  130. {
  131. LPSTR pch;
  132. /* see if <szFileName> contains CFSEPCHAR */
  133. if ((pch = fstrrchr(szFileName, CFSEPCHAR)) != NULL)
  134. {
  135. /* find the extension that precedes CFSEPCHAR,
  136. * e.g. "hlp" in "foo.bnd+bar.hlp+blorg.dib"
  137. */
  138. while ((pch > szFileName) && (*pch != '.') && (*pch != ':') && (*pch != '\\'))
  139. pch--;
  140. if (*pch == '.')
  141. {
  142. char aszFour[sizeof(FOURCC)+1];
  143. int i;
  144. for (i = 0, pch++; i < sizeof(FOURCC); i++)
  145. if (*pch == CFSEPCHAR)
  146. aszFour[i] = (char)0;
  147. else
  148. aszFour[i] = *pch++;
  149. aszFour[sizeof(FOURCC)] = (char)0;
  150. lpmmio->fccIOProc = mmioStringToFOURCC(aszFour, MMIO_TOUPPER);
  151. }
  152. }
  153. }
  154. /* if the caller didn't specify an IOProc, and the code above
  155. * didn't determine an IOProc ID, then the default is the DOS
  156. * IOProc.
  157. */
  158. if (lpmmio->fccIOProc == NULL)
  159. lpmmio->fccIOProc = FOURCC_DOS;
  160. }
  161. /* unless an IOProc address is specified explicitly, look up the
  162. * IOProc in the global IOProc ID-to-address table -- the default
  163. * is 'DOS' since we'll assume that custom storage system I/O
  164. * procedures would have been installed
  165. */
  166. lpmmio->pIOProc = FindIOProc(lpmmio->fccIOProc, lpmmio->htask ? lpmmio->htask : GetCurrentTask());
  167. if (lpmmio->pIOProc == NULL)
  168. lpmmio->pIOProc = mmioDOSIOProc;
  169. }
  170. }
  171. /* @doc EXTERNAL
  172. @api UINT | mmioRename | This function renames the specified file.
  173. @parm LPCSTR | szFilename | Specifies a far pointer to a string
  174. containing the filename of the file to rename.
  175. @parm LPCSTR | szNewFileName | Specifies a far pointer to a string
  176. containing the new filename.
  177. @parm LPMMIOINFO | lpmmioinfo | Specifies a far pointer to an
  178. <t MMIOINFO> structure containing extra parameters used by
  179. <f mmioRename>.
  180. If <p lpmmioinfo> is not NULL, all unused fields of the
  181. <t MMIOINFO> structure it references must be set to zero, including the
  182. reserved fields.
  183. @parm DWORD | dwRenameFlags | Specifies option flags for the rename
  184. operation. This should be set to zero.
  185. @rdesc The return value is zero if the file was renamed. Otherwise, the
  186. return value is an error code returned from <f mmioRename> or from the I/O
  187. procedure.
  188. */
  189. UINT WINAPI
  190. mmioRename(LPCSTR szFileName, LPCSTR szNewFileName, LPMMIOINFO lpmmioinfo, DWORD dwRenameFlags)
  191. {
  192. PMMIO pmmio;
  193. UINT uReturn;
  194. V_FLAGS(dwRenameFlags, 0, mmioRename, MMSYSERR_INVALFLAG);
  195. V_RPOINTER0(lpmmioinfo, sizeof(MMIOINFO), MMSYSERR_INVALPARAM);
  196. if ((pmmio = PH(NewHandle(TYPE_MMIO, sizeof(MMIOINFO)))) == NULL)
  197. return MMIOERR_OUTOFMEMORY;
  198. if (lpmmioinfo) {
  199. V_CALLBACK0((FARPROC)lpmmioinfo->pIOProc, MMSYSERR_INVALPARAM);
  200. *pmmio = *lpmmioinfo;
  201. }
  202. SetIOProc(szFileName, pmmio);
  203. uReturn = (UINT)(DWORD) (pmmio->pIOProc((LPSTR) pmmio, MMIOM_RENAME, (LPARAM) szFileName, (LPARAM) szNewFileName));
  204. FreeHandle((HLOCAL)pmmio);
  205. return uReturn;
  206. }
  207. /* @doc EXTERNAL
  208. @api HMMIO | mmioOpen | This function opens a file for unbuffered
  209. or buffered I/O. The file can be a DOS file, a memory file, or an
  210. element of a custom storage system.
  211. @parm LPSTR | szFilename | Specifies a far pointer to a string
  212. containing the filename of the file to open. If no I/O procedure is
  213. specified to open the file, then the filename determines how the file
  214. is opened, as follows:
  215. -- If the filename does not contain "+", then it is assumed
  216. to be the name of a DOS file.
  217. -- If the filename is of the form "foo.ext+bar", then the
  218. extension "EXT " is assumed to identify an installed I/O procedure
  219. which is called to perform I/O on the file (see <f mmioInstallIOProc>).
  220. -- If the filename is NULL and no I/O procedure is given, then
  221. <e MMIOINFO.adwInfo[0]> is assumed to be the DOS file handle
  222. of a currently open file.
  223. The filename should not be longer than 128 bytes, including the
  224. terminating NULL.
  225. When opening a memory file, set <p szFilename> to NULL.
  226. @parm LPMMIOINFO | lpmmioinfo | Specifies a far pointer to an
  227. <t MMIOINFO> structure containing extra parameters used by
  228. <f mmioOpen>. Unless you are opening a memory file, specifying the
  229. size of a buffer for buffered I/O, or specifying an uninstalled I/O
  230. procedure to open a file, this parameter should be NULL.
  231. If <p lpmmioinfo> is not NULL, all unused fields of the
  232. <t MMIOINFO> structure it references must be set to zero, including the
  233. reserved fields.
  234. @parm DWORD | dwOpenFlags | Specifies option flags for the open
  235. operation. The MMIO_READ, MMIO_WRITE, and MMIO_READWRITE flags are
  236. mutually exclusive--only one should be specified. The MMIO_COMPAT,
  237. MMIO_EXCLUSIVE, MMIO_DENYWRITE, MMIO_DENYREAD, and MMIO_DENYNONE flags
  238. are DOS file-sharing flags, and can only be used after the DOS
  239. command SHARE has been executed.
  240. @flag MMIO_READ | Opens the file for reading only. This is the
  241. default, if MMIO_WRITE and MMIO_READWRITE are not specified.
  242. @flag MMIO_WRITE | Opens the file for writing. You should not
  243. read from a file opened in this mode.
  244. @flag MMIO_READWRITE | Opens the file for both reading and writing.
  245. @flag MMIO_CREATE | Creates a new file.
  246. If the file already exists, it is truncated to zero length.
  247. For memory files, MMIO_CREATE indicates the end of the file
  248. is initially at the start of the buffer.
  249. @flag MMIO_DELETE | Deletes a file. If this flag is specified,
  250. <p szFilename> should not be NULL. The return
  251. value will be TRUE (cast to HMMIO) if the file was deleted
  252. successfully, FALSE otherwise. Do not call <f mmioClose>
  253. for a file that has been deleted. If this flag is specified,
  254. all other file opening flags are ignored.
  255. @flag MMIO_PARSE | Creates a fully qualified filename from the path
  256. specified in <p szFileName>. The fully qualified filename is
  257. placed back into <p szFileName>. The return value
  258. will be TRUE (cast to HMMIO) if the qualification was
  259. successful, FALSE otherwise. The file is not opened, and the function
  260. does not return a valid MMIO file handle, so do not attempt to
  261. close the file. If this flag is specified, all other file
  262. opening flags are ignored.
  263. @flag MMIO_EXIST | Determines whether the specified file exists
  264. and creates a fully qualified filename from the path
  265. specified in <p szFileName>. The fully qualified filename is
  266. placed back into <p szFileName>. The return value
  267. will be TRUE (cast to HMMIO) if the qualification was
  268. successful and the file exists, FALSE otherwise. The file is
  269. not opened, and the function does not return a valid MMIO file
  270. handle, so do not attempt to close the file.
  271. @flag MMIO_ALLOCBUF | Opens a file for buffered I/O.
  272. To allocate a buffer larger or smaller than the default
  273. buffer size (8K), set the <e MMIOINFO.cchBuffer> field of the
  274. <t MMIOINFO> structure to the desired buffer size. If
  275. <e MMIOINFO.cchBuffer> is zero, then the default buffer size
  276. is used. If you are providing your own I/O buffer, then the
  277. MMIO_ALLOCBUF flag should not be used.
  278. @flag MMIO_COMPAT | Opens the file with compatibility mode,
  279. allowing any process on a given machine to open the file
  280. any number of times. <f mmioOpen> fails if the file has
  281. been opened with any of the other sharing modes.
  282. @flag MMIO_EXCLUSIVE | Opens the file with exclusive mode,
  283. denying other processes both read and write access to the file.
  284. <f mmioOpen> fails if the file has been opened in any other
  285. mode for read or write access, even by the current process.
  286. @flag MMIO_DENYWRITE | Opens the file and denies other
  287. processes write access to the file. <f mmioOpen> fails
  288. if the file has been opened in compatibility or for write
  289. access by any other process.
  290. @flag MMIO_DENYREAD | Opens the file and denies other
  291. processes read access to the file. <f mmioOpen> fails if the
  292. file has been opened in compatibility mode or for read access
  293. by any other process.
  294. @flag MMIO_DENYNONE | Opens the file without denying other
  295. processes read or write access to the file. <f mmioOpen>
  296. fails if the file has been opened in compatibility mode
  297. by any other process.
  298. @flag MMIO_GETTEMP | Creates a temporary filename, optionally
  299. using the parameters passed in <p szFileName> to determine
  300. the temporary name. For example, you can specify "C:F" to
  301. create a temporary file residing on drive C, starting with
  302. letter "F". The resulting filename is placed in the buffer
  303. pointed to by <p szFileName>. The return value will be TRUE
  304. (cast to HMMIO) if the temporary filename was created successfully,
  305. FALSE otherwise. The file is
  306. not opened, and the function does not return a valid MMIO file
  307. handle, so do not attempt to close the file.
  308. This flag overrides all other flags.
  309. @rdesc The return value is a handle to the opened file. This handle
  310. is not a DOS file handle--do not use it with any file I/O functions
  311. other than MMIO functions.
  312. If the file cannot be opened, the return value is NULL. If
  313. <p lpmmioinfo> is not NULL, then its <e MMIOINFO.wErrorRet> field
  314. will contain extended error information returned by the I/O
  315. procedure.
  316. @comm If <p lpmmioinfo> references an <t MMIOINFO> structure, set
  317. up the fields as described below. All unused fields must be set to
  318. zero, including reserved fields.
  319. -- To request that a file be opened with an installed I/O
  320. procedure, set the <e MMIOINFO.fccIOProc> field
  321. to the four-character code of the I/O procedure,
  322. and set the <e MMIOINFO.pIOProc> field to NULL.
  323. -- To request that a file be opened with an uninstalled I/O procedure,
  324. set the <e MMIOINFO.pIOProc> field to
  325. point to the I/O procedure, and set <e MMIOINFO.fccIOProc> to NULL.
  326. -- To request that <f mmioOpen> determine which I/O procedure to use
  327. to open the file based on the filename contained in <p szFilename>,
  328. set both <e MMIOINFO.fccIOProc> and <e MMIOINFO.pIOProc> to NULL.
  329. This is the default behavior if no <t MMIOINFO> structure is specified.
  330. -- To open a memory file using an internally allocated and managed
  331. buffer, set the <e MMIOINFO.pchBuffer> field to NULL,
  332. <e MMIOINFO.fccIOProc> to FOURCC_MEM,
  333. <e MMIOINFO.cchBuffer> to the initial size of the buffer, and
  334. <e MMIOINFO.adwInfo[0]> to the incremental expansion size of the
  335. buffer. This memory file will automatically be expanded in increments of
  336. <e MMIOINFO.adwInfo[0]> bytes when necessary. Specify the MMIO_CREATE
  337. flag for the <p dwOpenFlags> parameter to initially set the end of
  338. the file to be the beginning of the buffer.
  339. -- To open a memory file using a caller-supplied buffer, set
  340. the <e MMIOINFO.pchBuffer> field to point to the memory buffer,
  341. <e MMIOINFO.fccIOProc> to FOURCC_MEM,
  342. <e MMIOINFO.cchBuffer> to the size of the buffer, and
  343. <e MMIOINFO.adwInfo[0]> to the incremental expansion size of the
  344. buffer. The expansion size in <e MMIOINFO.adwInfo[0]> should only
  345. be non-zero if <e MMIOINFO.pchBuffer> is a pointer obtained by calling
  346. <f GlobalAlloc> and <f GlobalLock>, since <f GlobalReAlloc> will be called to
  347. expand the buffer. In particular, if <e MMIOINFO.pchBuffer> points to a
  348. local or global array, a block of memory in the local heap, or a block
  349. of memory allocated by <f GlobalDosAlloc>, <e MMIOINFO.adwInfo[0]> must
  350. be zero.
  351. Specify the MMIO_CREATE flag for the <p dwOpenFlags> parameter to
  352. initially set the end of the file to be the beginning of the buffer;
  353. otherwise, the entire block of memory will be considered readable.
  354. -- To use a currently open DOS file handle with MMIO, set the
  355. <e MMIOINFO.fccIOProc> field to FOURCC_DOS,
  356. <e MMIOINFO.pchBuffer> to NULL, and <e MMIOINFO.adwInfo[0]> to the
  357. DOS file handle. Note that offsets within the file will be relative to
  358. the beginning of the file, and will not depend on the DOS file position
  359. at the time <f mmioOpen> is called; the initial MMIO offset will be the same
  360. as the DOS offset when <f mmioOpen> is called.
  361. Later, to close the MMIO file handle without closing the DOS
  362. file handle, pass the MMIO_FHOPEN flag to <f mmioClose>.
  363. You must call <f mmioClose> to close a file opened with <f mmioOpen>.
  364. Open files are not automatically closed when an application exits.
  365. @xref mmioClose
  366. */
  367. /* these are the changes to mmioOpen() to support compound files... */
  368. /* @doc CFDOC
  369. @api HMMIO | mmioOpen | ...The file can be a DOS file, a memory file,
  370. an element of a RIFF compound file...
  371. @parm LPSTR | szFilename | ...
  372. -- If <p szFilename> is of the form "foo+bar", then <f mmioOpen>
  373. opens the compound file element named "bar" that is stored inside
  374. the RIFF compound file named "foo".
  375. -- If <p szFilename> is of the form "foo.ext+bar", then the
  376. extension "ext" is assumed to identify the installed I/O procedure
  377. (see <f mmioInstallIOProc>). The extension "bnd", and any extensions
  378. that have not been installed, are assumed to refer to a RIFF compound
  379. file.
  380. @parm LPMMIOINFO | lpmmioinfo | ...
  381. @parm DWORD | dwOpenFlags | ...
  382. @rdesc ...
  383. @comm ...
  384. The following I/O procedure identifiers (type FOURCC) are predefined:
  385. ...
  386. FOURCC_BND: <p szFilename> is assumed to be the name of
  387. a RIFF compound file element, and <p adwInfo[0]> should
  388. contain the HMMCF of the compound file. Alternatively,
  389. <p szFilename> can include the name of the compound file
  390. (e.g. "foo.bnd+bar.dib" as described above), and <p adwInfo[0]>
  391. should be NULL, to automatically open the compound file.
  392. ...
  393. The easy way to open an element of a RIFF compound file: just
  394. include the name of the compound file in <p szFilename> preceded
  395. by a "+" as described above. For example, opening
  396. "c:\data\bar.bnd+blorg.dib" opens the compound file element
  397. named "blorg.dib" in the compound file "c:\data\bar.bnd".
  398. <p lpmmioinfo> can be null in this case -- set <p dwOpenFlags>
  399. as described above. You can use this same method to open an
  400. element of a custom storage system, if the file extension of the
  401. compound file ("bnd" in the above example) corresponds to an
  402. installed I/O procedure -- see <f mmioInstallIOProc> for details.
  403. To open an element of a RIFF compound file that was opened using
  404. <f mmioCFAccess> or <f mmioCFOpen>: set <p szFilename>
  405. to be the name of the compound file element; set <p fccIOProc>
  406. to FOURCC_BND; set <p adwInfo[0]> to the HMMCF of the open compound
  407. file; set <p dwOpenFlags> and <p cchBuffer> as described above;
  408. set all other fields of <p lpmmioinfo> to zero.
  409. ...
  410. */
  411. HMMIO WINAPI
  412. mmioOpen(LPSTR szFileName, LPMMIOINFO lpmmioinfo, DWORD dwOpenFlags)
  413. {
  414. PMMIO pmmio; // MMIO status block
  415. HPSTR hpBuffer;
  416. UINT w;
  417. V_FLAGS(dwOpenFlags, MMIO_OPEN_VALID, mmioOpen, NULL);
  418. V_WPOINTER0(lpmmioinfo, sizeof(MMIOINFO), NULL);
  419. if (lpmmioinfo) {
  420. lpmmioinfo->wErrorRet = 0;
  421. V_CALLBACK0((FARPROC)lpmmioinfo->pIOProc, NULL);
  422. }
  423. /* allocate MMIO status information block */
  424. if ((pmmio = PH(NewHandle(TYPE_MMIO, sizeof(MMIOINFO)))) == NULL)
  425. {
  426. if (lpmmioinfo)
  427. lpmmioinfo->wErrorRet = MMIOERR_OUTOFMEMORY;
  428. return NULL;
  429. }
  430. /* if user supplied <lpmmioinfo>, copy it to <pmmio> */
  431. if (lpmmioinfo != NULL)
  432. *pmmio = *lpmmioinfo;
  433. /* <dwOpenFlags> always takes precedence over contents of <pmmio> */
  434. pmmio->dwFlags = dwOpenFlags;
  435. pmmio->hmmio = HP(pmmio);
  436. /* MMIO_ALLOCBUF in the flags means that the user wants a buffer
  437. * allocated for buffered I/O, but after this point it means that
  438. * a buffer *was* allocated, so turn off the flag until the buffer
  439. * is actually allocated (which is done by mmioSetBuffer() below)
  440. */
  441. if (pmmio->dwFlags & MMIO_ALLOCBUF)
  442. {
  443. /* if a buffer size is not specified, use the default */
  444. if (pmmio->cchBuffer == 0)
  445. pmmio->cchBuffer = MMIO_DEFAULTBUFFER;
  446. pmmio->dwFlags &= ~MMIO_ALLOCBUF;
  447. }
  448. /* Set the pIOProc function as determined by the file name or the
  449. * parameters in the pmmio structure.
  450. */
  451. SetIOProc(szFileName, pmmio);
  452. /* The pmmio structure hasn't been set up for buffering, so we must
  453. * explicitly make sure that pchBuffer is NULL.
  454. */
  455. hpBuffer = pmmio->pchBuffer;
  456. pmmio->pchBuffer = NULL;
  457. /* set up buffered I/O however the user requested it */
  458. if (w = mmioSetBuffer(HP(pmmio), hpBuffer, pmmio->cchBuffer, 0))
  459. {
  460. if (lpmmioinfo)
  461. lpmmioinfo->wErrorRet = w;
  462. FreeHandle(HP(pmmio));
  463. return NULL;
  464. }
  465. /* let the I/O procedure open/delete/qualify the file */
  466. w = (UINT)(DWORD) (pmmio->pIOProc((LPSTR) pmmio, MMIOM_OPEN, (LPARAM) szFileName, (LPARAM) 0));
  467. /* If this is non-zero, return it to the user */
  468. if (w != 0)
  469. {
  470. if (lpmmioinfo != NULL)
  471. lpmmioinfo->wErrorRet = w;
  472. FreeHandle(HP(pmmio));
  473. return NULL;
  474. }
  475. if (pmmio->dwFlags & (MMIO_DELETE | MMIO_PARSE | MMIO_EXIST | MMIO_GETTEMP))
  476. {
  477. /* if the file is being deleted/parsed/name gotten, exit
  478. * QUICKLY because the file handle (or whatever) in <pmmio>
  479. * is not valid.
  480. */
  481. mmioSetBuffer(HP(pmmio), NULL, 0L, 0);
  482. FreeHandle(HP(pmmio));
  483. return (HMMIO) TRUE;
  484. }
  485. /* the initial "current buffered offset" will be equal to the initial
  486. * "current disk offset"
  487. */
  488. pmmio->lBufOffset = pmmio->lDiskOffset;
  489. return HP(pmmio);
  490. }
  491. /* @doc EXTERNAL
  492. @api UINT | mmioClose | This function closes a file opened with
  493. <f mmioOpen>.
  494. @parm HMMIO | hmmio | Specifies the file handle of the file to
  495. close.
  496. @parm UINT | wFlags | Specifies options for the close operation.
  497. @flag MMIO_FHOPEN | If the file was opened by passing the DOS
  498. file handle of an already-opened file to <f mmioOpen>, then
  499. using this flag tells <f mmioClose> to close the MMIO file
  500. handle, but not the DOS file handle.
  501. @rdesc The return value is zero if the function is successful.
  502. Otherwise, the return value is an error code, either from
  503. <f mmioFlush> or from the I/O procedure. The error code can be
  504. one of the following codes:
  505. @flag MMIOERR_CANNOTWRITE | The contents of the buffer could
  506. not be written to disk.
  507. @xref mmioOpen mmioFlush
  508. */
  509. UINT WINAPI
  510. mmioClose(HMMIO hmmio, UINT wFlags)
  511. {
  512. UINT w;
  513. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  514. if (mmioFlush(hmmio, 0) != 0) {
  515. DebugErr(DBF_WARNING, "MMIO File flush failed during close.\r\n");
  516. PH(hmmio)->dwFlags &= ~MMIO_DIRTY;
  517. }
  518. if ((w = (UINT)(DWORD) PH(hmmio)->pIOProc((LPSTR)PH(hmmio), MMIOM_CLOSE, (LPARAM)(DWORD) wFlags, (LPARAM) 0)) != 0)
  519. return w;
  520. /* free the buffer if necessary */
  521. mmioSetBuffer(hmmio, NULL, 0L, 0);
  522. FreeHandle(hmmio);
  523. return 0;
  524. }
  525. /* @doc EXTERNAL
  526. @api LONG | mmioRead | This function reads a specified number of
  527. bytes from a file opened with <f mmioOpen>.
  528. @parm HMMIO | hmmio | Specifies the file handle of the file to be
  529. read.
  530. @parm HPSTR | pch | Specifies a huge pointer to a buffer to contain
  531. the data read from the file.
  532. @parm LONG | cch | Specifies the number of bytes to read from the
  533. file.
  534. @rdesc The return value is the number of bytes actually read. If the
  535. end of the file has been reached and no more bytes can be read, the
  536. return value is zero. If there is an error reading from the file, the
  537. return value is -1.
  538. @xref mmioWrite
  539. */
  540. LONG WINAPI
  541. mmioRead(HMMIO hmmio, HPSTR pch, LONG cch)
  542. {
  543. LONG lTotalBytesRead = 0L; // total no. bytes read
  544. LONG lBytes; // no. bytes that can be read
  545. V_HANDLE(hmmio, TYPE_MMIO, -1);
  546. V_WPOINTER(pch, cch, -1);
  547. while (TRUE)
  548. {
  549. /* calculate the number of bytes that can be read */
  550. lBytes = PH(hmmio)->pchEndRead - PH(hmmio)->pchNext;
  551. /* can only read at most <cch> bytes from buffer */
  552. if (lBytes > cch)
  553. lBytes = cch;
  554. if (lBytes > 0)
  555. {
  556. /* this is where some performance improvements can
  557. * be made, especially for small reads... should
  558. * special-case cases when segment boundaries are
  559. * not crossed (or maybe MemCopy() should do that)
  560. */
  561. MemCopy(pch, PH(hmmio)->pchNext, lBytes);
  562. PH(hmmio)->pchNext += lBytes;
  563. pch += lBytes;
  564. cch -= lBytes;
  565. lTotalBytesRead += lBytes;
  566. }
  567. /* cannot do MMIOM_READ from memory files */
  568. if (PH(hmmio)->fccIOProc == FOURCC_MEM)
  569. return lTotalBytesRead;
  570. if (cch == 0) // no more to read?
  571. return lTotalBytesRead;
  572. /* we need to read beyond this buffer; if we have at least
  573. * another bufferful to read, just call the I/O procedure
  574. */
  575. if (cch > PH(hmmio)->cchBuffer)
  576. break;
  577. /* read the next bufferful and loop around */
  578. if (mmioAdvance(hmmio, NULL, MMIO_READ) != 0)
  579. return -1;
  580. /* if mmioAdvance() couldn't read any more data, we must be
  581. * at the end of the file
  582. */
  583. if (PH(hmmio)->pchNext == PH(hmmio)->pchEndRead)
  584. return lTotalBytesRead;
  585. }
  586. /* flush and empty the I/O buffer and manipulate <lBufOffset>
  587. * directly to change the current file position
  588. */
  589. if (mmioFlush(hmmio, MMIO_EMPTYBUF) != 0)
  590. return -1;
  591. /* call the I/O procedure to do the rest of the reading */
  592. lBytes = mmioDiskIO(PH(hmmio), MMIOM_READ, pch, cch);
  593. PH(hmmio)->lBufOffset = PH(hmmio)->lDiskOffset;
  594. return (lBytes == -1L) ? -1L : lTotalBytesRead + lBytes;
  595. }
  596. /* @doc EXTERNAL
  597. @api LONG | mmioWrite | This function writes a specified number of
  598. bytes to a file opened with <f mmioOpen>.
  599. @parm HMMIO | hmmio | Specifies the file handle of the file.
  600. @parm char _huge* | pch | Specifies a huge pointer to the buffer to be
  601. written to the file.
  602. @parm LONG | cch | Specifies the number of bytes to write to the
  603. file.
  604. @rdesc The return value is the number of bytes actually written. If
  605. there is an error writing to the file, the return value is -1.
  606. @comm The current file position is incremented by the number of
  607. bytes written.
  608. @xref mmioRead
  609. */
  610. LONG WINAPI
  611. mmioWrite(HMMIO hmmio, const char _huge* pch, LONG cch)
  612. {
  613. LONG lTotalBytesWritten = 0L; // total no. bytes written
  614. LONG lBytes; // no. bytes that can be written
  615. V_HANDLE(hmmio, TYPE_MMIO, -1);
  616. V_RPOINTER(pch, cch, -1);
  617. while (TRUE)
  618. {
  619. /* calculate the number of bytes that can be written */
  620. lBytes = PH(hmmio)->pchEndWrite - PH(hmmio)->pchNext;
  621. if ((cch > lBytes) && (PH(hmmio)->fccIOProc == FOURCC_MEM))
  622. {
  623. /* this is a memory file -- expand it */
  624. if (mmioExpandMemFile(PH(hmmio), cch - lBytes) != 0)
  625. return -1; // cannot expand
  626. lBytes = PH(hmmio)->pchEndWrite - PH(hmmio)->pchNext;
  627. }
  628. /* can only write at most <cch> bytes into the buffer */
  629. if (lBytes > cch)
  630. lBytes = cch;
  631. /* this is where some performance improvements can
  632. * be made, especially for small writes... should
  633. * special-case cases when segment boundaries are
  634. * not crossed (or maybe MemCopy() should do that)
  635. */
  636. if (lBytes > 0)
  637. {
  638. MemCopy(PH(hmmio)->pchNext, pch, lBytes);
  639. PH(hmmio)->dwFlags |= MMIO_DIRTY;
  640. PH(hmmio)->pchNext += lBytes;
  641. pch += lBytes;
  642. cch -= lBytes;
  643. lTotalBytesWritten += lBytes;
  644. }
  645. /* validate <pchEndRead>, i.e. re-enforce the invariant that
  646. * <pchEndRead> points past the last valid byte in the buffer
  647. */
  648. if (PH(hmmio)->pchEndRead < PH(hmmio)->pchNext)
  649. PH(hmmio)->pchEndRead = PH(hmmio)->pchNext;
  650. if (cch == 0) // no more to write?
  651. return lTotalBytesWritten;
  652. /* we need to read beyond this buffer; if we have at least
  653. * another bufferful to read, just call the I/O procedure
  654. */
  655. if (cch > PH(hmmio)->cchBuffer)
  656. break;
  657. /* write this buffer (if needed) and read the next
  658. * bufferful (if needed)
  659. */
  660. if (mmioAdvance(hmmio, NULL, MMIO_WRITE) != 0)
  661. return -1;
  662. }
  663. /* we should never need to do MMIOM_WRITE with memory files */
  664. /* flush and empty the I/O buffer and manipulate <lBufOffset>
  665. * directly to change the current file position
  666. */
  667. if (mmioFlush(hmmio, MMIO_EMPTYBUF) != 0)
  668. return -1;
  669. /* call the I/O procedure to do the rest of the writing */
  670. lBytes = mmioDiskIO(PH(hmmio), MMIOM_WRITE, (HPSTR)pch, cch);
  671. PH(hmmio)->lBufOffset = PH(hmmio)->lDiskOffset;
  672. return (lBytes == -1L) ? -1L : lTotalBytesWritten + lBytes;
  673. }
  674. /* @doc EXTERNAL
  675. @api LONG | mmioSeek | This function changes the current file
  676. position in a file opened with <f mmioOpen>. The current file
  677. position is the location in the file where data is read or written.
  678. @parm HMMIO | hmmio | Specifies the file handle of the file to seek
  679. in.
  680. @parm LONG | lOffset | Specifies an offset to change the file position.
  681. @parm int | iOrigin | Specifies how the offset specified by
  682. <p lOffset> is interpreted. Contains one of the following flags:
  683. @flag SEEK_SET | Seeks to <p lOffset> bytes from the beginning
  684. of the file.
  685. @flag SEEK_CUR | Seeks to <p lOffset> bytes from the current
  686. file position.
  687. @flag SEEK_END | Seeks to <p lOffset> bytes from the end
  688. of the file.
  689. @rdesc The return value is the new file position in bytes, relative
  690. to the beginning of the file. If there is an error, the return value
  691. is -1.
  692. @comm Seeking to an invalid location in the file, such as past the
  693. end of the file, may not cause <f mmioSeek> to return an error,
  694. but may cause subsequent I/O operations on the file to fail.
  695. To locate the end of a file, call <f mmioSeek> with <p lOffset>
  696. set to zero and <p iOrigin> set to SEEK_END.
  697. */
  698. LONG WINAPI
  699. mmioSeek(HMMIO hmmio, LONG lOffset, int iOrigin)
  700. {
  701. LONG lCurOffset; // disk offset of <pchNext>
  702. LONG lEndBufOffset; // disk offset of end of buffer
  703. LONG lNewOffset; // new disk offset
  704. V_HANDLE(hmmio, TYPE_MMIO, -1);
  705. /* careful! all this buffer pointer manipulation is fine, but keep
  706. * in mind that buffering may be disabled (in which case <pchEndRead>
  707. * and <pchBuffer> will both be NULL, so the buffer will appear to
  708. * be zero bytes in size)
  709. */
  710. /* <PH(hmmio)->lBufOffset> is the disk offset of the start of the start
  711. * of the buffer; determine <lCurOffset>, the offset of <pchNext>,
  712. * and <lEndBufOffset>, the offset of the end of the valid part
  713. * of the buffer
  714. */
  715. lCurOffset = PH(hmmio)->lBufOffset +
  716. (PH(hmmio)->pchNext - PH(hmmio)->pchBuffer);
  717. lEndBufOffset = PH(hmmio)->lBufOffset +
  718. (PH(hmmio)->pchEndRead - PH(hmmio)->pchBuffer);
  719. /* determine <lNewOffset>, the offset to seek to */
  720. switch (iOrigin)
  721. {
  722. case SEEK_SET: // seek relative to start of file
  723. lNewOffset = lOffset;
  724. break;
  725. case SEEK_CUR: // seek relative to current location
  726. lNewOffset = lCurOffset + lOffset;
  727. break;
  728. case SEEK_END: // seek relative to end of file
  729. if (PH(hmmio)->fccIOProc == FOURCC_MEM)
  730. lNewOffset = lEndBufOffset - lOffset;
  731. else
  732. {
  733. LONG lEndFileOffset;
  734. /* find out where the end of the file is */
  735. if ((lEndFileOffset = (LONG) PH(hmmio)->pIOProc((LPSTR) PH(hmmio),
  736. MMIOM_SEEK, (LPARAM) 0, (LPARAM) SEEK_END)) == -1)
  737. return -1;
  738. lNewOffset = lEndFileOffset - lOffset;
  739. }
  740. break;
  741. default:
  742. return -1;
  743. }
  744. if ((lNewOffset >= PH(hmmio)->lBufOffset) && (lNewOffset <= lEndBufOffset))
  745. {
  746. /* seeking within the valid part of the buffer
  747. * (possibly including seeking to <lEndBufOffset>)
  748. */
  749. PH(hmmio)->pchNext = PH(hmmio)->pchBuffer +
  750. (lNewOffset - PH(hmmio)->lBufOffset);
  751. }
  752. else
  753. {
  754. /* seeking outside the buffer */
  755. if (PH(hmmio)->fccIOProc == FOURCC_MEM)
  756. return -1; // can't seek outside mem. file buffer
  757. if (mmioFlush(hmmio, 0) != 0)
  758. return -1;
  759. /* the current "buffered file position" (same as <lDiskOffset>
  760. * for unbuffered files) equals <lBufOffset> +
  761. * (<pchNext> - <pchBuffer>); we'll move the current buffered
  762. * file position (and empty the buffer, since it becomes
  763. * invalid when <lBufOffset> changes) as follows...
  764. */
  765. PH(hmmio)->lBufOffset = lNewOffset;
  766. PH(hmmio)->pchNext = PH(hmmio)->pchEndRead = PH(hmmio)->pchBuffer;
  767. /* don't need to actually seek right now, since the next
  768. * MMIOM_READ or MMIOM_WRITE will have to seek anyway
  769. */
  770. }
  771. return lNewOffset;
  772. }
  773. /* @doc EXTERNAL
  774. @api UINT | mmioGetInfo | This function retrieves information
  775. about a file opened with <f mmioOpen>. This information allows the
  776. caller to directly access the I/O buffer, if the file is opened
  777. for buffered I/O.
  778. @parm HMMIO | hmmio | Specifies the file handle of the file.
  779. @parm LPMMIOINFO | lpmmioinfo | Specifies a far pointer to a
  780. caller-allocated <t MMIOINFO> structure that <f mmioGetInfo>
  781. fills with information about the file. See the <t MMIOINFO> structure
  782. and the <f mmioOpen> function for information about the fields in
  783. this structure.
  784. @parm UINT | wFlags | Is not used and should be set to zero.
  785. @rdesc The return value is zero if the function is successful.
  786. @comm To directly access the I/O buffer of a file opened for
  787. buffered I/O, use the following fields of the <t MMIOINFO> structure
  788. filled by <f mmioGetInfo>:
  789. -- The <e MMIOINFO.pchNext> field points to the next byte in the
  790. buffer that can be read or written. When you read or write, increment
  791. <e MMIOINFO.pchNext> by the number of bytes read or written.
  792. -- The <e MMIOINFO.pchEndRead> field points to one byte past the
  793. last valid byte in the buffer that can be read.
  794. -- The <e MMIOINFO.pchEndWrite> field points to one byte past the
  795. last location in the buffer that can be written.
  796. Once you read or write to the buffer and modify
  797. <e MMIOINFO.pchNext>, do not call any MMIO function except
  798. <f mmioAdvance> until you call <f mmioSetInfo>. Call <f mmioSetInfo>
  799. when you are finished directly accessing the buffer.
  800. When you reach the end of the buffer specified by
  801. <e MMIOINFO.pchEndRead> or <e MMIOINFO.pchEndWrite>, call
  802. <f mmioAdvance> to fill the buffer from the disk, or write
  803. the buffer to the disk. The <f mmioAdvance> function
  804. will update the <e MMIOINFO.pchNext>, <e MMIOINFO.pchEndRead>, and
  805. <e MMIOINFO.pchEndWrite> fields in the <t MMIOINFO> structure for the
  806. file.
  807. Before calling <f mmioAdvance> or <f mmioSetInfo> to flush a
  808. buffer to disk, set the MMIO_DIRTY flag in the <e MMIOINFO.dwFlags>
  809. field of the <t MMIOINFO> structure for the file. Otherwise, the
  810. buffer will not get written to disk.
  811. Do not decrement <e MMIOINFO.pchNext> or modify any fields in the
  812. <t MMIOINFO> structure other than <e MMIOINFO.pchNext> and
  813. <e MMIOINFO.dwFlags>. Do not set any flags in <e MMIOINFO.dwFlags>
  814. except MMIO_DIRTY.
  815. @xref mmioSetInfo MMIOINFO
  816. */
  817. UINT WINAPI
  818. mmioGetInfo(HMMIO hmmio, LPMMIOINFO lpmmioinfo, UINT wFlags)
  819. {
  820. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  821. V_WPOINTER(lpmmioinfo, sizeof(MMIOINFO), MMSYSERR_INVALPARAM);
  822. V_FLAGS(wFlags, 0, mmioGetInfo, MMSYSERR_INVALFLAG);
  823. *lpmmioinfo = *PH(hmmio);
  824. return 0;
  825. }
  826. /* @doc EXTERNAL
  827. @api UINT | mmioSetInfo | This function updates the information
  828. retrieved by <f mmioGetInfo> about a file opened with <f mmioOpen>.
  829. Use this function to terminate direct buffer access of a file opened
  830. for buffered I/O.
  831. @parm HMMIO | hmmio | Specifies the file handle of the file.
  832. @parm LPMMIOINFO | lpmmioinfo | Specifies a far pointer to an
  833. <t MMIOINFO> structure filled with information with
  834. <f mmioGetInfo>.
  835. @parm UINT | wFlags | Is not used and should be set to zero.
  836. @rdesc The return value is zero if the function is successful.
  837. @comm If you have written to the file I/O buffer, set the
  838. MMIO_DIRTY flag in the <e MMIOINFO.dwFlags> field of the <t MMIOINFO>
  839. structure before calling <f mmioSetInfo> to terminate direct buffer
  840. access. Otherwise, the buffer will not get flushed to disk.
  841. @xref mmioGetInfo MMIOINFO
  842. */
  843. UINT WINAPI
  844. mmioSetInfo(HMMIO hmmio, const MMIOINFO FAR* lpmmioinfo, UINT wFlags)
  845. {
  846. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  847. V_RPOINTER(lpmmioinfo, sizeof(MMIOINFO), MMSYSERR_INVALPARAM);
  848. V_WPOINTER0(lpmmioinfo->pchBuffer, lpmmioinfo->cchBuffer, MMSYSERR_INVALPARAM);
  849. V_CALLBACK((FARPROC)lpmmioinfo->pIOProc, MMSYSERR_INVALPARAM);
  850. V_FLAGS(wFlags, 0, mmioSetInfo, MMSYSERR_INVALFLAG);
  851. /* copy the relevant information from <lpmmioinfo> back into <hmmio> */
  852. *PH(hmmio) = *lpmmioinfo;
  853. /* validate <pchEndRead>, i.e. re-enforce the invariant that
  854. * <pchEndRead> points past the last valid byte in the buffer
  855. */
  856. if (PH(hmmio)->pchEndRead < PH(hmmio)->pchNext)
  857. PH(hmmio)->pchEndRead = PH(hmmio)->pchNext;
  858. return 0;
  859. }
  860. /* @doc EXTERNAL
  861. @api UINT | mmioSetBuffer | This function enables or disables
  862. buffered I/O, or changes the buffer or buffer size for a file opened
  863. with <f mmioOpen>.
  864. @parm HMMIO | hmmio | Specifies the file handle of the file.
  865. @parm LPSTR | pchBuffer | Specifies a far pointer to a
  866. caller-supplied buffer to use for buffered I/O. If NULL,
  867. <f mmioSetBuffer> allocates an internal buffer for buffered I/O.
  868. @parm LONG | cchBuffer | Specifies the size of the caller-supplied
  869. buffer, or the size of the buffer for <f mmioSetBuffer> to allocate.
  870. @parm UINT | wFlags | Is not used and should be set to zero.
  871. @rdesc The return value is zero if the function is successful.
  872. Otherwise, the return value specifies an error code. If an error
  873. occurs, the file handle remains valid. The error code can be one
  874. of the following codes:
  875. @flag MMIOERR_CANNOTWRITE | The contents of the old buffer could
  876. not be written to disk, so the operation was aborted.
  877. @flag MMIOERR_OUTOFMEMORY | The new buffer could not be allocated,
  878. probably due to a lack of available memory.
  879. @comm To enable buffering using an internal buffer, set
  880. <p pchBuffer> to NULL and <p cchBuffer> to the desired buffer size.
  881. To supply your own buffer, set <p pchBuffer> to point to the buffer,
  882. and set <p cchBuffer> to the size of the buffer.
  883. To disable buffered I/O, set <p pchBuffer> to NULL and
  884. <p cchBuffer> to zero.
  885. If buffered I/O is already enabled using an internal buffer, you
  886. can reallocate the buffer to a different size by setting
  887. <p pchBuffer> to NULL and <p cchBuffer> to the new buffer size. The
  888. contents of the buffer may be changed after resizing.
  889. */
  890. UINT WINAPI
  891. mmioSetBuffer(HMMIO hmmio, LPSTR pchBuffer, LONG cchBuffer, UINT wFlags)
  892. {
  893. UINT w;
  894. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  895. V_WPOINTER0(pchBuffer, cchBuffer, MMSYSERR_INVALPARAM);
  896. V_FLAGS(wFlags, 0, mmioSetBuffer, MMSYSERR_INVALFLAG);
  897. if ((PH(hmmio)->dwFlags & MMIO_ALLOCBUF) &&
  898. (pchBuffer == NULL) && (cchBuffer > 0))
  899. {
  900. /* grow or shrink buffer in-place */
  901. HPSTR pch;
  902. LONG lDeltaNext;
  903. LONG lDeltaEndRead;
  904. /* Since the ALLOCBUF flag is set, we must have a buffer */
  905. /* write the buffer to disk, but don't empty it */
  906. if ((w = mmioFlush(hmmio, 0)) != 0)
  907. return w;
  908. while (TRUE)
  909. {
  910. /* remember where <pchNext> and <pchEndRead> are
  911. * in the buffer
  912. */
  913. lDeltaNext = PH(hmmio)->pchNext - PH(hmmio)->pchBuffer;
  914. lDeltaEndRead = PH(hmmio)->pchEndRead - PH(hmmio)->pchBuffer;
  915. if (cchBuffer >= lDeltaNext)
  916. break;
  917. /* caller wants to truncate the part of the buffer
  918. * that contains <pchNext> -- handle this by
  919. * emptying the buffer, recalculating <lDeltaNext>
  920. * and <lDeltaEndRead>, and continuing below
  921. */
  922. if ((w = mmioFlush(hmmio, MMIO_EMPTYBUF)) != 0)
  923. return w;
  924. }
  925. /* reallocate buffer */
  926. pch = GlobalReAllocPtr(PH(hmmio)->pchBuffer, cchBuffer, GMEM_MOVEABLE);
  927. /* If we cannot allocate the new buffer, exit with no
  928. * harm done.
  929. */
  930. if (pch == NULL)
  931. return MMIOERR_OUTOFMEMORY; // out of memory
  932. /* transfer pointers to new buffer */
  933. PH(hmmio)->cchBuffer = cchBuffer;
  934. PH(hmmio)->pchBuffer = pch;
  935. PH(hmmio)->pchNext = pch + lDeltaNext;
  936. PH(hmmio)->pchEndRead = pch + lDeltaEndRead;
  937. /* <pchEndWrite> always points to the end of the buf. */
  938. PH(hmmio)->pchEndWrite = PH(hmmio)->pchBuffer + cchBuffer;
  939. /* check if the reallocation truncated valid data */
  940. if (lDeltaEndRead > cchBuffer)
  941. PH(hmmio)->pchEndRead = PH(hmmio)->pchEndWrite;
  942. return 0;
  943. }
  944. /* write the buffer to disk and stop using the buffer */
  945. if ((w = mmioFlush(hmmio, MMIO_EMPTYBUF)) != 0)
  946. return w;
  947. if (PH(hmmio)->dwFlags & MMIO_ALLOCBUF)
  948. {
  949. GlobalFreePtr(PH(hmmio)->pchBuffer);
  950. PH(hmmio)->dwFlags &= ~MMIO_ALLOCBUF;
  951. }
  952. /* Initially, no error. */
  953. w = 0;
  954. if ((pchBuffer == NULL) && (cchBuffer > 0))
  955. {
  956. pchBuffer = GlobalAllocPtr(GMEM_MOVEABLE, cchBuffer);
  957. /* If there is an error, change the file to be un-buffered
  958. * and return an error code. The file is still valid.
  959. * (Just for a little extra security.)
  960. */
  961. if (pchBuffer == NULL) {
  962. w = MMIOERR_OUTOFMEMORY;
  963. cchBuffer = 0L;
  964. } else
  965. PH(hmmio)->dwFlags |= MMIO_ALLOCBUF;
  966. }
  967. /* invariant: <pchEndRead> points past the end of the "valid" portion
  968. * of the buffer, and <pchEndWrite> points past the last byte that
  969. * can be written into; <pchNext> points to the next byte to read
  970. * or write; <lBufOffset> is the current disk offset of the start
  971. * of the buffer, and it will not change
  972. */
  973. PH(hmmio)->pchBuffer = pchBuffer;
  974. PH(hmmio)->cchBuffer = cchBuffer;
  975. PH(hmmio)->pchNext = PH(hmmio)->pchEndRead = PH(hmmio)->pchBuffer;
  976. PH(hmmio)->pchEndWrite = PH(hmmio)->pchBuffer + cchBuffer;
  977. return w;
  978. }
  979. /* @doc EXTERNAL
  980. @api UINT | mmioFlush | This function writes the I/O buffer of a
  981. file to disk, if the I/O buffer has been written to.
  982. @parm HMMIO | hmmio | Specifies the file handle of a file opened
  983. with <f mmioOpen>.
  984. @parm UINT | wFlags | Is not used and should be set to zero.
  985. @rdesc The return value is zero if the function is successful.
  986. Otherwise, the return value specifies an error code. The error
  987. code can be one of the following codes:
  988. @flag MMIOERR_CANNOTWRITE | The contents of the buffer could
  989. not be written to disk.
  990. @comm Closing a file with <f mmioClose> will automatically flush
  991. its buffer.
  992. If there is insufficient disk space to write the
  993. buffer, <f mmioFlush> will fail, even if the preceding <f mmioWrite>
  994. calls were successful.
  995. */
  996. UINT WINAPI
  997. mmioFlush(HMMIO hmmio, UINT wFlags)
  998. {
  999. LONG lBytesAsk; // no. bytes to write
  1000. LONG lBytesWritten; // no. bytes actually written
  1001. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  1002. V_FLAGS(wFlags, MMIO_FLUSH_VALID, mmioFlush, MMSYSERR_INVALFLAG);
  1003. if ((PH(hmmio)->fccIOProc == FOURCC_MEM) || (PH(hmmio)->pchBuffer == NULL))
  1004. return 0; // cannot flush memory files
  1005. /* if the file is unbuffered then the dirty flag should not be set */
  1006. if (PH(hmmio)->dwFlags & MMIO_DIRTY)
  1007. {
  1008. /* figure out how many bytes need to be flushed */
  1009. lBytesAsk = PH(hmmio)->pchEndRead - PH(hmmio)->pchBuffer;
  1010. /* write the buffer to disk */
  1011. lBytesWritten = mmioDiskIO(PH(hmmio), MMIOM_WRITEFLUSH,
  1012. PH(hmmio)->pchBuffer, lBytesAsk);
  1013. if (lBytesWritten != lBytesAsk)
  1014. return MMIOERR_CANNOTWRITE;
  1015. PH(hmmio)->dwFlags &= ~MMIO_DIRTY; // buffer is clean now
  1016. }
  1017. if (wFlags & MMIO_EMPTYBUF)
  1018. {
  1019. /* empty the I/O buffer, and update <lBufOffset> to reflect
  1020. * what the current file position is
  1021. */
  1022. PH(hmmio)->lBufOffset += (PH(hmmio)->pchNext - PH(hmmio)->pchBuffer);
  1023. PH(hmmio)->pchNext = PH(hmmio)->pchEndRead = PH(hmmio)->pchBuffer;
  1024. }
  1025. return 0;
  1026. }
  1027. /* @doc EXTERNAL
  1028. @api UINT | mmioAdvance | This function advances the I/O buffer of
  1029. a file set up for direct I/O buffer access with <f mmioGetInfo>. If
  1030. the file is opened for reading, the I/O buffer is filled from the
  1031. disk. If the file is opened for writing and the MMIO_DIRTY flag is
  1032. set in the <e MMIOINFO.dwFlags> field of the <t MMIOINFO> structure,
  1033. the buffer is written to disk. The <e MMIOINFO.pchNext>,
  1034. <e MMIOINFO.pchEndRead>, and <e MMIOINFO.pchEndWrite> fields of the
  1035. <t MMIOINFO> structure are updated to reflect the new state of
  1036. the I/O buffer.
  1037. @parm HMMIO | hmmio | Specifies the file handle for a file opened
  1038. with <f mmioOpen>.
  1039. @parm LPMMIOINFO | lpmmioinfo | Optionally specifies a far pointer to the
  1040. <t MMIOINFO> structure obtained with <f mmioGetInfo>, which is used to
  1041. set the current file information, then updated after the buffer is
  1042. advanced.
  1043. @parm UINT | wFlags | Specifies options for the operation.
  1044. Contains exactly one of the following two flags:
  1045. @flag MMIO_READ | The buffer is filled from the file.
  1046. @flag MMIO_WRITE | The buffer is written to the file.
  1047. @rdesc The return value is zero if the operation is successful.
  1048. Otherwise, the return value specifies an error code. The error
  1049. code can be one of the following codes:
  1050. @flag MMIOERR_CANNOTWRITE | The contents of the buffer could
  1051. not be written to disk.
  1052. @flag MMIOERR_CANNOTREAD | An error occurred while re-filling
  1053. the buffer.
  1054. @flag MMIOERR_UNBUFFERED | The specified file is not opened
  1055. for buffered I/O.
  1056. @flag MMIOERR_CANNOTEXPAND | The specified memory file cannot
  1057. be expanded, probably because the <e MMIOINFO.adwInfo[0]> field
  1058. was set to zero in the initial call to <f mmioOpen>.
  1059. @flag MMIOERR_OUTOFMEMORY | There was not enough memory to expand
  1060. a memory file for further writing.
  1061. @comm If the specified file is opened for writing or for both
  1062. reading and writing, the I/O buffer will be flushed to disk before
  1063. the next buffer is read. If the I/O buffer cannot be written to disk
  1064. because the disk is full, then <f mmioAdvance> will return
  1065. MMIOERR_CANNOTWRITE.
  1066. If the specified file is only open for writing, the MMIO_WRITE
  1067. flag must be specified.
  1068. If you have written to the I/O buffer, you must set the MMIO_DIRTY
  1069. flag in the <e MMIOINFO.dwFlags> field of the <t MMIOINFO> structure
  1070. before calling <f mmioAdvance>. Otherwise, the buffer will not be
  1071. written to disk.
  1072. If the end of file is reached, <f mmioAdvance> will still return
  1073. success, even though no more data can be read. Thus, to check for
  1074. the end of the file, it is necessary to see if the
  1075. <e MMIOINFO.pchNext> and <e MMIOINFO.pchEndRead> fields of the
  1076. <t MMIOINFO> structure are equal after calling <f mmioAdvance>.
  1077. @xref mmioGetInfo MMIOINFO
  1078. */
  1079. UINT WINAPI
  1080. mmioAdvance(HMMIO hmmio, LPMMIOINFO lpmmioinfo, UINT wFlags)
  1081. {
  1082. LONG lBytesRead; // bytes actually read
  1083. UINT w;
  1084. V_HANDLE(hmmio, TYPE_MMIO, MMSYSERR_INVALHANDLE);
  1085. V_FLAGS(wFlags, MMIO_ADVANCE_VALID, mmioAdvance, MMSYSERR_INVALFLAG);
  1086. if (PH(hmmio)->pchBuffer == NULL)
  1087. return MMIOERR_UNBUFFERED;
  1088. if (lpmmioinfo != NULL) {
  1089. V_WPOINTER(lpmmioinfo, sizeof(MMIOINFO), MMSYSERR_INVALPARAM);
  1090. mmioSetInfo(hmmio, lpmmioinfo, 0);
  1091. }
  1092. if (PH(hmmio)->fccIOProc == FOURCC_MEM)
  1093. {
  1094. /* this is a memory file:
  1095. * -- if the caller is reading, cannot advance
  1096. * -- if the caller is writing, then advance by expanding
  1097. * the buffer (if possible) if the there is less than
  1098. * <adwInfo[0]> bytes left in the buffer
  1099. */
  1100. if (!(wFlags & MMIO_WRITE))
  1101. return 0;
  1102. if ((DWORD)(PH(hmmio)->pchEndWrite - PH(hmmio)->pchNext) >= PH(hmmio)->adwInfo[0])
  1103. return 0;
  1104. if ((w = mmioExpandMemFile(PH(hmmio), 1L)) != 0)
  1105. return w; // out of memory, or whatever
  1106. goto GETINFO_AND_EXIT;
  1107. }
  1108. /* empty the I/O buffer, which will effectively advance the
  1109. * buffer by (<pchNext> - <pchBuffer>) bytes
  1110. */
  1111. if ((w = mmioFlush(hmmio, MMIO_EMPTYBUF)) != 0)
  1112. return w;
  1113. /* if MMIO_WRITE bit is not set in wFlags, fill the buffer */
  1114. if (!(wFlags & MMIO_WRITE))
  1115. {
  1116. /* read the next bufferful from the file */
  1117. lBytesRead = mmioDiskIO(PH(hmmio), MMIOM_READ,
  1118. PH(hmmio)->pchBuffer, PH(hmmio)->cchBuffer);
  1119. if (lBytesRead == -1)
  1120. return MMIOERR_CANNOTREAD;
  1121. /* reading zero bytes should not be treated as an error
  1122. * condition -- e.g. open a new file R+W and call
  1123. * mmioAdvance(), and MMIOM_READ will return zero bytes
  1124. * because the file started off empty
  1125. */
  1126. PH(hmmio)->pchEndRead += lBytesRead;
  1127. }
  1128. GETINFO_AND_EXIT:
  1129. /* copy <hmmio> back to <lpmmioinfo> if <lpmmioinfo> is provided */
  1130. if (lpmmioinfo != NULL)
  1131. mmioGetInfo(hmmio, lpmmioinfo, 0);
  1132. return 0;
  1133. }
  1134. /* @doc EXTERNAL
  1135. @api FOURCC | mmioStringToFOURCC | This function converts a
  1136. null-terminated string to a four-character code.
  1137. @parm LPCSTR | sz | Specifies a far pointer to a null-terminated
  1138. string to a four-character code.
  1139. @parm UINT | wFlags | Specifies options for the conversion:
  1140. @flag MMIO_TOUPPER | Converts all characters to uppercase.
  1141. @rdesc The return value is the four character code created from the
  1142. given string.
  1143. @comm This function does not check to see if the string referenced
  1144. by <p sz> follows any conventions regarding which characters to
  1145. include in a four-character code. The string is
  1146. simply copied to a four-character code and padded with blanks or
  1147. truncated to four characters if required.
  1148. @xref mmioFOURCC
  1149. */
  1150. FOURCC WINAPI
  1151. mmioStringToFOURCC(LPCSTR sz, UINT wFlags)
  1152. {
  1153. FOURCC fcc;
  1154. LPSTR pch = (LPSTR) &fcc;
  1155. int i;
  1156. V_STRING(sz, (UINT)-1, NULL);
  1157. V_FLAGS(wFlags, MMIO_FOURCC_VALID, mmioStringToFOURCC, (FOURCC)-1);
  1158. for (i = sizeof(FOURCC) - 1; i >= 0; i--)
  1159. {
  1160. if (!*sz)
  1161. *pch = ' ';
  1162. else {
  1163. *pch = *sz++;
  1164. if (wFlags & MMIO_TOUPPER)
  1165. *pch = (char)(WORD)(LONG)AnsiUpper((LPSTR)(LONG)*pch);
  1166. }
  1167. pch++;
  1168. }
  1169. return fcc;
  1170. }
  1171. /* @doc EXTERNAL
  1172. @api LPMMIOPROC | mmioInstallIOProc | This function installs or
  1173. removes a custom I/O procedure. It will also locate an installed I/O
  1174. procedure, given its corresponding four-character code.
  1175. @parm FOURCC | fccIOProc | Specifies a four-character code
  1176. identifying the I/O procedure to install, remove, or locate. All
  1177. characters in this four-character code should be uppercase characters.
  1178. @parm LPMMIOPROC | pIOProc | Specifies the address of the I/O
  1179. procedure to install. To remove or locate an I/O procedure, set this
  1180. parameter to NULL.
  1181. @parm DWORD | dwFlags | Specifies one of the following flags
  1182. indicating whether the I/O procedure is being installed, removed, or
  1183. located:
  1184. @flag MMIO_INSTALLPROC | Installs the specified I/O procedure.
  1185. @flag MMIO_GLOBALPROC | This flag is a modifier to the install flag,
  1186. and indicates the I/O procedure should be installed for global
  1187. use. This flag is ignored on removal or find.
  1188. @flag MMIO_REMOVEPROC | Removes the specified I/O procedure.
  1189. @flag MMIO_FINDPROC | Searches for the specified I/O procedure.
  1190. @rdesc The return value is the address of the I/O procedure
  1191. installed, removed, or located. If there is an error, the return value
  1192. is NULL.
  1193. @comm If the I/O procedure resides in the application, use
  1194. <f MakeProcInstance> to get a procedure-instance address and specify
  1195. this address for <p pIOProc>. You don't need to get a procedure-instance
  1196. address if the I/O procedure resides in a DLL.
  1197. @cb LRESULT FAR PASCAL | IOProc | <f IOProc> is a placeholder for the
  1198. application-supplied function name. The actual name must be exported
  1199. by including it in a EXPORTS statement in the application's
  1200. module-definitions file.
  1201. @parm LPSTR | lpmmioinfo | Specifies a far pointer to an
  1202. <t MMIOINFO> structure containing information about the open
  1203. file. The I/O procedure must maintain the <e MMIOINFO.lDiskOffset>
  1204. field in this structure to indicate the file offset to the
  1205. next read or write location. The I/O procedure can use the
  1206. <e MMIOINFO.adwInfo[]> field to store state information. The
  1207. I/O procedure should not modify any other fields of the
  1208. <t MMIOINFO> structure.
  1209. @parm UINT | wMsg | Specifies a message indicating the
  1210. requested I/O operation. Messages that can be received include
  1211. <m MMIOM_OPEN>, <m MMIOM_CLOSE>, <m MMIOM_READ>, <m MMIOM_WRITE>,
  1212. and <m MMIOM_SEEK>.
  1213. @parm LPARAM | lParam1 | Specifies a parameter for the message.
  1214. @parm LPARAM | lParam2 | Specifies a parameter for the message.
  1215. @rdesc The return value depends on the message specified by
  1216. <p wMsg>. If the I/O procedure does not recognize a message, it should
  1217. return zero.
  1218. @comm The four-character code specified by the
  1219. <e MMIOINFO.fccIOProc> field in the <t MMIOINFO> structure
  1220. associated with a file identifies a filename extension for a custom
  1221. storage system. When an application calls <f mmioOpen> with a
  1222. filename such as "foo.xyz!bar", the I/O procedure associated with the
  1223. four-character code "XYZ " is called to open the "bar" element of the
  1224. file "foo.xyz".
  1225. The <f mmioInstallIOProc> function maintains a separate list of
  1226. installed I/O procedures for each Windows application. Therefore,
  1227. different applications can use the same I/O procedure identifier for
  1228. different I/O procedures without conflict. Installing an I/O procedure
  1229. globally however enables any process to use the procedure.
  1230. If an application calls <f mmioInstallIOProc> more than once to
  1231. register the same I/O procedure, then it must call
  1232. <f mmioInstallIOProc> to remove the procedure once for each time it
  1233. installed the procedure.
  1234. <f mmioInstallIOProc> will not prevent an application from
  1235. installing two different I/O procedures with the same identifier, or
  1236. installing an I/O procedure with one of the predefined identifiers
  1237. ("DOS ", "MEM "). The most recently installed procedure
  1238. takes precedence, and the most recently installed procedure is the
  1239. first one to get removed.
  1240. When searching for a specified I/O procedure, local procedures are
  1241. searched first, then global procedures.
  1242. @xref mmioOpen
  1243. */
  1244. LPMMIOPROC WINAPI
  1245. mmioInstallIOProc(FOURCC fccIOProc, LPMMIOPROC pIOProc, DWORD dwFlags)
  1246. {
  1247. IOProcMapEntry *pEnt; // an entry in linked list
  1248. HTASK hTaskCurrent; // current Windows task handl
  1249. V_FLAGS(dwFlags, MMIO_VALIDPROC, mmioInstallIOProc, NULL);
  1250. if (fccIOProc == 0L)
  1251. return NULL;
  1252. hTaskCurrent = GetCurrentTask();
  1253. if (dwFlags & MMIO_INSTALLPROC)
  1254. {
  1255. /* install I/O procedure -- always add at the beginning of
  1256. * the list, so it overrides any other I/O procedures
  1257. * with the same identifier installed by the same task
  1258. */
  1259. V_CALLBACK((FARPROC)pIOProc, NULL);
  1260. if ((pEnt = (IOProcMapEntry NEAR *)
  1261. NewHandle(TYPE_IOPROC, sizeof(IOProcMapEntry))) == NULL)
  1262. return NULL; // out of memory
  1263. pEnt->fccIOProc = fccIOProc;
  1264. pEnt->pIOProc = pIOProc;
  1265. if (dwFlags & MMIO_GLOBALPROC) {
  1266. char libname[128];
  1267. char aszFour[sizeof(FOURCC)+1];
  1268. pEnt->hTask = NULL;
  1269. SetHandleOwner(pEnt, NULL);
  1270. //
  1271. // This is the hack to allow global IO Procs to be truly
  1272. // global. That is, if there is a matching 32 bit entry
  1273. // in win.ini under [IOProcs] for this fccIOProc will we try
  1274. // load the dll. The dll should install its 32 bit
  1275. // equivalent IOProc in the dll initialisation routine.
  1276. //
  1277. *(LPDWORD)&aszFour = (DWORD)fccIOProc;
  1278. aszFour[ sizeof(FOURCC) ] = '\0';
  1279. if ( GetProfileString( "IOProcs", aszFour, "", libname,
  1280. sizeof(libname) ) ) {
  1281. LoadLibraryEx32W( libname, 0L, 0L );
  1282. }
  1283. } else {
  1284. pEnt->hTask = hTaskCurrent;
  1285. }
  1286. pEnt->wFlags = 0;
  1287. pEnt->pNext = gIOProcMapHead;
  1288. gIOProcMapHead = pEnt;
  1289. return pIOProc;
  1290. }
  1291. if (!pIOProc)
  1292. if (dwFlags & MMIO_REMOVEPROC) {
  1293. LPMMIOPROC lpmmioproc;
  1294. lpmmioproc = RemoveIOProc(fccIOProc, hTaskCurrent);
  1295. if (!lpmmioproc)
  1296. lpmmioproc = RemoveIOProc(fccIOProc, NULL);
  1297. return lpmmioproc;
  1298. } else if (dwFlags & MMIO_FINDPROC)
  1299. return FindIOProc(fccIOProc, hTaskCurrent);
  1300. return NULL; // couldn't find requested I/O procedure
  1301. }
  1302. /* @doc EXTERNAL
  1303. @api LRESULT | mmioSendMessage | This function sends a message to the
  1304. I/O procedure associated with the specified file.
  1305. @parm HMMIO | hmmio | Specifies the file handle for a file opened
  1306. with <f mmioOpen>.
  1307. @parm UINT | wMsg | Specifies the message to send to the I/O procedure.
  1308. @parm LPARAM | lParam1 | Specifies a parameter for the message.
  1309. @parm LPARAM | lParam2 | Specifies a parameter for the message.
  1310. @rdesc The return value depends on the message. If the I/O procedure
  1311. does not recognize the message, the return value is zero.
  1312. @comm Use this function to send custom user-defined messages. Do
  1313. not use it to send the <m MMIOM_OPEN>, <m MMIOM_CLOSE>,
  1314. <m MMIOM_READ>, <m MMIOM_WRITE>, <m MMIOM_WRITEFLUSH>, or
  1315. <m MMIOM_SEEK> messages. Define
  1316. custom messages to be greater than or equal to the MMIOM_USER constant.
  1317. @xref mmioInstallIOProc
  1318. */
  1319. LRESULT WINAPI
  1320. mmioSendMessage(HMMIO hmmio, UINT wMsg, LPARAM lParam1, LPARAM lParam2)
  1321. {
  1322. V_HANDLE(hmmio, TYPE_MMIO, (LRESULT)0);
  1323. return PH(hmmio)->pIOProc((LPSTR)PH(hmmio), wMsg, lParam1, lParam2);
  1324. }
  1325. /* @doc INTERNAL
  1326. @api LONG | mmioDiskIO | Perform an unbuffered read or write.
  1327. Do not assume where the current disk offset <p lDiskOffset> will be.
  1328. @parm PMMIO | pmmio | The open file handle returned by <f mmioOpen>.
  1329. @parm UINT | wMsg | MMIOM_READ if <f mmioDiskIO> should read from the disk,
  1330. or MMIOM_WRITE if <f mmioDiskIO> should write to the disk.
  1331. @parm HPSTR | pch | The buffer to read into or write from.
  1332. @parm LONG | cch | The number of bytes to read or write.
  1333. <f mmioDiskIO> changes the disk offset to be <p lBufOffset>
  1334. and then performs an MMIOM_READ or MMIOM_WRITE operation as
  1335. specified by <p wMsg>, <p pch>, and <p cch>.
  1336. Note that if the I/O buffer is not empty at this point, this
  1337. function may not do what you expect.
  1338. Do not call this function for memory files.
  1339. */
  1340. static LONG NEAR PASCAL
  1341. mmioDiskIO(PMMIO pmmio, UINT wMsg, HPSTR pch, LONG cch)
  1342. {
  1343. if (pmmio->lDiskOffset != pmmio->lBufOffset)
  1344. {
  1345. if ((LONG) pmmio->pIOProc((LPSTR) pmmio, MMIOM_SEEK, (LPARAM) pmmio->lBufOffset,
  1346. (LPARAM) SEEK_SET) == -1)
  1347. return -1;
  1348. }
  1349. return (LONG) pmmio->pIOProc((LPSTR) pmmio, wMsg, (LPARAM) pch, (LPARAM) cch);
  1350. }
  1351. /* @doc INTERNAL
  1352. @api UINT | mmioExpandMemFile | Assuming that <p pmmio> is a memory file,
  1353. expand it by <p lExpand> bytes or <p adwInfo[0]> bytes, whichever
  1354. is larger. Do not disturb the contents of the buffer or change
  1355. the current file position.
  1356. @parm PMMIO | pmmio | The open file handle returned by <f mmioOpen>.
  1357. @parm LONG | lExpand | The minimum number of bytes to expand the buffer by.
  1358. @rdesc If the function succeeds, zero is returned. If the function fails,
  1359. an error code is returned. In particular, MMIOERR_OUTOFMEMORY is
  1360. returned if memory reallocation failed.
  1361. @comm Only call this function for memory files.
  1362. */
  1363. static UINT NEAR PASCAL
  1364. mmioExpandMemFile(PMMIO pmmio, LONG lExpand)
  1365. {
  1366. MMIOMEMINFO * pInfo = (MMIOMEMINFO *) pmmio->adwInfo;
  1367. DWORD dwFlagsTemp;
  1368. UINT w;
  1369. /* make sure buffer can be expanded */
  1370. /* Note: we used to check ALLOC_BUF here, we don't now. */
  1371. if (pInfo->lExpand == 0)
  1372. return MMIOERR_CANNOTEXPAND; // cannot grow file
  1373. /* how much should the buffer be expanded by? */
  1374. if (lExpand < pInfo->lExpand)
  1375. lExpand = pInfo->lExpand;
  1376. dwFlagsTemp = pmmio->dwFlags;
  1377. pmmio->dwFlags |= MMIO_ALLOCBUF;
  1378. w = mmioSetBuffer(HP(pmmio), NULL,
  1379. pmmio->cchBuffer + lExpand, 0);
  1380. pmmio->dwFlags = dwFlagsTemp;
  1381. return w;
  1382. }
  1383. /************************************************************************/
  1384. /*
  1385. @doc INTERNAL
  1386. @func UINT | lrename |
  1387. Renames the specified DOS file.
  1388. @parm LPCSTR | lszOrigPath |
  1389. Points to the DOS file to rename.
  1390. @parm LPCSTR | lszNewPath |
  1391. Points to the new name for the file.
  1392. @rdesc Returns zero if the file was renamed, else the DOS error code.
  1393. */
  1394. #pragma warning(4:4035)
  1395. #pragma warning(4:4704)
  1396. static UINT PASCAL NEAR lrename(
  1397. LPCSTR lszOrigPath,
  1398. LPCSTR lszNewPath)
  1399. {
  1400. _asm {
  1401. push ds
  1402. lds dx, lszOrigPath ; Original name.
  1403. les di, lszNewPath ; New name.
  1404. mov ah, 56H ; Rename file.
  1405. int 21h ; DOS.
  1406. sbb bx,bx ; if error (C) BX=FFFF, (NC) BX=000
  1407. and ax,bx ; set ax to zero if no error
  1408. pop ds
  1409. }
  1410. }
  1411. /* @doc INTERNAL
  1412. @api LRESULT | mmioDOSIOProc | The 'DOS' I/O procedure, which handles I/O
  1413. on ordinary DOS files.
  1414. @parm LPSTR | lpmmioinfo | A pointer to an MMIOINFO block that
  1415. contains information about the open file.
  1416. @parm UINT | wMsg | The message that the I/O procedure is being
  1417. asked to execute.
  1418. @parm LPARAM | lParam1 | Specifies additional message information.
  1419. @parm LPARAM | lParam2 | Specifies additional message information.
  1420. @rdesc Return value depends on <p wMsg>.
  1421. */
  1422. static LRESULT CALLBACK
  1423. mmioDOSIOProc(LPSTR lpmmioStr, UINT wMsg, LPARAM lParam1, LPARAM lParam2)
  1424. {
  1425. PMMIO pmmio = (PMMIO) (UINT) (LONG) lpmmioStr; // only in DLL!
  1426. MMIODOSINFO * pInfo = (MMIODOSINFO *) pmmio->adwInfo;
  1427. LONG lResult;
  1428. OFSTRUCT of;
  1429. switch (wMsg)
  1430. {
  1431. case MMIOM_OPEN:
  1432. /* If a temporary file name is to be returned, use the file
  1433. * name parameter as a disk followed by a prefix for the name
  1434. * to create. The extra info parameter optionally contains a
  1435. * sequence number to pass.
  1436. */
  1437. if (pmmio->dwFlags & MMIO_GETTEMP) {
  1438. V_RPOINTER((LPSTR)lParam1, 4, (LRESULT) MMSYSERR_INVALPARAM);
  1439. return (LRESULT)(LONG) (GetTempFileName(*(LPSTR)lParam1,
  1440. ((LPSTR)lParam1) + 3, (UINT)pmmio->adwInfo[0],
  1441. (LPSTR)lParam1) ? 0 : MMIOERR_FILENOTFOUND);
  1442. }
  1443. /* <lParam1> is either a file name or NULL; if it is
  1444. * NULL, then <adwInfo[0]>, which is actually <pInfo->fh>,
  1445. * should already contain an open DOS file handle.
  1446. * note that the low word of <dwFlags> is equivalent to
  1447. * the <wStyle> parameter of OpenFile()
  1448. */
  1449. if (lParam1 != 0)
  1450. pInfo->fh = OpenFile((LPSTR) lParam1, &of,
  1451. LOWORD(pmmio->dwFlags));
  1452. if (pInfo->fh == HFILE_ERROR)
  1453. return (LRESULT)(LONG) ((pmmio->dwFlags & MMIO_DELETE) ? MMIOERR_CANNOTWRITE : MMIOERR_FILENOTFOUND);
  1454. /* if file is being deleted, there's nothing more to do */
  1455. if (pmmio->dwFlags & MMIO_DELETE)
  1456. return (LRESULT) 0;
  1457. /* if file name is being parsed, translate to ansi */
  1458. if (pmmio->dwFlags & (MMIO_PARSE | MMIO_EXIST))
  1459. {
  1460. OemToAnsi(of.szPathName, (LPSTR) lParam1);
  1461. return (LRESULT) 0;
  1462. }
  1463. /* check the current file offset */
  1464. pmmio->lDiskOffset = _llseek(pInfo->fh, 0L, SEEK_CUR);
  1465. return (LRESULT) 0;
  1466. case MMIOM_CLOSE:
  1467. /* MMIO_FHOPEN flag means keep the DOS file handle open */
  1468. if (!((DWORD)lParam1 & MMIO_FHOPEN) && (_lclose(pInfo->fh) == HFILE_ERROR))
  1469. return (LRESULT) MMIOERR_CANNOTCLOSE;
  1470. else
  1471. return (LRESULT) 0;
  1472. case MMIOM_READ:
  1473. lResult = _hread(pInfo->fh, (LPVOID)lParam1, (LONG)lParam2);
  1474. if (lResult != -1L)
  1475. pmmio->lDiskOffset += lResult;
  1476. return (LRESULT) lResult;
  1477. case MMIOM_WRITE:
  1478. case MMIOM_WRITEFLUSH:
  1479. lResult = _hwrite(pInfo->fh, (LPVOID)lParam1, (LONG)lParam2);
  1480. if (lResult != -1L)
  1481. pmmio->lDiskOffset += lResult;
  1482. #ifdef DOSCANFLUSH
  1483. if (wMsg == MMIOM_WRITEFLUSH)
  1484. {
  1485. /* Issue hardware flush command */
  1486. }
  1487. #endif
  1488. return (LRESULT) lResult;
  1489. case MMIOM_SEEK:
  1490. lResult = _llseek(pInfo->fh, (LONG)lParam1, (int)(LONG)lParam2);
  1491. if (lResult != -1L)
  1492. pmmio->lDiskOffset = lResult;
  1493. return (LRESULT) lResult;
  1494. case MMIOM_RENAME:
  1495. if (lrename((LPCSTR)lParam1, (LPCSTR)lParam2))
  1496. return (LRESULT) MMIOERR_FILENOTFOUND;
  1497. break;
  1498. }
  1499. return (LRESULT) 0;
  1500. }
  1501. /* @doc INTERNAL
  1502. @api LRESULT | mmioMEMIOProc | The 'MEM' I/O procedure, which handles I/O
  1503. on memory files.
  1504. @parm LPSTR | lpmmioinfo | A pointer to an MMIOINFO block that
  1505. contains information about the open file.
  1506. @parm UINT | wMsg | The message that the I/O procedure is being
  1507. asked to execute.
  1508. @parm LPARAM | lParam1 | Specifies additional message information.
  1509. @parm LPARAM | lParam2 | Specifies additional message information.
  1510. @rdesc Return value depends on <p wMsg>.
  1511. */
  1512. static LRESULT CALLBACK
  1513. mmioMEMIOProc(LPSTR lpmmioStr, UINT wMsg, LPARAM lParam1, LPARAM lParam2)
  1514. {
  1515. PMMIO pmmio = (PMMIO) (UINT) (LONG) lpmmioStr; // only in DLL!
  1516. switch (wMsg)
  1517. {
  1518. case MMIOM_OPEN:
  1519. if (pmmio->dwFlags & ~(MMIO_CREATE | MMIO_READWRITE | MMIO_WRITE | MMIO_EXCLUSIVE | MMIO_DENYWRITE | MMIO_DENYREAD | MMIO_DENYNONE | MMIO_ALLOCBUF))
  1520. return (LRESULT) MMSYSERR_INVALFLAG;
  1521. /* all the data in the buffer is valid */
  1522. if (!(pmmio->dwFlags & MMIO_CREATE))
  1523. pmmio->pchEndRead = pmmio->pchEndWrite;
  1524. return (LRESULT) 0;
  1525. case MMIOM_CLOSE:
  1526. /* nothing special to do on close */
  1527. return (LRESULT) 0;
  1528. case MMIOM_READ:
  1529. case MMIOM_WRITE:
  1530. case MMIOM_WRITEFLUSH:
  1531. case MMIOM_SEEK:
  1532. return (LRESULT) -1;
  1533. }
  1534. return (LRESULT) 0;
  1535. }