MORTEVIELLE: Implemented more of the image decompression code

This commit is contained in:
Paul Gilbert 2012-01-14 11:35:08 +11:00 committed by Strangerke
parent e183f9f055
commit abc2f60edf
3 changed files with 372 additions and 64 deletions

View File

@ -25,11 +25,13 @@
namespace Mortevielle {
#define INCR_TAIX { if (_xSize & 1) ++_xSize; }
#define DEFAULT_WIDTH 320
#define BUFFER_SIZE 8192
void GfxSurface::decode(const byte *pSrc) {
_width = _height = 0;
_var1 = *pSrc++;
_entryCount = *pSrc++;
int entryCount = *pSrc++;
pSrc += 2;
if (!_var1)
@ -37,11 +39,12 @@ void GfxSurface::decode(const byte *pSrc) {
// First run through the data to calculate starting offsets
const byte *p = pSrc;
int xOffset = 0xffff, yOffset = 0xffff;
_xOffset = _yOffset = 0xffff;
for (int idx = 0; idx < _entryCount; ++idx) {
assert(entryCount > 0);
for (int idx = 0; idx < entryCount; ++idx) {
_xp = READ_BE_UINT16(p + 4);
if (_xp < xOffset)
if (_xp < _xOffset)
_xOffset = _xp;
_yp = READ_BE_UINT16(p + 6);
@ -60,131 +63,146 @@ void GfxSurface::decode(const byte *pSrc) {
byte outputBuffer[65536];
byte *pDest = &outputBuffer[0];
const byte *pSrcStart = pSrc;
const byte *pLookup = NULL;
byte table4140[256];
byte table7110[256];
byte lookupTable[BUFFER_SIZE];
byte srcBuffer[BUFFER_SIZE];
// Main processing loop
do {
_var4 = READ_BE_UINT16(pSrc);
_var6 = READ_BE_UINT16(pSrc + 2);
int lookupBytes = READ_BE_UINT16(pSrc);
int srcSize = READ_BE_UINT16(pSrc + 2);
_xp = READ_BE_UINT16(pSrc + 4) - _xOffset;
_yp = READ_BE_UINT16(pSrc + 6) - _yOffset;
pSrc += 8;
_varC = READ_BE_UINT16(pSrc);
int decomCode = READ_BE_UINT16(pSrc);
_xSize = READ_BE_UINT16(pSrc + 2) + 1;
_ySize = READ_BE_UINT16(pSrc + 4) + 1;
majTtxTty();
pSrc += 6;
pDest = &outputBuffer[0];
_var15 = _var18 = 0;
_var18 = 0;
_nibbleFlag = false;
int decomIndex = 0;
if (_varC >> 8) {
if (decomCode >> 8) {
// Build up reference table
int tableOffset = 0;
if (_varC & 1) {
if (decomCode & 1) {
// Handle decompression of the pattern lookup table
do {
_var12 = desanalyse(pSrc);
_var14 = desanalyse(pSrc);
int savedVar15 = _var15;
bool savedVar15 = _nibbleFlag;
int savedVar18 = _var18;
do {
const byte *pTemp = pSrc;
_var15 = savedVar15;
_nibbleFlag = savedVar15;
_var18 = savedVar18;
assert(_var14 < 256);
for (int idx = 0; idx < _var14; ++idx, ++tableOffset)
table4140[tableOffset] = suiv(pTemp);
assert(tableOffset < BUFFER_SIZE);
lookupTable[tableOffset] = suiv(pTemp);
} while (--_var12 > 0);
} while (_var18 < (_var4 - 1));
} while (_var18 < (lookupBytes - 1));
} else {
assert(_var4 < 256);
for (int idx = 0; idx < _var4; ++idx)
table4140[idx] = suiv(pSrc);
assert(lookupBytes < BUFFER_SIZE);
for (int idx = 0; idx < (lookupBytes * 2); ++idx)
lookupTable[idx] = suiv(pSrc);
}
if (_var15) {
if (_nibbleFlag) {
++pSrc;
_var15 = 0;
_nibbleFlag = false;
}
if ((_var4 + _var6) & 1)
if ((lookupBytes + srcSize) & 1)
++pSrc;
tableOffset = 0;
_var18 = 0;
if (_varC & 2) {
if (decomCode & 2) {
// Handle decompression of the temporary source buffer
do {
_var12 = desanalyse(pSrc);
_var18 = _var14 = desanalyse(pSrc);
if (_var15 & 1) {
_var14 = desanalyse(pSrc);
_var18 += _var14;
if (_nibbleFlag) {
++pSrc;
++_var18;
_var15 = 0;
_nibbleFlag = false;
}
const byte *pStart = pSrc;
do {
pSrc = pStart;
for (int idx = 0; idx < _var14; ++idx) {
table7110[tableOffset++] = *pSrc++;
assert(tableOffset < BUFFER_SIZE);
srcBuffer[tableOffset++] = *pSrc++;
}
} while (--_var12 > 0);
} while (_var18 < (_var6 - 1));
} while (_var18 < (srcSize - 1));
} else {
assert(_var6 < 256);
for (int idx = 0; idx < _var4; ++idx)
table7110[idx] = *pSrc++;
assert(srcSize < BUFFER_SIZE);
for (int idx = 0; idx < srcSize; ++idx)
srcBuffer[idx] = *pSrc++;
}
if (_var15)
if (_nibbleFlag)
++pSrc;
// Switch over to using the decompressed source and lookup buffers
pSrcStart = pSrc;
pDest = &outputBuffer[_yp * SCREEN_WIDTH + _xp];
pSrc = &table7110[0];
_var1A = _var18 = _var15 = 0;
decomIndex = _varC;
pDest = &outputBuffer[_yp * DEFAULT_WIDTH + _xp];
pSrc = &srcBuffer[0];
pLookup = &lookupTable[0] - 1;
_lookupValue = _var18 = 0;
_nibbleFlag = false;
decomIndex = decomCode >> 8;
}
// Main decompression switch
switch (decomIndex) {
case 0:
// Draw rect at pos
pDest = &outputBuffer[_yp * SCREEN_WIDTH + _xp];
pDest = &outputBuffer[_yp * DEFAULT_WIDTH + _xp];
pSrcStart = pSrc;
INCR_TAIX;
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest += SCREEN_WIDTH) {
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest += DEFAULT_WIDTH) {
byte *pDestLine = pDest;
for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
*pDestLine++ = suiv(pSrc);
}
}
pSrc = pSrcStart + _var4 + ((_var4 & 1) ? 1 : 0);
pSrc = pSrcStart + lookupBytes + ((lookupBytes & 1) ? 1 : 0);
break;
case 1:
// Draw rect alternating left to right, right to left
INCR_TAIX;
for (int yCtr = 0; yCtr < _ySize; ++yCtr) {
if (yCtr % 2) {
if ((yCtr % 2) == 0) {
for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
*pDest++ = suiv(pSrc);
*pDest++ = csuiv(pSrc, pLookup);
}
} else {
for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
*--pDest = suiv(pSrc);
*--pDest = csuiv(pSrc, pLookup);
}
}
pDest += SCREEN_WIDTH;
pDest += DEFAULT_WIDTH;
}
break;
@ -193,12 +211,12 @@ void GfxSurface::decode(const byte *pSrc) {
INCR_TAIX;
for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
if (xCtr % 2) {
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest += SCREEN_WIDTH) {
*pDest = suiv(pSrc);
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest += DEFAULT_WIDTH) {
*pDest = csuiv(pSrc, pLookup);
}
} else {
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest -= SCREEN_WIDTH) {
*pDest = suiv(pSrc);
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest -= DEFAULT_WIDTH) {
*pDest = csuiv(pSrc, pLookup);
}
}
++pDest;
@ -206,14 +224,141 @@ void GfxSurface::decode(const byte *pSrc) {
break;
case 3:
// Draw horizontal line?
_var1C = 2;
horizontal(pSrc, pDest);
// Draw horizontal area?
_thickness = 2;
horizontal(pSrc, pDest, pLookup);
break;
case 4:
// Draw vertical area?
_thickness = 2;
vertical(pSrc, pDest, pLookup);
break;
case 5:
_thickness = 3;
horizontal(pSrc, pDest, pLookup);
break;
case 6:
_thickness = 4;
vertical(pSrc, pDest, pLookup);
break;
case 7:
// Draw box
INCR_TAIX;
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest += DEFAULT_WIDTH) {
byte *pDestLine = pDest;
for (int xCtr = 0; xCtr < _xSize; ++xCtr, ++pDestLine)
*pDestLine++ = csuiv(pSrc, pLookup);
}
break;
case 8:
// Draw box
for (int xCtr = 0; xCtr < _xSize; ++xCtr, ++pDest) {
byte *pDestLine = pDest;
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDestLine += DEFAULT_WIDTH)
*pDestLine = csuiv(pSrc, pLookup);
}
break;
case 9:
_thickness = 4;
horizontal(pSrc, pDest, pLookup);
break;
case 10:
_thickness = 6;
horizontal(pSrc, pDest, pLookup);
break;
case 11:
warning("TODO: Switch 11");
break;
case 12:
INCR_TAIX;
_thickness = _var22 = 1;
_var1E = 320;
_var20 = _ySize;
_var24 = _xSize;
diag();
break;
case 13:
INCR_TAIX;
_thickness = _xSize;
_var1E = 1;
_var20 = _xSize;
_var22 = 320;
_var24 = _ySize;
diag();
break;
case 14:
_thickness = _var1E = 1;
_var20 = _xSize;
_var22 = 320;
_var24 = _ySize;
diag();
break;
case 15:
INCR_TAIX;
_thickness = 2;
_var1E = 320;
_var20 = _ySize;
_var22 = 1;
_var24 = _xSize;
diag();
break;
case 16:
_thickness = 3;
_var1E = 1;
_var20 = _xSize;
_var22 = 320;
_var24 = _ySize;
diag();
break;
case 17:
INCR_TAIX;
_thickness = 3;
_var1E = 320;
_var20 = _ySize;
_var22 = 1;
_var24 = _xSize;
diag();
break;
case 18:
INCR_TAIX;
_thickness = 5;
_var1E = 320;
_var20 = _ySize;
_var22 = 1;
_var24 = _xSize;
diag();
break;
}
pSrc = pSrcStart;
} while (--_entryCount > 0);
} while (--entryCount > 0);
// At this point, the outputBuffer has the data for the image. The pitch is
// 320 pixels, the _xOffset and _yOffset represent the top left of the image data,
// and the _xSize and _ySize fields indicate the image size
create(_xOffset + _xSize, _yOffset + _ySize, Graphics::PixelFormat::createFormatCLUT8());
for (int yCtr = 0; yCtr < _ySize; ++yCtr) {
const byte *copySrc = &outputBuffer[yCtr * DEFAULT_WIDTH];
byte *copyDest = (byte *)getBasePtr(_xOffset, yCtr + _yOffset);
Common::copy(copySrc, copySrc + _xSize, copyDest);
}
}
void GfxSurface::majTtxTty() {
@ -226,24 +371,41 @@ void GfxSurface::majTtxTty() {
byte GfxSurface::suiv(const byte *&pSrc) {
int v = *pSrc;
if (_var15) {
if (_nibbleFlag) {
++pSrc;
++_var18;
_var15 = 0;
_nibbleFlag = false;
return v & 0xf;
} else {
_var15 = ~(v >> 8);
_nibbleFlag = !_nibbleFlag;
return v >> 4;
}
}
byte GfxSurface::csuiv(const byte *&pSrc, const byte *&pLookup) {
assert(pLookup);
while (!_lookupValue) {
int v;
do {
v = suiv(pSrc) & 0xff;
_lookupValue += v;
} while (v == 0xf);
++pLookup;
}
--_lookupValue;
return *pLookup;
}
int GfxSurface::desanalyse(const byte *&pSrc) {
int total = 0;
int v = suiv(pSrc);
if (!v) {
if (v == 15) {
int v2;
do {
v2 = suiv(pSrc);
total += v2;
} while (v2 == 15);
total *= 15;
@ -254,8 +416,151 @@ int GfxSurface::desanalyse(const byte *&pSrc) {
return total;
}
void GfxSurface::horizontal(const byte *&pSrc, byte *&pDest) {
void GfxSurface::horizontal(const byte *&pSrc, byte *&pDest, const byte *&pLookup) {
INCR_TAIX;
byte *pDestEnd = pDest + (_ySize - 1) * DEFAULT_WIDTH + _xSize;
for (;;) {
// If position is past end point, then skip this line
if (((_thickness - 1) * DEFAULT_WIDTH) + pDest >= pDestEnd) {
if (--_thickness == 0) break;
continue;
}
bool continueFlag = false;
do {
for (int xIndex = 0; xIndex < _xSize; ++xIndex, ++pDest) {
if ((xIndex % 2) == 0) {
// Write out vertical slice top to bottom
for (int yIndex = 0; yIndex < _thickness; ++yIndex, pDest += DEFAULT_WIDTH)
*pDest = csuiv(pSrc, pLookup);
} else {
// Write out vertical slice bottom to top
for (int yIndex = 0; yIndex < _thickness; ++yIndex, pDest -= DEFAULT_WIDTH)
*pDest = csuiv(pSrc, pLookup);
}
}
if ((_xSize % 2) == 0) {
int blockSize = _thickness * DEFAULT_WIDTH;
pDest += blockSize;
blockSize -= DEFAULT_WIDTH;
if (pDestEnd < (pDest + blockSize)) {
do {
if (--_thickness == 0)
return;
} while ((pDest + (_thickness - 1) * DEFAULT_WIDTH) >= pDestEnd);
}
} else {
while ((pDest + (_thickness - 1) * DEFAULT_WIDTH) >= pDestEnd) {
if (--_thickness == 0)
return;
}
}
for (int xIndex = 0; xIndex < _xSize; ++xIndex, --pDest) {
if ((xIndex % 2) == 0) {
// Write out vertical slice top to bottom
for (int yIndex = 0; yIndex < _thickness; ++yIndex, pDest += DEFAULT_WIDTH)
*pDest = csuiv(pSrc, pLookup);
} else {
// Write out vertical slice top to bottom
for (int yIndex = 0; yIndex < _thickness; ++yIndex, pDest -= DEFAULT_WIDTH)
*pDest = csuiv(pSrc, pLookup);
}
}
if ((_xSize % 2) == 1) {
++pDest;
if ((pDest + (_thickness - 1) * DEFAULT_WIDTH) < pDestEnd) {
continueFlag = true;
break;
}
} else {
pDest += _thickness * DEFAULT_WIDTH + 1;
continueFlag = true;
break;
}
++pDest;
} while (((_thickness - 1) * DEFAULT_WIDTH + pDest) < pDestEnd);
if (continueFlag)
continue;
// Move to next line
if (--_thickness == 0)
break;
}
}
void GfxSurface::vertical(const byte *&pSrc, byte *&pDest, const byte *&pLookup) {
// byte *pDestEnd = pDest + (_ySize - 1) * DEFAULT_WIDTH + _xSize;
int var28 = 0;
for (;;) {
// Reduce thickness as necessary
while ((var28 + _thickness) >= _xSize) {
if (--_thickness == 0)
return;
}
// Loop
for (int idx = 0; idx < _thickness; ++idx) {
if ((idx % 2) == 0) {
if (idx > 0)
pDest -= DEFAULT_WIDTH;
// Write out horizontal slice left to right
var28 += _thickness;
for (int xIndex = 0; xIndex < _thickness; ++xIndex)
*pDest++ = suiv(pSrc);
} else {
// Write out horizontal slice right to left
pDest += DEFAULT_WIDTH;
var28 -= _thickness;
for (int xIndex = 0; xIndex < _thickness; ++xIndex)
*pDest-- = csuiv(pSrc, pLookup);
}
}
if ((_thickness % 2) == 0) {
pDest += _thickness;
var28 += _thickness;
}
// Reduce thickness as necessary
while ((var28 + _thickness) < _xSize) {
if (--_thickness == 0)
return;
}
for (int yIndex = 0; yIndex < _ySize; ++yIndex) {
if ((yIndex % 2) == 0) {
if (yIndex > 0)
pDest -= DEFAULT_WIDTH;
// Draw horizontal slice
var28 += _thickness;
for (int xIndex = 0; xIndex < _thickness; ++xIndex)
*pDest++ = suiv(pSrc);
} else {
pDest -= DEFAULT_WIDTH;
var28 -= _thickness;
for (int xIndex = 0; xIndex < _thickness; ++xIndex)
*pDest-- = csuiv(pSrc, pLookup);
}
}
}
}
void GfxSurface::diag() {
// The diag method in the original source doesn't seem to have any exit point,
// which if the case, means the routine may not be used by the game
error("Non-exitable method diag() called");
}
} // End of namespace Mortevielle

View File

@ -31,20 +31,22 @@ namespace Mortevielle {
class GfxSurface: public Graphics::Surface {
private:
int _var1;
int _entryCount;
int _var4, _var6;
int _xp, _yp;
int _varC, _xSize, _ySize, _var12;
int _var14, _var15, _var18, _var1A;
int _var1C, _var1E, _var20, _var22;
int _var24, _var26, _var28;
int _xSize, _ySize, _var12;
int _var14, _var18, _lookupValue;
bool _nibbleFlag;
int _thickness, _var1E, _var20, _var22;
int _var24, _var26;
int _width, _height;
int _xOffset, _yOffset;
void majTtxTty();
byte suiv(const byte *&pSrc);
byte csuiv(const byte *&pSrc, const byte *&pLookup);
int desanalyse(const byte *&pSrc);
void horizontal(const byte *&pSrc, byte *&pDest);
void horizontal(const byte *&pSrc, byte *&pDest, const byte *&pLookup);
void vertical(const byte *&pSrc, byte *&pDest, const byte *&pLookup);
void diag();
public:
void decode(const byte *pSrc);
};

View File

@ -33,7 +33,8 @@
namespace Mortevielle {
enum {
kMortevielleCore = 1 << 0
kMortevielleCore = 1 << 0,
kMortevielleGraphics = 1 << 1
};
#define SCREEN_WIDTH 640