COMMON: Uplift rnc decoder from toon to common

Toon has a RNC implementation with 2 differences from the one taken from SKY:

1. Protection against overread. Add it to common version and adjust callers
   in sky.
2. Lack of key parameter for obfuscation. We ignore it anyway and don't
   support obfuscation, so delete it in common version as well
This commit is contained in:
Vladimir Serbinenko 2022-11-25 02:43:21 +01:00
parent 074888e6b8
commit c5373134d2
7 changed files with 187 additions and 467 deletions

View file

@ -37,6 +37,13 @@ namespace Common {
RncDecoder::RncDecoder() {
initCrc();
_bitBuffl = 0;
_bitBuffh = 0;
_bitCount = 0;
_srcPtr = nullptr;
_dstPtr = nullptr;
_inputByteLeft = 0;
}
RncDecoder::~RncDecoder() { }
@ -94,6 +101,15 @@ uint16 RncDecoder::inputBits(uint8 amount) {
newBitBuffl >>= newBitCount;
newBitBuffl |= remBits;
_srcPtr += 2;
// added some more check here to prevent reading in the buffer
// if there are no bytes anymore.
_inputByteLeft -= 2;
if (_inputByteLeft <= 0)
newBitBuffh = 0;
else if (_inputByteLeft == 1)
newBitBuffh = *_srcPtr;
else
newBitBuffh = READ_LE_UINT16(_srcPtr);
amount -= newBitCount;
newBitCount = 16 - amount;
@ -161,7 +177,18 @@ uint16 RncDecoder::inputValue(uint16 *table) {
return value;
}
int32 RncDecoder::unpackM1(const void *input, void *output, uint16 key) {
int RncDecoder::getbit() {
if (_bitCount == 0) {
_bitBuffl = *_srcPtr++;
_bitCount = 8;
}
byte temp = (_bitBuffl & 0x80) >> 7;
_bitBuffl <<= 1;
_bitCount--;
return temp;
}
int32 RncDecoder::unpackM1(const void *input, uint inputSize, void *output) {
uint8 *outputLow, *outputHigh;
const uint8 *inputHigh, *inputptr = (const uint8 *)input;
@ -172,12 +199,13 @@ int32 RncDecoder::unpackM1(const void *input, void *output, uint16 key) {
uint16 crcPacked = 0;
_inputByteLeft = inputSize;
_bitBuffl = 0;
_bitBuffh = 0;
_bitCount = 0;
//Check for "RNC "
if (READ_BE_UINT32(inputptr) != kRncSignature)
if (READ_BE_UINT32(inputptr) != kRnc1Signature)
return NOT_PACKED;
inputptr += 4;
@ -210,6 +238,8 @@ int32 RncDecoder::unpackM1(const void *input, void *output, uint16 key) {
_srcPtr = (_dstPtr-packLen);
}
_inputByteLeft -= HEADER_LEN;
_dstPtr = (uint8 *)output;
_bitCount = 0;
@ -228,11 +258,28 @@ int32 RncDecoder::unpackM1(const void *input, void *output, uint16 key) {
uint32 inputOffset;
if (inputLength) {
if (_inputByteLeft < (int32) inputLength || inputLength > 0xff000000) {
return NOT_PACKED;
}
memcpy(_dstPtr, _srcPtr, inputLength); //memcpy is allowed here
_dstPtr += inputLength;
_srcPtr += inputLength;
uint16 a = READ_LE_UINT16(_srcPtr);
uint16 b = READ_LE_UINT16(_srcPtr + 2);
_inputByteLeft -= inputLength;
uint16 a;
if (_inputByteLeft <= 0)
a = 0;
else if (_inputByteLeft == 1)
a = *_srcPtr;
else
a = READ_LE_UINT16(_srcPtr);
uint16 b;
if (_inputByteLeft <= 2)
b = 0;
else if (_inputByteLeft == 3)
b = *(_srcPtr + 2);
else
b = READ_LE_UINT16(_srcPtr + 2);
_bitBuffl &= ((1 << _bitCount) - 1);
_bitBuffl |= (a << _bitCount);
@ -258,4 +305,124 @@ int32 RncDecoder::unpackM1(const void *input, void *output, uint16 key) {
return unpackLen;
}
int32 RncDecoder::unpackM2(const void *input, void *output) {
const uint8 *inputptr = (const uint8 *)input;
uint32 unpackLen = 0;
uint32 packLen = 0;
uint16 crcUnpacked = 0;
uint16 crcPacked = 0;
_bitBuffl = 0;
_bitCount = 0;
// Check for "RNC "
if (READ_BE_UINT32(inputptr) != kRnc2Signature)
return NOT_PACKED;
inputptr += 4;
// Read unpacked/packed file length
unpackLen = READ_BE_UINT32(inputptr);
inputptr += 4;
packLen = READ_BE_UINT32(inputptr);
inputptr += 4;
// Read CRCs
crcUnpacked = READ_BE_UINT16(inputptr);
inputptr += 2;
crcPacked = READ_BE_UINT16(inputptr);
inputptr += 2;
inputptr = (inputptr + HEADER_LEN - 16);
if (crcBlock(inputptr, packLen) != crcPacked)
return PACKED_CRC;
inputptr = (((const uint8 *)input) + HEADER_LEN);
_srcPtr = inputptr;
_dstPtr = (uint8 *)output;
uint16 ofs, len;
byte ofs_hi, ofs_lo;
len = 0;
ofs_hi = 0;
ofs_lo = 0;
getbit();
getbit();
while (1) {
bool loadVal = false;
while (getbit() == 0)
*_dstPtr++ = *_srcPtr++;
len = 2;
ofs_hi = 0;
if (getbit() == 0) {
len = (len << 1) | getbit();
if (getbit() == 1) {
len--;
len = (len << 1) | getbit();
if (len == 9) {
len = 4;
while (len--)
ofs_hi = (ofs_hi << 1) | getbit();
len = (ofs_hi + 3) * 4;
while (len--)
*_dstPtr++ = *_srcPtr++;
continue;
}
}
loadVal = true;
} else {
if (getbit() == 1) {
len++;
if (getbit() == 1) {
len = *_srcPtr++;
if (len == 0) {
if (getbit() == 1)
continue;
else
break;
}
len += 8;
}
loadVal = true;
} else {
loadVal = false;
}
}
if (loadVal) {
if (getbit() == 1) {
ofs_hi = (ofs_hi << 1) | getbit();
if (getbit() == 1) {
ofs_hi = ((ofs_hi << 1) | getbit()) | 4;
if (getbit() == 0)
ofs_hi = (ofs_hi << 1) | getbit();
} else if (ofs_hi == 0) {
ofs_hi = 2 | getbit();
}
}
}
ofs_lo = *_srcPtr++;
ofs = (ofs_hi << 8) | ofs_lo;
while (len--) {
*_dstPtr = *(byte *)(_dstPtr - ofs - 1);
_dstPtr++;
}
}
if (crcBlock((uint8 *)output, unpackLen) != crcUnpacked)
return UNPACKED_CRC;
// all is done..return the amount of unpacked bytes
return unpackLen;
}
} // End of namespace Common

View file

@ -41,12 +41,16 @@ protected:
const uint8 *_srcPtr;
uint8 *_dstPtr;
int32 _inputByteLeft;
public:
RncDecoder();
~RncDecoder();
int32 unpackM1(const void *input, void *output, uint16 key);
int32 unpackM1(const void *input, uint inputSize, void *output);
int32 unpackM2(const void *input, void *output);
static const uint32 kRncSignature = 0x524E4301; // "RNC\001"
static const uint32 kRnc1Signature = 0x524E4301; // "RNC\001"
static const uint32 kRnc2Signature = 0x524E4302; // "RNC\002"
protected:
void initCrc();
@ -54,7 +58,7 @@ protected:
uint16 inputBits(uint8 amount);
void makeHufftable(uint16 *table);
uint16 inputValue(uint16 *table);
int getbit();
};
} // End of namespace Common

View file

@ -132,7 +132,7 @@ uint8 *Disk::loadFile(uint16 fileNr) {
if ((fileFlags >> 22) & 0x1) { //do we include the header?
// don't return the file's header
output = uncompDest;
unpackLen = _rncDecoder.unpackM1(input, output, 0);
unpackLen = _rncDecoder.unpackM1(input, fileSize - sizeof(DataFileHeader), output);
} else {
#ifdef SCUMM_BIG_ENDIAN
// Convert DataFileHeader to BE (it only consists of 16 bit words)
@ -143,7 +143,7 @@ uint8 *Disk::loadFile(uint16 fileNr) {
memcpy(uncompDest, fileDest, sizeof(DataFileHeader));
output = uncompDest + sizeof(DataFileHeader);
unpackLen = _rncDecoder.unpackM1(input, output, 0);
unpackLen = _rncDecoder.unpackM1(input, fileSize - sizeof(DataFileHeader), output);
if (unpackLen)
unpackLen += sizeof(DataFileHeader);
}

View file

@ -20,9 +20,9 @@
*/
#include "common/debug.h"
#include "common/rnc_deco.h"
#include "toon/hotspot.h"
#include "toon/tools.h"
namespace Toon {
@ -123,10 +123,10 @@ bool Hotspots::loadRif(const Common::String &rifName, const Common::String &addi
_items = new HotspotData[_numItems];
// RIFs are compressed in RNC1
RncDecoder decoder;
Common::RncDecoder decoder;
decoder.unpackM1(rifData, size, _items);
if (rifsize2) {
RncDecoder decoder2;
Common::RncDecoder decoder2;
decoder2.unpackM1(rifData2 , size2, _items + (rifsize >> 9));
for (int32 i = 0; i < (rifsize2 >> 9); i++) {
HotspotData *hot = _items + (rifsize >> 9) + i;

View file

@ -24,6 +24,7 @@
#include "common/debug.h"
#include "common/rect.h"
#include "common/rnc_deco.h"
#include "common/stack.h"
namespace Toon {
@ -92,7 +93,7 @@ bool Picture::loadPicture(const Common::String &file) {
return true;
}
case kCompRNC1: {
Toon::RncDecoder rnc;
Common::RncDecoder rnc;
// allocate enough place
uint32 decSize = READ_BE_UINT32(fileData + 4);
@ -111,7 +112,7 @@ bool Picture::loadPicture(const Common::String &file) {
return true;
}
case kCompRNC2: {
Toon::RncDecoder rnc;
Common::RncDecoder rnc;
// allocate enough place
uint32 decSize = READ_BE_UINT32(fileData + 4);

View file

@ -123,421 +123,4 @@ uint32 decompressSPCN(byte *src, byte *dst, uint32 dstsize) {
return (dstp - dst);
}
//return codes
#define NOT_PACKED 0
#define PACKED_CRC -1
#define UNPACKED_CRC -2
//other defines
#define TABLE_SIZE (16 * 8)
#define MIN_LENGTH 2
#define HEADER_LEN 18
RncDecoder::RncDecoder() {
initCrc();
_bitBuffl = 0;
_bitBuffh = 0;
_bitCount = 0;
_srcPtr = nullptr;
_dstPtr = nullptr;
_inputByteLeft = 0;
}
RncDecoder::~RncDecoder() { }
void RncDecoder::initCrc() {
debugC(1, kDebugTools, "initCrc()");
uint16 cnt = 0;
uint16 tmp1 = 0;
uint16 tmp2 = 0;
for (tmp2 = 0; tmp2 < 0x100; tmp2++) {
tmp1 = tmp2;
for (cnt = 8; cnt > 0; cnt--) {
if (tmp1 % 2) {
tmp1 >>= 1;
tmp1 ^= 0x0a001;
} else
tmp1 >>= 1;
}
_crcTable[tmp2] = tmp1;
}
}
//calculate 16 bit crc of a block of memory
uint16 RncDecoder::crcBlock(const uint8 *block, uint32 size) {
debugC(1, kDebugTools, "crcBlock(block, %d)", size);
uint16 crc = 0;
uint8 *crcTable8 = (uint8 *)_crcTable; //make a uint8* to crc_table
uint8 tmp;
uint32 i;
for (i = 0; i < size; i++) {
tmp = *block++;
crc ^= tmp;
tmp = (uint8)((crc >> 8) & 0x00FF);
crc &= 0x00FF;
crc = *(uint16 *)&crcTable8[crc << 1];
crc ^= tmp;
}
return crc;
}
uint16 RncDecoder::inputBits(uint8 amount) {
debugC(5, kDebugTools, "inputBits(%d)", amount);
uint16 newBitBuffh = _bitBuffh;
uint16 newBitBuffl = _bitBuffl;
int16 newBitCount = _bitCount;
uint16 remBits, returnVal;
returnVal = ((1 << amount) - 1) & newBitBuffl;
newBitCount -= amount;
if (newBitCount < 0) {
newBitCount += amount;
remBits = (newBitBuffh << (16 - newBitCount));
newBitBuffh >>= newBitCount;
newBitBuffl >>= newBitCount;
newBitBuffl |= remBits;
_srcPtr += 2;
// added some more check here to prevent reading in the buffer
// if there are no bytes anymore.
_inputByteLeft -= 2;
if (_inputByteLeft <= 0)
newBitBuffh = 0;
else if (_inputByteLeft == 1)
newBitBuffh = *_srcPtr;
else
newBitBuffh = READ_LE_UINT16(_srcPtr);
amount -= newBitCount;
newBitCount = 16 - amount;
}
remBits = (newBitBuffh << (16 - amount));
_bitBuffh = newBitBuffh >> amount;
_bitBuffl = (newBitBuffl >> amount) | remBits;
_bitCount = (uint8)newBitCount;
return returnVal;
}
void RncDecoder::makeHufftable(uint16 *table) {
debugC(1, kDebugTools, "makeHufftable(table)");
uint16 bitLength, i, j;
uint16 numCodes = inputBits(5);
if (!numCodes)
return;
uint8 huffLength[16];
for (i = 0; i < numCodes; i++)
huffLength[i] = (uint8)(inputBits(4) & 0x00FF);
uint16 huffCode = 0;
for (bitLength = 1; bitLength < 17; bitLength++) {
for (i = 0; i < numCodes; i++) {
if (huffLength[i] == bitLength) {
*table++ = (1 << bitLength) - 1;
uint16 b = huffCode >> (16 - bitLength);
uint16 a = 0;
for (j = 0; j < bitLength; j++)
a |= ((b >> j) & 1) << (bitLength - j - 1);
*table++ = a;
*(table + 0x1e) = (huffLength[i] << 8) | (i & 0x00FF);
huffCode += 1 << (16 - bitLength);
}
}
}
}
uint16 RncDecoder::inputValue(uint16 *table) {
debugC(5, kDebugTools, "inputValue(table)");
uint16 valOne, valTwo, value = _bitBuffl;
do {
valTwo = (*table++) & value;
valOne = *table++;
} while (valOne != valTwo);
value = *(table + 0x1e);
inputBits((uint8)((value >> 8) & 0x00FF));
value &= 0x00FF;
if (value >= 2) {
value--;
valOne = inputBits((uint8)value & 0x00FF);
valOne |= (1 << value);
value = valOne;
}
return value;
}
int RncDecoder::getbit() {
debugC(6, kDebugTools, "getbits()");
if (_bitCount == 0) {
_bitBuffl = *_srcPtr++;
_bitCount = 8;
}
byte temp = (_bitBuffl & 0x80) >> 7;
_bitBuffl <<= 1;
_bitCount--;
return temp;
}
int32 RncDecoder::unpackM1(const void *input, uint16 inputSize, void *output) {
debugC(1, kDebugTools, "unpackM1(input, output)");
uint8 *outputLow, *outputHigh;
const uint8 *inputHigh, *inputptr = (const uint8 *)input;
uint32 unpackLen = 0;
uint32 packLen = 0;
uint16 counts = 0;
uint16 crcUnpacked = 0;
uint16 crcPacked = 0;
_inputByteLeft = inputSize;
_bitBuffl = 0;
_bitBuffh = 0;
_bitCount = 0;
//Check for "RNC "
if (READ_BE_UINT32(inputptr) != RNC1_SIGNATURE)
return NOT_PACKED;
inputptr += 4;
// read unpacked/packed file length
unpackLen = READ_BE_UINT32(inputptr);
inputptr += 4;
packLen = READ_BE_UINT32(inputptr);
inputptr += 4;
uint8 blocks = *(inputptr + 5);
//read CRC's
crcUnpacked = READ_BE_UINT16(inputptr);
inputptr += 2;
crcPacked = READ_BE_UINT16(inputptr);
inputptr += 2;
inputptr = (inputptr + HEADER_LEN - 16);
if (crcBlock(inputptr, packLen) != crcPacked)
return PACKED_CRC;
inputptr = (((const uint8 *)input) + HEADER_LEN);
_srcPtr = inputptr;
inputHigh = ((const uint8 *)input) + packLen + HEADER_LEN;
outputLow = (uint8 *)output;
outputHigh = *(((const uint8 *)input) + 16) + unpackLen + outputLow;
if (!((inputHigh <= outputLow) || (outputHigh <= inputHigh))) {
_srcPtr = inputHigh;
_dstPtr = outputHigh;
memcpy((_dstPtr - packLen), (_srcPtr - packLen), packLen);
_srcPtr = (_dstPtr - packLen);
}
_inputByteLeft -= HEADER_LEN;
_dstPtr = (uint8 *)output;
_bitCount = 0;
_bitBuffl = READ_LE_UINT16(_srcPtr);
inputBits(2);
do {
makeHufftable(_rawTable);
makeHufftable(_posTable);
makeHufftable(_lenTable);
counts = inputBits(16);
do {
uint32 inputLength = inputValue(_rawTable);
uint32 inputOffset;
if (inputLength) {
memcpy(_dstPtr, _srcPtr, inputLength); //memcpy is allowed here
_dstPtr += inputLength;
_srcPtr += inputLength;
_inputByteLeft -= inputLength;
uint16 a;
if (_inputByteLeft <= 0)
a = 0;
else if (_inputByteLeft == 1)
a = *_srcPtr;
else
a = READ_LE_UINT16(_srcPtr);
uint16 b;
if (_inputByteLeft <= 2)
b = 0;
else if (_inputByteLeft == 3)
b = *(_srcPtr + 2);
else
b = READ_LE_UINT16(_srcPtr + 2);
_bitBuffl &= ((1 << _bitCount) - 1);
_bitBuffl |= (a << _bitCount);
_bitBuffh = (a >> (16 - _bitCount)) | (b << _bitCount);
}
if (counts > 1) {
inputOffset = inputValue(_posTable) + 1;
inputLength = inputValue(_lenTable) + MIN_LENGTH;
// Don't use memcpy here! because input and output overlap.
uint8 *tmpPtr = (_dstPtr - inputOffset);
while (inputLength--)
*_dstPtr++ = *tmpPtr++;
}
} while (--counts);
} while (--blocks);
if (crcBlock((uint8 *)output, unpackLen) != crcUnpacked)
return UNPACKED_CRC;
// all is done..return the amount of unpacked bytes
return unpackLen;
}
int32 RncDecoder::unpackM2(const void *input, void *output) {
debugC(1, kDebugTools, "unpackM2(input, output)");
const uint8 *inputptr = (const uint8 *)input;
uint32 unpackLen = 0;
uint32 packLen = 0;
uint16 crcUnpacked = 0;
uint16 crcPacked = 0;
_bitBuffl = 0;
_bitCount = 0;
//Check for "RNC "
if (READ_BE_UINT32(inputptr) != RNC2_SIGNATURE)
return NOT_PACKED;
inputptr += 4;
// read unpacked/packed file length
unpackLen = READ_BE_UINT32(inputptr);
inputptr += 4;
packLen = READ_BE_UINT32(inputptr);
inputptr += 4;
//read CRC's
crcUnpacked = READ_BE_UINT16(inputptr);
inputptr += 2;
crcPacked = READ_BE_UINT16(inputptr);
inputptr += 2;
inputptr = (inputptr + HEADER_LEN - 16);
if (crcBlock(inputptr, packLen) != crcPacked)
return PACKED_CRC;
inputptr = (((const uint8 *)input) + HEADER_LEN);
_srcPtr = inputptr;
_dstPtr = (uint8 *)output;
uint16 ofs, len;
byte ofs_hi, ofs_lo;
len = 0;
ofs_hi = 0;
ofs_lo = 0;
getbit();
getbit();
while (1) {
bool loadVal = false;
while (getbit() == 0)
*_dstPtr++ = *_srcPtr++;
len = 2;
ofs_hi = 0;
if (getbit() == 0) {
len = (len << 1) | getbit();
if (getbit() == 1) {
len--;
len = (len << 1) | getbit();
if (len == 9) {
len = 4;
while (len--)
ofs_hi = (ofs_hi << 1) | getbit();
len = (ofs_hi + 3) * 4;
while (len--)
*_dstPtr++ = *_srcPtr++;
continue;
}
}
loadVal = true;
} else {
if (getbit() == 1) {
len++;
if (getbit() == 1) {
len = *_srcPtr++;
if (len == 0) {
if (getbit() == 1)
continue;
else
break;
}
len += 8;
}
loadVal = true;
} else {
loadVal = false;
}
}
if (loadVal) {
if (getbit() == 1) {
ofs_hi = (ofs_hi << 1) | getbit();
if (getbit() == 1) {
ofs_hi = ((ofs_hi << 1) | getbit()) | 4;
if (getbit() == 0)
ofs_hi = (ofs_hi << 1) | getbit();
} else if (ofs_hi == 0) {
ofs_hi = 2 | getbit();
}
}
}
ofs_lo = *_srcPtr++;
ofs = (ofs_hi << 8) | ofs_lo;
while (len--) {
*_dstPtr = *(byte *)(_dstPtr - ofs - 1);
_dstPtr++;
}
}
if (crcBlock((uint8 *)output, unpackLen) != crcUnpacked)
return UNPACKED_CRC;
// all is done..return the amount of unpacked bytes
return unpackLen;
}
} // End of namespace Toon

View file

@ -25,9 +25,6 @@
#include "common/scummsys.h"
#include "common/endian.h"
#define RNC1_SIGNATURE 0x524E4301 // "RNC\001"
#define RNC2_SIGNATURE 0x524E4302 // "RNC\002"
namespace Toon {
const uint32 kCompLZSS = 0x4C5A5353;
@ -38,38 +35,6 @@ const uint32 kCompRNC2 = 0x524E4302;
uint32 decompressSPCN(byte *src, byte *dst, uint32 dstsize);
uint32 decompressLZSS(byte *src, byte *dst, int dstsize);
class RncDecoder {
protected:
uint16 _rawTable[64];
uint16 _posTable[64];
uint16 _lenTable[64];
uint16 _crcTable[256];
uint16 _bitBuffl;
uint16 _bitBuffh;
uint8 _bitCount;
const uint8 *_srcPtr;
uint8 *_dstPtr;
int16 _inputByteLeft;
public:
RncDecoder();
~RncDecoder();
int32 unpackM1(const void *input, uint16 inputSize, void *output);
int32 unpackM2(const void *input, void *output);
protected:
void initCrc();
uint16 crcBlock(const uint8 *block, uint32 size);
uint16 inputBits(uint8 amount);
void makeHufftable(uint16 *table);
uint16 inputValue(uint16 *table);
int getbit();
};
} // End of namespace Toon
#endif