PageRenderTime 101ms CodeModel.GetById 18ms app.highlight 76ms RepoModel.GetById 1ms app.codeStats 0ms

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