Files
Aerofoil/unpacktool/PrefixCode.cpp
2024-07-31 20:23:34 +12:00

451 lines
11 KiB
C++

#include <limits.h>
#include "PrefixCode.h"
struct XADCodeTreeNode
{
int branches[2];
};
struct XADCodeTableEntry
{
uint32_t length;
int32_t value;
};
static inline XADCodeTreeNode *NodePointer(XADPrefixCode *self, int node) { return &self->tree[node]; }
static inline int Branch(XADPrefixCode *self, int node, int bit) { return NodePointer(self, node)->branches[bit]; }
static inline void SetBranch(XADPrefixCode *self, int node, int bit, int nextnode) { NodePointer(self, node)->branches[bit] = nextnode; }
static inline int LeftBranch(XADPrefixCode *self, int node) { return Branch(self, node, 0); }
static inline int RightBranch(XADPrefixCode *self, int node) { return Branch(self, node, 1); }
static inline void SetLeftBranch(XADPrefixCode *self, int node, int nextnode) { SetBranch(self, node, 0, nextnode); }
static inline void SetRightBranch(XADPrefixCode *self, int node, int nextnode) { SetBranch(self, node, 1, nextnode); }
static inline int LeafValue(XADPrefixCode *self, int node) { return LeftBranch(self, node); }
static inline void SetLeafValue(XADPrefixCode *self, int node, int value) { SetLeftBranch(self, node, value); SetRightBranch(self, node, value); }
static inline void SetEmptyNode(XADPrefixCode *self, int node) { SetLeftBranch(self, node, -1); SetRightBranch(self, node, -2); }
static inline bool IsInvalidNode(XADPrefixCode *self, int node) { return node < 0; }
static inline bool IsOpenBranch(XADPrefixCode *self, int node, int bit) { return IsInvalidNode(self, Branch(self, node, bit)); }
static inline bool IsEmptyNode(XADPrefixCode *self, int node) { return LeftBranch(self, node) == -1 && RightBranch(self, node) == -2; }
static inline bool IsLeafNode(XADPrefixCode *self, int node) { return LeftBranch(self, node) == RightBranch(self, node); }
static inline int NewNode(XADPrefixCode *self)
{
self->tree.resize(self->numentries + 1);
SetEmptyNode(self, self->numentries);
return self->numentries++;
}
bool CSInputNextSymbolUsingCode(CSInputBuffer *buf, XADPrefixCode *code, int &outSymbol)
{
if (!code->table1)
code->_makeTable();
unsigned int bits;
if (!CSInputPeekBitString(buf, code->tablesize, bits))
return false;
int length = code->table1[bits].length;
int value = code->table1[bits].value;
if (length < 0)
return false;
if (length <= code->tablesize)
{
if (!CSInputSkipPeekedBits(buf, length))
return false;
outSymbol = value;
return true;
}
if (!CSInputSkipPeekedBits(buf, code->tablesize))
return false;
int node = value;
while (!IsLeafNode(code, node))
{
unsigned int bit;
if (!CSInputNextBit(buf, bit))
return false;
if (IsOpenBranch(code, node, bit))
return false;
node = Branch(code, node, bit);
}
outSymbol = LeafValue(code, node);
return true;
}
bool CSInputNextSymbolUsingCodeLE(CSInputBuffer *buf, XADPrefixCode *code, int &outSymbol)
{
if (!code->table2)
code->_makeTableLE();
unsigned int bits;
if (!CSInputPeekBitStringLE(buf, code->tablesize, bits))
return false;
int length = code->table2[bits].length;
int value = code->table2[bits].value;
if (length < 0)
return false;
if (length <= code->tablesize)
{
if (!CSInputSkipPeekedBitsLE(buf, length))
return false;
outSymbol = value;
return true;
}
if (!CSInputSkipPeekedBitsLE(buf, code->tablesize))
return false;
int node = value;
while (!IsLeafNode(code, node))
{
unsigned int bit;
if (!CSInputNextBitLE(buf, bit))
return false;
if (IsOpenBranch(code, node, bit))
return false;
node = Branch(code, node, bit);
}
outSymbol = LeafValue(code, node);
return true;
}
/*int CSInputNextSymbolUsingCode(CSInputBuffer *buf,XADPrefixCode *code)
{
int node=0;
while(!IsLeafNode(code,node))
{
int bit=CSInputNextBit(buf);
if(IsOpenBranch(code,node,bit)) [NSException raise:XADInvalidPrefixCodeException format:@"Invalid prefix code in bitstream"];
node=Branch(code,node,bit);
}
return LeafValue(code,node);
}
int CSInputNextSymbolUsingCodeLE(CSInputBuffer *buf,XADPrefixCode *code)
{
int node=0;
while(!IsLeafNode(code,node))
{
int bit=CSInputNextBitLE(buf);
if(IsOpenBranch(code,node,bit)) [NSException raise:XADInvalidPrefixCodeException format:@"Invalid prefix code in bitstream"];
node=Branch(code,node,bit);
}
return LeafValue(code,node);
}*/
XADPrefixCode *XADPrefixCode::prefixCode()
{
XADPrefixCode *self = new XADPrefixCode();
self->init();
return self;
}
XADPrefixCode *XADPrefixCode::prefixCodeWithLengths(const int *lengths, int numsymbols, int maxlength, bool zeros)
{
XADPrefixCode *self = prefixCode();
if (!self->initWithLengths(lengths, numsymbols, maxlength, zeros))
{
delete self;
self = nullptr;
}
return self;
}
void XADPrefixCode::init()
{
tree.resize(1);
SetEmptyNode(this, 0);
numentries = 1;
minlength = INT_MAX;
maxlength = INT_MIN;
isstatic = false;
table1 = table2 = NULL;
}
bool XADPrefixCode::initWithLengths(const int *lengths, int numsymbols, int maxcodelength, bool zeros)
{
int code = 0, symbolsleft = numsymbols;
for (int length = 1; length <= maxcodelength; length++)
{
for (int i = 0; i < numsymbols; i++)
{
if (lengths[i] != length)
continue;
// Instead of reversing to get a low-bit-first code, we shift and use high-bit-first.
if (zeros)
{
if (!this->addValueHighBitFirst(i, code, length))
return false;
}
else
{
if (!this->addValueHighBitFirst(i, ~code, length))
return false;
}
code++;
if (--symbolsleft == 0)
return true; // early exit if all codes have been handled
}
code <<= 1;
}
return true;
}
XADPrefixCode::XADPrefixCode()
: numentries(0)
, minlength(0)
, maxlength(0)
, isstatic(false)
, currnode(0)
, tablesize(0)
, table1(nullptr)
, table2(nullptr)
{
}
XADPrefixCode::~XADPrefixCode()
{
delete[] table1;
delete[] table2;
}
bool XADPrefixCode::addValueHighBitFirst(int value, uint32_t code, int length)
{
return this->addValueHighBitFirst(value, code, length, length);
}
bool XADPrefixCode::addValueHighBitFirst(int value, uint32_t code, int length, int repeatpos)
{
if (isstatic)
return false;
delete[] table1;
delete[] table2;
table1 = table2 = NULL;
if (length > maxlength) maxlength = length;
if (length < minlength) minlength = length;
repeatpos = length - 1 - repeatpos;
if (repeatpos == 0 || (repeatpos >= 0 && (((code >> (repeatpos - 1)) & 3) == 0 || ((code >> (repeatpos - 1)) & 3) == 3)))
return false;
int lastnode = 0;
for (int bitpos = length - 1; bitpos >= 0; bitpos--)
{
int bit = (code >> bitpos) & 1;
if (IsLeafNode(this, lastnode))
return false;
if (bitpos == repeatpos)
{
if (!IsOpenBranch(this, lastnode, bit))
return false;
int repeatnode = NewNode(this);
int nextnode = NewNode(this);
SetBranch(this, lastnode, bit, repeatnode);
SetBranch(this, repeatnode, bit, repeatnode);
SetBranch(this, repeatnode, bit ^ 1, nextnode);
lastnode = nextnode;
bitpos++; // terminating bit already handled, skip it
}
else
{
if (IsOpenBranch(this, lastnode, bit))
SetBranch(this, lastnode, bit, NewNode(this));
lastnode = Branch(this, lastnode, bit);
}
}
if (!IsEmptyNode(this, lastnode))
return false;
SetLeafValue(this, lastnode, value);
return true;
}
static uint32_t Reverse32(uint32_t val)
{
val = ((val >> 1) & 0x55555555) | ((val & 0x55555555) << 1);
val = ((val >> 2) & 0x33333333) | ((val & 0x33333333) << 2);
val = ((val >> 4) & 0x0F0F0F0F) | ((val & 0x0F0F0F0F) << 4);
val = ((val >> 8) & 0x00FF00FF) | ((val & 0x00FF00FF) << 8);
return (val >> 16) | (val << 16);
}
static uint32_t ReverseN(uint32_t val, int length)
{
return Reverse32(val) >> (32 - length);
}
bool XADPrefixCode::addValueLowBitFirst(int value, uint32_t code, int length)
{
return this->addValueHighBitFirst(value, ReverseN(code, length), length, length);
}
bool XADPrefixCode::addValueLowBitFirst(int value, uint32_t code, int length, int repeatpos)
{
return this->addValueHighBitFirst(value, ReverseN(code, length), length, repeatpos);
}
void XADPrefixCode::startBuildingTree()
{
currnode = 0;
stack.resize(0);
}
void XADPrefixCode::startZeroBranch()
{
int newNode = NewNode(this);
SetBranch(this, currnode, 0, newNode);
this->_pushNode();
currnode = newNode;
}
void XADPrefixCode::startOneBranch()
{
int newNode = NewNode(this);
SetBranch(this, currnode, 1, newNode);
this->_pushNode();
currnode = newNode;
}
void XADPrefixCode::finishBranches()
{
this->_popNode();
}
void XADPrefixCode::makeLeafWithValue(int value)
{
SetLeafValue(this, currnode, value);
this->_popNode();
}
void XADPrefixCode::_pushNode()
{
stack.push_back(currnode);
}
void XADPrefixCode::_popNode()
{
if (stack.size() == 0) return; // the final pop will underflow the stack otherwise
int num = stack.back();
stack.pop_back();
currnode = num;
}
static void MakeTable(XADPrefixCode *code, int node, XADCodeTableEntry *table, int depth, int maxdepth)
{
int currtablesize = 1 << (maxdepth - depth);
if (IsLeafNode(code, node))
{
for (int i = 0; i < currtablesize; i++)
{
table[i].length = depth;
table[i].value = LeafValue(code, node);
}
}
else if (IsInvalidNode(code, node))
{
for (int i = 0; i < currtablesize; i++) table[i].length = -1;
}
else
{
if (depth == maxdepth)
{
table[0].length = maxdepth + 1;
table[0].value = node;
}
else
{
MakeTable(code, LeftBranch(code, node), table, depth + 1, maxdepth);
MakeTable(code, RightBranch(code, node), table + currtablesize / 2, depth + 1, maxdepth);
}
}
}
static void MakeTableLE(XADPrefixCode *code, int node, XADCodeTableEntry *table, int depth, int maxdepth)
{
int currtablesize = 1 << (maxdepth - depth);
int currstride = 1 << depth;
if (IsLeafNode(code, node))
{
for (int i = 0; i < currtablesize; i++)
{
table[i*currstride].length = depth;
table[i*currstride].value = LeafValue(code, node);
}
}
else if (IsInvalidNode(code, node))
{
for (int i = 0; i < currtablesize; i++) table[i*currstride].length = -1;
}
else
{
if (depth == maxdepth)
{
table[0].length = maxdepth + 1;
table[0].value = node;
}
else
{
MakeTableLE(code, LeftBranch(code, node), table, depth + 1, maxdepth);
MakeTableLE(code, RightBranch(code, node), table + currstride, depth + 1, maxdepth);
}
}
}
#define TableMaxSize 10
void XADPrefixCode::_makeTable()
{
if (table1) return;
if (maxlength < minlength) tablesize = TableMaxSize; // no code lengths recorded
else if (maxlength >= TableMaxSize) tablesize = TableMaxSize;
else tablesize = maxlength;
table1 = new XADCodeTableEntry[1 << tablesize];
MakeTable(this, 0, table1, 0, tablesize);
}
void XADPrefixCode::_makeTableLE()
{
if (table2) return;
if (maxlength < minlength) tablesize = TableMaxSize; // no code lengths recorded
else if (maxlength >= TableMaxSize) tablesize = TableMaxSize;
else tablesize = maxlength;
table2 = new XADCodeTableEntry[1 << tablesize];
MakeTableLE(this, 0, table2, 0, tablesize);
}