📄 gfxdecode.cpp
字号:
/***********************************************************************
*
* Description: GfxDecode -- functions for reading Diablo's GFX files
* Author: Marko Friedemann <marko.friedemann@bmx-chemnitz.de>
* Created at: Son Jan 27 15:20:43 CET 2002
* Computer: hangloose.flachland-chemnitz.de
* System: Linux 2.4.16 on i686
*
* Copyright (c) 2002 BMX-Chemnitz.DE All rights reserved.
*
* ---------------------------------------------------------------------
* included are functions for getting:
* - the framecount of .CEL-files -> celGetFrameCount()
* - single frames of .CEL-files -> celGetFrameData()
* - the framecount of .CL2-files -> cl2GetFrameCount()
* - single directions of .CL2-files (all frames) -> cl2GetDirData()
* - single .PCX-files (256 color; v2, v5) -> pcxGetData()
***********************************************************************/
#include <vector>
#include <cmath>
#include <iostream>
#include "StormLib.h"
#define TRANS_COL 256
using std::cerr;
using std::vector;
/****** RAMP stuff *****************************************************
* for a more detailed description/explanation see below
***********************************************************************/
// two variations: one/two ramp(s)
static const uint16_t c_2RampSize = 544; // the frame size
static const uint16_t c_1RampSize = 800; // the frame size
// ramps (both variations) can be either left or right
static const uint16_t c_RampOffsetLeft[17] = {
0, // __
8, // + 8 note that this __--
24, // + 16 "drawing" is __--
48, // + 24 upside down! __-- this area
80, // + 32 __-- is always
120, // + 40 __-- colored
168, // + 48 __--
224, // + 56 __-- lower ramp ends here (+30 == 254)
288, // + 64 --__ upper ramp might be missing
348, // + 60 | --__
400, // + 52 | --__ this area
444, // + 44 | --__ is always
480, // + 36 | --__ colored
508, // + 28 | either trans- --__
528, // + 20 | parent or colored --__
540, // + 12 | --__ +2 Pixels = 544
542 // + 2 | this last one doesn't exist, it's those^ 2 pixels
};
static const uint16_t c_RampOffsetRight[17] = {
2, // __ before this, there are 2 Pixels
14, // + 12 --__ 4^2 - 2
34, // + 20 --__ 6^2 - 2
62, // + 28 this area --__ 8^2 - 2
98, // + 36 is always --__ 10^2 - 2 pattern anyone? ;)
142, // + 44 colored --__
194, // + 52 --__
254, // + 60 lower ramp ends here --__ (-30 == 224)
318, // + 64 upper ramp might be missing __--
374, // + 56 __-- |
422, // + 48 this area __-- | note that this
462, // + 40 is always __-- | "drawing"
494, // + 32 colored __-- eiter trans- | is upside down!
518, // + 24 __-- parent or colored |
534, // + 16 __-- |
542, // + 8 __-- +2 Startpixels = 544 |
542 // + 0 this last one doesn't exist, it | would be EOF
};
/****** FrameBuffer class **********************************************
* holds buffers and size information of the actual target image
* purpose: buffer management and avoidance of ugly globals
**********************************************************************/
class FrameBuf
{
protected:
vector <uint8_t *> vecData;
uint8_t *pCurrRow;
uint8_t *pPalette;
uint16_t uRows;
uint16_t *upYSize;
uint16_t *upMaxX;
public:
uint16_t uCols;
uint16_t uXSize;
uint16_t uMaxBlock;
uint16_t uFrameNum;
bool bHasBlocks;
bool bHasRamps;
FrameBuf(
uint8_t *pPal=NULL, uint16_t frame=0, uint16_t xsize=0,
uint16_t *pysize=NULL, uint16_t *pmaxx=NULL, uint16_t maxblock=0)
{
pCurrRow = new uint8_t[4*xsize];
pPalette = pPal;
uCols = 0;
uRows = 0;
uXSize = xsize;
uFrameNum = frame;
uMaxBlock = maxblock;
upYSize = pysize;
upMaxX = pmaxx;
bHasBlocks= false;
bHasRamps = false;
}
~FrameBuf()
{
delete[] pCurrRow;
for (vector <uint8_t *>::iterator vi=vecData.begin(); vi!=vecData.end(); vi++)
delete[] *vi;
vecData.clear();
}
void addLine()
{
++uRows;
uCols = 0;
vecData.push_back(pCurrRow);
pCurrRow = new uint8_t[4*uXSize];
}
void addPixel(uint16_t uColorNum)
{
if (uColorNum > TRANS_COL) {
cerr << "\n*** there seemed to be an error, illegal color index " << uColorNum;
cerr << "\n +++ at (" << uCols << "," << uRows << "), see for yourself *** \n\n";
uColorNum = TRANS_COL; // sane setting to avoid segfaults
}
memcpy(pCurrRow + 4*uCols, pPalette + 4*uColorNum, 4*sizeof(uint8_t));
if (++uCols == uXSize)
addLine();
else if ((uColorNum != TRANS_COL) && (upMaxX != NULL) && (uCols > *upMaxX))
*upMaxX = uCols;
}
// used to return the actual image data
uint8_t *getData()
{
uint16_t i;
vector <uint8_t *>::reverse_iterator vri;
// allocate a buffer to hold the actual image size
uint8_t *tmp = new uint8_t[4*uXSize*uRows];
// the lines are upside down inside the vector, use reverse iterator
for (i=0, vri=vecData.rbegin(); vri!=vecData.rend(); vri++, i++)
memcpy(tmp+4*uXSize*i, *vri, 4*uXSize*sizeof(uint8_t));
*upYSize = uRows; // set height
if (uCols > 0) {
cerr << "\n*** there seemed to be an error (last line does not match boundary, " << uCols << " pixels left)";
cerr << "\n +++ this is often caused by specifying an invalid width, see for yourself *** \n\n";
}
return tmp;
}
};
uint16_t WINAPI celGetFrameCount(uint8_t *pFileBuf)
{
uint32_t tmp;
memcpy(&tmp, pFileBuf, sizeof(uint32_t));
return (uint16_t)tmp;
}
/***** Block Decoder ***************************************************
* one of three possible decoding techniques necessary for .cel
* possible block contents are either colored (in that case the
* appropriate number of pixels are read) or transparent pixels
* there are neither ramps nor plain pixels allowed here
***********************************************************************/
uint8_t *celDecodeBlocks(uint8_t *pFileBuf, FrameBuf *pFrame, uint32_t *framestart)
{
uint32_t uFilePos=framestart[pFrame->uFrameNum];
uint8_t cRead=0, i=0;
if (!pFrame->bHasBlocks) // sanity check
return NULL;
while (uFilePos < framestart[pFrame->uFrameNum+1]) {
cRead = pFileBuf[uFilePos++];
if ((uFilePos == framestart[pFrame->uFrameNum]+1))
// TODO: what is this 0x0A 0x00 stuff all about?
if ((cRead == 0x0A) && (pFileBuf[uFilePos] == 0x00)) {
uFilePos += 9;
cRead = pFileBuf[uFilePos++];
}
if (cRead > 0x7F)
// transparent block (complement, 256-val)
for (i=0; i<256-cRead; i++)
pFrame->addPixel(TRANS_COL);
else if (cRead < pFrame->uMaxBlock + 1)
// pixel block (block size pixels to be read!)
for (i=0; i<cRead; i++)
pFrame->addPixel(pFileBuf[uFilePos++]);
else
cerr << "\n*** block mode: illegal block (> max_size) ***\n\n";
}
return pFrame->getData();
}
/***** Ramp Decoder ****************************************************
* the second of three possible decoding techniques necessary for .cel
* each block save the first/last is enclosed by two 0x00 pairs
* those blocks affect _TWO_ rows with one being 2 colored pixels shorter
* the first/last "block" affects only one row
***********************************************************************/
uint8_t *celDecodeRamps(uint8_t *pFileBuf, FrameBuf *pFrame, uint32_t *framestart, bool bLeft)
{
uint32_t uFrameLen = framestart[pFrame->uFrameNum+1]-framestart[pFrame->uFrameNum];
uint32_t uFilePos=0;
uint16_t uBlockLen=0, i=0, j=0;
bool bFirstLonger=false;
if (!pFrame->bHasRamps) // sanity check
return NULL;
if (pFrame->uXSize != 32) // only used in that case
return NULL;
if (!bLeft) { // get first two pixels for right side ramps
pFrame->addPixel(pFileBuf[framestart[pFrame->uFrameNum]]);
pFrame->addPixel(pFileBuf[framestart[pFrame->uFrameNum]+1]);
}
// do all the ramp blocks
for (i=0; i<(uFrameLen == c_2RampSize ? 15 : 7); i++) {
uBlockLen = (bLeft ? (c_RampOffsetLeft[i+1] - c_RampOffsetLeft[i]) : (c_RampOffsetRight[i+1] - c_RampOffsetRight[i]));
uFilePos = framestart[pFrame->uFrameNum] + (bLeft ? c_RampOffsetLeft[i] : c_RampOffsetRight[i]) + 2;
bFirstLonger = (i>(bLeft ? 7 : 6));
if (bLeft) {
// OK, first line, starting with transparency
for (j=0; j<pFrame->uXSize - uBlockLen/2 + (bFirstLonger ? 0 : 2); j++)
pFrame->addPixel(TRANS_COL);
// fill it up with the pixel block
for (j=0; j<uBlockLen/2 - (bFirstLonger ? 0 : 2); j++)
pFrame->addPixel(pFileBuf[uFilePos++]);
// second line, starting again with transparency
for (j=0; j<pFrame->uXSize - uBlockLen/2 + (bFirstLonger ? 2 : 0); j++)
pFrame->addPixel(TRANS_COL);
// fill the second line with the remaining pixels
for (j=0; j<uBlockLen/2 - (bFirstLonger ? 2 : 0); j++)
pFrame->addPixel(pFileBuf[uFilePos++]);
} else {
if (pFrame->uCols != 0) // fill current line with trans (if not empty)
for (j=pFrame->uXSize - pFrame->uCols; j>0; j--)
pFrame->addPixel(TRANS_COL);
// OK, insert the first pixels into a new line
for (j=0; j<uBlockLen/2 - (bFirstLonger ? 0 : 2); j++)
pFrame->addPixel(pFileBuf[uFilePos++]);
// fill the line with transparency
for (j=0; j<pFrame->uXSize - uBlockLen/2 + (bFirstLonger ? 0 : 2); j++)
pFrame->addPixel(TRANS_COL);
// start a second line with the remaining pixels
for (j=0; j<uBlockLen/2 - (bFirstLonger ? 2 : 0); j++)
pFrame->addPixel(pFileBuf[uFilePos++]);
}
}
// now read the last 0x00 pair and fill up
uBlockLen = (uFrameLen == c_2RampSize ? 30 : 2); // one or two ramps?
uFilePos = framestart[pFrame->uFrameNum] + (bLeft ? c_RampOffsetLeft[i] : c_RampOffsetRight[i]) + 2;
// the transparency for the last (single) 0x00 pair
for (j=0; j<uBlockLen; j++)
pFrame->addPixel(TRANS_COL);
if (bLeft) { // left side only: the remaining line
for (j=0; j<pFrame->uXSize - uBlockLen; j++)
pFrame->addPixel(pFileBuf[uFilePos++]);
}
// now the rest of the file (plain)
while (uFilePos < framestart[pFrame->uFrameNum+1])
pFrame->addPixel(pFileBuf[uFilePos++]);
// the uppermost line is emtpy when 2 ramps are used
if (uFrameLen == c_2RampSize)
for (j=0; j<pFrame->uXSize; j++)
pFrame->addPixel(TRANS_COL);
return pFrame->getData();
}
/***** celGetFrameData *************************************************
* decode .cel data for given frame and xsize
* Args:
* *vpFileBuf the buffer containing the filecontent
* *palette the palette (4 bytes for each of the 257 entries)
* 256 colors are needed + 1 for alpha
* uXSize this information must be given
* uFrameNume the frame to get
* *uYSize the actual value is returned therein
* *uMaxX this can be used (if != NULL) to get the column
* of the rightmost nontransparent pixel (useable
* eg for fonts)
*
* Returns: an array containing 4 Bytes (RGBA) for each pixel
*
* ---------------------------------------------------------------
* Comments: dirty hack, started from scratch @ 2000-10-11
* cleanly rewritten during incorporation into ladiks StormLib
* status: structured hack ;)
*
* It took me approx. 6 days to understand the format basics (hex viewer)
* For this I had a little help from a dos tool ("ddecode", from
* www.cowlevel.com, binary only, no sources) which, however, gave
* me the general idea what the pictures are actually supposed to look like.
*
* The fine adjustments, however, took quite some time and a little luck.
* After I had written to various people (mickyk and ladik), which could
* not help me, but wished best luck (thanks, btw, it helped ;)), I tried
* some reverse engineering which was not succesful in the end.
*
* I then had incidentally a new idea of what could be going on @ 2002-01-23.
* It just came to my mind that I could retry some actual painting in
* reverse order (had done that before to no avail) and when looking closer
* at it I realized the "ramp" stuff. This really is the trickiest part and
* it took me some eight days to implement it without breaking the other
* parts of the code. Very odd format indeed.
*
* TODO: learn what 0x0A 0x00 means
**********************************************************************/
uint8_t * WINAPI celGetFrameData(uint8_t *pFileBuf, uint8_t *palette, uint16_t uXSize, uint16_t uFrameNum, uint16_t *uYSize, uint16_t *uMaxX)
{
FrameBuf *pFrame;
uint32_t *framestart=NULL, frames=0, uFilePos=0;
uint16_t i, tmpWord=0;
uint8_t cRead=0, *data;
memcpy(&frames, pFileBuf, sizeof(uint32_t));
uFilePos += sizeof(uint32_t);
if (pFileBuf == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
if (palette == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -