IFF.CPP 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. /*
  2. ** Command & Conquer Red Alert(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /****************************************************************************
  19. *
  20. * C O N F I D E N T I A L --- W E S T W O O D S T U D I O S
  21. *
  22. *----------------------------------------------------------------------------
  23. *
  24. * FILE
  25. * iff.c
  26. *
  27. * DESCRIPTION
  28. * IFF manager. (32-Bit protected mode)
  29. *
  30. * PROGRAMMER
  31. * Denzil E. Long, Jr.
  32. *
  33. * DATE
  34. * January 26, 1995
  35. *
  36. *----------------------------------------------------------------------------
  37. *
  38. * PUBLIC
  39. * OpenIFF - Open an IFF for reading or writting.
  40. * CloseIFF - Close an IFF.
  41. * ReadForm - Read the IFF FORM, size and type of the file.
  42. * WriteForm - Write IFF form ID, size and type fields.
  43. * ReadChunkHeader - Read the IFF chunk identification header.
  44. * WriteChunkHeader - Write an IFF chunk identification header.
  45. * WriteChunk - Write an IFF chunk with data from a buffer.
  46. * WriteChunkBytes - Write data from a buffer to the IFF stream.
  47. * SkipChunkBytes - Skip bytes in a chunk.
  48. * FindChunk - Scan for a specific chunk name.
  49. * IDtoStr - Convert a longword identifier to a NULL-terminated
  50. * string.
  51. * CurrentFilePos - Get the current file position.
  52. *
  53. ****************************************************************************/
  54. #include <stdio.h>
  55. #include <malloc.h>
  56. #include <io.h>
  57. #include <fcntl.h>
  58. #include <sys\stat.h>
  59. #include <mem.h>
  60. #include "iff.h"
  61. /****************************************************************************
  62. *
  63. * NAME
  64. * OpenIFF - Open an IFF for reading or writting.
  65. *
  66. * SYNOPSIS
  67. * IFFHandle = OpenIFF(Name, Mode)
  68. *
  69. * IFFHandle *OpenIFF(char *, long);
  70. *
  71. * FUNCTION
  72. * Opens an IFF for a new read or write. The direction of the I/O is
  73. * given by the value of Mode, which can be either IFF_READ or IFF_WRITE.
  74. *
  75. * INPUTS
  76. * Name - Pointer to name of file to open.
  77. * Mode - IFF_READ or IFF_WRITE.
  78. *
  79. * RESULT
  80. * IFFHandle - Pointer to IFFHandle structure or NULL if error.
  81. *
  82. ****************************************************************************/
  83. IFFHandle *OpenIFF(char *name, long mode)
  84. {
  85. IFFHandle *iff;
  86. /* Allocate IFFHandle structure. */
  87. if ((iff = (IFFHandle *)malloc(sizeof(IFFHandle))) != NULL) {
  88. /* Initialize handle.*/
  89. memset(iff, 0, sizeof(IFFHandle));
  90. iff->flags = mode;
  91. switch (mode) {
  92. case IFFF_READ:
  93. iff->fh = open(name, O_RDONLY|O_BINARY);
  94. break;
  95. case IFFF_WRITE:
  96. iff->fh = open(name, (O_CREAT|O_TRUNC|O_WRONLY|O_BINARY),
  97. (S_IREAD|S_IWRITE));
  98. printf("\r");
  99. break;
  100. case (IFFF_READ|IFFF_WRITE):
  101. iff->fh = open(name, (O_RDWR|O_BINARY), (S_IREAD|S_IWRITE));
  102. break;
  103. default:
  104. iff->fh = -1;
  105. break;
  106. }
  107. /* If something went wrong we must free up any resources
  108. * that we have opened.
  109. */
  110. if (iff->fh == -1) {
  111. free(iff);
  112. iff = NULL;
  113. }
  114. }
  115. return (iff);
  116. }
  117. /****************************************************************************
  118. *
  119. * NAME
  120. * CloseIFF - Close an IFF.
  121. *
  122. * SYNOPSIS
  123. * CloseIFF(IFFHandle)
  124. *
  125. * void CloseIFF(IFFHandle *);
  126. *
  127. * FUNCTION
  128. * Completes an IFF read or write operation.
  129. *
  130. * INPUTS
  131. * IFFHandle - Pointer to IFFHandle structure.
  132. *
  133. * RESULT
  134. * NONE
  135. *
  136. ****************************************************************************/
  137. void CloseIFF(IFFHandle *iff)
  138. {
  139. long length;
  140. /* Write the length of the FORM */
  141. if ((iff->flags & IFFF_WRITE) && ((iff->form.size == 0)
  142. || (iff->scan > iff->form.size))) {
  143. lseek(iff->fh, 4, SEEK_SET);
  144. length = REVERSE_LONG(iff->scan);
  145. write(iff->fh, &length, 4);
  146. }
  147. close(iff->fh);
  148. free(iff);
  149. }
  150. /****************************************************************************
  151. *
  152. * NAME
  153. * ReadForm - Read the IFF FORM, size and type of the file.
  154. *
  155. * SYNOPSIS
  156. * Error = ReadForm(IFFHandle, FormHeader)
  157. *
  158. * long ReadForm(IFFHandle *, FormHeader *);
  159. *
  160. * FUNCTION
  161. * Read in the IFF form, size, type information. If the FormHeader
  162. * structure pointer is NULL then the FORM will be read into the
  163. * IFFHandles form structure.
  164. *
  165. * INPUTS
  166. * IFFHandle - Pointer to IFFHandle structure.
  167. * FormHeader - Pointer to FormHeader structure.
  168. *
  169. * RESULT
  170. * Error - 0 if successful or IFFERR_??? error code.
  171. *
  172. ****************************************************************************/
  173. long ReadForm(IFFHandle *iff, FormHeader *form)
  174. {
  175. FormHeader *ptr;
  176. long error;
  177. /* Read the FORM into the IFFHandle or the provided FormHeader. */
  178. if (form == NULL) {
  179. ptr = &iff->form;
  180. } else {
  181. ptr = form;
  182. }
  183. /* Read in IFF FORM from the file stream.. */
  184. if ((error = read(iff->fh, ptr, 12)) == 12) {
  185. ptr->size = REVERSE_LONG(ptr->size);
  186. iff->scan = 4;
  187. error = 0;
  188. } else {
  189. if (error == -1)
  190. error = IFFERR_READ;
  191. else if (error == 0)
  192. error = IFFERR_EOF;
  193. }
  194. return (error);
  195. }
  196. /****************************************************************************
  197. *
  198. * NAME
  199. * WriteForm - Write IFF form ID, size and type fields.
  200. *
  201. * SYNOPSIS
  202. * Error = WriteForm(IFFHandle)
  203. *
  204. * long WriteForm(IFFHandle, FormHeader *);
  205. *
  206. * FUNCTION
  207. * Write out the IFF form, size, type information. If the size field
  208. * is zero then the IFF form size will be calculated and written by
  209. * the CloseIFF() function. If the FormHeader structure pointer is NULL
  210. * the the form from the IFFHandle will be written.
  211. *
  212. * INPUTS
  213. * IFFHandle - Pointer to IFFHandle structure.
  214. * FormHeader - Pointer to FormHeader structure.
  215. *
  216. * RESULT
  217. * Error - 0 if successful or IFFERR_??? error code.
  218. *
  219. ****************************************************************************/
  220. long WriteForm(IFFHandle *iff, FormHeader *form)
  221. {
  222. FormHeader *ptr;
  223. long error = 0;
  224. /* Use the FORM from the IFFHandle or the provided FormHeader. */
  225. if (form == NULL) {
  226. ptr = &iff->form;
  227. } else {
  228. ptr = form;
  229. }
  230. /* Write the IFF form to the file stream. */
  231. if (iff->flags & IFFF_WRITE) {
  232. ptr->size = REVERSE_LONG(ptr->size);
  233. if (write(iff->fh, ptr, 12) == 12) {
  234. iff->scan = 4;
  235. } else {
  236. error = IFFERR_WRITE;
  237. }
  238. } else {
  239. error = IFFERR_WRITE;
  240. }
  241. return (error);
  242. }
  243. /****************************************************************************
  244. *
  245. * NAME
  246. * ReadChunkHeader - Read the IFF chunk identification header.
  247. *
  248. * SYNOPSIS
  249. * Error = ReadChunkHeader(IFFHandle)
  250. *
  251. * long ReadChunkHeader(IFFHandle *);
  252. *
  253. * FUNCTION
  254. * Read the IFF identification header from the files data stream.
  255. *
  256. * INPUTS
  257. * IFFHandle - Pointer to IFFHandle structure.
  258. *
  259. * RESULT
  260. * Error - 0 if successful or IFFERR_??? error code.
  261. *
  262. ****************************************************************************/
  263. long ReadChunkHeader(IFFHandle *iff)
  264. {
  265. long error = 0;
  266. /* Skip any part of the previous chunk that hasn't been processed. */
  267. if ((iff->cn.size != 0) && (iff->cn.scan < PADSIZE(iff->cn.size))) {
  268. error = lseek(iff->fh, (PADSIZE(iff->cn.size) - iff->cn.scan), SEEK_CUR);
  269. if (error == -1) {
  270. error = IFFERR_READ;
  271. } else {
  272. error = 0;
  273. }
  274. }
  275. /* Read in the next chunk header context. */
  276. if (!error) {
  277. if ((error = read(iff->fh, &iff->cn, 8)) == 8) {
  278. error = 0;
  279. iff->scan += 8;
  280. iff->cn.size = REVERSE_LONG(iff->cn.size);
  281. iff->cn.scan = 0;
  282. } else {
  283. if (error == -1) {
  284. error = IFFERR_READ;
  285. } else if (error == 0) {
  286. error = IFFERR_EOF;
  287. }
  288. }
  289. }
  290. return (error);
  291. }
  292. /****************************************************************************
  293. *
  294. * NAME
  295. * WriteChunkHeader - Write an IFF chunk identification header.
  296. *
  297. * SYNOPSIS
  298. * Error = WriteChunkHeader(IFFHandle, ID, Size)
  299. *
  300. * long WriteChunkHeader(IFFHandle *, long, long);
  301. *
  302. * FUNCTION
  303. * Write an IFF identification header to the files data stream.
  304. *
  305. * INPUTS
  306. * IFFHandle - Pointer to IFFHandle structure.
  307. * ID - ID code of chunk.
  308. * Size - Size of chunk in bytes (WORD aligned).
  309. *
  310. * RESULT
  311. * Error - 0 if successful or IFFERR_??? error code.
  312. *
  313. ****************************************************************************/
  314. long WriteChunkHeader(IFFHandle *iff, long id, long size)
  315. {
  316. long error = 0;
  317. /* Make sure it is okay to write. */
  318. if (iff->flags & IFFF_WRITE) {
  319. iff->cn.id = id;
  320. iff->cn.size = REVERSE_LONG(size);
  321. iff->cn.scan = 0;
  322. if (write(iff->fh, &iff->cn, 8) == 8) {
  323. iff->scan += 8;
  324. } else {
  325. error = IFFERR_WRITE;
  326. }
  327. }
  328. return (error);
  329. }
  330. /****************************************************************************
  331. *
  332. * NAME
  333. * WriteChunk - Write an IFF chunk with data from a buffer.
  334. *
  335. * SYNOPSIS
  336. * Actual = WriteChunk(IFFHandle, ID, Buffer, Size)
  337. *
  338. * long WriteChunk(IFFHandle *, long, char *, long);
  339. *
  340. * FUNCTION
  341. * Write a IFF chunk at the current file position.
  342. *
  343. * INPUTS
  344. * IFFHandle - Pointer to IFFHandle structure.
  345. * ID - ID code of chunk.
  346. * Buffer - Pointer to buffer area with bytes to be written.
  347. * Size - Number of bytes to write.
  348. *
  349. * RESULT
  350. * Actual - (positive) Bytes written or (negative) IFFERR_??? error code.
  351. *
  352. ****************************************************************************/
  353. long WriteChunk(IFFHandle *iff, long id, char *buffer, long size)
  354. {
  355. Context cn;
  356. long actual;
  357. /* Make sure we can write to this file. */
  358. if (iff->flags & IFFF_WRITE) {
  359. cn.id = id;
  360. cn.size = REVERSE_LONG(size);
  361. /* Write chunk header. */
  362. if (write(iff->fh, &cn, 8) == 8) {
  363. iff->scan += 8;
  364. iff->cn.scan += 8;
  365. /* Write chunk data. */
  366. actual = write(iff->fh, buffer, size);
  367. if (actual == size) {
  368. iff->scan += actual;
  369. iff->cn.scan += actual;
  370. /* Write chunk padding if necessary. */
  371. if (PADSIZE(size) > size) {
  372. id = 0;
  373. if (write(iff->fh, &id, 1) == 1) {
  374. iff->scan++;
  375. iff->cn.scan++;
  376. } else {
  377. actual = IFFERR_WRITE;
  378. }
  379. }
  380. } else {
  381. actual = IFFERR_WRITE;
  382. }
  383. } else {
  384. actual = IFFERR_WRITE;
  385. }
  386. } else {
  387. actual = IFFERR_WRITE;
  388. }
  389. return (actual);
  390. }
  391. /****************************************************************************
  392. *
  393. * NAME
  394. * WriteChunkBytes - Write data from a buffer to the IFF stream.
  395. *
  396. * SYNOPSIS
  397. * Actual = WriteChunkBytes(IFFHandle, Buffer, Size)
  398. *
  399. * long WriteChunk(IFFHandle *, char *, long);
  400. *
  401. * FUNCTION
  402. * Write a IFF chunk at the current file position.
  403. *
  404. * INPUTS
  405. * IFFHandle - Pointer to IFFHandle structure.
  406. * Buffer - Pointer to buffer area with bytes to be written.
  407. * Size - Number of bytes to write.
  408. *
  409. * RESULT
  410. * Actual - (positive) Bytes written or (negative) IFFERR_??? error code.
  411. *
  412. ****************************************************************************/
  413. long WriteChunkBytes(IFFHandle *iff, char *buffer, long size)
  414. {
  415. long actual;
  416. /* Make sure we can write to this file. */
  417. if (iff->flags & IFFF_WRITE) {
  418. /* Write data. */
  419. if ((actual = (unsigned short)write(iff->fh, buffer, size)) == size) {
  420. iff->scan += actual;
  421. iff->cn.scan += actual;
  422. } else {
  423. actual = IFFERR_WRITE;
  424. }
  425. } else {
  426. actual = IFFERR_WRITE;
  427. }
  428. return (actual);
  429. }
  430. /****************************************************************************
  431. *
  432. * NAME
  433. * ReadChunkBytes - Read data from a chunk into a buffer.
  434. *
  435. * SYNOPSIS
  436. * Actual = ReadChunkBytes(IFFHandle, Buffer, Length)
  437. *
  438. * long ReadChunkBytes(IFFHandle *, char *, long);
  439. *
  440. * FUNCTION
  441. * Read in 'Length' number of bytes from the current chunk context.
  442. * If the specified length exceeds the number of bytes remaining in the
  443. * chunk ReadChunkBytes() will read in only the number of remaining
  444. * bytes. ReadChunkBytes() will never read beyond the scope of the
  445. * current chunk.
  446. *
  447. * INPUTS
  448. * IFFHandle - Pointer to IFFHandle structure.
  449. * Buffer - Pointer to buffer to read data into.
  450. * Length - Number of bytes to read.
  451. *
  452. * RESULT
  453. * Actual - (positive) Bytes written or (negative) IFFERR_??? error code.
  454. *
  455. ****************************************************************************/
  456. long ReadChunkBytes(IFFHandle *iff, char *buffer, long size)
  457. {
  458. long actual;
  459. /* If the actual bytes remaining in the current chunk is less than
  460. * the requested bytes to read then adjust the read request size
  461. * to only read in the bytes that remain in the chunk.
  462. */
  463. actual = (iff->cn.size - iff->cn.scan);
  464. if (size > actual) {
  465. size = actual;
  466. }
  467. /* Read in the requested number of bytes. */
  468. if ((actual = read(iff->fh, buffer, size)) != size) {
  469. actual = IFFERR_READ;
  470. } else {
  471. iff->scan += actual;
  472. iff->cn.scan += actual;
  473. }
  474. return (actual);
  475. }
  476. /****************************************************************************
  477. *
  478. * NAME
  479. * SkipChunkBytes - Skip bytes in a chunk.
  480. *
  481. * SYNOPSIS
  482. * Error = SkipChunkBytes(IFFHandle, Skip)
  483. *
  484. * long SkipChunkBytes(IFFHandle *, long);
  485. *
  486. * FUNCTION
  487. * Skip the specified number of bytes of the chunk.
  488. *
  489. * INPUTS
  490. * IFFHandle - Pointer to IFFHandle structure.
  491. * Skip - Number of bytes to skip.
  492. *
  493. * RESULT
  494. * Error - 0 if successful or FAIL_??? error code.
  495. *
  496. ****************************************************************************/
  497. long SkipChunkBytes(IFFHandle *iff, long skip)
  498. {
  499. long error = 0;
  500. if (lseek(iff->fh, skip, SEEK_CUR) == -1) {
  501. error = IFFERR_READ;
  502. } else {
  503. iff->scan += skip;
  504. iff->cn.scan += skip;
  505. }
  506. return (error);
  507. }
  508. /****************************************************************************
  509. *
  510. * NAME
  511. * FindChunk - Scan for a specific chunk name.
  512. *
  513. * SYNOPSIS
  514. * Error = FindChunk(IFFHandle, ID)
  515. *
  516. * long FindChunk(IFFHandle *, long);
  517. *
  518. * FUNCTION
  519. * Scan from the current file position for the next occurance of the
  520. * specified chunk ID. When a match is found the function will return
  521. * with the matching chunk as the current context.
  522. *
  523. * INPUTS
  524. * IFFHandle - Pointer to IFFHandle structure.
  525. * ID - ID code of chunk.
  526. *
  527. * RESULT
  528. * Error - 0 if successful or FAIL_??? error code.
  529. *
  530. ****************************************************************************/
  531. long FindChunk(IFFHandle *iff, long id)
  532. {
  533. long found = 0;
  534. long error = 0;
  535. /* Invalid handle check. */
  536. if (iff != NULL) {
  537. /* Scan until we have a match or an error. */
  538. while ((found == 0) && !(error = ReadChunkHeader(iff))) {
  539. /* If we found a match the terminate scan, otherwise skip this
  540. * chunk and process the next.
  541. */
  542. if (iff->cn.id == id) {
  543. found = 1;
  544. } else {
  545. error = SkipChunkBytes(iff, PADSIZE(iff->cn.size));
  546. }
  547. }
  548. }
  549. return (error);
  550. }
  551. /****************************************************************************
  552. *
  553. * NAME
  554. * IDtoStr - Convert a longword identifier to a NULL-terminated string.
  555. *
  556. * SYNOPSIS
  557. * String = IDtoStr(ID, Buffer)
  558. *
  559. * char *IDtoStr(long, char *);
  560. *
  561. * FUNCTION
  562. * Writes the ASCII equivalent of the given longword ID into buffer as a
  563. * NULL-terminated string.
  564. *
  565. * INPUTS
  566. * ID - Longword ID.
  567. * Buffer - Character buffer to accept string (at least 5 characters).
  568. *
  569. * RESULT
  570. * String - The value of "Buffer".
  571. *
  572. ****************************************************************************/
  573. char *IDtoStr(long id, char *buf)
  574. {
  575. memcpy(buf, &id, 4);
  576. *(buf + 4) = 0;
  577. return (buf);
  578. }
  579. /****************************************************************************
  580. *
  581. * NAME
  582. * CurrentFilePos - Get the current file position.
  583. *
  584. * SYNOPSIS
  585. * Position = CurrentFilePos(IFFHandle)
  586. *
  587. * long CurrentFilePos(IFFHandle *);
  588. *
  589. * FUNCTION
  590. * This function returns the offset in bytes of the current file position
  591. * from the beginning of the IFF.
  592. *
  593. * INPUTS
  594. * IFFHandle - Pointer to IFFHandle structure.
  595. *
  596. * RESULT
  597. * Position - Offset in bytes from the beginning of the file to the
  598. * current position.
  599. *
  600. ****************************************************************************/
  601. long CurrentFilePos(IFFHandle *iff)
  602. {
  603. long offset;
  604. if ((offset = lseek(iff->fh, 0, SEEK_CUR)) == -1) {
  605. offset = IFFERR_READ;
  606. }
  607. return (offset);
  608. }