/StormLib/stormlib/GfxDecode.cpp

http://ghostcb.googlecode.com/ · C++ · 697 lines · 442 code · 77 blank · 178 comment · 137 complexity · 06609a0698dafeedb2dc7be453cbc9dc MD5 · raw file

  1. /***********************************************************************
  2. *
  3. * Description: GfxDecode -- functions for reading Diablo's GFX files
  4. * Author: Marko Friedemann <marko.friedemann@bmx-chemnitz.de>
  5. * Created at: Son Jan 27 15:20:43 CET 2002
  6. * Computer: hangloose.flachland-chemnitz.de
  7. * System: Linux 2.4.16 on i686
  8. *
  9. * Copyright (c) 2002 BMX-Chemnitz.DE All rights reserved.
  10. *
  11. * ---------------------------------------------------------------------
  12. * included are functions for getting:
  13. * - the framecount of .CEL-files -> celGetFrameCount()
  14. * - single frames of .CEL-files -> celGetFrameData()
  15. * - the framecount of .CL2-files -> cl2GetFrameCount()
  16. * - single directions of .CL2-files (all frames) -> cl2GetDirData()
  17. * - single .PCX-files (256 color; v2, v5) -> pcxGetData()
  18. ***********************************************************************/
  19. #include <vector>
  20. #include <cmath>
  21. #include <iostream>
  22. #include "StormLib.h"
  23. #define TRANS_COL 256
  24. using std::cerr;
  25. using std::vector;
  26. /****** RAMP stuff *****************************************************
  27. * for a more detailed description/explanation see below
  28. ***********************************************************************/
  29. // two variations: one/two ramp(s)
  30. static const uint16_t c_2RampSize = 544; // the frame size
  31. static const uint16_t c_1RampSize = 800; // the frame size
  32. // ramps (both variations) can be either left or right
  33. static const uint16_t c_RampOffsetLeft[17] = {
  34. 0, // __
  35. 8, // + 8 note that this __--
  36. 24, // + 16 "drawing" is __--
  37. 48, // + 24 upside down! __-- this area
  38. 80, // + 32 __-- is always
  39. 120, // + 40 __-- colored
  40. 168, // + 48 __--
  41. 224, // + 56 __-- lower ramp ends here (+30 == 254)
  42. 288, // + 64 --__ upper ramp might be missing
  43. 348, // + 60 | --__
  44. 400, // + 52 | --__ this area
  45. 444, // + 44 | --__ is always
  46. 480, // + 36 | --__ colored
  47. 508, // + 28 | either trans- --__
  48. 528, // + 20 | parent or colored --__
  49. 540, // + 12 | --__ +2 Pixels = 544
  50. 542 // + 2 | this last one doesn't exist, it's those^ 2 pixels
  51. };
  52. static const uint16_t c_RampOffsetRight[17] = {
  53. 2, // __ before this, there are 2 Pixels
  54. 14, // + 12 --__ 4^2 - 2
  55. 34, // + 20 --__ 6^2 - 2
  56. 62, // + 28 this area --__ 8^2 - 2
  57. 98, // + 36 is always --__ 10^2 - 2 pattern anyone? ;)
  58. 142, // + 44 colored --__
  59. 194, // + 52 --__
  60. 254, // + 60 lower ramp ends here --__ (-30 == 224)
  61. 318, // + 64 upper ramp might be missing __--
  62. 374, // + 56 __-- |
  63. 422, // + 48 this area __-- | note that this
  64. 462, // + 40 is always __-- | "drawing"
  65. 494, // + 32 colored __-- eiter trans- | is upside down!
  66. 518, // + 24 __-- parent or colored |
  67. 534, // + 16 __-- |
  68. 542, // + 8 __-- +2 Startpixels = 544 |
  69. 542 // + 0 this last one doesn't exist, it | would be EOF
  70. };
  71. /****** FrameBuffer class **********************************************
  72. * holds buffers and size information of the actual target image
  73. * purpose: buffer management and avoidance of ugly globals
  74. **********************************************************************/
  75. class FrameBuf
  76. {
  77. protected:
  78. vector <uint8_t *> vecData;
  79. uint8_t *pCurrRow;
  80. uint8_t *pPalette;
  81. uint16_t uRows;
  82. uint16_t *upYSize;
  83. uint16_t *upMaxX;
  84. public:
  85. uint16_t uCols;
  86. uint16_t uXSize;
  87. uint16_t uMaxBlock;
  88. uint16_t uFrameNum;
  89. bool bHasBlocks;
  90. bool bHasRamps;
  91. FrameBuf(
  92. uint8_t *pPal=NULL, uint16_t frame=0, uint16_t xsize=0,
  93. uint16_t *pysize=NULL, uint16_t *pmaxx=NULL, uint16_t maxblock=0)
  94. {
  95. pCurrRow = new uint8_t[4*xsize];
  96. pPalette = pPal;
  97. uCols = 0;
  98. uRows = 0;
  99. uXSize = xsize;
  100. uFrameNum = frame;
  101. uMaxBlock = maxblock;
  102. upYSize = pysize;
  103. upMaxX = pmaxx;
  104. bHasBlocks= false;
  105. bHasRamps = false;
  106. }
  107. ~FrameBuf()
  108. {
  109. delete[] pCurrRow;
  110. for (vector <uint8_t *>::iterator vi=vecData.begin(); vi!=vecData.end(); vi++)
  111. delete[] *vi;
  112. vecData.clear();
  113. }
  114. void addLine()
  115. {
  116. ++uRows;
  117. uCols = 0;
  118. vecData.push_back(pCurrRow);
  119. pCurrRow = new uint8_t[4*uXSize];
  120. }
  121. void addPixel(uint16_t uColorNum)
  122. {
  123. if (uColorNum > TRANS_COL) {
  124. cerr << "\n*** there seemed to be an error, illegal color index " << uColorNum;
  125. cerr << "\n +++ at (" << uCols << "," << uRows << "), see for yourself *** \n\n";
  126. uColorNum = TRANS_COL; // sane setting to avoid segfaults
  127. }
  128. memcpy(pCurrRow + 4*uCols, pPalette + 4*uColorNum, 4*sizeof(uint8_t));
  129. if (++uCols == uXSize)
  130. addLine();
  131. else if ((uColorNum != TRANS_COL) && (upMaxX != NULL) && (uCols > *upMaxX))
  132. *upMaxX = uCols;
  133. }
  134. // used to return the actual image data
  135. uint8_t *getData()
  136. {
  137. uint16_t i;
  138. vector <uint8_t *>::reverse_iterator vri;
  139. // allocate a buffer to hold the actual image size
  140. uint8_t *tmp = new uint8_t[4*uXSize*uRows];
  141. // the lines are upside down inside the vector, use reverse iterator
  142. for (i=0, vri=vecData.rbegin(); vri!=vecData.rend(); vri++, i++)
  143. memcpy(tmp+4*uXSize*i, *vri, 4*uXSize*sizeof(uint8_t));
  144. *upYSize = uRows; // set height
  145. if (uCols > 0) {
  146. cerr << "\n*** there seemed to be an error (last line does not match boundary, " << uCols << " pixels left)";
  147. cerr << "\n +++ this is often caused by specifying an invalid width, see for yourself *** \n\n";
  148. }
  149. return tmp;
  150. }
  151. };
  152. uint16_t WINAPI celGetFrameCount(uint8_t *pFileBuf)
  153. {
  154. uint32_t tmp;
  155. memcpy(&tmp, pFileBuf, sizeof(uint32_t));
  156. return (uint16_t)tmp;
  157. }
  158. /***** Block Decoder ***************************************************
  159. * one of three possible decoding techniques necessary for .cel
  160. * possible block contents are either colored (in that case the
  161. * appropriate number of pixels are read) or transparent pixels
  162. * there are neither ramps nor plain pixels allowed here
  163. ***********************************************************************/
  164. uint8_t *celDecodeBlocks(uint8_t *pFileBuf, FrameBuf *pFrame, uint32_t *framestart)
  165. {
  166. uint32_t uFilePos=framestart[pFrame->uFrameNum];
  167. uint8_t cRead=0, i=0;
  168. if (!pFrame->bHasBlocks) // sanity check
  169. return NULL;
  170. while (uFilePos < framestart[pFrame->uFrameNum+1]) {
  171. cRead = pFileBuf[uFilePos++];
  172. if ((uFilePos == framestart[pFrame->uFrameNum]+1))
  173. // TODO: what is this 0x0A 0x00 stuff all about?
  174. if ((cRead == 0x0A) && (pFileBuf[uFilePos] == 0x00)) {
  175. uFilePos += 9;
  176. cRead = pFileBuf[uFilePos++];
  177. }
  178. if (cRead > 0x7F)
  179. // transparent block (complement, 256-val)
  180. for (i=0; i<256-cRead; i++)
  181. pFrame->addPixel(TRANS_COL);
  182. else if (cRead < pFrame->uMaxBlock + 1)
  183. // pixel block (block size pixels to be read!)
  184. for (i=0; i<cRead; i++)
  185. pFrame->addPixel(pFileBuf[uFilePos++]);
  186. else
  187. cerr << "\n*** block mode: illegal block (> max_size) ***\n\n";
  188. }
  189. return pFrame->getData();
  190. }
  191. /***** Ramp Decoder ****************************************************
  192. * the second of three possible decoding techniques necessary for .cel
  193. * each block save the first/last is enclosed by two 0x00 pairs
  194. * those blocks affect _TWO_ rows with one being 2 colored pixels shorter
  195. * the first/last "block" affects only one row
  196. ***********************************************************************/
  197. uint8_t *celDecodeRamps(uint8_t *pFileBuf, FrameBuf *pFrame, uint32_t *framestart, bool bLeft)
  198. {
  199. uint32_t uFrameLen = framestart[pFrame->uFrameNum+1]-framestart[pFrame->uFrameNum];
  200. uint32_t uFilePos=0;
  201. uint16_t uBlockLen=0, i=0, j=0;
  202. bool bFirstLonger=false;
  203. if (!pFrame->bHasRamps) // sanity check
  204. return NULL;
  205. if (pFrame->uXSize != 32) // only used in that case
  206. return NULL;
  207. if (!bLeft) { // get first two pixels for right side ramps
  208. pFrame->addPixel(pFileBuf[framestart[pFrame->uFrameNum]]);
  209. pFrame->addPixel(pFileBuf[framestart[pFrame->uFrameNum]+1]);
  210. }
  211. // do all the ramp blocks
  212. for (i=0; i<(uFrameLen == c_2RampSize ? 15 : 7); i++) {
  213. uBlockLen = (bLeft ? (c_RampOffsetLeft[i+1] - c_RampOffsetLeft[i]) : (c_RampOffsetRight[i+1] - c_RampOffsetRight[i]));
  214. uFilePos = framestart[pFrame->uFrameNum] + (bLeft ? c_RampOffsetLeft[i] : c_RampOffsetRight[i]) + 2;
  215. bFirstLonger = (i>(bLeft ? 7 : 6));
  216. if (bLeft) {
  217. // OK, first line, starting with transparency
  218. for (j=0; j<pFrame->uXSize - uBlockLen/2 + (bFirstLonger ? 0 : 2); j++)
  219. pFrame->addPixel(TRANS_COL);
  220. // fill it up with the pixel block
  221. for (j=0; j<uBlockLen/2 - (bFirstLonger ? 0 : 2); j++)
  222. pFrame->addPixel(pFileBuf[uFilePos++]);
  223. // second line, starting again with transparency
  224. for (j=0; j<pFrame->uXSize - uBlockLen/2 + (bFirstLonger ? 2 : 0); j++)
  225. pFrame->addPixel(TRANS_COL);
  226. // fill the second line with the remaining pixels
  227. for (j=0; j<uBlockLen/2 - (bFirstLonger ? 2 : 0); j++)
  228. pFrame->addPixel(pFileBuf[uFilePos++]);
  229. } else {
  230. if (pFrame->uCols != 0) // fill current line with trans (if not empty)
  231. for (j=pFrame->uXSize - pFrame->uCols; j>0; j--)
  232. pFrame->addPixel(TRANS_COL);
  233. // OK, insert the first pixels into a new line
  234. for (j=0; j<uBlockLen/2 - (bFirstLonger ? 0 : 2); j++)
  235. pFrame->addPixel(pFileBuf[uFilePos++]);
  236. // fill the line with transparency
  237. for (j=0; j<pFrame->uXSize - uBlockLen/2 + (bFirstLonger ? 0 : 2); j++)
  238. pFrame->addPixel(TRANS_COL);
  239. // start a second line with the remaining pixels
  240. for (j=0; j<uBlockLen/2 - (bFirstLonger ? 2 : 0); j++)
  241. pFrame->addPixel(pFileBuf[uFilePos++]);
  242. }
  243. }
  244. // now read the last 0x00 pair and fill up
  245. uBlockLen = (uFrameLen == c_2RampSize ? 30 : 2); // one or two ramps?
  246. uFilePos = framestart[pFrame->uFrameNum] + (bLeft ? c_RampOffsetLeft[i] : c_RampOffsetRight[i]) + 2;
  247. // the transparency for the last (single) 0x00 pair
  248. for (j=0; j<uBlockLen; j++)
  249. pFrame->addPixel(TRANS_COL);
  250. if (bLeft) { // left side only: the remaining line
  251. for (j=0; j<pFrame->uXSize - uBlockLen; j++)
  252. pFrame->addPixel(pFileBuf[uFilePos++]);
  253. }
  254. // now the rest of the file (plain)
  255. while (uFilePos < framestart[pFrame->uFrameNum+1])
  256. pFrame->addPixel(pFileBuf[uFilePos++]);
  257. // the uppermost line is emtpy when 2 ramps are used
  258. if (uFrameLen == c_2RampSize)
  259. for (j=0; j<pFrame->uXSize; j++)
  260. pFrame->addPixel(TRANS_COL);
  261. return pFrame->getData();
  262. }
  263. /***** celGetFrameData *************************************************
  264. * decode .cel data for given frame and xsize
  265. * Args:
  266. * *vpFileBuf the buffer containing the filecontent
  267. * *palette the palette (4 bytes for each of the 257 entries)
  268. * 256 colors are needed + 1 for alpha
  269. * uXSize this information must be given
  270. * uFrameNume the frame to get
  271. * *uYSize the actual value is returned therein
  272. * *uMaxX this can be used (if != NULL) to get the column
  273. * of the rightmost nontransparent pixel (useable
  274. * eg for fonts)
  275. *
  276. * Returns: an array containing 4 Bytes (RGBA) for each pixel
  277. *
  278. * ---------------------------------------------------------------
  279. * Comments: dirty hack, started from scratch @ 2000-10-11
  280. * cleanly rewritten during incorporation into ladiks StormLib
  281. * status: structured hack ;)
  282. *
  283. * It took me approx. 6 days to understand the format basics (hex viewer)
  284. * For this I had a little help from a dos tool ("ddecode", from
  285. * www.cowlevel.com, binary only, no sources) which, however, gave
  286. * me the general idea what the pictures are actually supposed to look like.
  287. *
  288. * The fine adjustments, however, took quite some time and a little luck.
  289. * After I had written to various people (mickyk and ladik), which could
  290. * not help me, but wished best luck (thanks, btw, it helped ;)), I tried
  291. * some reverse engineering which was not succesful in the end.
  292. *
  293. * I then had incidentally a new idea of what could be going on @ 2002-01-23.
  294. * It just came to my mind that I could retry some actual painting in
  295. * reverse order (had done that before to no avail) and when looking closer
  296. * at it I realized the "ramp" stuff. This really is the trickiest part and
  297. * it took me some eight days to implement it without breaking the other
  298. * parts of the code. Very odd format indeed.
  299. *
  300. * TODO: learn what 0x0A 0x00 means
  301. **********************************************************************/
  302. uint8_t * WINAPI celGetFrameData(uint8_t *pFileBuf, uint8_t *palette, uint16_t uXSize, uint16_t uFrameNum, uint16_t *uYSize, uint16_t *uMaxX)
  303. {
  304. FrameBuf *pFrame;
  305. uint32_t *framestart=NULL, frames=0, uFilePos=0;
  306. uint16_t i, tmpWord=0;
  307. uint8_t cRead=0, *data;
  308. memcpy(&frames, pFileBuf, sizeof(uint32_t));
  309. uFilePos += sizeof(uint32_t);
  310. if (pFileBuf == NULL) {
  311. SetLastError(ERROR_INVALID_PARAMETER);
  312. return NULL;
  313. }
  314. if (palette == NULL) {
  315. SetLastError(ERROR_INVALID_PARAMETER);
  316. return NULL;
  317. }
  318. if (uFrameNum > frames-1) {
  319. SetLastError(ERROR_INVALID_PARAMETER);
  320. return NULL;
  321. }
  322. if (uYSize == NULL) {
  323. SetLastError(ERROR_INVALID_PARAMETER);
  324. return NULL;
  325. }
  326. // in case we want to know the rightmost pixels column (usable eg. for fonts)
  327. if (uMaxX != NULL)
  328. *uMaxX = 0;
  329. // get the frame offsets
  330. framestart = new uint32_t[frames+1];
  331. for (i=0; i<frames+1; i++) {
  332. memcpy(&framestart[i], pFileBuf+uFilePos, sizeof(uint32_t));
  333. uFilePos += sizeof(uint32_t);
  334. }
  335. /****** block size *************************************************
  336. * depends on the image width
  337. ******************************/
  338. double erg = rint(sqrt(pow(2, rint(log((double)(framestart[uFrameNum+1] - framestart[uFrameNum])) / log(2.0)))));
  339. pFrame = new FrameBuf(palette, uFrameNum, uXSize, uYSize, uMaxX, max((uint16_t)min((int)erg, 0x7F), uXSize));
  340. /****** ramp detection -- AFAIK only needed for 32x32 tiles ********
  341. * here I use hard coded constants because this is the only simple
  342. * way to get the detection done; plus this stuff is only to be
  343. * found in such 32x32 (tile) files and so wont hurt anyone ;)
  344. ******************************************************************/
  345. uint32_t uFrameLen = framestart[uFrameNum+1] - framestart[uFrameNum];
  346. if ((uXSize == 32) && ((uFrameLen == c_2RampSize) || (uFrameLen == c_1RampSize))) {
  347. // use the static arrays for the check
  348. for (i=0; i<(uFrameLen == c_2RampSize ? 16 : 8); i++) {
  349. memcpy(&tmpWord, pFileBuf+framestart[uFrameNum]+c_RampOffsetLeft[i], sizeof(uint16_t));
  350. if (tmpWord != 0)
  351. break;
  352. }
  353. bool bRampsLeft = pFrame->bHasRamps = (i==(uFrameLen == c_2RampSize ? 16 : 8));
  354. if (!pFrame->bHasRamps) { // only one can apply
  355. for (i=0; i<(uFrameLen == c_2RampSize ? 16 : 8); i++) {
  356. memcpy(&tmpWord, pFileBuf+framestart[uFrameNum]+c_RampOffsetRight[i], sizeof(uint16_t));
  357. if (tmpWord != 0)
  358. break;
  359. }
  360. pFrame->bHasRamps = (i==(uFrameLen == c_2RampSize ? 16 : 8)); // bRampsLeft stays false in this case
  361. }
  362. if (pFrame->bHasRamps) { // decode ramps and be off (if appropriate)
  363. data = celDecodeRamps(pFileBuf, pFrame, framestart, bRampsLeft);
  364. delete pFrame;
  365. delete[] framestart;
  366. return data;
  367. }
  368. }
  369. /*********** block detection ***************************************
  370. * 0x0A as start byte seems to be sufficient (though I still dunno
  371. * what the trailing 10 bytes mean); in any other case we act as if
  372. * blocks were to be used and check afterwards if the image looks
  373. * OK (that is, the last line has no pixels in it)
  374. ******************************************************************/
  375. cRead = pFileBuf[framestart[uFrameNum]];
  376. if (cRead == 0x0A) // sufficient
  377. pFrame->bHasBlocks = true;
  378. // if width == 32 && framelen == 32*32, assume plain
  379. else if ((uXSize != 32) || (uFrameLen != 32*32)) { // check needed
  380. uFilePos=framestart[uFrameNum];
  381. i=0;
  382. // rush through the frame
  383. while (uFilePos < framestart[uFrameNum+1]) {
  384. cRead = pFileBuf[uFilePos++];
  385. // transparency blocks
  386. while (cRead > 0x7F) {
  387. i += 256-cRead;
  388. i %= uXSize;
  389. if (uFilePos < framestart[uFrameNum+1])
  390. cRead = pFileBuf[uFilePos++];
  391. else
  392. cRead = 0;
  393. }
  394. // colored pixel block
  395. if (uFilePos < framestart[uFrameNum+1]) {
  396. if (cRead < pFrame->uMaxBlock + 1) {
  397. i+=cRead;
  398. i%=uXSize;
  399. uFilePos+=cRead;
  400. } else {
  401. // when the value is out of valid blockrange
  402. i=1; // trigger error (1%uXSize != 0)
  403. break;
  404. }
  405. }
  406. }
  407. if (i%uXSize == 0) // looks as if we got it right
  408. pFrame->bHasBlocks=true;
  409. }
  410. if (pFrame->bHasBlocks) { // use block decoder if appropriate
  411. data = celDecodeBlocks(pFileBuf, pFrame, framestart);
  412. delete pFrame;
  413. delete[] framestart;
  414. return data;
  415. }
  416. // plain mode (#3), read each color index and write the pixel
  417. uFilePos=framestart[uFrameNum];
  418. while (uFilePos < framestart[uFrameNum+1])
  419. pFrame->addPixel(pFileBuf[uFilePos++]);
  420. // cleanup, return image data and height
  421. data = pFrame->getData();
  422. delete pFrame;
  423. delete[] framestart;
  424. return data;
  425. }
  426. uint16_t WINAPI cl2GetFrameCount(uint8_t *pFileBuf)
  427. {
  428. uint32_t tmp;
  429. memcpy(&tmp, pFileBuf, sizeof(uint32_t));
  430. memcpy(&tmp, pFileBuf+tmp, sizeof(uint32_t));
  431. return (uint16_t)tmp;
  432. }
  433. /***** cl2GetDirData ***************************************************
  434. * decodes all frames of a .cl2 for given direction and xsize
  435. * Args:
  436. * *pFileBuf the buffer containing the filecontent
  437. * *palette the palette (4 bytes for each of the 257 entries)
  438. * 256 colors are needed + 1 for alpha
  439. * uXSize this information must be given
  440. * uDirNum the direction to get the frames from
  441. * *uYSize the actual height is returned herein
  442. *
  443. * Returns: <frames> arrays containing 4 Bytes (RGBA) for each pixel
  444. * where <frames> is read at runtime and handed back via *uFrames
  445. *
  446. * ---------------------------------------------------------------
  447. * Comments: dirty hack, started from scratch @ 2000-10-12
  448. *
  449. * The format basics are similar to .cel, with the main difference
  450. * that the values read have reverse interpretation. In .cel a value
  451. * greater than 0x7F means transparency, while in .cl2 this means
  452. * color and vice-versa. .cl2 has the additional understanding
  453. * of blocks of the same color (0x80 .. 0xBF) where the one color is
  454. * written multiple times.
  455. *
  456. * .cl2 only uses the block scheme, so there is no detection
  457. * necessary in order to get it right. The only thing still unknown
  458. * is that 0x0A 0x00 stuff...
  459. *
  460. * TODO: learn what 0x0A 0x00 means
  461. ***********************************************************************/
  462. BYTE ** WINAPI cl2GetDirData(BYTE *pFileBuf, BYTE *palette, WORD uXSize, WORD uDirNum, WORD *uYSize)
  463. {
  464. FrameBuf *pFrame;
  465. uint32_t frames=0, *framestart=NULL, uFilePos=0;
  466. uint16_t i, fc;
  467. uint8_t cRead=0, **data=NULL;
  468. if (pFileBuf == NULL) {
  469. SetLastError(ERROR_INVALID_PARAMETER);
  470. return NULL;
  471. }
  472. if (palette == NULL) {
  473. SetLastError(ERROR_INVALID_PARAMETER);
  474. return NULL;
  475. }
  476. if (uDirNum > 7) {
  477. SetLastError(ERROR_INVALID_PARAMETER);
  478. return NULL;
  479. }
  480. if (uYSize == NULL) {
  481. SetLastError(ERROR_INVALID_PARAMETER);
  482. return NULL;
  483. }
  484. // get direction offsets
  485. uint32_t dirstart[8];
  486. for (i=0; i<8; i++) {
  487. memcpy(&dirstart[i], pFileBuf+uFilePos, sizeof(uint32_t));
  488. uFilePos += sizeof(uint32_t);
  489. }
  490. uFilePos = dirstart[uDirNum];
  491. memcpy(&frames, pFileBuf+uFilePos, sizeof(uint32_t));
  492. uFilePos += sizeof(uint32_t);
  493. data = new uint8_t*[frames];
  494. // get frame offsets
  495. framestart = new uint32_t[frames+1];
  496. for (i=0; i<frames+1; i++) {
  497. memcpy(&framestart[i], pFileBuf+uFilePos, sizeof(uint32_t));
  498. uFilePos += sizeof(uint32_t);
  499. }
  500. // get frame data
  501. for (fc=0; fc<frames; fc++) {
  502. pFrame = new FrameBuf(palette, 0, uXSize, uYSize, NULL, 0);
  503. uFilePos = dirstart[uDirNum] + framestart[fc];
  504. while (uFilePos < dirstart[uDirNum] + framestart[fc+1]) {
  505. cRead = pFileBuf[uFilePos++];
  506. if (cRead < 0x80) { // transparency
  507. // TODO: what is this 0x0A 0x00 stuff all about?
  508. if ((cRead == 0x0A) && (pFileBuf[uFilePos] == 0) && (uFilePos == dirstart[uDirNum] + framestart[fc] + 1))
  509. uFilePos += 9; // ignore the 9 bytes after 0x0A 0x00 at the framestart
  510. else
  511. for (i=0; i<cRead; i++)
  512. pFrame->addPixel(TRANS_COL);
  513. } else if (cRead < 0xC0) {
  514. // read the next byte and write it <0xBF - cRead> times
  515. for (i=0; i<0xBF - cRead; i++)
  516. pFrame->addPixel(pFileBuf[uFilePos]);
  517. ++uFilePos;
  518. } else // cRead > 0xBF
  519. // read a block of the given size and write it
  520. for (i=0; i < 256-cRead; i++)
  521. pFrame->addPixel(pFileBuf[uFilePos++]);
  522. }
  523. // got the frame data, save it
  524. data[fc] = pFrame->getData();
  525. delete pFrame;
  526. }
  527. delete[] framestart;
  528. return data;
  529. }
  530. /****** pcxGetData *****************************************************
  531. * decodes pcx files (256 color, as in diablo mpq)
  532. * Args:
  533. * *pFileBuf the buffer containing the filecontent
  534. * uFileSize the size of the file buffer
  535. * uTransColor the palette entry to be transparent
  536. * *uXSize the actual width is returned herein
  537. * *uYSize the actual height is returned herein
  538. *
  539. * Returns: an array containing 4 Bytes (RGBA) for each pixel
  540. *
  541. * ---------------------------------------------------------------
  542. * Comments: format info and pseudocode taken from:
  543. * Klaus Holtorf, "Das Handbuch der Grafikformate"
  544. * ISBN 3-7723-6393-8
  545. ***********************************************************************/
  546. BYTE * WINAPI pcxGetData(BYTE *pFileBuf, DWORD uFileSize, BYTE uTransColor, WORD *uXSize, WORD *uYSize)
  547. {
  548. uint32_t uFilePos=0;
  549. uint32_t uDataRead=0; // potentially big! (logo.pcx: 550 * 216 * 15 = 1,782,000)
  550. uint16_t i=0;
  551. uint8_t *data, *palette;
  552. uint8_t uColorNum=0, uCount=0;
  553. struct pcx_header_t {
  554. uint8_t id;
  555. uint8_t version;
  556. uint8_t compressed;
  557. uint8_t bpp;
  558. uint16_t x0;
  559. uint16_t y0;
  560. uint16_t x1;
  561. uint16_t y1;
  562. uint16_t xdpi;
  563. uint16_t ydpi;
  564. uint8_t pal[16][3];
  565. uint8_t reserved;
  566. uint8_t layers;
  567. uint16_t rowbytes;
  568. uint16_t colortype;
  569. uint8_t pad[58];
  570. } pcxHeader;
  571. if (pFileBuf == NULL) {
  572. SetLastError(ERROR_INVALID_PARAMETER);
  573. return NULL;
  574. }
  575. if (uXSize == NULL) {
  576. SetLastError(ERROR_INVALID_PARAMETER);
  577. return NULL;
  578. }
  579. if (uYSize == NULL) {
  580. SetLastError(ERROR_INVALID_PARAMETER);
  581. return NULL;
  582. }
  583. // get image information
  584. memcpy(&pcxHeader, pFileBuf, sizeof(struct pcx_header_t));
  585. *uXSize = (pcxHeader.x1 - pcxHeader.x0 + 1);
  586. *uYSize = (pcxHeader.y1 - pcxHeader.y0 + 1);
  587. if ((pcxHeader.version != 2) && (pcxHeader.version != 5)) {
  588. cerr << "cannot handle pcx v" << pcxHeader.version << "\n";
  589. return NULL;
  590. }
  591. // get palette
  592. palette = new uint8_t[256*4];
  593. if (pFileBuf[uFileSize - 768 - 1] != 0x0C) {
  594. cerr << "palette error at " << uFileSize - 768 - 1 << "\n";
  595. return NULL;
  596. }
  597. for (i=0; i<256; i++) {
  598. memcpy(palette+i*4, pFileBuf+uFileSize-768+i*3, 3*sizeof(uint8_t));
  599. palette[i*4+3] = 0xFF;
  600. }
  601. memset(palette+uTransColor*4, 0, 4*sizeof(uint8_t)); // transparent black
  602. // start right after the header
  603. uFilePos = sizeof(struct pcx_header_t);
  604. data = new uint8_t[*uXSize * *uYSize * 4];
  605. while (uDataRead < (uint32_t)(*uXSize * *uYSize)) {
  606. // decompress
  607. uColorNum = pFileBuf[uFilePos++];
  608. if ((pcxHeader.compressed) && (uColorNum > 0xBF)) {
  609. uCount = (uColorNum & 0x3F);
  610. uColorNum = pFileBuf[uFilePos++];
  611. } else
  612. uCount = 1;
  613. // draw count pixels with color val
  614. for (i=0; i<uCount; i++)
  615. memcpy(data+(uDataRead++)*4, palette+uColorNum*4, 4*sizeof(uint8_t));
  616. }
  617. // cleanup
  618. delete[] palette;
  619. return data;
  620. }