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.

394 lines
15 KiB

  1. /* Copyright (c) 1991-1995 Microsoft Corporation */
  2. /* mmioriff.c
  3. *
  4. * MMIO RIFF functions.
  5. */
  6. /* Revision history:
  7. LaurieGr: Jan 92 Ported from win16. Source tree fork, not common code.
  8. */
  9. #define VALIDATE_PARMS
  10. #include "winmmi.h"
  11. #include "mmioi.h"
  12. static BYTE bPad;
  13. /* @doc EXTERNAL
  14. @api MMRESULT | mmioDescend | This function descends into a chunk of a
  15. RIFF file opened with <f mmioOpen>. It can also search for a given
  16. chunk.
  17. @parm HMMIO | hmmio | Specifies the file handle of an open RIFF file.
  18. @parm LPMMCKINFO | lpck | Specifies a pointer to a
  19. caller-supplied <t MMCKINFO> structure that <f mmioDescend> fills
  20. with the following information:
  21. -- The <e MMCKINFO.ckid> field is the chunk ID of the chunk.
  22. -- The <e MMCKINFO.cksize> field is the size of the data portion
  23. of the chunk. The data size includes the form type or list type (if
  24. any), but does not include the 8-byte chunk header or the pad byte at
  25. the end of the data (if any).
  26. -- The <e MMCKINFO.fccType> field is the form type if
  27. <e MMCKINFO.ckid> is "RIFF", or the list type if
  28. <e MMCKINFO.ckid> is "LIST". Otherwise, it is NULL.
  29. -- The <e MMCKINFO.dwDataOffset> field is the file offset of the
  30. beginning of the data portion of the chunk. If the chunk is a
  31. "RIFF" chunk or a "LIST" chunk, then <e MMCKINFO.dwDataOffset>
  32. is the offset of the form type or list type.
  33. -- The <e MMCKINFO.dwFlags> contains other information about the chunk.
  34. Currently, this information is not used and is set to zero.
  35. If the MMIO_FINDCHUNK, MMIO_FINDRIFF, or MMIO_FINDLIST flag is
  36. specified for <p uFlags>, then the <t MMCKINFO> structure is also
  37. used to pass parameters to <f mmioDescend>:
  38. -- The <e MMCKINFO.ckid> field specifies the four-character code
  39. of the chunk ID, form type, or list type to search for.
  40. @parm LPMMCKINFO | lpckParent | Specifies a pointer to an
  41. optional caller-supplied <t MMCKINFO> structure identifying
  42. the parent of the chunk being searched for.
  43. A parent of a chunk is the enclosing chunk--only "RIFF" and "LIST"
  44. chunks can be parents. If <p lpckParent> is not NULL, then
  45. <f mmioDescend> assumes the <t MMCKINFO> structure it refers to
  46. was filled when <f mmioDescend> was called to descend into the parent
  47. chunk, and <f mmioDescend> will only search for a chunk within the
  48. parent chunk. Set <p lpckParent> to NULL if no parent chunk is
  49. being specified.
  50. @parm UINT | uFlags | Specifies search options. Contains up to one
  51. of the following flags. If no flags are specified,
  52. <f mmioDescend> descends into the chunk beginning at the current file
  53. position.
  54. @flag MMIO_FINDCHUNK | Searches for a chunk with the specified chunk ID.
  55. @flag MMIO_FINDRIFF | Searches for a chunk with chunk ID "RIFF"
  56. and with the specified form type.
  57. @flag MMIO_FINDLIST | Searches for a chunk with chunk ID "LIST"
  58. and with the specified form type.
  59. @rdesc The return value is zero if the function is successful.
  60. Otherwise, the return value specifies an error code. If the end of
  61. the file (or the end of the parent chunk, if given) is reached before
  62. the desired chunk is found, the return value is
  63. MMIOERR_CHUNKNOTFOUND.
  64. Other error return values are possible, for instance MMIOERR_CANNOTSEEK.
  65. @comm A RIFF chunk consists of a four-byte chunk ID (type FOURCC),
  66. followed by a four-byte chunk size (type DWORD), followed
  67. by the data portion of the chunk, followed by a null pad byte if
  68. the size of the data portion is odd. If the chunk ID is "RIFF" or
  69. "LIST", the first four bytes of the data portion of the chunk are
  70. a form type or list type (type FOURCC).
  71. If <f mmioDescend> is used to search for a chunk, the file
  72. position should be at the beginning of a
  73. chunk before calling <f mmioDescend>. The search begins at the
  74. current file position and continues to the end of the file. If a
  75. parent chunk is specified, the file position should be somewhere
  76. within the parent chunk before calling <f mmioDescend>. In this case,
  77. the search begins at the current file position and continues to the
  78. end of the parent chunk.
  79. If <f mmioDescend> is unsuccessful in searching for a chunk, the
  80. current file position is undefined. If <f mmioDescend> is
  81. successful, the current file position is changed. If the chunk
  82. is a "RIFF" or "LIST" chunk, the new file position
  83. will be just after the form type or list type (12 bytes from the
  84. beginning of the chunk). For other chunks, the new file position will be
  85. the start of the data portion of the chunk (8 bytes from the
  86. beginning of the chunk).
  87. For efficient RIFF file I/O, use buffered I/O.
  88. @xref mmioAscend MMCKINFO
  89. */
  90. MMRESULT APIENTRY
  91. mmioDescend(HMMIO hmmio, LPMMCKINFO lpck, LPCMMCKINFO lpckParent, UINT uFlags)
  92. {
  93. FOURCC ckidFind; // chunk ID to find (or NULL)
  94. FOURCC fccTypeFind; // form/list type to find (or NULL)
  95. #ifdef VALIDATE_PARMS
  96. V_FLAGS(uFlags, MMIO_DESCEND_VALID, mmioDescend, MMSYSERR_INVALFLAG);
  97. V_WPOINTER(lpck, sizeof(MMCKINFO), MMSYSERR_INVALPARAM);
  98. V_RPOINTER0(lpckParent, sizeof(MMCKINFO), MMSYSERR_INVALPARAM);
  99. #endif
  100. /* figure out what chunk id and form/list type to search for */
  101. if (uFlags & MMIO_FINDCHUNK)
  102. ckidFind = lpck->ckid, fccTypeFind = 0;
  103. else
  104. if (uFlags & MMIO_FINDRIFF)
  105. ckidFind = FOURCC_RIFF, fccTypeFind = lpck->fccType;
  106. else
  107. if (uFlags & MMIO_FINDLIST)
  108. ckidFind = FOURCC_LIST, fccTypeFind = lpck->fccType;
  109. else
  110. ckidFind = fccTypeFind = 0;
  111. lpck->dwFlags = 0L;
  112. if (hmmio == NULL) return MMIOERR_OUTOFMEMORY;
  113. for(;;)
  114. {
  115. MMRESULT mmr;
  116. /* read the chunk header */
  117. if (mmioRead(hmmio, (LPSTR) lpck, 2 * sizeof(DWORD)) !=
  118. 2 * sizeof(DWORD))
  119. return MMIOERR_CHUNKNOTFOUND;
  120. /* store the offset of the data part of the chunk */
  121. if ((lpck->dwDataOffset = mmioSeek(hmmio, 0L, SEEK_CUR)) == -1)
  122. return MMIOERR_CANNOTSEEK;
  123. /* see if the chunk is within the parent chunk (if given) */
  124. if ((lpckParent != NULL) &&
  125. (lpck->dwDataOffset - 8L >=
  126. lpckParent->dwDataOffset + lpckParent->cksize))
  127. return MMIOERR_CHUNKNOTFOUND;
  128. /* if the chunk if a 'RIFF' or 'LIST' chunk, read the
  129. * form type or list type
  130. */
  131. if ((lpck->ckid == FOURCC_RIFF) || (lpck->ckid == FOURCC_LIST))
  132. {
  133. if (!hmmio)
  134. return MMIOERR_CHUNKNOTFOUND;
  135. if (mmioRead(hmmio, (LPSTR) &lpck->fccType,
  136. sizeof(DWORD)) != sizeof(DWORD))
  137. return MMIOERR_CHUNKNOTFOUND;
  138. }
  139. else
  140. lpck->fccType = 0;
  141. /* if this is the chunk we're looking for, stop looking */
  142. if ( ((ckidFind == 0) || (ckidFind == lpck->ckid)) &&
  143. ((fccTypeFind == 0) || (fccTypeFind == lpck->fccType)) )
  144. break;
  145. /* ascend out of the chunk and try again */
  146. if ((mmr = mmioAscend(hmmio, lpck, 0)) != 0)
  147. return mmr;
  148. }
  149. return 0;
  150. }
  151. /* @doc EXTERNAL MMIO_RIFF
  152. @api MMRESULT | mmioAscend | This function ascends out of a chunk in a
  153. RIFF file descended into with <f mmioDescend> or created with
  154. <f mmioCreateChunk>.
  155. @parm HMMIO | hmmio | Specifies the file handle of an open RIFF file.
  156. @parm LPMMCKINFO | lpck | Specifies a pointer to a
  157. caller-supplied <t MMCKINFO> structure previously filled by
  158. <f mmioDescend> or <f mmioCreateChunk>.
  159. @parm UINT | uFlags | Is not used and should be set to zero.
  160. @rdesc The return value is zero if the function is successful.
  161. Otherwise, the return value specifies an error code. The error
  162. code can be one of the following codes:
  163. @flag MMIOERR_CANNOTWRITE | The contents of the buffer could
  164. not be written to disk.
  165. @flag MMIOERR_CANNOTSEEK | There was an error while seeking to
  166. the end of the chunk.
  167. @comm If the chunk was descended into using <f mmioDescend>, then
  168. <f mmioAscend> seeks to the location following the end of the
  169. chunk (past the extra pad byte, if any).
  170. If the chunk was created and descended into using
  171. <f mmioCreateChunk>, or if the MMIO_DIRTY flag is set in the
  172. <e MMCKINFO.dwFlags> field of the <t MMCKINFO> structure
  173. referenced by <p lpck>, then the current file position
  174. is assumed to be the end of the data portion of the chunk.
  175. If the chunk size is not the same as the value stored
  176. in the <e MMCKINFO.cksize> field when <f mmioCreateChunk>
  177. was called, then <f mmioAscend> corrects the chunk
  178. size in the file before ascending from the chunk. If the chunk
  179. size is odd, <f mmioAscend> writes a null pad byte at the end of the
  180. chunk. After ascending from the chunk, the current file position is
  181. the location following the end of the chunk (past the extra pad byte,
  182. if any).
  183. @xref mmioDescend mmioCreateChunk MMCKINFO
  184. */
  185. MMRESULT APIENTRY
  186. mmioAscend(HMMIO hmmio, LPMMCKINFO lpck, UINT uFlags)
  187. {
  188. LONG lSeekPos;
  189. #ifdef VALIDATE_PARMS
  190. V_FLAGS(uFlags, 0, mmioAscend, MMSYSERR_INVALFLAG);
  191. V_WPOINTER(lpck, sizeof(MMCKINFO), MMSYSERR_INVALPARAM);
  192. #endif
  193. if (lpck->dwFlags & MMIO_DIRTY)
  194. {
  195. /* <lpck> refers to a chunk created by mmioCreateChunk();
  196. * check that the chunk size that was written when
  197. * mmioCreateChunk() was called is the real chunk size;
  198. * if not, fix it
  199. */
  200. LONG lOffset; // current offset in file
  201. LONG lActualSize; // actual size of chunk data
  202. if (hmmio == NULL) return MMIOERR_OUTOFMEMORY;
  203. if ((lOffset = mmioSeek(hmmio, 0L, SEEK_CUR)) == -1)
  204. return MMIOERR_CANNOTSEEK;
  205. if ((lActualSize = lOffset - lpck->dwDataOffset) < 0)
  206. return MMIOERR_CANNOTWRITE;
  207. if (LOWORD(lActualSize) & 1)
  208. {
  209. if (hmmio == NULL)
  210. return MMIOERR_CANNOTWRITE;
  211. /* chunk size is odd -- write a null pad byte */
  212. if (mmioWrite(hmmio, (LPSTR) &bPad, sizeof(bPad))
  213. != sizeof(bPad))
  214. return MMIOERR_CANNOTWRITE;
  215. }
  216. if (lpck->cksize == (DWORD)lActualSize)
  217. return 0;
  218. /* fix the chunk header */
  219. lpck->cksize = lActualSize;
  220. if (mmioSeek(hmmio, lpck->dwDataOffset
  221. - sizeof(DWORD), SEEK_SET) == -1)
  222. return MMIOERR_CANNOTSEEK;
  223. if (mmioWrite(hmmio, (LPSTR) &lpck->cksize,
  224. sizeof(DWORD)) != sizeof(DWORD))
  225. return MMIOERR_CANNOTWRITE;
  226. }
  227. // make sure that when we seek, we will be ADVANCING. otherwise
  228. // we could get stuck in a loop trying to descend/ascend and never
  229. // going forward through the file
  230. //
  231. lSeekPos = lpck->dwDataOffset + lpck->cksize + (lpck->cksize & 1);
  232. if ((LONG)lpck->dwDataOffset < 0 || lSeekPos < (LONG)lpck->dwDataOffset)
  233. return MMIOERR_INVALIDFILE;
  234. /* seek to the end of the chunk, past the null pad byte
  235. * (which is only there if chunk size is odd)
  236. */
  237. if (mmioSeek(hmmio, lSeekPos, SEEK_SET) == -1)
  238. return MMIOERR_CANNOTSEEK;
  239. return 0;
  240. }
  241. /* @doc EXTERNAL MMIO_RIFF
  242. @api MMRESULT | mmioCreateChunk | This function creates a chunk in a
  243. RIFF file opened with <f mmioOpen>. The new chunk is created at the
  244. current file position. After the new chunk is created, the current
  245. file position is the beginning of the data portion of the new chunk.
  246. @parm HMMIO | hmmio | Specifies the file handle of an open RIFF
  247. file.
  248. @parm LPMMCKINFO | lpck | Specifies a pointer to a caller-supplied
  249. <t MMCKINFO> structure containing information about the chunk to be
  250. created. The <t MMCKINFO> structure should be set up as follows:
  251. -- The <e MMCKINFO.ckid> field specifies the chunk ID of the
  252. chunk. If <p uFlags> includes MMIO_CREATERIFF or MMIO_CREATELIST,
  253. this field will be filled by <f mmioCreateChunk>.
  254. -- The <e MMCKINFO.cksize> field specifies the size of the data
  255. portion of the chunk, including the form type or list type (if any).
  256. If this value is not correct when <f mmioAscend> is called to mark
  257. the end of the chunk, them <f mmioAscend> will correct the chunk
  258. size.
  259. -- The <e MMCKINFO.fccType> field specifies the form type or list
  260. type if the chunk is a "RIFF" or "LIST" chunk. If the chunk is not a
  261. "RIFF" or "LIST" chunk, this field need not be filled in.
  262. -- The <e MMCKINFO.dwDataOffset> field need not be filled in. The
  263. <f mmioCreateChunk> function will fill this field with the file
  264. offset of the data portion of the chunk.
  265. -- The <e MMCKINFO.dwFlags> field need not be filled in. The
  266. <f mmioCreateChunk> function will set the MMIO_DIRTY flag in
  267. <e MMCKINFO.dwFlags>.
  268. @parm UINT | uFlags | Specifies flags to optionally create either a
  269. "RIFF" chunk or a "LIST" chunk. Can contain one of the following
  270. flags:
  271. @flag MMIO_CREATERIFF | Creates a "RIFF" chunk.
  272. @flag MMIO_CREATELIST | Creates a "LIST" chunk.
  273. @rdesc The return value is zero if the function is successful.
  274. Otherwise, the return value specifies an error code. The error
  275. code can be one of the following codes:
  276. @flag MMIOERR_CANNOTWRITE | Unable to write the chunk header.
  277. @flag MMIOERR_CANNOTSEEK | Uanble to determine offset of data
  278. portion of the chunk.
  279. @comm This function cannot insert a chunk into the middle of a
  280. file. If a chunk is created anywhere but the end of a file,
  281. <f mmioCreateChunk> will overwrite existing information in the file.
  282. */
  283. MMRESULT APIENTRY
  284. mmioCreateChunk(HMMIO hmmio, LPMMCKINFO lpck, UINT uFlags)
  285. {
  286. int iBytes; // bytes to write
  287. LONG lOffset; // current offset in file
  288. #ifdef VALIDATE_PARMS
  289. V_FLAGS(uFlags, MMIO_CREATE_VALID, mmioCreateChunk, MMSYSERR_INVALFLAG);
  290. V_WPOINTER(lpck, sizeof(MMCKINFO), MMSYSERR_INVALPARAM);
  291. #endif
  292. /* store the offset of the data part of the chunk */
  293. if ((lOffset = mmioSeek(hmmio, 0L, SEEK_CUR)) == -1)
  294. return MMIOERR_CANNOTSEEK;
  295. lpck->dwDataOffset = lOffset + 2 * sizeof(DWORD);
  296. /* figure out if a form/list type needs to be written */
  297. if (uFlags & MMIO_CREATERIFF)
  298. lpck->ckid = FOURCC_RIFF, iBytes = 3 * sizeof(DWORD);
  299. else
  300. if (uFlags & MMIO_CREATELIST)
  301. lpck->ckid = FOURCC_LIST, iBytes = 3 * sizeof(DWORD);
  302. else
  303. iBytes = 2 * sizeof(DWORD);
  304. if (hmmio == NULL) return MMIOERR_CANNOTWRITE;
  305. /* write the chunk header */
  306. if (mmioWrite(hmmio, (LPSTR) lpck, (LONG) iBytes) != (LONG) iBytes)
  307. return MMIOERR_CANNOTWRITE;
  308. lpck->dwFlags = MMIO_DIRTY;
  309. return 0;
  310. }