mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-23 06:53:43 +00:00
Add unpacktool
This commit is contained in:
62
unpacktool/ArchiveDescription.cpp
Normal file
62
unpacktool/ArchiveDescription.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "ArchiveDescription.h"
|
||||
|
||||
ArchiveCompressedChunkDesc::ArchiveCompressedChunkDesc()
|
||||
: m_filePosition(0)
|
||||
, m_uncompressedSize(0)
|
||||
, m_compressedSize(0)
|
||||
, m_compressionMethod(CompressionMethods::kUnknown)
|
||||
{
|
||||
}
|
||||
|
||||
ArchiveItem::ArchiveItem()
|
||||
: m_isDirectory(false)
|
||||
, m_children(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ArchiveItem::ArchiveItem(ArchiveItem &&other)
|
||||
: m_isDirectory(other.m_isDirectory)
|
||||
, m_fileNameUTF8(other.m_fileNameUTF8)
|
||||
, m_macProperties(other.m_macProperties)
|
||||
, m_dataForkDesc(other.m_dataForkDesc)
|
||||
, m_resourceForkDesc(other.m_resourceForkDesc)
|
||||
, m_commentDesc(other.m_commentDesc)
|
||||
, m_children(other.m_children)
|
||||
{
|
||||
other.m_children = nullptr;
|
||||
}
|
||||
|
||||
ArchiveItem::ArchiveItem(const ArchiveItem &other)
|
||||
: m_isDirectory(other.m_isDirectory)
|
||||
, m_fileNameUTF8(other.m_fileNameUTF8)
|
||||
, m_macProperties(other.m_macProperties)
|
||||
, m_dataForkDesc(other.m_dataForkDesc)
|
||||
, m_resourceForkDesc(other.m_resourceForkDesc)
|
||||
, m_commentDesc(other.m_commentDesc)
|
||||
, m_children(other.m_children ? (new ArchiveItemList(*(other.m_children))) : nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ArchiveItem::~ArchiveItem()
|
||||
{
|
||||
if (m_children)
|
||||
delete m_children;
|
||||
}
|
||||
|
||||
ArchiveItemList::ArchiveItemList()
|
||||
{
|
||||
}
|
||||
|
||||
ArchiveItemList::ArchiveItemList(ArchiveItemList &&other)
|
||||
: m_items(std::move(other.m_items))
|
||||
{
|
||||
}
|
||||
|
||||
ArchiveItemList::ArchiveItemList(const ArchiveItemList &other)
|
||||
: m_items(other.m_items)
|
||||
{
|
||||
}
|
||||
|
||||
ArchiveItemList::~ArchiveItemList()
|
||||
{
|
||||
}
|
71
unpacktool/ArchiveDescription.h
Normal file
71
unpacktool/ArchiveDescription.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include "MacFileInfo.h"
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace CompressionMethods
|
||||
{
|
||||
enum CompressionMethod
|
||||
{
|
||||
kNone,
|
||||
kUnknown,
|
||||
|
||||
kStuffItRLE90,
|
||||
kStuffItLZW,
|
||||
kStuffItHuffman,
|
||||
kStuffItLZAH,
|
||||
kStuffItFixedHuffman,
|
||||
kStuffItMW,
|
||||
kStuffIt13,
|
||||
kStuffIt14,
|
||||
kStuffItArsenic,
|
||||
|
||||
kCompactProRLE,
|
||||
kCompactProLZHRLE,
|
||||
};
|
||||
}
|
||||
|
||||
typedef CompressionMethods::CompressionMethod CompressionMethod_t;
|
||||
|
||||
struct ArchiveItemList;
|
||||
|
||||
struct ArchiveCompressedChunkDesc
|
||||
{
|
||||
ArchiveCompressedChunkDesc();
|
||||
|
||||
uint64_t m_filePosition;
|
||||
size_t m_uncompressedSize;
|
||||
size_t m_compressedSize;
|
||||
CompressionMethod_t m_compressionMethod;
|
||||
};
|
||||
|
||||
struct ArchiveItem
|
||||
{
|
||||
ArchiveItem();
|
||||
ArchiveItem(ArchiveItem &&other);
|
||||
ArchiveItem(const ArchiveItem &other);
|
||||
~ArchiveItem();
|
||||
|
||||
bool m_isDirectory;
|
||||
std::vector<uint8_t> m_fileNameUTF8;
|
||||
PortabilityLayer::MacFileProperties m_macProperties;
|
||||
|
||||
ArchiveCompressedChunkDesc m_dataForkDesc;
|
||||
ArchiveCompressedChunkDesc m_resourceForkDesc;
|
||||
ArchiveCompressedChunkDesc m_commentDesc;
|
||||
|
||||
ArchiveItemList *m_children;
|
||||
};
|
||||
|
||||
struct ArchiveItemList
|
||||
{
|
||||
public:
|
||||
ArchiveItemList();
|
||||
ArchiveItemList(ArchiveItemList &&other);
|
||||
ArchiveItemList(const ArchiveItemList &other);
|
||||
~ArchiveItemList();
|
||||
|
||||
std::vector<ArchiveItem> m_items;
|
||||
};
|
218
unpacktool/BWT.cpp
Normal file
218
unpacktool/BWT.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
#include "BWT.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Inverse BWT
|
||||
|
||||
void CalculateInverseBWT(uint32_t *transform,uint8_t *block,int blocklen)
|
||||
{
|
||||
int counts[256]={0},cumulativecounts[256];
|
||||
|
||||
for(int i=0;i<blocklen;i++) counts[block[i]]++;
|
||||
|
||||
int total=0;
|
||||
for(int i=0;i<256;i++)
|
||||
{
|
||||
cumulativecounts[i]=total;
|
||||
total+=counts[i];
|
||||
counts[i]=0;
|
||||
}
|
||||
|
||||
for(int i=0;i<blocklen;i++)
|
||||
{
|
||||
transform[cumulativecounts[block[i]]+counts[block[i]]]=i;
|
||||
counts[block[i]]++;
|
||||
}
|
||||
}
|
||||
|
||||
void UnsortBWT(uint8_t *dest,uint8_t *src,int blocklen,int firstindex,uint32_t *transform)
|
||||
{
|
||||
CalculateInverseBWT(transform,src,blocklen);
|
||||
|
||||
int transformindex=firstindex;
|
||||
for(int i=0;i<blocklen;i++)
|
||||
{
|
||||
transformindex=transform[transformindex];
|
||||
dest[i]=src[transformindex];
|
||||
}
|
||||
}
|
||||
|
||||
void UnsortST4(uint8_t *dest,uint8_t *src,int blocklen,int firstindex,uint32_t *transform)
|
||||
{
|
||||
int counts[256];
|
||||
for(int i=0;i<256;i++) counts[i]=0;
|
||||
|
||||
int *array2=static_cast<int*>(calloc(sizeof(int),256*256));
|
||||
|
||||
for(int i=0;i<blocklen;i++) counts[src[i]]++;
|
||||
|
||||
int total=0;
|
||||
for(int i=0;i<256;i++)
|
||||
{
|
||||
int count=counts[i];
|
||||
counts[i]=total;
|
||||
|
||||
for(int j=0;j<count;j++) array2[(src[total+j]<<8)|i]++;
|
||||
|
||||
total+=count;
|
||||
}
|
||||
|
||||
uint8_t *bitvec=dest;
|
||||
memset(bitvec,0,(blocklen+7)/8);
|
||||
|
||||
int array3[256];
|
||||
for(int i=0;i<256;i++) array3[i]=-1;
|
||||
|
||||
uint32_t counts2[256];
|
||||
memcpy(counts2,counts,sizeof(counts));
|
||||
|
||||
total=0;
|
||||
for(int i=0;i<0x10000;i++)
|
||||
{
|
||||
int count=array2[i];
|
||||
|
||||
for(int j=0;j<count;j++)
|
||||
{
|
||||
int byte=src[total+j];
|
||||
if(array3[byte]!=total)
|
||||
{
|
||||
array3[byte]=total;
|
||||
int x=counts[byte];
|
||||
bitvec[x>>3]|=1<<(x&7);
|
||||
}
|
||||
counts[byte]++;
|
||||
}
|
||||
|
||||
total+=count;
|
||||
}
|
||||
|
||||
for(int i=0;i<256;i++) array3[i]=0;
|
||||
|
||||
int index=0;
|
||||
for(int i=0;i<blocklen;i++)
|
||||
{
|
||||
if(bitvec[i/8]&(1<<(i&7))) index=i;
|
||||
|
||||
int byte=src[i];
|
||||
if(index<array3[byte])
|
||||
{
|
||||
transform[i]=(array3[byte]-1)|0x800000;
|
||||
}
|
||||
else
|
||||
{
|
||||
transform[i]=counts2[byte];
|
||||
array3[byte]=i+1;
|
||||
}
|
||||
counts2[byte]++;
|
||||
transform[i]|=byte<<24;
|
||||
}
|
||||
|
||||
index=firstindex;
|
||||
uint32_t tval=transform[firstindex];
|
||||
|
||||
for(int i=0;i<blocklen;i++)
|
||||
{
|
||||
if(tval&0x800000)
|
||||
{
|
||||
index=transform[tval&0x7fffff]&0x7fffff;
|
||||
transform[tval&0x7fffff]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
transform[index]++;
|
||||
index=tval&0x7fffff;
|
||||
}
|
||||
|
||||
tval=transform[index];
|
||||
dest[i]=tval>>24;
|
||||
}
|
||||
|
||||
free(array2);
|
||||
}
|
||||
|
||||
/*void UnsortBWTStuffItX(uint8_t *dest,int blocklen,int firstindex,uint8_t *src,uint32_t *transform)
|
||||
{
|
||||
int counts[256]={0};
|
||||
|
||||
for(int i=0;i<blocklen;i++)
|
||||
{
|
||||
transform[i]=counts[src[i]];
|
||||
counts[src[i]]++;
|
||||
}
|
||||
|
||||
int total=0;
|
||||
for(int i=0;i<256;i++)
|
||||
{
|
||||
int oldtotal=total;
|
||||
total+=counts[i];
|
||||
counts[i]=oldtotal;
|
||||
}
|
||||
|
||||
int index=firstindex;
|
||||
for(int i=blocklen-1;i>=0;i--)
|
||||
{
|
||||
dest[i]=src[index];
|
||||
index=transform[index]+counts[src[index]];
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
// MTF Decoder
|
||||
|
||||
void ResetMTFDecoder(MTFState *self)
|
||||
{
|
||||
for(int i=0;i<256;i++) self->table[i]=i;
|
||||
}
|
||||
|
||||
int DecodeMTF(MTFState *self,int symbol)
|
||||
{
|
||||
int res=self->table[symbol];
|
||||
for(int i=symbol;i>0;i--) self->table[i]=self->table[i-1];
|
||||
self->table[0]=res;
|
||||
return res;
|
||||
}
|
||||
|
||||
void DecodeMTFBlock(uint8_t *block,int blocklen)
|
||||
{
|
||||
MTFState mtf;
|
||||
ResetMTFDecoder(&mtf);
|
||||
for(int i=0;i<blocklen;i++) block[i]=DecodeMTF(&mtf,block[i]);
|
||||
}
|
||||
|
||||
void DecodeM1FFNBlock(uint8_t *block,int blocklen,int order)
|
||||
{
|
||||
MTFState mtf;
|
||||
ResetMTFDecoder(&mtf);
|
||||
int lasthead=order-1;
|
||||
|
||||
for(int i=0;i<blocklen;i++)
|
||||
{
|
||||
int symbol=block[i];
|
||||
block[i]=mtf.table[symbol];
|
||||
|
||||
if(symbol==0)
|
||||
{
|
||||
lasthead=0;
|
||||
}
|
||||
else if(symbol==1)
|
||||
{
|
||||
if(lasthead>=order)
|
||||
{
|
||||
int val=mtf.table[1];
|
||||
mtf.table[1]=mtf.table[0];
|
||||
mtf.table[0]=val;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int val=mtf.table[symbol];
|
||||
for(int i=symbol;i>1;i--) mtf.table[i]=mtf.table[i-1];
|
||||
mtf.table[1]=val;
|
||||
}
|
||||
|
||||
lasthead++;
|
||||
}
|
||||
}
|
21
unpacktool/BWT.h
Normal file
21
unpacktool/BWT.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef __BWT_H__
|
||||
#define __BWT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void CalculateInverseBWT(uint32_t *transform, uint8_t *block, int blocklen);
|
||||
void UnsortBWT(uint8_t *dest, uint8_t *src, int blocklen, int firstindex, uint32_t *transformbuf);
|
||||
|
||||
void UnsortST4(uint8_t *dest, uint8_t *src, int blocklen, int firstindex, uint32_t *transformbuf);
|
||||
|
||||
typedef struct MTFState
|
||||
{
|
||||
int table[256];
|
||||
} MTFState;
|
||||
|
||||
void ResetMTFDecoder(MTFState *self);
|
||||
int DecodeMTF(MTFState *self, int symbol);
|
||||
void DecodeMTFBlock(uint8_t *block, int blocklen);
|
||||
void DecodeM1FFNBlock(uint8_t *block, int blocklen, int order);
|
||||
|
||||
#endif
|
208
unpacktool/CRC.cpp
Normal file
208
unpacktool/CRC.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
#include "CRC.h"
|
||||
|
||||
uint32_t XADCRC(uint32_t prevcrc, uint8_t byte, const uint32_t *table)
|
||||
{
|
||||
return table[(prevcrc^byte) & 0xff] ^ (prevcrc >> 8);
|
||||
}
|
||||
|
||||
uint32_t XADCalculateCRC(uint32_t prevcrc, const uint8_t *buffer, int length, const uint32_t *table)
|
||||
{
|
||||
uint32_t crc = prevcrc;
|
||||
for (int i = 0; i < length; i++) crc = XADCRC(crc, buffer[i], table);
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint64_t XADCRC64(uint64_t prevcrc, uint8_t byte, const uint64_t *table)
|
||||
{
|
||||
return table[(prevcrc^byte) & 0xff] ^ (prevcrc >> 8);
|
||||
}
|
||||
|
||||
uint64_t XADCalculateCRC64(uint64_t prevcrc, const uint8_t *buffer, int length, const uint64_t *table)
|
||||
{
|
||||
uint64_t crc = prevcrc;
|
||||
for (int i = 0; i < length; i++) crc = XADCRC64(crc, buffer[i], table);
|
||||
return crc;
|
||||
}
|
||||
|
||||
int XADUnReverseCRC16(int val)
|
||||
{
|
||||
val = ((val >> 8) & 0x00FF) | ((val & 0x00FF) << 8);
|
||||
return val;
|
||||
}
|
||||
|
||||
const uint32_t XADCRCTable_a001[256] =
|
||||
{
|
||||
0x00000000,0x0000c0c1,0x0000c181,0x00000140,0x0000c301,0x000003c0,0x00000280,0x0000c241,
|
||||
0x0000c601,0x000006c0,0x00000780,0x0000c741,0x00000500,0x0000c5c1,0x0000c481,0x00000440,
|
||||
0x0000cc01,0x00000cc0,0x00000d80,0x0000cd41,0x00000f00,0x0000cfc1,0x0000ce81,0x00000e40,
|
||||
0x00000a00,0x0000cac1,0x0000cb81,0x00000b40,0x0000c901,0x000009c0,0x00000880,0x0000c841,
|
||||
0x0000d801,0x000018c0,0x00001980,0x0000d941,0x00001b00,0x0000dbc1,0x0000da81,0x00001a40,
|
||||
0x00001e00,0x0000dec1,0x0000df81,0x00001f40,0x0000dd01,0x00001dc0,0x00001c80,0x0000dc41,
|
||||
0x00001400,0x0000d4c1,0x0000d581,0x00001540,0x0000d701,0x000017c0,0x00001680,0x0000d641,
|
||||
0x0000d201,0x000012c0,0x00001380,0x0000d341,0x00001100,0x0000d1c1,0x0000d081,0x00001040,
|
||||
0x0000f001,0x000030c0,0x00003180,0x0000f141,0x00003300,0x0000f3c1,0x0000f281,0x00003240,
|
||||
0x00003600,0x0000f6c1,0x0000f781,0x00003740,0x0000f501,0x000035c0,0x00003480,0x0000f441,
|
||||
0x00003c00,0x0000fcc1,0x0000fd81,0x00003d40,0x0000ff01,0x00003fc0,0x00003e80,0x0000fe41,
|
||||
0x0000fa01,0x00003ac0,0x00003b80,0x0000fb41,0x00003900,0x0000f9c1,0x0000f881,0x00003840,
|
||||
0x00002800,0x0000e8c1,0x0000e981,0x00002940,0x0000eb01,0x00002bc0,0x00002a80,0x0000ea41,
|
||||
0x0000ee01,0x00002ec0,0x00002f80,0x0000ef41,0x00002d00,0x0000edc1,0x0000ec81,0x00002c40,
|
||||
0x0000e401,0x000024c0,0x00002580,0x0000e541,0x00002700,0x0000e7c1,0x0000e681,0x00002640,
|
||||
0x00002200,0x0000e2c1,0x0000e381,0x00002340,0x0000e101,0x000021c0,0x00002080,0x0000e041,
|
||||
0x0000a001,0x000060c0,0x00006180,0x0000a141,0x00006300,0x0000a3c1,0x0000a281,0x00006240,
|
||||
0x00006600,0x0000a6c1,0x0000a781,0x00006740,0x0000a501,0x000065c0,0x00006480,0x0000a441,
|
||||
0x00006c00,0x0000acc1,0x0000ad81,0x00006d40,0x0000af01,0x00006fc0,0x00006e80,0x0000ae41,
|
||||
0x0000aa01,0x00006ac0,0x00006b80,0x0000ab41,0x00006900,0x0000a9c1,0x0000a881,0x00006840,
|
||||
0x00007800,0x0000b8c1,0x0000b981,0x00007940,0x0000bb01,0x00007bc0,0x00007a80,0x0000ba41,
|
||||
0x0000be01,0x00007ec0,0x00007f80,0x0000bf41,0x00007d00,0x0000bdc1,0x0000bc81,0x00007c40,
|
||||
0x0000b401,0x000074c0,0x00007580,0x0000b541,0x00007700,0x0000b7c1,0x0000b681,0x00007640,
|
||||
0x00007200,0x0000b2c1,0x0000b381,0x00007340,0x0000b101,0x000071c0,0x00007080,0x0000b041,
|
||||
0x00005000,0x000090c1,0x00009181,0x00005140,0x00009301,0x000053c0,0x00005280,0x00009241,
|
||||
0x00009601,0x000056c0,0x00005780,0x00009741,0x00005500,0x000095c1,0x00009481,0x00005440,
|
||||
0x00009c01,0x00005cc0,0x00005d80,0x00009d41,0x00005f00,0x00009fc1,0x00009e81,0x00005e40,
|
||||
0x00005a00,0x00009ac1,0x00009b81,0x00005b40,0x00009901,0x000059c0,0x00005880,0x00009841,
|
||||
0x00008801,0x000048c0,0x00004980,0x00008941,0x00004b00,0x00008bc1,0x00008a81,0x00004a40,
|
||||
0x00004e00,0x00008ec1,0x00008f81,0x00004f40,0x00008d01,0x00004dc0,0x00004c80,0x00008c41,
|
||||
0x00004400,0x000084c1,0x00008581,0x00004540,0x00008701,0x000047c0,0x00004680,0x00008641,
|
||||
0x00008201,0x000042c0,0x00004380,0x00008341,0x00004100,0x000081c1,0x00008081,0x00004040,
|
||||
};
|
||||
|
||||
const uint32_t XADCRCReverseTable_1021[256] =
|
||||
{
|
||||
0x00000000,0x00002110,0x00004220,0x00006330,0x00008440,0x0000a550,0x0000c660,0x0000e770,
|
||||
0x00000881,0x00002991,0x00004aa1,0x00006bb1,0x00008cc1,0x0000add1,0x0000cee1,0x0000eff1,
|
||||
0x00003112,0x00001002,0x00007332,0x00005222,0x0000b552,0x00009442,0x0000f772,0x0000d662,
|
||||
0x00003993,0x00001883,0x00007bb3,0x00005aa3,0x0000bdd3,0x00009cc3,0x0000fff3,0x0000dee3,
|
||||
0x00006224,0x00004334,0x00002004,0x00000114,0x0000e664,0x0000c774,0x0000a444,0x00008554,
|
||||
0x00006aa5,0x00004bb5,0x00002885,0x00000995,0x0000eee5,0x0000cff5,0x0000acc5,0x00008dd5,
|
||||
0x00005336,0x00007226,0x00001116,0x00003006,0x0000d776,0x0000f666,0x00009556,0x0000b446,
|
||||
0x00005bb7,0x00007aa7,0x00001997,0x00003887,0x0000dff7,0x0000fee7,0x00009dd7,0x0000bcc7,
|
||||
0x0000c448,0x0000e558,0x00008668,0x0000a778,0x00004008,0x00006118,0x00000228,0x00002338,
|
||||
0x0000ccc9,0x0000edd9,0x00008ee9,0x0000aff9,0x00004889,0x00006999,0x00000aa9,0x00002bb9,
|
||||
0x0000f55a,0x0000d44a,0x0000b77a,0x0000966a,0x0000711a,0x0000500a,0x0000333a,0x0000122a,
|
||||
0x0000fddb,0x0000dccb,0x0000bffb,0x00009eeb,0x0000799b,0x0000588b,0x00003bbb,0x00001aab,
|
||||
0x0000a66c,0x0000877c,0x0000e44c,0x0000c55c,0x0000222c,0x0000033c,0x0000600c,0x0000411c,
|
||||
0x0000aeed,0x00008ffd,0x0000eccd,0x0000cddd,0x00002aad,0x00000bbd,0x0000688d,0x0000499d,
|
||||
0x0000977e,0x0000b66e,0x0000d55e,0x0000f44e,0x0000133e,0x0000322e,0x0000511e,0x0000700e,
|
||||
0x00009fff,0x0000beef,0x0000dddf,0x0000fccf,0x00001bbf,0x00003aaf,0x0000599f,0x0000788f,
|
||||
0x00008891,0x0000a981,0x0000cab1,0x0000eba1,0x00000cd1,0x00002dc1,0x00004ef1,0x00006fe1,
|
||||
0x00008010,0x0000a100,0x0000c230,0x0000e320,0x00000450,0x00002540,0x00004670,0x00006760,
|
||||
0x0000b983,0x00009893,0x0000fba3,0x0000dab3,0x00003dc3,0x00001cd3,0x00007fe3,0x00005ef3,
|
||||
0x0000b102,0x00009012,0x0000f322,0x0000d232,0x00003542,0x00001452,0x00007762,0x00005672,
|
||||
0x0000eab5,0x0000cba5,0x0000a895,0x00008985,0x00006ef5,0x00004fe5,0x00002cd5,0x00000dc5,
|
||||
0x0000e234,0x0000c324,0x0000a014,0x00008104,0x00006674,0x00004764,0x00002454,0x00000544,
|
||||
0x0000dba7,0x0000fab7,0x00009987,0x0000b897,0x00005fe7,0x00007ef7,0x00001dc7,0x00003cd7,
|
||||
0x0000d326,0x0000f236,0x00009106,0x0000b016,0x00005766,0x00007676,0x00001546,0x00003456,
|
||||
0x00004cd9,0x00006dc9,0x00000ef9,0x00002fe9,0x0000c899,0x0000e989,0x00008ab9,0x0000aba9,
|
||||
0x00004458,0x00006548,0x00000678,0x00002768,0x0000c018,0x0000e108,0x00008238,0x0000a328,
|
||||
0x00007dcb,0x00005cdb,0x00003feb,0x00001efb,0x0000f98b,0x0000d89b,0x0000bbab,0x00009abb,
|
||||
0x0000754a,0x0000545a,0x0000376a,0x0000167a,0x0000f10a,0x0000d01a,0x0000b32a,0x0000923a,
|
||||
0x00002efd,0x00000fed,0x00006cdd,0x00004dcd,0x0000aabd,0x00008bad,0x0000e89d,0x0000c98d,
|
||||
0x0000267c,0x0000076c,0x0000645c,0x0000454c,0x0000a23c,0x0000832c,0x0000e01c,0x0000c10c,
|
||||
0x00001fef,0x00003eff,0x00005dcf,0x00007cdf,0x00009baf,0x0000babf,0x0000d98f,0x0000f89f,
|
||||
0x0000176e,0x0000367e,0x0000554e,0x0000745e,0x0000932e,0x0000b23e,0x0000d10e,0x0000f01e,
|
||||
};
|
||||
|
||||
|
||||
const uint32_t XADCRCTable_edb88320[256] =
|
||||
{
|
||||
0x00000000,0x77073096,0xee0e612c,0x990951ba,0x076dc419,0x706af48f,0xe963a535,0x9e6495a3,
|
||||
0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988,0x09b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91,
|
||||
0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de,0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7,
|
||||
0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec,0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5,
|
||||
0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172,0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b,
|
||||
0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940,0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59,
|
||||
0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116,0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f,
|
||||
0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924,0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d,
|
||||
0x76dc4190,0x01db7106,0x98d220bc,0xefd5102a,0x71b18589,0x06b6b51f,0x9fbfe4a5,0xe8b8d433,
|
||||
0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818,0x7f6a0dbb,0x086d3d2d,0x91646c97,0xe6635c01,
|
||||
0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e,0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457,
|
||||
0x65b0d9c6,0x12b7e950,0x8bbeb8ea,0xfcb9887c,0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65,
|
||||
0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2,0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb,
|
||||
0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0,0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9,
|
||||
0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086,0x5768b525,0x206f85b3,0xb966d409,0xce61e49f,
|
||||
0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4,0x59b33d17,0x2eb40d81,0xb7bd5c3b,0xc0ba6cad,
|
||||
0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a,0xead54739,0x9dd277af,0x04db2615,0x73dc1683,
|
||||
0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8,0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1,
|
||||
0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe,0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7,
|
||||
0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc,0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5,
|
||||
0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252,0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b,
|
||||
0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60,0xdf60efc3,0xa867df55,0x316e8eef,0x4669be79,
|
||||
0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236,0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f,
|
||||
0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04,0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d,
|
||||
0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a,0x9c0906a9,0xeb0e363f,0x72076785,0x05005713,
|
||||
0x95bf4a82,0xe2b87a14,0x7bb12bae,0x0cb61b38,0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21,
|
||||
0x86d3d2d4,0xf1d4e242,0x68ddb3f8,0x1fda836e,0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777,
|
||||
0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c,0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45,
|
||||
0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2,0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db,
|
||||
0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0,0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9,
|
||||
0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6,0xbad03605,0xcdd70693,0x54de5729,0x23d967bf,
|
||||
0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94,0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d,
|
||||
};
|
||||
|
||||
const uint64_t XADCRCTable_c96c5795d7870f42[256] =
|
||||
{
|
||||
0x0000000000000000,0xb32e4cbe03a75f6f,0xf4843657a840a05b,0x47aa7ae9abe7ff34,
|
||||
0x7bd0c384ff8f5e33,0xc8fe8f3afc28015c,0x8f54f5d357cffe68,0x3c7ab96d5468a107,
|
||||
0xf7a18709ff1ebc66,0x448fcbb7fcb9e309,0x0325b15e575e1c3d,0xb00bfde054f94352,
|
||||
0x8c71448d0091e255,0x3f5f08330336bd3a,0x78f572daa8d1420e,0xcbdb3e64ab761d61,
|
||||
0x7d9ba13851336649,0xceb5ed8652943926,0x891f976ff973c612,0x3a31dbd1fad4997d,
|
||||
0x064b62bcaebc387a,0xb5652e02ad1b6715,0xf2cf54eb06fc9821,0x41e11855055bc74e,
|
||||
0x8a3a2631ae2dda2f,0x39146a8fad8a8540,0x7ebe1066066d7a74,0xcd905cd805ca251b,
|
||||
0xf1eae5b551a2841c,0x42c4a90b5205db73,0x056ed3e2f9e22447,0xb6409f5cfa457b28,
|
||||
0xfb374270a266cc92,0x48190ecea1c193fd,0x0fb374270a266cc9,0xbc9d3899098133a6,
|
||||
0x80e781f45de992a1,0x33c9cd4a5e4ecdce,0x7463b7a3f5a932fa,0xc74dfb1df60e6d95,
|
||||
0x0c96c5795d7870f4,0xbfb889c75edf2f9b,0xf812f32ef538d0af,0x4b3cbf90f69f8fc0,
|
||||
0x774606fda2f72ec7,0xc4684a43a15071a8,0x83c230aa0ab78e9c,0x30ec7c140910d1f3,
|
||||
0x86ace348f355aadb,0x3582aff6f0f2f5b4,0x7228d51f5b150a80,0xc10699a158b255ef,
|
||||
0xfd7c20cc0cdaf4e8,0x4e526c720f7dab87,0x09f8169ba49a54b3,0xbad65a25a73d0bdc,
|
||||
0x710d64410c4b16bd,0xc22328ff0fec49d2,0x85895216a40bb6e6,0x36a71ea8a7ace989,
|
||||
0x0adda7c5f3c4488e,0xb9f3eb7bf06317e1,0xfe5991925b84e8d5,0x4d77dd2c5823b7ba,
|
||||
0x64b62bcaebc387a1,0xd7986774e864d8ce,0x90321d9d438327fa,0x231c512340247895,
|
||||
0x1f66e84e144cd992,0xac48a4f017eb86fd,0xebe2de19bc0c79c9,0x58cc92a7bfab26a6,
|
||||
0x9317acc314dd3bc7,0x2039e07d177a64a8,0x67939a94bc9d9b9c,0xd4bdd62abf3ac4f3,
|
||||
0xe8c76f47eb5265f4,0x5be923f9e8f53a9b,0x1c4359104312c5af,0xaf6d15ae40b59ac0,
|
||||
0x192d8af2baf0e1e8,0xaa03c64cb957be87,0xeda9bca512b041b3,0x5e87f01b11171edc,
|
||||
0x62fd4976457fbfdb,0xd1d305c846d8e0b4,0x96797f21ed3f1f80,0x2557339fee9840ef,
|
||||
0xee8c0dfb45ee5d8e,0x5da24145464902e1,0x1a083bacedaefdd5,0xa9267712ee09a2ba,
|
||||
0x955cce7fba6103bd,0x267282c1b9c65cd2,0x61d8f8281221a3e6,0xd2f6b4961186fc89,
|
||||
0x9f8169ba49a54b33,0x2caf25044a02145c,0x6b055fede1e5eb68,0xd82b1353e242b407,
|
||||
0xe451aa3eb62a1500,0x577fe680b58d4a6f,0x10d59c691e6ab55b,0xa3fbd0d71dcdea34,
|
||||
0x6820eeb3b6bbf755,0xdb0ea20db51ca83a,0x9ca4d8e41efb570e,0x2f8a945a1d5c0861,
|
||||
0x13f02d374934a966,0xa0de61894a93f609,0xe7741b60e174093d,0x545a57dee2d35652,
|
||||
0xe21ac88218962d7a,0x5134843c1b317215,0x169efed5b0d68d21,0xa5b0b26bb371d24e,
|
||||
0x99ca0b06e7197349,0x2ae447b8e4be2c26,0x6d4e3d514f59d312,0xde6071ef4cfe8c7d,
|
||||
0x15bb4f8be788911c,0xa6950335e42fce73,0xe13f79dc4fc83147,0x521135624c6f6e28,
|
||||
0x6e6b8c0f1807cf2f,0xdd45c0b11ba09040,0x9aefba58b0476f74,0x29c1f6e6b3e0301b,
|
||||
0xc96c5795d7870f42,0x7a421b2bd420502d,0x3de861c27fc7af19,0x8ec62d7c7c60f076,
|
||||
0xb2bc941128085171,0x0192d8af2baf0e1e,0x4638a2468048f12a,0xf516eef883efae45,
|
||||
0x3ecdd09c2899b324,0x8de39c222b3eec4b,0xca49e6cb80d9137f,0x7967aa75837e4c10,
|
||||
0x451d1318d716ed17,0xf6335fa6d4b1b278,0xb199254f7f564d4c,0x02b769f17cf11223,
|
||||
0xb4f7f6ad86b4690b,0x07d9ba1385133664,0x4073c0fa2ef4c950,0xf35d8c442d53963f,
|
||||
0xcf273529793b3738,0x7c0979977a9c6857,0x3ba3037ed17b9763,0x888d4fc0d2dcc80c,
|
||||
0x435671a479aad56d,0xf0783d1a7a0d8a02,0xb7d247f3d1ea7536,0x04fc0b4dd24d2a59,
|
||||
0x3886b22086258b5e,0x8ba8fe9e8582d431,0xcc0284772e652b05,0x7f2cc8c92dc2746a,
|
||||
0x325b15e575e1c3d0,0x8175595b76469cbf,0xc6df23b2dda1638b,0x75f16f0cde063ce4,
|
||||
0x498bd6618a6e9de3,0xfaa59adf89c9c28c,0xbd0fe036222e3db8,0x0e21ac88218962d7,
|
||||
0xc5fa92ec8aff7fb6,0x76d4de52895820d9,0x317ea4bb22bfdfed,0x8250e80521188082,
|
||||
0xbe2a516875702185,0x0d041dd676d77eea,0x4aae673fdd3081de,0xf9802b81de97deb1,
|
||||
0x4fc0b4dd24d2a599,0xfceef8632775faf6,0xbb44828a8c9205c2,0x086ace348f355aad,
|
||||
0x34107759db5dfbaa,0x873e3be7d8faa4c5,0xc094410e731d5bf1,0x73ba0db070ba049e,
|
||||
0xb86133d4dbcc19ff,0x0b4f7f6ad86b4690,0x4ce50583738cb9a4,0xffcb493d702be6cb,
|
||||
0xc3b1f050244347cc,0x709fbcee27e418a3,0x3735c6078c03e797,0x841b8ab98fa4b8f8,
|
||||
0xadda7c5f3c4488e3,0x1ef430e13fe3d78c,0x595e4a08940428b8,0xea7006b697a377d7,
|
||||
0xd60abfdbc3cbd6d0,0x6524f365c06c89bf,0x228e898c6b8b768b,0x91a0c532682c29e4,
|
||||
0x5a7bfb56c35a3485,0xe955b7e8c0fd6bea,0xaeffcd016b1a94de,0x1dd181bf68bdcbb1,
|
||||
0x21ab38d23cd56ab6,0x9285746c3f7235d9,0xd52f0e859495caed,0x6601423b97329582,
|
||||
0xd041dd676d77eeaa,0x636f91d96ed0b1c5,0x24c5eb30c5374ef1,0x97eba78ec690119e,
|
||||
0xab911ee392f8b099,0x18bf525d915feff6,0x5f1528b43ab810c2,0xec3b640a391f4fad,
|
||||
0x27e05a6e926952cc,0x94ce16d091ce0da3,0xd3646c393a29f297,0x604a2087398eadf8,
|
||||
0x5c3099ea6de60cff,0xef1ed5546e415390,0xa8b4afbdc5a6aca4,0x1b9ae303c601f3cb,
|
||||
0x56ed3e2f9e224471,0xe5c372919d851b1e,0xa26908783662e42a,0x114744c635c5bb45,
|
||||
0x2d3dfdab61ad1a42,0x9e13b115620a452d,0xd9b9cbfcc9edba19,0x6a978742ca4ae576,
|
||||
0xa14cb926613cf817,0x1262f598629ba778,0x55c88f71c97c584c,0xe6e6c3cfcadb0723,
|
||||
0xda9c7aa29eb3a624,0x69b2361c9d14f94b,0x2e184cf536f3067f,0x9d36004b35545910,
|
||||
0x2b769f17cf112238,0x9858d3a9ccb67d57,0xdff2a94067518263,0x6cdce5fe64f6dd0c,
|
||||
0x50a65c93309e7c0b,0xe388102d33392364,0xa4226ac498dedc50,0x170c267a9b79833f,
|
||||
0xdcd7181e300f9e5e,0x6ff954a033a8c131,0x28532e49984f3e05,0x9b7d62f79be8616a,
|
||||
0xa707db9acf80c06d,0x14299724cc279f02,0x5383edcd67c06036,0xe0ada17364673f59,
|
||||
};
|
16
unpacktool/CRC.h
Normal file
16
unpacktool/CRC.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t XADCRC(uint32_t prevcrc, uint8_t byte, const uint32_t *table);
|
||||
uint32_t XADCalculateCRC(uint32_t prevcrc, const uint8_t *buffer, int length, const uint32_t *table);
|
||||
|
||||
uint64_t XADCRC64(uint64_t prevcrc, uint8_t byte, const uint64_t *table);
|
||||
uint64_t XADCalculateCRC64(uint64_t prevcrc, const uint8_t *buffer, int length, const uint64_t *table);
|
||||
|
||||
int XADUnReverseCRC16(int val);
|
||||
|
||||
extern const uint32_t XADCRCTable_a001[256];
|
||||
extern const uint32_t XADCRCReverseTable_1021[256];
|
||||
extern const uint32_t XADCRCTable_edb88320[256];
|
||||
extern const uint64_t XADCRCTable_c96c5795d7870f42[256];
|
379
unpacktool/CSInputBuffer.cpp
Normal file
379
unpacktool/CSInputBuffer.cpp
Normal file
@@ -0,0 +1,379 @@
|
||||
#include "CSInputBuffer.h"
|
||||
#include "IFileReader.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Allocation and management
|
||||
|
||||
CSInputBuffer *CSInputBufferAlloc(IFileReader *parent, int size)
|
||||
{
|
||||
CSInputBuffer *self = static_cast<CSInputBuffer*>(malloc(sizeof(CSInputBuffer) + size));
|
||||
if (!self) return NULL;
|
||||
|
||||
self->parent = parent;
|
||||
self->startoffs = parent->GetPosition();
|
||||
self->eof = false;
|
||||
|
||||
self->buffer = (uint8_t *)&self[1];
|
||||
self->bufsize = size;
|
||||
self->bufbytes = 0;
|
||||
self->currbyte = 0;
|
||||
self->bits = 0;
|
||||
self->numbits = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
CSInputBuffer *CSInputBufferAllocWithBuffer(const uint8_t *buffer, int length, IFileReader::FilePos_t startoffs)
|
||||
{
|
||||
CSInputBuffer *self = static_cast<CSInputBuffer*>(malloc(sizeof(CSInputBuffer)));
|
||||
if (!self) return NULL;
|
||||
|
||||
self->parent = NULL;
|
||||
self->startoffs = -startoffs;
|
||||
self->eof = true;
|
||||
|
||||
self->buffer = (uint8_t *)buffer; // Since eof is set, the buffer won't be written to.
|
||||
self->bufsize = length;
|
||||
self->bufbytes = length;
|
||||
self->currbyte = 0;
|
||||
self->bits = 0;
|
||||
self->numbits = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
CSInputBuffer *CSInputBufferAllocEmpty()
|
||||
{
|
||||
CSInputBuffer *self = static_cast<CSInputBuffer*>(malloc(sizeof(CSInputBuffer)));
|
||||
if (!self) return NULL;
|
||||
|
||||
self->parent = NULL;
|
||||
self->startoffs = 0;
|
||||
self->eof = true;
|
||||
|
||||
self->buffer = NULL;
|
||||
self->bufsize = 0;
|
||||
self->bufbytes = 0;
|
||||
self->currbyte = 0;
|
||||
self->bits = 0;
|
||||
self->numbits = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void CSInputBufferFree(CSInputBuffer *self)
|
||||
{
|
||||
free(self);
|
||||
}
|
||||
|
||||
void CSInputSetMemoryBuffer(CSInputBuffer *self, uint8_t *buffer, int length, IFileReader::FilePos_t startoffs)
|
||||
{
|
||||
self->eof = true;
|
||||
self->startoffs = -startoffs;
|
||||
self->buffer = buffer;
|
||||
self->bufsize = length;
|
||||
self->bufbytes = length;
|
||||
self->currbyte = 0;
|
||||
self->bits = 0;
|
||||
self->numbits = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Buffer and file positioning
|
||||
|
||||
void CSInputRestart(CSInputBuffer *self)
|
||||
{
|
||||
CSInputSeekToFileOffset(self, self->startoffs);
|
||||
}
|
||||
|
||||
void CSInputFlush(CSInputBuffer *self)
|
||||
{
|
||||
self->currbyte = self->bufbytes = 0;
|
||||
self->bits = 0;
|
||||
self->numbits = 0;
|
||||
}
|
||||
|
||||
void CSInputSynchronizeFileOffset(CSInputBuffer *self)
|
||||
{
|
||||
CSInputSeekToFileOffset(self, CSInputFileOffset(self));
|
||||
}
|
||||
|
||||
void CSInputSeekToFileOffset(CSInputBuffer *self, IFileReader::FilePos_t offset)
|
||||
{
|
||||
self->parent->SeekStart(offset);
|
||||
self->eof = false;
|
||||
CSInputFlush(self);
|
||||
}
|
||||
|
||||
void CSInputSeekToBufferOffset(CSInputBuffer *self, IFileReader::FilePos_t offset)
|
||||
{
|
||||
CSInputSeekToFileOffset(self, offset + self->startoffs);
|
||||
}
|
||||
|
||||
void CSInputSetStartOffset(CSInputBuffer *self, IFileReader::FilePos_t offset)
|
||||
{
|
||||
self->startoffs = offset;
|
||||
}
|
||||
|
||||
IFileReader::FilePos_t CSInputBufferOffset(CSInputBuffer *self)
|
||||
{
|
||||
return CSInputFileOffset(self) - self->startoffs;
|
||||
}
|
||||
|
||||
IFileReader::FilePos_t CSInputFileOffset(CSInputBuffer *self)
|
||||
{
|
||||
if (self->parent) return self->parent->GetPosition() - self->bufbytes + self->currbyte;
|
||||
else return self->currbyte;
|
||||
}
|
||||
|
||||
IFileReader::FilePos_t CSInputBufferBitOffset(CSInputBuffer *self)
|
||||
{
|
||||
return CSInputBufferOffset(self) * 8 - (self->numbits & 7);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Byte reading
|
||||
|
||||
void _CSInputFillBuffer(CSInputBuffer *self)
|
||||
{
|
||||
int left = _CSInputBytesLeftInBuffer(self);
|
||||
|
||||
if (left >= 0) memmove(self->buffer, self->buffer + self->currbyte, left);
|
||||
else
|
||||
{
|
||||
self->parent->SeekCurrent(-left);
|
||||
left = 0;
|
||||
}
|
||||
|
||||
int actual = self->parent->Read(self->buffer + left, self->bufsize - left);
|
||||
if (actual == 0) self->eof = true;
|
||||
|
||||
self->bufbytes = left + actual;
|
||||
self->currbyte = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Bitstream reading
|
||||
|
||||
// TODO: clean up and/or make faster
|
||||
bool _CSInputFillBits(CSInputBuffer *self)
|
||||
{
|
||||
_CSInputCheckAndFillBuffer(self);
|
||||
|
||||
int numbytes = (32 - self->numbits) >> 3;
|
||||
int left = _CSInputBytesLeftInBuffer(self);
|
||||
if (numbytes > left) numbytes = left;
|
||||
|
||||
int startoffset = self->numbits >> 3;
|
||||
// int shift=24-self->numbits;
|
||||
|
||||
// for(int i=0;i<numbytes;i++)
|
||||
// {
|
||||
// self->bits|=_CSInputPeekByteWithoutEOF(self,i+startoffset)<<shift;
|
||||
// shift-=8;
|
||||
// }
|
||||
|
||||
switch (numbytes)
|
||||
{
|
||||
case 4:
|
||||
self->bits =
|
||||
(_CSInputPeekByteWithoutEOF(self, startoffset) << 24) |
|
||||
(_CSInputPeekByteWithoutEOF(self, startoffset + 1) << 16) |
|
||||
(_CSInputPeekByteWithoutEOF(self, startoffset + 2) << 8) |
|
||||
_CSInputPeekByteWithoutEOF(self, startoffset + 3);
|
||||
break;
|
||||
case 3:
|
||||
self->bits |= (
|
||||
(_CSInputPeekByteWithoutEOF(self, startoffset) << 16) |
|
||||
(_CSInputPeekByteWithoutEOF(self, startoffset + 1) << 8) |
|
||||
(_CSInputPeekByteWithoutEOF(self, startoffset + 2) << 0)
|
||||
) << (8 - self->numbits);
|
||||
break;
|
||||
case 2:
|
||||
self->bits |= (
|
||||
(_CSInputPeekByteWithoutEOF(self, startoffset) << 8) |
|
||||
(_CSInputPeekByteWithoutEOF(self, startoffset + 1) << 0)
|
||||
) << (16 - self->numbits);
|
||||
break;
|
||||
case 1:
|
||||
self->bits |= _CSInputPeekByteWithoutEOF(self, startoffset) << (24 - self->numbits);
|
||||
break;
|
||||
}
|
||||
|
||||
self->numbits += numbytes * 8;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _CSInputFillBitsLE(CSInputBuffer *self)
|
||||
{
|
||||
_CSInputCheckAndFillBuffer(self);
|
||||
|
||||
int numbytes = (32 - self->numbits) >> 3;
|
||||
int left = _CSInputBytesLeftInBuffer(self);
|
||||
if (numbytes > left) numbytes = left;
|
||||
|
||||
int startoffset = self->numbits >> 3;
|
||||
|
||||
for (int i = 0; i < numbytes; i++)
|
||||
{
|
||||
self->bits |= _CSInputPeekByteWithoutEOF(self, i + startoffset) << self->numbits;
|
||||
self->numbits += 8;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSInputNextBit(CSInputBuffer *self, unsigned int &bit)
|
||||
{
|
||||
if (!CSInputPeekBitString(self, 1, bit))
|
||||
return false;
|
||||
return CSInputSkipPeekedBits(self, 1);
|
||||
}
|
||||
|
||||
bool CSInputNextBitLE(CSInputBuffer *self, unsigned int &bit)
|
||||
{
|
||||
if (!CSInputPeekBitStringLE(self, 1, bit))
|
||||
return false;
|
||||
return CSInputSkipPeekedBitsLE(self, 1);
|
||||
}
|
||||
|
||||
bool CSInputNextBitString(CSInputBuffer *self, int numbits, unsigned int &bits)
|
||||
{
|
||||
if (numbits == 0)
|
||||
{
|
||||
bits = 0;
|
||||
return true;
|
||||
}
|
||||
if (!CSInputPeekBitString(self, numbits, bits))
|
||||
return false;
|
||||
return CSInputSkipPeekedBits(self, numbits);
|
||||
}
|
||||
|
||||
bool CSInputNextBitStringLE(CSInputBuffer *self, int numbits, unsigned int &bits)
|
||||
{
|
||||
if (numbits == 0)
|
||||
{
|
||||
bits = 0;
|
||||
return true;
|
||||
}
|
||||
if (!CSInputPeekBitStringLE(self, numbits, bits))
|
||||
return false;
|
||||
if (!CSInputSkipPeekedBitsLE(self, numbits))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSInputNextLongBitString(CSInputBuffer *self, int numbits, unsigned int &bits)
|
||||
{
|
||||
if (numbits <= 25)
|
||||
return CSInputNextBitString(self, numbits, bits);
|
||||
else
|
||||
{
|
||||
int rest = numbits - 25;
|
||||
unsigned int readBits;
|
||||
if (!CSInputNextBitString(self, 25, readBits))
|
||||
return false;
|
||||
readBits <<= rest;
|
||||
unsigned int moreBits;
|
||||
if (!CSInputNextBitString(self, rest, moreBits))
|
||||
return false;
|
||||
bits = (readBits | moreBits);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CSInputNextLongBitStringLE(CSInputBuffer *self, int numbits, unsigned int &bits)
|
||||
{
|
||||
if (numbits <= 25)
|
||||
return CSInputNextBitStringLE(self, numbits, bits);
|
||||
else
|
||||
{
|
||||
int rest = numbits - 25;
|
||||
unsigned int readBits;
|
||||
if (!CSInputNextBitStringLE(self, 25, readBits))
|
||||
return false;
|
||||
unsigned int moreBits;
|
||||
if (!CSInputNextBitStringLE(self, rest, moreBits))
|
||||
return false;
|
||||
bits = (readBits | (moreBits << 25));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CSInputSkipBits(CSInputBuffer *self, int numbits)
|
||||
{
|
||||
if (numbits <= self->numbits)
|
||||
return CSInputSkipPeekedBits(self, numbits);
|
||||
else
|
||||
{
|
||||
int skipbits = numbits - (self->numbits & 7);
|
||||
CSInputSkipToByteBoundary(self);
|
||||
CSInputSkipBytes(self, skipbits >> 3);
|
||||
if (skipbits & 7)
|
||||
{
|
||||
unsigned int scratch;
|
||||
if (!CSInputNextBitString(self, skipbits & 7, scratch))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CSInputSkipBitsLE(CSInputBuffer *self, int numbits)
|
||||
{
|
||||
if (numbits <= self->numbits)
|
||||
return CSInputSkipPeekedBitsLE(self, numbits);
|
||||
else
|
||||
{
|
||||
int skipbits = numbits - (self->numbits & 7);
|
||||
CSInputSkipToByteBoundary(self);
|
||||
CSInputSkipBytes(self, skipbits >> 3);
|
||||
if (skipbits & 7)
|
||||
{
|
||||
unsigned int scratch;
|
||||
if (!CSInputNextBitStringLE(self, skipbits & 7, scratch))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CSInputOnByteBoundary(CSInputBuffer *self)
|
||||
{
|
||||
return (self->numbits & 7) == 0;
|
||||
}
|
||||
|
||||
bool CSInputSkipToByteBoundary(CSInputBuffer *self)
|
||||
{
|
||||
self->bits = 0;
|
||||
self->numbits = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSInputSkipTo16BitBoundary(CSInputBuffer *self)
|
||||
{
|
||||
if (!CSInputSkipToByteBoundary(self))
|
||||
return false;
|
||||
if (CSInputBufferOffset(self) & 1)
|
||||
{
|
||||
if (!CSInputSkipBytes(self, 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
232
unpacktool/CSInputBuffer.h
Normal file
232
unpacktool/CSInputBuffer.h
Normal file
@@ -0,0 +1,232 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "UPByteSwap.h"
|
||||
#include "IFileReader.h"
|
||||
|
||||
struct IFileReader;
|
||||
|
||||
struct CSInputBuffer
|
||||
{
|
||||
IFileReader *parent;
|
||||
IFileReader::FilePos_t startoffs;
|
||||
bool eof;
|
||||
|
||||
uint8_t *buffer;
|
||||
unsigned int bufsize, bufbytes, currbyte;
|
||||
|
||||
uint32_t bits;
|
||||
unsigned int numbits;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Allocation and management
|
||||
|
||||
CSInputBuffer *CSInputBufferAlloc(IFileReader *parent, int size);
|
||||
CSInputBuffer *CSInputBufferAllocWithBuffer(const uint8_t *buffer, int length, IFileReader::FilePos_t startoffs);
|
||||
CSInputBuffer *CSInputBufferAllocEmpty();
|
||||
void CSInputBufferFree(CSInputBuffer *self);
|
||||
|
||||
void CSInputSetMemoryBuffer(CSInputBuffer *self, uint8_t *buffer, int length, size_t startoffs);
|
||||
|
||||
static inline IFileReader *CSInputHandle(CSInputBuffer *self)
|
||||
{
|
||||
return self->parent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Buffer and file positioning
|
||||
|
||||
void CSInputRestart(CSInputBuffer *self);
|
||||
void CSInputFlush(CSInputBuffer *self);
|
||||
|
||||
void CSInputSynchronizeFileOffset(CSInputBuffer *self);
|
||||
void CSInputSeekToFileOffset(CSInputBuffer *self, IFileReader::FilePos_t offset);
|
||||
void CSInputSeekToBufferOffset(CSInputBuffer *self, IFileReader::FilePos_t offset);
|
||||
void CSInputSetStartOffset(CSInputBuffer *self, IFileReader::FilePos_t offset);
|
||||
IFileReader::FilePos_t CSInputBufferOffset(CSInputBuffer *self);
|
||||
IFileReader::FilePos_t CSInputFileOffset(CSInputBuffer *self);
|
||||
IFileReader::FilePos_t CSInputBufferBitOffset(CSInputBuffer *self);
|
||||
|
||||
void _CSInputFillBuffer(CSInputBuffer *self);
|
||||
|
||||
|
||||
|
||||
|
||||
// Byte reading
|
||||
|
||||
#define CSInputBufferLookAhead 4
|
||||
|
||||
static inline int _CSInputBytesLeftInBuffer(CSInputBuffer *self)
|
||||
{
|
||||
return self->bufbytes - self->currbyte;
|
||||
}
|
||||
|
||||
static inline void _CSInputCheckAndFillBuffer(CSInputBuffer *self)
|
||||
{
|
||||
if (!self->eof&&_CSInputBytesLeftInBuffer(self) <= CSInputBufferLookAhead) _CSInputFillBuffer(self);
|
||||
}
|
||||
|
||||
static inline bool CSInputSkipBytes(CSInputBuffer *self, int num)
|
||||
{
|
||||
self->currbyte += num;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int _CSInputPeekByteWithoutEOF(CSInputBuffer *self, int offs)
|
||||
{
|
||||
return self->buffer[self->currbyte + offs];
|
||||
}
|
||||
|
||||
static inline bool CSInputPeekByte(CSInputBuffer *self, int offs, int &byte)
|
||||
{
|
||||
_CSInputCheckAndFillBuffer(self);
|
||||
if (offs >= _CSInputBytesLeftInBuffer(self))
|
||||
{
|
||||
byte = -1;
|
||||
return true;
|
||||
}
|
||||
byte = _CSInputPeekByteWithoutEOF(self, offs);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool CSInputNextByte(CSInputBuffer *self, int &byte)
|
||||
{
|
||||
if (!CSInputPeekByte(self, 0, byte))
|
||||
return false;
|
||||
if (!CSInputSkipBytes(self, 1))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool CSInputAtEOF(CSInputBuffer *self, bool &isEOF)
|
||||
{
|
||||
_CSInputCheckAndFillBuffer(self);
|
||||
isEOF = _CSInputBytesLeftInBuffer(self) <= 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Bitstream reading
|
||||
|
||||
bool _CSInputFillBits(CSInputBuffer *self);
|
||||
bool _CSInputFillBitsLE(CSInputBuffer *self);
|
||||
|
||||
bool CSInputNextBit(CSInputBuffer *self, unsigned int &bit);
|
||||
bool CSInputNextBitLE(CSInputBuffer *self, unsigned int &bit);
|
||||
bool CSInputNextBitString(CSInputBuffer *self, int numbits, unsigned int &bits);
|
||||
bool CSInputNextBitStringLE(CSInputBuffer *self, int numbits, unsigned int &bits);
|
||||
bool CSInputNextLongBitString(CSInputBuffer *self, int numbits, unsigned int &bits);
|
||||
bool CSInputNextLongBitStringLE(CSInputBuffer *self, int numbits, unsigned int &bits);
|
||||
|
||||
bool CSInputSkipBits(CSInputBuffer *self, int numbits);
|
||||
bool CSInputSkipBitsLE(CSInputBuffer *self, int numbits);
|
||||
bool CSInputOnByteBoundary(CSInputBuffer *self);
|
||||
bool CSInputSkipToByteBoundary(CSInputBuffer *self);
|
||||
bool CSInputSkipTo16BitBoundary(CSInputBuffer *self);
|
||||
|
||||
static inline unsigned int CSInputBitsLeftInBuffer(CSInputBuffer *self)
|
||||
{
|
||||
_CSInputCheckAndFillBuffer(self);
|
||||
return _CSInputBytesLeftInBuffer(self) * 8 + (self->numbits & 7);
|
||||
}
|
||||
|
||||
static inline bool _CSInputCheckAndFillBits(CSInputBuffer *self, int numbits)
|
||||
{
|
||||
if (static_cast<unsigned int>(numbits) > self->numbits)
|
||||
return _CSInputFillBits(self);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool _CSInputCheckAndFillBitsLE(CSInputBuffer *self, int numbits)
|
||||
{
|
||||
if (static_cast<unsigned int>(numbits) > self->numbits)
|
||||
return _CSInputFillBitsLE(self);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool CSInputPeekBitString(CSInputBuffer *self, int numbits, unsigned int &bits)
|
||||
{
|
||||
if (numbits == 0)
|
||||
{
|
||||
bits = 0;
|
||||
return true;
|
||||
}
|
||||
if (!_CSInputCheckAndFillBits(self, numbits))
|
||||
return false;
|
||||
bits = self->bits >> (32 - numbits);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool CSInputPeekBitStringLE(CSInputBuffer *self, int numbits, unsigned int &bits)
|
||||
{
|
||||
if (numbits == 0)
|
||||
{
|
||||
bits = 0;
|
||||
return true;
|
||||
}
|
||||
if (!_CSInputCheckAndFillBitsLE(self, numbits))
|
||||
return false;
|
||||
bits = self->bits&((1 << numbits) - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool CSInputSkipPeekedBits(CSInputBuffer *self, int numbits)
|
||||
{
|
||||
int numbytes = (numbits - (self->numbits & 7) + 7) >> 3;
|
||||
CSInputSkipBytes(self, numbytes);
|
||||
|
||||
if (_CSInputBytesLeftInBuffer(self) < 0)
|
||||
return false;
|
||||
|
||||
self->bits <<= numbits;
|
||||
self->numbits -= numbits;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool CSInputSkipPeekedBitsLE(CSInputBuffer *self, int numbits)
|
||||
{
|
||||
int numbytes = (numbits - (self->numbits & 7) + 7) >> 3;
|
||||
CSInputSkipBytes(self, numbytes);
|
||||
|
||||
if (_CSInputBytesLeftInBuffer(self) < 0)
|
||||
return false;
|
||||
|
||||
self->bits >>= numbits;
|
||||
self->numbits -= numbits;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Multibyte reading
|
||||
|
||||
#define CSInputNextValueImpl(type,name,conv) \
|
||||
static inline type name(CSInputBuffer *self) \
|
||||
{ \
|
||||
_CSInputCheckAndFillBuffer(self); \
|
||||
type val=conv(self->buffer+self->currbyte); \
|
||||
CSInputSkipBytes(self,sizeof(type)); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
CSInputNextValueImpl(int16_t, CSInputNextInt16LE, ParseInt16LE)
|
||||
CSInputNextValueImpl(int32_t, CSInputNextInt32LE, ParseInt32LE)
|
||||
CSInputNextValueImpl(uint16_t, CSInputNextUInt16LE, ParseUInt16LE)
|
||||
CSInputNextValueImpl(uint32_t, CSInputNextUInt32LE, ParseUInt32LE)
|
||||
CSInputNextValueImpl(int16_t, CSInputNextInt16BE, ParseInt16BE)
|
||||
CSInputNextValueImpl(int32_t, CSInputNextInt32BE, ParseInt32BE)
|
||||
CSInputNextValueImpl(uint16_t, CSInputNextUInt16BE, ParseUInt16BE)
|
||||
CSInputNextValueImpl(uint32_t, CSInputNextUInt32BE, ParseUInt32BE)
|
||||
|
||||
|
||||
|
129
unpacktool/CompactProLZHDecompressor.cpp
Normal file
129
unpacktool/CompactProLZHDecompressor.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "CompactProLZHDecompressor.h"
|
||||
|
||||
#include "CSInputBuffer.h"
|
||||
#include "PrefixCode.h"
|
||||
|
||||
CompactProLZHDecompressor::CompactProLZHDecompressor(int blocksize)
|
||||
: LZSSDecompressor(8192)
|
||||
, literalcode(nullptr)
|
||||
, lengthcode(nullptr)
|
||||
, offsetcode(nullptr)
|
||||
, blocksize(blocksize)
|
||||
, blockcount(0)
|
||||
, blockstart(0)
|
||||
{
|
||||
}
|
||||
|
||||
CompactProLZHDecompressor::~CompactProLZHDecompressor()
|
||||
{
|
||||
delete literalcode;
|
||||
delete lengthcode;
|
||||
delete offsetcode;
|
||||
}
|
||||
|
||||
bool CompactProLZHDecompressor::resetLZSSHandle()
|
||||
{
|
||||
blockcount = blocksize;
|
||||
blockstart = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompactProLZHDecompressor::nextLiteralOrOffset(int *offset, int *length, int &result)
|
||||
{
|
||||
if (blockcount >= blocksize)
|
||||
{
|
||||
if (blockstart)
|
||||
{
|
||||
// Don't let your bad implementations leak into your file formats, people!
|
||||
if (!CSInputSkipToByteBoundary(input))
|
||||
return false;
|
||||
|
||||
if ((CSInputBufferOffset(input) - blockstart) & 1)
|
||||
{
|
||||
if (!CSInputSkipBytes(input, 3))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CSInputSkipBytes(input, 2))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete literalcode;
|
||||
delete lengthcode;
|
||||
delete offsetcode;
|
||||
literalcode = lengthcode = offsetcode = nullptr;
|
||||
literalcode = allocAndParseCodeOfSize(256);
|
||||
if (!literalcode)
|
||||
return false;
|
||||
lengthcode = allocAndParseCodeOfSize(64);
|
||||
if (!lengthcode)
|
||||
return false;
|
||||
offsetcode = allocAndParseCodeOfSize(128);
|
||||
if (!offsetcode)
|
||||
return false;
|
||||
blockcount = 0;
|
||||
blockstart = CSInputBufferOffset(input);
|
||||
}
|
||||
|
||||
unsigned int nextBit;
|
||||
if (!CSInputNextBit(input, nextBit))
|
||||
return false;
|
||||
|
||||
if (nextBit)
|
||||
{
|
||||
blockcount += 2;
|
||||
return CSInputNextSymbolUsingCode(input, literalcode, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
blockcount += 3;
|
||||
|
||||
if (!CSInputNextSymbolUsingCode(input, lengthcode, *length))
|
||||
return false;
|
||||
|
||||
int offsetSym;
|
||||
if (!CSInputNextSymbolUsingCode(input, offsetcode, offsetSym))
|
||||
return false;
|
||||
|
||||
unsigned int offsetBits;
|
||||
if (!CSInputNextBitString(input, 6, offsetBits))
|
||||
return false;
|
||||
|
||||
*offset = offsetSym << 6;
|
||||
*offset |= offsetBits;
|
||||
|
||||
result = XADLZSSMatch;
|
||||
return true;
|
||||
}
|
||||
|
||||
result = XADLZSSEnd;
|
||||
return true;
|
||||
}
|
||||
|
||||
XADPrefixCode *CompactProLZHDecompressor::allocAndParseCodeOfSize(int size)
|
||||
{
|
||||
int numbytes;
|
||||
if (!CSInputNextByte(input, numbytes))
|
||||
return nullptr;
|
||||
|
||||
if (numbytes * 2 > size)
|
||||
return nullptr;
|
||||
|
||||
std::vector<int> codelengths;
|
||||
codelengths.resize(size);
|
||||
|
||||
for (int i = 0; i < numbytes; i++)
|
||||
{
|
||||
int val;
|
||||
if (!CSInputNextByte(input, val))
|
||||
return nullptr;
|
||||
codelengths[2 * i] = val >> 4;
|
||||
codelengths[2 * i + 1] = val & 0x0f;
|
||||
}
|
||||
for (int i = numbytes * 2; i < size; i++) codelengths[i] = 0;
|
||||
|
||||
return XADPrefixCode::prefixCodeWithLengths(codelengths.data(), size, 15, true);
|
||||
}
|
23
unpacktool/CompactProLZHDecompressor.h
Normal file
23
unpacktool/CompactProLZHDecompressor.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "LZSSDecompressor.h"
|
||||
#include "IFileReader.h"
|
||||
|
||||
class XADPrefixCode;
|
||||
|
||||
class CompactProLZHDecompressor : public LZSSDecompressor
|
||||
{
|
||||
public:
|
||||
explicit CompactProLZHDecompressor(int blocksize);
|
||||
~CompactProLZHDecompressor();
|
||||
|
||||
private:
|
||||
bool nextLiteralOrOffset(int *offset, int *length, int &result) override;
|
||||
bool resetLZSSHandle() override;
|
||||
|
||||
XADPrefixCode *allocAndParseCodeOfSize(int size);
|
||||
|
||||
XADPrefixCode *literalcode, *lengthcode, *offsetcode;
|
||||
int blocksize, blockcount;
|
||||
IFileReader::FilePos_t blockstart;
|
||||
};
|
53
unpacktool/CompactProLZHRLEDecompressor.cpp
Normal file
53
unpacktool/CompactProLZHRLEDecompressor.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "CompactProLZHRLEDecompressor.h"
|
||||
#include "CompactProRLEDecompressor.h"
|
||||
#include "CompactProLZHDecompressor.h"
|
||||
#include "DecompressorProxyReader.h"
|
||||
|
||||
CompactProLZHRLEDecompressor::CompactProLZHRLEDecompressor(int blockSize)
|
||||
: m_proxyReader(nullptr)
|
||||
, m_proxyInput(nullptr)
|
||||
, m_lzhDecompressor(nullptr)
|
||||
, m_rleDecompressor(nullptr)
|
||||
, m_blockSize(blockSize)
|
||||
{
|
||||
}
|
||||
|
||||
CompactProLZHRLEDecompressor::~CompactProLZHRLEDecompressor()
|
||||
{
|
||||
delete m_rleDecompressor;
|
||||
delete m_lzhDecompressor;
|
||||
delete m_proxyReader;
|
||||
CSInputBufferFree(m_proxyInput);
|
||||
}
|
||||
|
||||
bool CompactProLZHRLEDecompressor::Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize)
|
||||
{
|
||||
delete m_rleDecompressor;
|
||||
delete m_lzhDecompressor;
|
||||
delete m_proxyReader;
|
||||
CSInputBufferFree(m_proxyInput);
|
||||
m_rleDecompressor = nullptr;
|
||||
m_lzhDecompressor = nullptr;
|
||||
m_proxyReader = nullptr;
|
||||
m_proxyInput = nullptr;
|
||||
|
||||
m_lzhDecompressor = nullptr;
|
||||
m_proxyReader = nullptr;
|
||||
m_lzhDecompressor = new CompactProLZHDecompressor(m_blockSize);
|
||||
m_proxyReader = new DecompressorProxyReader(m_lzhDecompressor);
|
||||
// FIXME maybe: This is a really stupid workaround to compensate for the decompressor not actually supporting EOFs, and the fact that we don't know the intermediate size
|
||||
m_proxyInput = CSInputBufferAlloc(m_proxyReader, 1);
|
||||
m_rleDecompressor = new CompactProRLEDecompressor();
|
||||
|
||||
if (!m_lzhDecompressor->Reset(input, compressedSize, 0))
|
||||
return false;
|
||||
if (!m_rleDecompressor->Reset(m_proxyInput, 0, decompressedSize))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompactProLZHRLEDecompressor::ReadBytes(void *dest, size_t numBytes)
|
||||
{
|
||||
return m_rleDecompressor->ReadBytes(dest, numBytes);
|
||||
}
|
26
unpacktool/CompactProLZHRLEDecompressor.h
Normal file
26
unpacktool/CompactProLZHRLEDecompressor.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "IDecompressor.h"
|
||||
#include "PrefixCode.h"
|
||||
|
||||
struct IFileReader;
|
||||
class IDecompressor;
|
||||
class CompactProLZHDecompressor;
|
||||
class CompactProRLEDecompressor;
|
||||
|
||||
class CompactProLZHRLEDecompressor : public IDecompressor
|
||||
{
|
||||
public:
|
||||
explicit CompactProLZHRLEDecompressor(int blockSize);
|
||||
~CompactProLZHRLEDecompressor();
|
||||
|
||||
bool Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize) override;
|
||||
bool ReadBytes(void *dest, size_t numBytes) override;
|
||||
|
||||
private:
|
||||
IFileReader *m_proxyReader;
|
||||
CSInputBuffer *m_proxyInput;
|
||||
IDecompressor *m_lzhDecompressor;
|
||||
IDecompressor *m_rleDecompressor;
|
||||
int m_blockSize;
|
||||
};
|
213
unpacktool/CompactProParser.cpp
Normal file
213
unpacktool/CompactProParser.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
#include "CompactProParser.h"
|
||||
|
||||
#include "IFileReader.h"
|
||||
#include "UPByteSwap.h"
|
||||
#include "PLBigEndian.h"
|
||||
#include "CRC.h"
|
||||
|
||||
#include "ArchiveDescription.h"
|
||||
#include "StringCommon.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
bool CompactProParser::Check(IFileReader &reader)
|
||||
{
|
||||
uint8_t bytes[8];
|
||||
|
||||
if (!reader.SeekStart(0))
|
||||
return false;
|
||||
|
||||
if (!reader.ReadExact(bytes, sizeof(bytes)))
|
||||
return false;
|
||||
|
||||
if (bytes[0] != 1)
|
||||
return false;
|
||||
|
||||
uint32_t offset = ParseUInt32BE(bytes + 4);
|
||||
|
||||
if (offset > reader.FileSize() - 7)
|
||||
return false;
|
||||
|
||||
if (!reader.SeekStart(offset))
|
||||
return false;
|
||||
|
||||
uint8_t buf[256];
|
||||
|
||||
if (!reader.ReadExact(buf, 4))
|
||||
return false;
|
||||
|
||||
uint32_t correctcrc = ParseUInt32BE(buf);
|
||||
|
||||
uint32_t crc = 0xffffffff;
|
||||
if (!reader.ReadExact(buf, 3))
|
||||
return false;
|
||||
|
||||
crc = XADCalculateCRC(crc, buf, 3, XADCRCTable_edb88320);
|
||||
|
||||
int numentries = ParseUInt16BE(buf);
|
||||
int commentsize = buf[2];
|
||||
|
||||
if (!reader.ReadExact(buf, commentsize))
|
||||
return false;
|
||||
|
||||
crc = XADCalculateCRC(crc, buf, commentsize, XADCRCTable_edb88320);
|
||||
|
||||
for (int i = 0; i < numentries; i++)
|
||||
{
|
||||
uint8_t namelen;
|
||||
if (!reader.ReadExact(&namelen, 1))
|
||||
return false;
|
||||
|
||||
crc = XADCRC(crc, namelen, XADCRCTable_edb88320);
|
||||
|
||||
if (!reader.ReadExact(buf, namelen & 0x7f))
|
||||
return false;
|
||||
|
||||
crc = XADCalculateCRC(crc, buf, namelen & 0x7f, XADCRCTable_edb88320);
|
||||
|
||||
int metadatasize;
|
||||
if (namelen & 0x80)
|
||||
metadatasize = 2;
|
||||
else
|
||||
metadatasize = 45;
|
||||
|
||||
if (!reader.ReadExact(buf, metadatasize))
|
||||
return false;
|
||||
|
||||
crc = XADCalculateCRC(crc, buf, metadatasize, XADCRCTable_edb88320);
|
||||
}
|
||||
|
||||
if (crc == correctcrc)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct CompactProArchiveHeader
|
||||
{
|
||||
uint8_t m_marker;
|
||||
uint8_t m_volume;
|
||||
BEUInt16_t m_xmagic;
|
||||
BEUInt32_t m_catalogOffset;
|
||||
};
|
||||
|
||||
struct CompactProCatalogHeader
|
||||
{
|
||||
BEUInt32_t m_headerCRC;
|
||||
BEUInt16_t m_numEntries;
|
||||
uint8_t m_commentLength;
|
||||
};
|
||||
|
||||
struct CompactProCatalogFileEntry
|
||||
{
|
||||
uint8_t m_volume;
|
||||
BEUInt32_t m_fileOffset;
|
||||
uint8_t m_fileType[4];
|
||||
uint8_t m_fileCreator[4];
|
||||
BEUInt32_t m_creationDate;
|
||||
BEUInt32_t m_modificationDate;
|
||||
BEUInt16_t m_finderFlags;
|
||||
BEUInt32_t m_crc;
|
||||
BEUInt16_t m_flags;
|
||||
|
||||
BEUInt32_t m_resUncompressedSize;
|
||||
BEUInt32_t m_dataUncompressedSize;
|
||||
BEUInt32_t m_resCompressedSize;
|
||||
BEUInt32_t m_dataCompressedSize;
|
||||
};
|
||||
|
||||
ArchiveItemList *CompactProParser::Parse(IFileReader &reader)
|
||||
{
|
||||
if (!reader.SeekStart(0))
|
||||
return nullptr;
|
||||
|
||||
CompactProArchiveHeader arcHeader;
|
||||
if (!reader.ReadExact(&arcHeader, sizeof(arcHeader)))
|
||||
return nullptr;
|
||||
|
||||
if (!reader.SeekStart(arcHeader.m_catalogOffset))
|
||||
return nullptr;
|
||||
|
||||
CompactProCatalogHeader catHeader;
|
||||
if (!reader.ReadExact(&catHeader, sizeof(catHeader)))
|
||||
return nullptr;
|
||||
|
||||
if (!reader.SeekCurrent(catHeader.m_commentLength))
|
||||
return nullptr;
|
||||
|
||||
return ParseDirectory(catHeader.m_numEntries, reader);
|
||||
}
|
||||
|
||||
ArchiveItemList *CompactProParser::ParseDirectory(uint32_t numEntries, IFileReader &reader)
|
||||
{
|
||||
ArchiveItemList itemList;
|
||||
|
||||
for (uint32_t itemIndex = 0; itemIndex < numEntries; itemIndex++)
|
||||
{
|
||||
itemList.m_items.push_back(ArchiveItem());
|
||||
ArchiveItem &item = itemList.m_items.back();
|
||||
|
||||
uint8_t nameLengthAndDirFlag;
|
||||
if (!reader.ReadExact(&nameLengthAndDirFlag, 1))
|
||||
return nullptr;
|
||||
|
||||
uint8_t nameLength = (nameLengthAndDirFlag & 0x7f);
|
||||
|
||||
uint8_t fname[127];
|
||||
if (!reader.ReadExact(fname, nameLength))
|
||||
return nullptr;
|
||||
|
||||
StringCommon::ConvertMacRomanFileName(item.m_fileNameUTF8, fname, nameLength);
|
||||
|
||||
if (nameLengthAndDirFlag & 0x80)
|
||||
{
|
||||
BEUInt16_t numChildrenBE;
|
||||
if (!reader.ReadExact(&numChildrenBE, sizeof(numChildrenBE)))
|
||||
return nullptr;
|
||||
|
||||
uint16_t numChildren = numChildrenBE;
|
||||
|
||||
if (numChildren > numEntries - itemIndex)
|
||||
return nullptr;
|
||||
|
||||
item.m_isDirectory = true;
|
||||
|
||||
ArchiveItemList *children = ParseDirectory(numChildren, reader);
|
||||
if (!children)
|
||||
return nullptr;
|
||||
|
||||
item.m_children = children;
|
||||
numEntries -= numChildren;
|
||||
}
|
||||
else
|
||||
{
|
||||
CompactProCatalogFileEntry fileEntry;
|
||||
if (!reader.ReadExact(&fileEntry, sizeof(fileEntry)))
|
||||
return nullptr;
|
||||
|
||||
if (fileEntry.m_resUncompressedSize)
|
||||
{
|
||||
item.m_resourceForkDesc.m_compressedSize = fileEntry.m_resCompressedSize;
|
||||
item.m_resourceForkDesc.m_uncompressedSize = fileEntry.m_resUncompressedSize;
|
||||
item.m_resourceForkDesc.m_filePosition = fileEntry.m_fileOffset;
|
||||
item.m_resourceForkDesc.m_compressionMethod = (fileEntry.m_flags & 2) ? CompressionMethods::kCompactProLZHRLE : CompressionMethods::kCompactProRLE;
|
||||
}
|
||||
|
||||
if (fileEntry.m_dataUncompressedSize)
|
||||
{
|
||||
item.m_dataForkDesc.m_compressedSize = fileEntry.m_dataCompressedSize;
|
||||
item.m_dataForkDesc.m_uncompressedSize = fileEntry.m_dataUncompressedSize;
|
||||
item.m_dataForkDesc.m_filePosition = fileEntry.m_fileOffset + fileEntry.m_resCompressedSize;
|
||||
item.m_dataForkDesc.m_compressionMethod = (fileEntry.m_flags & 4) ? CompressionMethods::kCompactProLZHRLE : CompressionMethods::kCompactProRLE;
|
||||
}
|
||||
|
||||
item.m_macProperties.m_creationDate = fileEntry.m_creationDate;
|
||||
item.m_macProperties.m_modifiedDate = fileEntry.m_modificationDate;
|
||||
memcpy(item.m_macProperties.m_fileCreator, fileEntry.m_fileCreator, 4);
|
||||
memcpy(item.m_macProperties.m_fileType, fileEntry.m_fileType, 4);
|
||||
item.m_macProperties.m_finderFlags = fileEntry.m_finderFlags;
|
||||
}
|
||||
}
|
||||
|
||||
return new ArchiveItemList(std::move(itemList));
|
||||
}
|
15
unpacktool/CompactProParser.h
Normal file
15
unpacktool/CompactProParser.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "IArchiveParser.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class CompactProParser : public IArchiveParser
|
||||
{
|
||||
public:
|
||||
bool Check(IFileReader &reader) override;
|
||||
ArchiveItemList *Parse(IFileReader &reader) override;
|
||||
|
||||
private:
|
||||
ArchiveItemList *ParseDirectory(uint32_t numEntries, IFileReader &reader);
|
||||
};
|
112
unpacktool/CompactProRLEDecompressor.cpp
Normal file
112
unpacktool/CompactProRLEDecompressor.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "CompactProRLEDecompressor.h"
|
||||
|
||||
#include "CSInputBuffer.h"
|
||||
|
||||
CompactProRLEDecompressor::CompactProRLEDecompressor()
|
||||
: input(nullptr)
|
||||
, saved(0)
|
||||
, repeat(0)
|
||||
, halfescaped(false)
|
||||
{
|
||||
}
|
||||
|
||||
CompactProRLEDecompressor::~CompactProRLEDecompressor()
|
||||
{
|
||||
}
|
||||
|
||||
bool CompactProRLEDecompressor::Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize)
|
||||
{
|
||||
saved = 0;
|
||||
repeat = 0;
|
||||
halfescaped = false;
|
||||
this->input = input;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompactProRLEDecompressor::ReadBytes(void *dest, size_t numBytes)
|
||||
{
|
||||
uint8_t *destBytes = static_cast<uint8_t*>(dest);
|
||||
|
||||
while (numBytes)
|
||||
{
|
||||
if (!EmitOneByte(*destBytes))
|
||||
return false;
|
||||
|
||||
numBytes--;
|
||||
destBytes++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompactProRLEDecompressor::EmitOneByte(uint8_t &b)
|
||||
{
|
||||
if (repeat)
|
||||
{
|
||||
repeat--;
|
||||
b = saved;
|
||||
return true;
|
||||
}
|
||||
|
||||
int byte;
|
||||
if (halfescaped)
|
||||
{
|
||||
byte = 0x81;
|
||||
halfescaped = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CSInputNextByte(input, byte))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (byte == 0x81)
|
||||
{
|
||||
if (!CSInputNextByte(input, byte))
|
||||
return false;
|
||||
|
||||
if (byte == 0x82)
|
||||
{
|
||||
if (!CSInputNextByte(input, byte))
|
||||
return false;
|
||||
|
||||
if (byte != 0)
|
||||
{
|
||||
repeat = byte - 2; // ?
|
||||
b = saved;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
repeat = 1;
|
||||
saved = 0x82;
|
||||
b = 0x81;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (byte == 0x81)
|
||||
{
|
||||
halfescaped = true;
|
||||
saved = 0x81;
|
||||
b = saved;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
repeat = 1;
|
||||
saved = byte;
|
||||
b = 0x81;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
saved = byte;
|
||||
b = saved;
|
||||
return true;
|
||||
}
|
||||
}
|
20
unpacktool/CompactProRLEDecompressor.h
Normal file
20
unpacktool/CompactProRLEDecompressor.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "IDecompressor.h"
|
||||
|
||||
class CompactProRLEDecompressor : public IDecompressor
|
||||
{
|
||||
public:
|
||||
CompactProRLEDecompressor();
|
||||
~CompactProRLEDecompressor();
|
||||
|
||||
bool Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize);
|
||||
bool ReadBytes(void *dest, size_t numBytes);
|
||||
|
||||
public:
|
||||
CSInputBuffer *input;
|
||||
int saved, repeat;
|
||||
bool halfescaped;
|
||||
|
||||
bool EmitOneByte(uint8_t &b);
|
||||
};
|
45
unpacktool/DecompressorProxyReader.cpp
Normal file
45
unpacktool/DecompressorProxyReader.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "DecompressorProxyReader.h"
|
||||
|
||||
#include "IDecompressor.h"
|
||||
|
||||
DecompressorProxyReader::DecompressorProxyReader(IDecompressor *decompressor)
|
||||
: m_decompressor(decompressor)
|
||||
, m_currentPosition(0)
|
||||
{
|
||||
}
|
||||
|
||||
size_t DecompressorProxyReader::Read(void *buffer, size_t sz)
|
||||
{
|
||||
if (!m_decompressor->ReadBytes(buffer, sz))
|
||||
return 0;
|
||||
|
||||
m_currentPosition += sz;
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
size_t DecompressorProxyReader::FileSize() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DecompressorProxyReader::SeekStart(FilePos_t pos)
|
||||
{
|
||||
return pos == m_currentPosition;
|
||||
}
|
||||
|
||||
bool DecompressorProxyReader::SeekCurrent(FilePos_t pos)
|
||||
{
|
||||
return pos == 0;
|
||||
}
|
||||
|
||||
bool DecompressorProxyReader::SeekEnd(FilePos_t pos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IFileReader::FilePos_t DecompressorProxyReader::GetPosition() const
|
||||
{
|
||||
return m_currentPosition;
|
||||
}
|
||||
|
22
unpacktool/DecompressorProxyReader.h
Normal file
22
unpacktool/DecompressorProxyReader.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "IFileReader.h"
|
||||
|
||||
class IDecompressor;
|
||||
|
||||
class DecompressorProxyReader final : public IFileReader
|
||||
{
|
||||
public:
|
||||
explicit DecompressorProxyReader(IDecompressor *decompressor);
|
||||
|
||||
size_t Read(void *buffer, size_t sz) override;
|
||||
size_t FileSize() const override;
|
||||
bool SeekStart(FilePos_t pos) override;
|
||||
bool SeekCurrent(FilePos_t pos) override;
|
||||
bool SeekEnd(FilePos_t pos) override;
|
||||
FilePos_t GetPosition() const override;
|
||||
|
||||
private:
|
||||
FilePos_t m_currentPosition;
|
||||
IDecompressor *m_decompressor;
|
||||
};
|
11
unpacktool/IArchiveParser.h
Normal file
11
unpacktool/IArchiveParser.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
struct IFileReader;
|
||||
struct ArchiveItemList;
|
||||
|
||||
class IArchiveParser
|
||||
{
|
||||
public:
|
||||
virtual bool Check(IFileReader &reader) = 0;
|
||||
virtual ArchiveItemList *Parse(IFileReader &reader) = 0;
|
||||
};
|
13
unpacktool/IDecompressor.h
Normal file
13
unpacktool/IDecompressor.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct CSInputBuffer;
|
||||
|
||||
class IDecompressor
|
||||
{
|
||||
public:
|
||||
virtual ~IDecompressor() { }
|
||||
virtual bool Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize) = 0;
|
||||
virtual bool ReadBytes(void *dest, size_t numBytes) = 0;
|
||||
};
|
22
unpacktool/IFileReader.h
Normal file
22
unpacktool/IFileReader.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct IFileReader
|
||||
{
|
||||
public:
|
||||
typedef int64_t FilePos_t;
|
||||
typedef uint64_t UFilePos_t;
|
||||
|
||||
virtual size_t Read(void *buffer, size_t sz) = 0;
|
||||
virtual size_t FileSize() const = 0;
|
||||
virtual bool SeekStart(FilePos_t pos) = 0;
|
||||
virtual bool SeekCurrent(FilePos_t pos) = 0;
|
||||
virtual bool SeekEnd(FilePos_t pos) = 0;
|
||||
virtual FilePos_t GetPosition() const = 0;
|
||||
|
||||
inline bool ReadExact(void *buffer, size_t sz)
|
||||
{
|
||||
return this->Read(buffer, sz) == sz;
|
||||
}
|
||||
};
|
529
unpacktool/LICENSE.txt
Normal file
529
unpacktool/LICENSE.txt
Normal file
@@ -0,0 +1,529 @@
|
||||
This program, "The Unarchiver", its accompanying libraries, "XADMaster"
|
||||
and "UniversalDetector", and the various smaller utility programs, such
|
||||
as "unar" and "lsar", are distributed under the GNU Lesser General
|
||||
Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
"UniversalDetector" is also available under other licenses, such as the
|
||||
Mozilla Public License. Please refer to the files in its subdirectory
|
||||
for further information.
|
||||
|
||||
The GNU Lesser General Public License might be too restrictive for some
|
||||
users of this code. Parts of the code are derived from earlier
|
||||
LGPL-licensed code and will as such always be bound by the LGPL, but
|
||||
some parts of the code are developed from scratch by the author of The
|
||||
Unarchiver, Dag Ågren, and can thus be made available under a more
|
||||
permissive license. For simplicity, everything is currently licensed
|
||||
under the LGPL, but if you are interested in using any code from this
|
||||
project under another license, please contact the author for further
|
||||
information.
|
||||
|
||||
- Dag Ågren, <paracelsus@gmail.com>
|
||||
|
||||
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
83
unpacktool/LZSSDecompressor.cpp
Normal file
83
unpacktool/LZSSDecompressor.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "LZSSDecompressor.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
bool LZSSDecompressor::ReadBytes(void *dest, size_t numBytes)
|
||||
{
|
||||
for (size_t i = 0; i < numBytes; i++)
|
||||
{
|
||||
if (!ReadOneByte(static_cast<uint8_t*>(dest)[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LZSSDecompressor::Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize)
|
||||
{
|
||||
this->input = input;
|
||||
|
||||
windowbuffer = new uint8_t[windowsize];
|
||||
windowmask = windowsize - 1; // Assumes windows are always power-of-two sized!
|
||||
|
||||
matchlength = 0;
|
||||
matchoffset = 0;
|
||||
memset(windowbuffer, 0, windowmask + 1);
|
||||
|
||||
return this->resetLZSSHandle();
|
||||
}
|
||||
|
||||
LZSSDecompressor::LZSSDecompressor(int windowsize)
|
||||
: windowbuffer(nullptr)
|
||||
, windowmask(0)
|
||||
, matchlength(0)
|
||||
, matchoffset(0)
|
||||
, input(nullptr)
|
||||
, pos(0)
|
||||
, windowsize(windowsize)
|
||||
{
|
||||
}
|
||||
|
||||
LZSSDecompressor::~LZSSDecompressor()
|
||||
{
|
||||
delete[] windowbuffer;
|
||||
}
|
||||
|
||||
|
||||
bool LZSSDecompressor::ReadOneByte(uint8_t &b)
|
||||
{
|
||||
if (!matchlength)
|
||||
{
|
||||
int offset, length;
|
||||
int val;
|
||||
if (!this->nextLiteralOrOffset(&offset, &length, val))
|
||||
return false;
|
||||
|
||||
if (val >= 0)
|
||||
{
|
||||
windowbuffer[pos&windowmask] = val;
|
||||
b = val;
|
||||
pos++;
|
||||
return true;
|
||||
}
|
||||
else if (val == XADLZSSEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
matchlength = length;
|
||||
matchoffset = (int)(pos - offset);
|
||||
}
|
||||
}
|
||||
|
||||
matchlength--;
|
||||
|
||||
uint8_t byte = windowbuffer[matchoffset++&windowmask];
|
||||
|
||||
windowbuffer[pos&windowmask] = byte;
|
||||
pos++;
|
||||
|
||||
b = byte;
|
||||
return true;
|
||||
}
|
40
unpacktool/LZSSDecompressor.h
Normal file
40
unpacktool/LZSSDecompressor.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "IDecompressor.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define XADLZSSMatch -1
|
||||
#define XADLZSSEnd -2
|
||||
|
||||
|
||||
class LZSSDecompressor : public IDecompressor
|
||||
{
|
||||
public:
|
||||
explicit LZSSDecompressor(int windowsize);
|
||||
~LZSSDecompressor();
|
||||
|
||||
bool Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize) override;
|
||||
bool ReadBytes(void *dest, size_t numBytes) override;
|
||||
|
||||
uint8_t ByteFromWindow(size_t absolutepos);
|
||||
|
||||
private:
|
||||
virtual bool nextLiteralOrOffset(int *offset, int *length, int &result) = 0;
|
||||
virtual bool resetLZSSHandle() = 0;
|
||||
|
||||
bool ReadOneByte(uint8_t &b);
|
||||
|
||||
uint8_t *windowbuffer;
|
||||
int windowmask,matchlength,matchoffset;
|
||||
int windowsize;
|
||||
int pos;
|
||||
|
||||
protected:
|
||||
CSInputBuffer *input;
|
||||
};
|
||||
|
||||
inline uint8_t LZSSDecompressor::ByteFromWindow(size_t absolutepos)
|
||||
{
|
||||
return this->windowbuffer[absolutepos&this->windowmask];
|
||||
}
|
159
unpacktool/LZW.cpp
Normal file
159
unpacktool/LZW.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "LZW.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
LZW *AllocLZW(int maxsymbols,int reservedsymbols)
|
||||
{
|
||||
LZW *self=(LZW *)malloc(sizeof(LZW)+sizeof(LZWTreeNode)*(maxsymbols - 1));
|
||||
if(!self) return 0;
|
||||
|
||||
self->maxsymbols=maxsymbols;
|
||||
self->reservedsymbols=reservedsymbols;
|
||||
|
||||
self->buffer=NULL;
|
||||
self->buffersize=0;
|
||||
|
||||
for(int i=0;i<256;i++)
|
||||
{
|
||||
self->nodes[i].chr=i;
|
||||
self->nodes[i].parent=-1;
|
||||
}
|
||||
|
||||
ClearLZWTable(self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void FreeLZW(LZW *self)
|
||||
{
|
||||
if(self)
|
||||
{
|
||||
free(self->buffer);
|
||||
free(self);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearLZWTable(LZW *self)
|
||||
{
|
||||
self->numsymbols=256+self->reservedsymbols;
|
||||
self->prevsymbol=-1;
|
||||
self->symbolsize=9; // TODO: technically this depends on reservedsymbols
|
||||
}
|
||||
|
||||
static uint8_t FindFirstByte(LZWTreeNode *nodes,int symbol)
|
||||
{
|
||||
while(nodes[symbol].parent>=0) symbol=nodes[symbol].parent;
|
||||
return nodes[symbol].chr;
|
||||
}
|
||||
|
||||
int NextLZWSymbol(LZW *self,int symbol)
|
||||
{
|
||||
if(self->prevsymbol<0)
|
||||
{
|
||||
if(symbol>=self->numsymbols) return LZWInvalidCodeError;
|
||||
self->prevsymbol=symbol;
|
||||
|
||||
return LZWNoError;
|
||||
}
|
||||
|
||||
int postfixbyte;
|
||||
if(symbol<self->numsymbols) postfixbyte=FindFirstByte(self->nodes,symbol);
|
||||
else if(symbol==self->numsymbols) postfixbyte=FindFirstByte(self->nodes,self->prevsymbol);
|
||||
else return LZWInvalidCodeError;
|
||||
|
||||
int parent=self->prevsymbol;
|
||||
self->prevsymbol=symbol;
|
||||
|
||||
if(!LZWSymbolListFull(self))
|
||||
{
|
||||
self->nodes[self->numsymbols].parent=parent;
|
||||
self->nodes[self->numsymbols].chr=postfixbyte;
|
||||
self->numsymbols++;
|
||||
|
||||
if(!LZWSymbolListFull(self))
|
||||
if((self->numsymbols&self->numsymbols-1)==0) self->symbolsize++;
|
||||
|
||||
return LZWNoError;
|
||||
}
|
||||
else
|
||||
{
|
||||
return LZWTooManyCodesError;
|
||||
}
|
||||
}
|
||||
|
||||
int ReplaceLZWSymbol(LZW *self,int oldsymbol,int symbol)
|
||||
{
|
||||
if(symbol>=self->numsymbols) return LZWInvalidCodeError;
|
||||
|
||||
self->nodes[oldsymbol].parent=self->prevsymbol;
|
||||
self->nodes[oldsymbol].chr=FindFirstByte(self->nodes,symbol);
|
||||
|
||||
self->prevsymbol=symbol;
|
||||
|
||||
return LZWNoError;
|
||||
}
|
||||
|
||||
int LZWOutputLength(LZW *self)
|
||||
{
|
||||
int symbol=self->prevsymbol;
|
||||
int n=0;
|
||||
|
||||
while(symbol>=0)
|
||||
{
|
||||
symbol=self->nodes[symbol].parent;
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int LZWOutputToBuffer(LZW *self,uint8_t *buffer)
|
||||
{
|
||||
int symbol=self->prevsymbol;
|
||||
int n=LZWOutputLength(self);
|
||||
buffer+=n;
|
||||
|
||||
while(symbol>=0)
|
||||
{
|
||||
*--buffer=self->nodes[symbol].chr;
|
||||
symbol=self->nodes[symbol].parent;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int LZWReverseOutputToBuffer(LZW *self,uint8_t *buffer)
|
||||
{
|
||||
int symbol=self->prevsymbol;
|
||||
int n=0;
|
||||
|
||||
while(symbol>=0)
|
||||
{
|
||||
*buffer++=self->nodes[symbol].chr;
|
||||
symbol=self->nodes[symbol].parent;
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int LZWOutputToInternalBuffer(LZW *self)
|
||||
{
|
||||
int symbol=self->prevsymbol;
|
||||
int n=LZWOutputLength(self);
|
||||
|
||||
if(n>self->buffersize)
|
||||
{
|
||||
free(self->buffer);
|
||||
self->buffersize+=1024;
|
||||
self->buffer=static_cast<uint8_t*>(malloc(self->buffersize));
|
||||
}
|
||||
|
||||
uint8_t *buffer=self->buffer+n;
|
||||
while(symbol>=0)
|
||||
{
|
||||
*--buffer=self->nodes[symbol].chr;
|
||||
symbol=self->nodes[symbol].parent;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
66
unpacktool/LZW.h
Normal file
66
unpacktool/LZW.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef __LZW_H__
|
||||
#define __LZW_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define LZWNoError 0
|
||||
#define LZWInvalidCodeError 1
|
||||
#define LZWTooManyCodesError 2
|
||||
|
||||
typedef struct LZWTreeNode
|
||||
{
|
||||
uint8_t chr;
|
||||
int parent;
|
||||
} LZWTreeNode;
|
||||
|
||||
typedef struct LZW
|
||||
{
|
||||
int numsymbols,maxsymbols,reservedsymbols;
|
||||
int prevsymbol;
|
||||
int symbolsize;
|
||||
|
||||
uint8_t *buffer;
|
||||
int buffersize;
|
||||
|
||||
LZWTreeNode nodes[1];
|
||||
} LZW;
|
||||
|
||||
LZW *AllocLZW(int maxsymbols,int reservedsymbols);
|
||||
void FreeLZW(LZW *self);
|
||||
void ClearLZWTable(LZW *self);
|
||||
|
||||
int NextLZWSymbol(LZW *self,int symbol);
|
||||
int ReplaceLZWSymbol(LZW *self,int oldsymbol,int symbol);
|
||||
int LZWOutputLength(LZW *self);
|
||||
int LZWOutputToBuffer(LZW *self,uint8_t *buffer);
|
||||
int LZWReverseOutputToBuffer(LZW *self,uint8_t *buffer);
|
||||
int LZWOutputToInternalBuffer(LZW *self);
|
||||
|
||||
static inline int LZWSuggestedSymbolSize(LZW *self)
|
||||
{
|
||||
return self->symbolsize;
|
||||
}
|
||||
|
||||
static inline uint8_t *LZWInternalBuffer(LZW *self)
|
||||
{
|
||||
return self->buffer;
|
||||
}
|
||||
|
||||
static inline int LZWSymbolCount(LZW *self)
|
||||
{
|
||||
return self->numsymbols;
|
||||
}
|
||||
|
||||
static inline bool LZWSymbolListFull(LZW *self)
|
||||
{
|
||||
return self->numsymbols==self->maxsymbols;
|
||||
}
|
||||
|
||||
static inline LZWTreeNode *LZWSymbols(LZW *self)
|
||||
{
|
||||
return self->nodes;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
81
unpacktool/LZWDecompressor.cpp
Normal file
81
unpacktool/LZWDecompressor.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "LZWDecompressor.h"
|
||||
|
||||
#include "CSInputBuffer.h"
|
||||
|
||||
LZWDecompressor::LZWDecompressor(int compressFlags)
|
||||
: input(nullptr)
|
||||
, compressFlags(compressFlags)
|
||||
, blockmode(false)
|
||||
, lzw(nullptr)
|
||||
, symbolcounter(0)
|
||||
, buffer(nullptr)
|
||||
, bufferend(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
LZWDecompressor::~LZWDecompressor()
|
||||
{
|
||||
FreeLZW(lzw);
|
||||
}
|
||||
|
||||
|
||||
bool LZWDecompressor::Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize)
|
||||
{
|
||||
this->input = input;
|
||||
blockmode = (compressFlags & 0x80) != 0;
|
||||
lzw = AllocLZW(1 << (compressFlags & 0x1f), blockmode ? 1 : 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LZWDecompressor::ReadBytes(void *dest, size_t numBytes)
|
||||
{
|
||||
uint8_t *nextByte = static_cast<uint8_t*>(dest);
|
||||
|
||||
while (numBytes--)
|
||||
{
|
||||
if (buffer >= bufferend)
|
||||
{
|
||||
unsigned int symbol;
|
||||
for (;;)
|
||||
{
|
||||
bool atEOF;
|
||||
if (!CSInputAtEOF(input, atEOF))
|
||||
return false;
|
||||
|
||||
if (atEOF)
|
||||
return false;
|
||||
|
||||
if (!CSInputNextBitStringLE(input, LZWSuggestedSymbolSize(lzw), symbol))
|
||||
return false;
|
||||
|
||||
symbolcounter++;
|
||||
if (symbol == 256 && blockmode)
|
||||
{
|
||||
// Skip garbage data after a clear. God damn, this is dumb.
|
||||
int symbolsize = LZWSuggestedSymbolSize(lzw);
|
||||
if (symbolcounter % 8)
|
||||
{
|
||||
if (!CSInputSkipBitsLE(input, symbolsize*(8 - symbolcounter % 8)))
|
||||
return false;
|
||||
}
|
||||
ClearLZWTable(lzw);
|
||||
symbolcounter = 0;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (NextLZWSymbol(lzw, symbol) == LZWInvalidCodeError)
|
||||
return false;
|
||||
|
||||
int n = LZWOutputToInternalBuffer(lzw);
|
||||
buffer = LZWInternalBuffer(lzw);
|
||||
bufferend = buffer + n;
|
||||
}
|
||||
|
||||
*nextByte++ = *buffer++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
26
unpacktool/LZWDecompressor.h
Normal file
26
unpacktool/LZWDecompressor.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "IDecompressor.h"
|
||||
#include "LZW.h"
|
||||
|
||||
|
||||
class LZWDecompressor : public IDecompressor
|
||||
{
|
||||
public:
|
||||
explicit LZWDecompressor(int compressFlags);
|
||||
~LZWDecompressor();
|
||||
|
||||
bool Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize) override;
|
||||
bool ReadBytes(void *dest, size_t numBytes) override;
|
||||
|
||||
private:
|
||||
CSInputBuffer *input;
|
||||
int compressFlags;
|
||||
|
||||
bool blockmode;
|
||||
|
||||
LZW *lzw;
|
||||
int symbolcounter;
|
||||
|
||||
uint8_t *buffer, *bufferend;
|
||||
};
|
29
unpacktool/NullDecompressor.cpp
Normal file
29
unpacktool/NullDecompressor.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "NullDecompressor.h"
|
||||
#include "CSInputBuffer.h"
|
||||
|
||||
NullDecompressor::NullDecompressor()
|
||||
: m_input(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool NullDecompressor::Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize)
|
||||
{
|
||||
m_input = input;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NullDecompressor::ReadBytes(void *dest, size_t numBytes)
|
||||
{
|
||||
uint8_t *output = static_cast<uint8_t *>(dest);
|
||||
|
||||
for (size_t i = 0; i < numBytes; i++)
|
||||
{
|
||||
int byte;
|
||||
if (!CSInputNextByte(m_input, byte))
|
||||
return false;
|
||||
|
||||
output[i] = byte;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
15
unpacktool/NullDecompressor.h
Normal file
15
unpacktool/NullDecompressor.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "IDecompressor.h"
|
||||
|
||||
class NullDecompressor : public IDecompressor
|
||||
{
|
||||
public:
|
||||
NullDecompressor();
|
||||
|
||||
bool Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize) override;
|
||||
bool ReadBytes(void *dest, size_t numBytes) override;
|
||||
|
||||
private:
|
||||
CSInputBuffer *m_input;
|
||||
};
|
448
unpacktool/PrefixCode.cpp
Normal file
448
unpacktool/PrefixCode.cpp
Normal file
@@ -0,0 +1,448 @@
|
||||
#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);
|
||||
}
|
52
unpacktool/PrefixCode.h
Normal file
52
unpacktool/PrefixCode.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "CSInputBuffer.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
typedef struct XADCodeTreeNode XADCodeTreeNode;
|
||||
typedef struct XADCodeTableEntry XADCodeTableEntry;
|
||||
|
||||
class XADPrefixCode final
|
||||
{
|
||||
public:
|
||||
XADPrefixCode();
|
||||
~XADPrefixCode();
|
||||
|
||||
void startBuildingTree();
|
||||
void startZeroBranch();
|
||||
void startOneBranch();
|
||||
void finishBranches();
|
||||
void makeLeafWithValue(int value);
|
||||
|
||||
|
||||
std::vector<XADCodeTreeNode> tree;
|
||||
int numentries, minlength, maxlength;
|
||||
bool isstatic;
|
||||
|
||||
int currnode;
|
||||
std::vector<int> stack;
|
||||
|
||||
int tablesize;
|
||||
XADCodeTableEntry *table1, *table2;
|
||||
|
||||
static XADPrefixCode *prefixCode();
|
||||
static XADPrefixCode *prefixCodeWithLengths(const int *lengths, int numsymbols, int maxlength, bool zeros);
|
||||
|
||||
void init();
|
||||
bool initWithLengths(const int *lengths, int numSymbols, int maxlength, bool zeros);
|
||||
|
||||
bool addValueHighBitFirst(int value, uint32_t code, int length);
|
||||
bool addValueHighBitFirst(int value, uint32_t code, int length, int repeatpos);
|
||||
bool addValueLowBitFirst(int value, uint32_t code, int length);
|
||||
bool addValueLowBitFirst(int value, uint32_t code, int length, int repeatpos);
|
||||
|
||||
void _pushNode();
|
||||
void _popNode();
|
||||
|
||||
void _makeTable();
|
||||
void _makeTableLE();
|
||||
};
|
||||
|
||||
bool CSInputNextSymbolUsingCode(CSInputBuffer *buf, XADPrefixCode *code, int &outSymbol);
|
||||
bool CSInputNextSymbolUsingCodeLE(CSInputBuffer *buf, XADPrefixCode *code, int &outSymbol);
|
72
unpacktool/RLE90Decompressor.cpp
Normal file
72
unpacktool/RLE90Decompressor.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "RLE90Decompressor.h"
|
||||
|
||||
#include "CSInputBuffer.h"
|
||||
|
||||
RLE90Decompressor::RLE90Decompressor()
|
||||
: m_input(nullptr)
|
||||
, m_repeatedByte(0)
|
||||
, m_count(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool RLE90Decompressor::Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize)
|
||||
{
|
||||
m_input = input;
|
||||
m_repeatedByte = 0;
|
||||
m_count = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RLE90Decompressor::ReadBytes(void *dest, size_t numBytes)
|
||||
{
|
||||
uint8_t *destBytes = static_cast<uint8_t*>(dest);
|
||||
for (size_t i = 0; i < numBytes; i++)
|
||||
{
|
||||
if (m_count)
|
||||
{
|
||||
m_count--;
|
||||
destBytes[i] = m_repeatedByte;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isEOF;
|
||||
if (!CSInputAtEOF(m_input, isEOF))
|
||||
return false;
|
||||
|
||||
if (isEOF)
|
||||
return false;
|
||||
|
||||
int b;
|
||||
if (!CSInputNextByte(m_input, b))
|
||||
return false;
|
||||
|
||||
if (b != 0x90)
|
||||
{
|
||||
destBytes[i] = b;
|
||||
m_repeatedByte = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
int c;
|
||||
if (!CSInputNextByte(m_input, c))
|
||||
return false;
|
||||
|
||||
if (c == 0)
|
||||
{
|
||||
m_repeatedByte = 0x90;
|
||||
destBytes[i] = 0x90;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == 1)
|
||||
return false;
|
||||
m_count = c - 2;
|
||||
destBytes[i] = m_repeatedByte;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
17
unpacktool/RLE90Decompressor.h
Normal file
17
unpacktool/RLE90Decompressor.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "IDecompressor.h"
|
||||
|
||||
class RLE90Decompressor : public IDecompressor
|
||||
{
|
||||
public:
|
||||
RLE90Decompressor();
|
||||
|
||||
bool Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize) override;
|
||||
bool ReadBytes(void *dest, size_t numBytes) override;
|
||||
|
||||
private:
|
||||
CSInputBuffer *m_input;
|
||||
uint8_t m_repeatedByte;
|
||||
uint8_t m_count;
|
||||
};
|
17
unpacktool/StringCommon.cpp
Normal file
17
unpacktool/StringCommon.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "StringCommon.h"
|
||||
|
||||
#include "MacRomanConversion.h"
|
||||
#include "UTF8.h"
|
||||
|
||||
void StringCommon::ConvertMacRomanFileName(std::vector<uint8_t> &utf8FileName, const uint8_t *macRomanName, size_t macRomanLength)
|
||||
{
|
||||
for (size_t i = 0; i < macRomanLength; i++)
|
||||
{
|
||||
uint8_t bytes[8];
|
||||
size_t bytesEmitted;
|
||||
PortabilityLayer::UTF8Processor::EncodeCodePoint(bytes, bytesEmitted, MacRoman::ToUnicode(macRomanName[i]));
|
||||
|
||||
for (size_t bi = 0; bi < bytesEmitted; bi++)
|
||||
utf8FileName.push_back(bytes[bi]);
|
||||
}
|
||||
}
|
9
unpacktool/StringCommon.h
Normal file
9
unpacktool/StringCommon.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace StringCommon
|
||||
{
|
||||
void ConvertMacRomanFileName(std::vector<uint8_t> &utf8FileName, const uint8_t *macRomanName, size_t macRomanLength);
|
||||
}
|
493
unpacktool/StuffIt13Decompressor.cpp
Normal file
493
unpacktool/StuffIt13Decompressor.cpp
Normal file
@@ -0,0 +1,493 @@
|
||||
#include "StuffIt13Decompressor.h"
|
||||
#include "CSInputBuffer.h"
|
||||
#include "PrefixCode.h"
|
||||
|
||||
|
||||
static const int FirstCodeLengths_1[321] =
|
||||
{
|
||||
4, 5, 7, 8, 8, 9, 9, 9, 9, 7, 9, 9, 9, 8, 9, 9,
|
||||
9, 9, 9, 9, 9, 9, 9,10, 9, 9,10,10, 9,10, 9, 9,
|
||||
5, 9, 9, 9, 9,10, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9,
|
||||
9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
9, 8, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9, 9, 7, 8, 9,
|
||||
7, 9, 9, 7, 7, 9, 9, 9, 9,10, 9,10,10,10, 9, 9,
|
||||
9, 5, 9, 8, 7, 5, 9, 8, 8, 7, 9, 9, 8, 8, 5, 5,
|
||||
7,10, 5, 8, 5, 8, 9, 9, 9, 9, 9,10, 9, 9,10, 9,
|
||||
9,10,10,10,10,10,10,10, 9,10,10,10,10,10,10,10,
|
||||
9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
9,10,10,10,10,10,10,10, 9, 9,10,10,10,10,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10, 9,10,10,10,10,10,
|
||||
9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
9,10,10,10,10,10,10,10,10,10,10,10, 9, 9,10,10,
|
||||
9,10,10,10,10,10,10,10, 9,10,10,10, 9,10, 9, 5,
|
||||
6, 5, 5, 8, 9, 9, 9, 9, 9, 9,10,10,10, 9,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
10,10,10, 9,10, 9, 9, 9,10, 9,10, 9,10, 9,10, 9,
|
||||
10,10,10, 9,10, 9,10,10, 9, 9, 9, 6, 9, 9,10, 9,
|
||||
5,
|
||||
};
|
||||
|
||||
static const int SecondCodeLengths_1[321] =
|
||||
{
|
||||
4, 5, 6, 6, 7, 7, 6, 7, 7, 7, 6, 8, 7, 8, 8, 8,
|
||||
8, 9, 6, 9, 8, 9, 8, 9, 9, 9, 8,10, 5, 9, 7, 9,
|
||||
6, 9, 8,10, 9,10, 8, 8, 9, 9, 7, 9, 8, 9, 8, 9,
|
||||
8, 8, 6, 9, 9, 8, 8, 9, 9,10, 8, 9, 9,10, 8,10,
|
||||
8, 8, 8, 8, 8, 9, 7,10, 6, 9, 9,11, 7, 8, 8, 9,
|
||||
8,10, 7, 8, 6, 9,10, 9, 9,10, 8,11, 9,11, 9,10,
|
||||
9, 8, 9, 8, 8, 8, 8,10, 9, 9,10,10, 8, 9, 8, 8,
|
||||
8,11, 9, 8, 8, 9, 9,10, 8,11,10,10, 8,10, 9,10,
|
||||
8, 9, 9,11, 9,11, 9,10,10,11,10,12, 9,12,10,11,
|
||||
10,11, 9,10,10,11,10,11,10,11,10,11,10,10,10, 9,
|
||||
9, 9, 8, 7, 6, 8,11,11, 9,12,10,12, 9,11,11,11,
|
||||
10,12,11,11,10,12,10,11,10,10,10,11,10,11,11,11,
|
||||
9,12,10,12,11,12,10,11,10,12,11,12,11,12,11,12,
|
||||
10,12,11,12,11,11,10,12,10,11,10,12,10,12,10,12,
|
||||
10,11,11,11,10,11,11,11,10,12,11,12,10,10,11,11,
|
||||
9,12,11,12,10,11,10,12,10,11,10,12,10,11,10, 7,
|
||||
5, 4, 6, 6, 7, 7, 7, 8, 8, 7, 7, 6, 8, 6, 7, 7,
|
||||
9, 8, 9, 9,10,11,11,11,12,11,10,11,12,11,12,11,
|
||||
12,12,12,12,11,12,12,11,12,11,12,11,13,11,12,10,
|
||||
13,10,14,14,13,14,15,14,16,15,15,18,18,18, 9,18,
|
||||
8,
|
||||
};
|
||||
|
||||
static const int OffsetCodeLengths_1[11] =
|
||||
{
|
||||
5, 6, 3, 3, 3, 3, 3, 3, 3, 4, 6,
|
||||
};
|
||||
|
||||
static const int FirstCodeLengths_2[321] =
|
||||
{
|
||||
4, 7, 7, 8, 7, 8, 8, 8, 8, 7, 8, 7, 8, 7, 9, 8,
|
||||
8, 8, 9, 9, 9, 9,10,10, 9,10,10,10,10,10, 9, 9,
|
||||
5, 9, 8, 9, 9,11,10, 9, 8, 9, 9, 9, 8, 9, 7, 8,
|
||||
8, 8, 9, 9, 9, 9, 9,10, 9, 9, 9,10, 9, 9,10, 9,
|
||||
8, 8, 7, 7, 7, 8, 8, 9, 8, 8, 9, 9, 8, 8, 7, 8,
|
||||
7,10, 8, 7, 7, 9, 9, 9, 9,10,10,11,11,11,10, 9,
|
||||
8, 6, 8, 7, 7, 5, 7, 7, 7, 6, 9, 8, 6, 7, 6, 6,
|
||||
7, 9, 6, 6, 6, 7, 8, 8, 8, 8, 9,10, 9,10, 9, 9,
|
||||
8, 9,10,10, 9,10,10, 9, 9,10,10,10,10,10,10,10,
|
||||
9,10,10,11,10,10,10,10,10,10,10,11,10,11,10,10,
|
||||
9,11,10,10,10,10,10,10, 9, 9,10,11,10,11,10,11,
|
||||
10,12,10,11,10,12,11,12,10,12,10,11,10,11,11,11,
|
||||
9,10,11,11,11,12,12,10,10,10,11,11,10,11,10,10,
|
||||
9,11,10,11,10,11,11,11,10,11,11,12,11,11,10,10,
|
||||
10,11,10,10,11,11,12,10,10,11,11,12,11,11,10,11,
|
||||
9,12,10,11,11,11,10,11,10,11,10,11, 9,10, 9, 7,
|
||||
3, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9,11,10,10,10,
|
||||
12,13,11,12,12,11,13,12,12,11,12,12,13,12,14,13,
|
||||
14,13,15,13,14,15,15,14,13,15,15,14,15,14,15,15,
|
||||
14,15,13,13,14,15,15,14,14,16,16,15,15,15,12,15,
|
||||
10,
|
||||
};
|
||||
|
||||
static const int SecondCodeLengths_2[321] =
|
||||
{
|
||||
5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 7, 8, 7, 7,
|
||||
7, 8, 8, 8, 8, 9, 8, 9, 8, 9, 9, 9, 7, 9, 8, 8,
|
||||
6, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 8,
|
||||
8, 8, 8, 9, 8, 9, 8, 9, 9,10, 8,10, 8, 9, 9, 8,
|
||||
8, 8, 7, 8, 8, 9, 8, 9, 7, 9, 8,10, 8, 9, 8, 9,
|
||||
8, 9, 8, 8, 8, 9, 9, 9, 9,10, 9,11, 9,10, 9,10,
|
||||
8, 8, 8, 9, 8, 8, 8, 9, 9, 8, 9,10, 8, 9, 8, 8,
|
||||
8,11, 8, 7, 8, 9, 9, 9, 9,10, 9,10, 9,10, 9, 8,
|
||||
8, 9, 9,10, 9,10, 9,10, 8,10, 9,10, 9,11,10,11,
|
||||
9,11,10,10,10,11, 9,11, 9,10, 9,11, 9,11,10,10,
|
||||
9,10, 9, 9, 8,10, 9,11, 9, 9, 9,11,10,11, 9,11,
|
||||
9,11, 9,11,10,11,10,11,10,11, 9,10,10,11,10,10,
|
||||
8,10, 9,10,10,11, 9,11, 9,10,10,11, 9,10,10, 9,
|
||||
9,10, 9,10, 9,10, 9,10, 9,11, 9,11,10,10, 9,10,
|
||||
9,11, 9,11, 9,11, 9,10, 9,11, 9,11, 9,11, 9,10,
|
||||
8,11, 9,10, 9,10, 9,10, 8,10, 8, 9, 8, 9, 8, 7,
|
||||
4, 4, 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 7, 8, 8,
|
||||
9, 9,10,10,10,10,10,10,11,11,10,10,12,11,11,12,
|
||||
12,11,12,12,11,12,12,12,12,12,12,11,12,11,13,12,
|
||||
13,12,13,14,14,14,15,13,14,13,14,18,18,17, 7,16,
|
||||
9,
|
||||
};
|
||||
|
||||
static const int OffsetCodeLengths_2[13] =
|
||||
{
|
||||
5, 6, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 6,
|
||||
};
|
||||
|
||||
static const int FirstCodeLengths_3[321] =
|
||||
{
|
||||
6, 6, 6, 6, 6, 9, 8, 8, 4, 9, 8, 9, 8, 9, 9, 9,
|
||||
8, 9, 9,10, 8,10,10,10, 9,10,10,10, 9,10,10, 9,
|
||||
9, 9, 8,10, 9,10, 9,10, 9,10, 9,10, 9, 9, 8, 9,
|
||||
8, 9, 9, 9,10,10,10,10, 9, 9, 9,10, 9,10, 9, 9,
|
||||
7, 8, 8, 9, 8, 9, 9, 9, 8, 9, 9,10, 9, 9, 8, 9,
|
||||
8, 9, 8, 8, 8, 9, 9, 9, 9, 9,10,10,10,10,10, 9,
|
||||
8, 8, 9, 8, 9, 7, 8, 8, 9, 8,10,10, 8, 9, 8, 8,
|
||||
8,10, 8, 8, 8, 8, 9, 9, 9, 9,10,10,10,10,10, 9,
|
||||
7, 9, 9,10,10,10,10,10, 9,10,10,10,10,10,10, 9,
|
||||
9,10,10,10,10,10,10,10,10, 9,10,10,10,10,10,10,
|
||||
9,10,10,10,10,10,10,10, 9, 9, 9,10,10,10,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10, 9,10,10,10,10, 9,
|
||||
8, 9,10,10,10,10,10,10,10,10,10,10, 9,10,10,10,
|
||||
9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9,
|
||||
9,10,10,10,10,10,10, 9,10,10,10,10,10,10, 9, 9,
|
||||
9,10,10,10,10,10,10, 9, 9,10, 9, 9, 8, 9, 8, 9,
|
||||
4, 6, 6, 6, 7, 8, 8, 9, 9,10,10,10, 9,10,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10,10,10,10,10, 7,10,
|
||||
10,10, 7,10,10, 7, 7, 7, 7, 7, 6, 7,10, 7, 7,10,
|
||||
7, 7, 7, 6, 7, 6, 6, 7, 7, 6, 6, 9, 6, 9,10, 6,
|
||||
10,
|
||||
};
|
||||
|
||||
static const int SecondCodeLengths_3[321] =
|
||||
{
|
||||
5, 6, 6, 6, 6, 7, 7, 7, 6, 8, 7, 8, 7, 9, 8, 8,
|
||||
7, 7, 8, 9, 9, 9, 9,10, 8, 9, 9,10, 8,10, 9, 8,
|
||||
6,10, 8,10, 8,10, 9, 9, 9, 9, 9,10, 9, 9, 8, 9,
|
||||
8, 9, 8, 9, 9,10, 9,10, 9, 9, 8,10, 9,11,10, 8,
|
||||
8, 8, 8, 9, 7, 9, 9,10, 8, 9, 8,11, 9,10, 9,10,
|
||||
8, 9, 9, 9, 9, 8, 9, 9,10,10,10,12,10,11,10,10,
|
||||
8, 9, 9, 9, 8, 9, 8, 8,10, 9,10,11, 8,10, 9, 9,
|
||||
8,12, 8, 9, 9, 9, 9, 8, 9,10, 9,12,10,10,10, 8,
|
||||
7,11,10, 9,10,11, 9,11, 7,11,10,12,10,12,10,11,
|
||||
9,11, 9,12,10,12,10,12,10, 9,11,12,10,12,10,11,
|
||||
9,10, 9,10, 9,11,11,12, 9,10, 8,12,11,12, 9,12,
|
||||
10,12,10,13,10,12,10,12,10,12,10, 9,10,12,10, 9,
|
||||
8,11,10,12,10,12,10,12,10,11,10,12, 8,12,10,11,
|
||||
10,10,10,12, 9,11,10,12,10,12,11,12,10, 9,10,12,
|
||||
9,10,10,12,10,11,10,11,10,12, 8,12, 9,12, 8,12,
|
||||
8,11,10,11,10,11, 9,10, 8,10, 9, 9, 8, 9, 8, 7,
|
||||
4, 3, 5, 5, 6, 5, 6, 6, 7, 7, 8, 8, 8, 7, 7, 7,
|
||||
9, 8, 9, 9,11, 9,11, 9, 8, 9, 9,11,12,11,12,12,
|
||||
13,13,12,13,14,13,14,13,14,13,13,13,12,13,13,12,
|
||||
13,13,14,14,13,13,14,14,14,14,15,18,17,18, 8,16,
|
||||
10,
|
||||
};
|
||||
|
||||
static const int OffsetCodeLengths_3[14] =
|
||||
{
|
||||
6, 7, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 5, 7,
|
||||
};
|
||||
|
||||
static const int FirstCodeLengths_4[321] =
|
||||
{
|
||||
2, 6, 6, 7, 7, 8, 7, 8, 7, 8, 8, 9, 8, 9, 9, 9,
|
||||
8, 8, 9, 9, 9,10,10, 9, 8,10, 9,10, 9,10, 9, 9,
|
||||
6, 9, 8, 9, 9,10, 9, 9, 9,10, 9, 9, 9, 9, 8, 8,
|
||||
8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9,
|
||||
7, 7, 8, 8, 8, 8, 9, 9, 7, 8, 9,10, 8, 8, 7, 8,
|
||||
8,10, 8, 8, 8, 9, 8, 9, 9,10, 9,11,10,11, 9, 9,
|
||||
8, 7, 9, 8, 8, 6, 8, 8, 8, 7,10, 9, 7, 8, 7, 7,
|
||||
8,10, 7, 7, 7, 8, 9, 9, 9, 9,10,11, 9,11,10, 9,
|
||||
7, 9,10,10,10,11,11,10,10,11,10,10,10,11,11,10,
|
||||
9,10,10,11,10,11,10,11,10,10,10,11,10,11,10,10,
|
||||
9,10,10,11,10,10,10,10, 9,10,10,10,10,11,10,11,
|
||||
10,11,10,11,11,11,10,12,10,11,10,11,10,11,11,10,
|
||||
8,10,10,11,10,11,11,11,10,11,10,11,10,11,11,11,
|
||||
9,10,11,11,10,11,11,11,10,11,11,11,10,10,10,10,
|
||||
10,11,10,10,11,11,10,10, 9,11,10,10,11,11,10,10,
|
||||
10,11,10,10,10,10,10,10, 9,11,10,10, 8,10, 8, 6,
|
||||
5, 6, 6, 7, 7, 8, 8, 8, 9,10,11,10,10,11,11,12,
|
||||
12,10,11,12,12,12,12,13,13,13,13,13,12,13,13,15,
|
||||
14,12,14,15,16,12,12,13,15,14,16,15,17,18,15,17,
|
||||
16,15,15,15,15,13,13,10,14,12,13,17,17,18,10,17,
|
||||
4,
|
||||
};
|
||||
|
||||
static const int SecondCodeLengths_4[321] =
|
||||
{
|
||||
4, 5, 6, 6, 6, 6, 7, 7, 6, 7, 7, 9, 6, 8, 8, 7,
|
||||
7, 8, 8, 8, 6, 9, 8, 8, 7, 9, 8, 9, 8, 9, 8, 9,
|
||||
6, 9, 8, 9, 8,10, 9, 9, 8,10, 8,10, 8, 9, 8, 9,
|
||||
8, 8, 7, 9, 9, 9, 9, 9, 8,10, 9,10, 9,10, 9, 8,
|
||||
7, 8, 9, 9, 8, 9, 9, 9, 7,10, 9,10, 9, 9, 8, 9,
|
||||
8, 9, 8, 8, 8, 9, 9,10, 9, 9, 8,11, 9,11,10,10,
|
||||
8, 8,10, 8, 8, 9, 9, 9,10, 9,10,11, 9, 9, 9, 9,
|
||||
8, 9, 8, 8, 8,10,10, 9, 9, 8,10,11,10,11,11, 9,
|
||||
8, 9,10,11, 9,10,11,11, 9,12,10,10,10,12,11,11,
|
||||
9,11,11,12, 9,11, 9,10,10,10,10,12, 9,11,10,11,
|
||||
9,11,11,11,10,11,11,12, 9,10,10,12,11,11,10,11,
|
||||
9,11,10,11,10,11, 9,11,11, 9, 8,11,10,11,11,10,
|
||||
7,12,11,11,11,11,11,12,10,12,11,13,11,10,12,11,
|
||||
10,11,10,11,10,11,11,11,10,12,11,11,10,11,10,10,
|
||||
10,11,10,12,11,12,10,11, 9,11,10,11,10,11,10,12,
|
||||
9,11,11,11, 9,11,10,10, 9,11,10,10, 9,10, 9, 7,
|
||||
4, 5, 5, 5, 6, 6, 7, 6, 8, 7, 8, 9, 9, 7, 8, 8,
|
||||
10, 9,10,10,12,10,11,11,11,11,10,11,12,11,11,11,
|
||||
11,11,13,12,11,12,13,12,12,12,13,11, 9,12,13, 7,
|
||||
13,11,13,11,10,11,13,15,15,12,14,15,15,15, 6,15,
|
||||
5,
|
||||
};
|
||||
|
||||
static const int OffsetCodeLengths_4[11] =
|
||||
{
|
||||
3, 6, 5, 4, 2, 3, 3, 3, 4, 4, 6,
|
||||
};
|
||||
|
||||
static const int FirstCodeLengths_5[321] =
|
||||
{
|
||||
7, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 9, 7, 9, 9,
|
||||
9, 9, 9, 9, 9, 9, 9,10, 9,10, 9,10, 9,10, 9, 9,
|
||||
5, 9, 7, 9, 9, 9, 9, 9, 7, 7, 7, 9, 7, 7, 8, 7,
|
||||
8, 8, 7, 7, 9, 9, 9, 9, 7, 7, 7, 9, 9, 9, 9, 9,
|
||||
9, 7, 9, 7, 7, 7, 7, 9, 9, 7, 9, 9, 7, 7, 7, 7,
|
||||
7, 9, 7, 8, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
9, 7, 8, 7, 7, 7, 8, 8, 6, 7, 9, 7, 7, 8, 7, 5,
|
||||
6, 9, 5, 7, 5, 6, 7, 7, 9, 8, 9, 9, 9, 9, 9, 9,
|
||||
9, 9,10, 9,10,10,10, 9, 9,10,10,10,10,10,10,10,
|
||||
9,10,10,10,10,10,10,10,10,10,10,10, 9,10,10,10,
|
||||
9,10,10,10, 9, 9,10, 9, 9, 9, 9,10,10,10,10,10,
|
||||
10,10,10,10,10,10, 9,10,10,10,10,10,10,10,10,10,
|
||||
9,10,10,10, 9,10,10,10, 9, 9, 9,10,10,10,10,10,
|
||||
9,10, 9,10,10, 9,10,10, 9,10,10,10,10,10,10,10,
|
||||
9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
9,10,10,10,10,10,10,10, 9,10, 9,10, 9,10,10, 9,
|
||||
5, 6, 8, 8, 7, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
10,10,10,10,10,10,10,10, 9,10,10, 5,10, 8, 9, 8,
|
||||
9,
|
||||
};
|
||||
|
||||
static const int SecondCodeLengths_5[321] =
|
||||
{
|
||||
8,10,11,11,11,12,11,11,12, 6,11,12,10, 5,12,12,
|
||||
12,12,12,12,12,13,13,14,13,13,12,13,12,13,12,15,
|
||||
4,10, 7, 9,11,11,10, 9, 6, 7, 8, 9, 6, 7, 6, 7,
|
||||
8, 7, 7, 8, 8, 8, 8, 8, 8, 9, 8, 7,10, 9,10,10,
|
||||
11, 7, 8, 6, 7, 8, 8, 9, 8, 7,10,10, 8, 7, 8, 8,
|
||||
7,10, 7, 6, 7, 9, 9, 8,11,11,11,10,11,11,11, 8,
|
||||
11, 6, 7, 6, 6, 6, 6, 8, 7, 6,10, 9, 6, 7, 6, 6,
|
||||
7,10, 6, 5, 6, 7, 7, 7,10, 8,11, 9,13, 7,14,16,
|
||||
12,14,14,15,15,16,16,14,15,15,15,15,15,15,15,15,
|
||||
14,15,13,14,14,16,15,17,14,17,15,17,12,14,13,16,
|
||||
12,17,13,17,14,13,13,14,14,12,13,15,15,14,15,17,
|
||||
14,17,15,14,15,16,12,16,15,14,15,16,15,16,17,17,
|
||||
15,15,17,17,13,14,15,15,13,12,16,16,17,14,15,16,
|
||||
15,15,13,13,15,13,16,17,15,17,17,17,16,17,14,17,
|
||||
14,16,15,17,15,15,14,17,15,17,15,16,15,15,16,16,
|
||||
14,17,17,15,15,16,15,17,15,14,16,16,16,16,16,12,
|
||||
4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 9, 9,
|
||||
9, 9, 9,10,10,10,11,10,11,11,11,11,11,12,12,12,
|
||||
13,13,12,13,12,14,14,12,13,13,13,13,14,12,13,13,
|
||||
14,14,14,13,14,14,15,15,13,15,13,17,17,17, 9,17,
|
||||
7,
|
||||
};
|
||||
|
||||
static const int OffsetCodeLengths_5[11] =
|
||||
{
|
||||
6, 7, 7, 6, 4, 3, 2, 2, 3, 3, 6,
|
||||
};
|
||||
|
||||
static const int *FirstCodeLengths[5] =
|
||||
{
|
||||
FirstCodeLengths_1,FirstCodeLengths_2,FirstCodeLengths_3,FirstCodeLengths_4,FirstCodeLengths_5
|
||||
};
|
||||
|
||||
static const int *SecondCodeLengths[5] =
|
||||
{
|
||||
SecondCodeLengths_1,SecondCodeLengths_2,SecondCodeLengths_3,SecondCodeLengths_4,SecondCodeLengths_5
|
||||
};
|
||||
|
||||
static const int *OffsetCodeLengths[5] =
|
||||
{
|
||||
OffsetCodeLengths_1,OffsetCodeLengths_2,OffsetCodeLengths_3,OffsetCodeLengths_4,OffsetCodeLengths_5
|
||||
};
|
||||
|
||||
static const int OffsetCodeSize[5] = { 11,13,14,11,11 };
|
||||
|
||||
static const int MetaCodes[37] =
|
||||
{
|
||||
0x5d8,0x058,0x040,0x0c0,0x000,0x078,0x02b,0x014,
|
||||
0x00c,0x01c,0x01b,0x00b,0x010,0x020,0x038,0x018,
|
||||
0x0d8,0xbd8,0x180,0x680,0x380,0xf80,0x780,0x480,
|
||||
0x080,0x280,0x3d8,0xfd8,0x7d8,0x9d8,0x1d8,0x004,
|
||||
0x001,0x002,0x007,0x003,0x008
|
||||
};
|
||||
|
||||
static const int MetaCodeLengths[37] =
|
||||
{
|
||||
11,8,8,8,8,7,6,5,5,5,5,6,5,6,7,7,9,12,10,11,11,12,
|
||||
12,11,11,11,12,12,12,12,12,5,2,2,3,4,5
|
||||
};
|
||||
|
||||
|
||||
|
||||
StuffIt13Decompressor::StuffIt13Decompressor()
|
||||
: LZSSDecompressor(65536)
|
||||
, firstcodei(nullptr)
|
||||
, secondcodei(nullptr)
|
||||
, offsetcodei(nullptr)
|
||||
, firstcoder(nullptr)
|
||||
, secondcoder(nullptr)
|
||||
, offsetcoder(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
StuffIt13Decompressor::~StuffIt13Decompressor()
|
||||
{
|
||||
delete firstcodei;
|
||||
delete secondcodei;
|
||||
delete offsetcodei;
|
||||
}
|
||||
|
||||
bool StuffIt13Decompressor::resetLZSSHandle()
|
||||
{
|
||||
delete firstcodei;
|
||||
delete secondcodei;
|
||||
delete offsetcodei;
|
||||
firstcodei = secondcodei = offsetcodei = nullptr;
|
||||
firstcoder = secondcoder = offsetcoder = nullptr;
|
||||
|
||||
int val;
|
||||
if (!CSInputNextByte(input, val))
|
||||
return false;
|
||||
|
||||
int code = val >> 4;
|
||||
|
||||
if (code == 0)
|
||||
{
|
||||
XADPrefixCode *metacode = XADPrefixCode::prefixCode();
|
||||
for (int i = 0; i < 37; i++)
|
||||
metacode->addValueLowBitFirst(i, MetaCodes[i], MetaCodeLengths[i]);
|
||||
|
||||
firstcodei = firstcoder = this->allocAndParseCodeOfSize(321, metacode);
|
||||
if (val & 0x08)
|
||||
secondcoder = firstcoder;
|
||||
else
|
||||
secondcodei = secondcoder = this->allocAndParseCodeOfSize(321, metacode);
|
||||
|
||||
offsetcodei = offsetcoder = this->allocAndParseCodeOfSize((val & 0x07) + 10, metacode);
|
||||
}
|
||||
else if (code < 6)
|
||||
{
|
||||
firstcoder = firstcodei = XADPrefixCode::prefixCodeWithLengths(FirstCodeLengths[code - 1], 321, 32, true);
|
||||
secondcoder = secondcodei = XADPrefixCode::prefixCodeWithLengths(SecondCodeLengths[code - 1], 321, 32, true);
|
||||
offsetcoder = offsetcodei = XADPrefixCode::prefixCodeWithLengths(OffsetCodeLengths[code - 1], OffsetCodeSize[code - 1], 32, true);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
currcode = firstcoder;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
XADPrefixCode *StuffIt13Decompressor::allocAndParseCodeOfSize(int numcodes, XADPrefixCode *metacode)
|
||||
{
|
||||
int length = 0;
|
||||
std::vector<int> lengths;
|
||||
lengths.resize(numcodes);
|
||||
|
||||
for (int i = 0; i < numcodes; i++)
|
||||
{
|
||||
int val;
|
||||
if (!CSInputNextSymbolUsingCodeLE(input, metacode, val))
|
||||
return nullptr;
|
||||
|
||||
switch (val)
|
||||
{
|
||||
case 31: length = -1; break;
|
||||
case 32: length++; break;
|
||||
case 33: length--; break;
|
||||
case 34:
|
||||
{
|
||||
unsigned int bit;
|
||||
if (!CSInputNextBitLE(input, bit))
|
||||
return nullptr;
|
||||
|
||||
if (bit)
|
||||
lengths[i++] = length;
|
||||
}
|
||||
break;
|
||||
case 35:
|
||||
{
|
||||
unsigned int bits;
|
||||
if (!CSInputNextBitStringLE(input, 3, bits))
|
||||
return false;
|
||||
|
||||
val = bits + 2;
|
||||
while (val--)
|
||||
lengths[i++] = length;
|
||||
}
|
||||
break;
|
||||
case 36:
|
||||
{
|
||||
unsigned int bits;
|
||||
if (!CSInputNextBitStringLE(input, 6, bits))
|
||||
return false;
|
||||
|
||||
val = bits + 10;
|
||||
while (val--)
|
||||
lengths[i++] = length;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
length = val + 1;
|
||||
break;
|
||||
}
|
||||
lengths[i] = length;
|
||||
}
|
||||
|
||||
return XADPrefixCode::prefixCodeWithLengths(lengths.data(), numcodes, 32, true);
|
||||
}
|
||||
|
||||
bool StuffIt13Decompressor::nextLiteralOrOffset(int *offset, int *length, int &result)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (!CSInputNextSymbolUsingCodeLE(input, currcode, val))
|
||||
return false;
|
||||
|
||||
if (val < 0x100)
|
||||
{
|
||||
currcode = firstcoder;
|
||||
result = val;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
currcode = secondcoder;
|
||||
|
||||
if (val < 0x13e)
|
||||
*length = val - 0x100 + 3;
|
||||
else if (val == 0x13e)
|
||||
{
|
||||
unsigned int bits;
|
||||
if (!CSInputNextBitStringLE(input, 10, bits))
|
||||
return false;
|
||||
*length = bits + 65;
|
||||
}
|
||||
else if (val == 0x13f)
|
||||
{
|
||||
unsigned int bits;
|
||||
if (!CSInputNextBitStringLE(input, 15, bits))
|
||||
return false;
|
||||
*length = bits + 65;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = XADLZSSEnd;
|
||||
return true;
|
||||
}
|
||||
|
||||
int bitlength;
|
||||
if (!CSInputNextSymbolUsingCodeLE(input, offsetcoder, bitlength))
|
||||
return false;
|
||||
|
||||
if (bitlength == 0)
|
||||
*offset = 1;
|
||||
else if (bitlength == 1)
|
||||
*offset = 2;
|
||||
else
|
||||
{
|
||||
unsigned int bits;
|
||||
if (!CSInputNextBitStringLE(input, bitlength - 1, bits))
|
||||
return false;
|
||||
*offset = (1 << (bitlength - 1)) + bits + 1;
|
||||
}
|
||||
|
||||
result = XADLZSSMatch;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
22
unpacktool/StuffIt13Decompressor.h
Normal file
22
unpacktool/StuffIt13Decompressor.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "LZSSDecompressor.h"
|
||||
#include "IFileReader.h"
|
||||
|
||||
class XADPrefixCode;
|
||||
|
||||
class StuffIt13Decompressor : public LZSSDecompressor
|
||||
{
|
||||
public:
|
||||
StuffIt13Decompressor();
|
||||
~StuffIt13Decompressor();
|
||||
|
||||
private:
|
||||
XADPrefixCode *firstcodei, *secondcodei, *offsetcodei;
|
||||
XADPrefixCode *firstcoder, *secondcoder, *offsetcoder;
|
||||
XADPrefixCode *currcode;
|
||||
|
||||
bool resetLZSSHandle();
|
||||
XADPrefixCode *allocAndParseCodeOfSize(int numcodes, XADPrefixCode *metacode);
|
||||
bool nextLiteralOrOffset(int *offset, int *length, int &result);
|
||||
};
|
333
unpacktool/StuffIt5Parser.cpp
Normal file
333
unpacktool/StuffIt5Parser.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
#include "StuffIt5Parser.h"
|
||||
#include "UPByteSwap.h"
|
||||
#include "IFileReader.h"
|
||||
#include "ArchiveDescription.h"
|
||||
#include "StuffItCommon.h"
|
||||
#include "PLBigEndian.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "CSInputBuffer.h"
|
||||
|
||||
struct StuffIt5Header
|
||||
{
|
||||
uint8_t m_signature[80];
|
||||
uint8_t m_unknown1[4];
|
||||
BEUInt32_t m_fileSize;
|
||||
uint8_t m_unknown2[4];
|
||||
BEUInt16_t m_numRootDirEntries;
|
||||
BEUInt32_t m_rootDirFirstEntryOffset;
|
||||
};
|
||||
|
||||
struct StuffIt5DataBlockDesc
|
||||
{
|
||||
BEUInt32_t m_uncompressedSize;
|
||||
BEUInt32_t m_compressedSize;
|
||||
BEUInt16_t m_crc;
|
||||
uint8_t m_unknown[2];
|
||||
uint8_t m_algorithm_dirNumFilesHigh;
|
||||
uint8_t m_passwordDataLength_dirNumFilesLow;
|
||||
};
|
||||
|
||||
struct StuffIt5BlockHeader
|
||||
{
|
||||
uint8_t m_identifier[4];
|
||||
uint8_t m_version;
|
||||
uint8_t m_unknown1;
|
||||
BEUInt16_t m_headerSize;
|
||||
uint8_t m_unknown2;
|
||||
uint8_t m_flags;
|
||||
BEUInt32_t m_creationDate;
|
||||
BEUInt32_t m_modificationDate;
|
||||
BEUInt32_t m_prevEntryOffset;
|
||||
BEUInt32_t m_nextEntryOffset;
|
||||
BEUInt32_t m_dirEntryOffset;
|
||||
BEUInt16_t m_fileNameSize;
|
||||
BEUInt16_t m_headerCRC;
|
||||
|
||||
StuffIt5DataBlockDesc m_dataForkDesc;
|
||||
|
||||
// Followed by:
|
||||
// uint8_t passwordData[m_passwordDataLength_dirNumFilesLow]
|
||||
// uint8_t filename[m_fileNameSize]
|
||||
// BEUInt16 commentSize
|
||||
// uint8_t unknown4[2]
|
||||
// Comment[commentSize]
|
||||
};
|
||||
|
||||
struct StuffIt5BlockAnnex1
|
||||
{
|
||||
BEUInt16_t m_unknown1; // Low bit = has resource fork
|
||||
BEUInt16_t m_unknown2;
|
||||
uint8_t m_fileType[4];
|
||||
uint8_t m_fileCreator[4];
|
||||
BEUInt16_t m_finderFlags;
|
||||
};
|
||||
|
||||
|
||||
#define SIT5_ID 0xA5A5A5A5
|
||||
|
||||
#define SIT5FLAGS_DIRECTORY 0x40
|
||||
#define SIT5FLAGS_CRYPTED 0x20
|
||||
#define SIT5FLAGS_RSRC_FORK 0x10
|
||||
|
||||
#define SIT5_ARCHIVEVERSION 5
|
||||
|
||||
#define SIT5_ARCHIVEFLAGS_14BYTES 0x10
|
||||
#define SIT5_ARCHIVEFLAGS_20 0x20
|
||||
#define SIT5_ARCHIVEFLAGS_40 0x40
|
||||
#define SIT5_ARCHIVEFLAGS_CRYPTED 0x80
|
||||
|
||||
#define SIT5_KEY_LENGTH 5 /* 40 bits */
|
||||
|
||||
struct StuffIt5Block
|
||||
{
|
||||
StuffIt5BlockHeader m_header;
|
||||
StuffIt5BlockAnnex1 m_annex1;
|
||||
|
||||
bool m_isDirectory;
|
||||
|
||||
bool m_hasResourceFork;
|
||||
StuffIt5DataBlockDesc m_resourceForkDesc;
|
||||
std::vector<uint8_t> m_resourceForkPasswordData;
|
||||
|
||||
IFileReader::FilePos_t m_dataForkPos;
|
||||
IFileReader::FilePos_t m_resForkPos;
|
||||
|
||||
uint16_t m_commentSize;
|
||||
IFileReader::FilePos_t m_commentPos;
|
||||
|
||||
std::vector<uint8_t> m_passwordData;
|
||||
std::vector<uint8_t> m_filename;
|
||||
|
||||
std::vector<StuffIt5Block> m_children;
|
||||
|
||||
bool Read(IFileReader &reader)
|
||||
{
|
||||
if (!reader.ReadExact(&m_header, sizeof(m_header)))
|
||||
return false;
|
||||
|
||||
uint32_t sizeWithOnlyNameAndPasswordInfo = sizeof(m_header);
|
||||
|
||||
m_isDirectory = ((m_header.m_flags & SIT5FLAGS_DIRECTORY) != 0);
|
||||
|
||||
if (!m_isDirectory && m_header.m_dataForkDesc.m_passwordDataLength_dirNumFilesLow != 0)
|
||||
{
|
||||
m_passwordData.resize(m_header.m_dataForkDesc.m_passwordDataLength_dirNumFilesLow);
|
||||
if (!reader.ReadExact(&m_passwordData[0], m_header.m_dataForkDesc.m_passwordDataLength_dirNumFilesLow))
|
||||
return false;
|
||||
|
||||
sizeWithOnlyNameAndPasswordInfo += m_header.m_dataForkDesc.m_passwordDataLength_dirNumFilesLow;
|
||||
}
|
||||
|
||||
if (m_header.m_fileNameSize)
|
||||
{
|
||||
m_filename.resize(m_header.m_fileNameSize);
|
||||
if (!reader.ReadExact(&m_filename[0], m_header.m_fileNameSize))
|
||||
return false;
|
||||
|
||||
sizeWithOnlyNameAndPasswordInfo += m_header.m_fileNameSize;
|
||||
}
|
||||
|
||||
uint32_t sizeWithCommentData = sizeWithOnlyNameAndPasswordInfo;
|
||||
|
||||
if (sizeWithOnlyNameAndPasswordInfo != m_header.m_headerSize)
|
||||
{
|
||||
if (sizeWithOnlyNameAndPasswordInfo > m_header.m_headerSize || m_header.m_headerSize - sizeWithOnlyNameAndPasswordInfo < 4)
|
||||
return false;
|
||||
|
||||
BEUInt16_t extras[2];
|
||||
if (!reader.ReadExact(extras, sizeof(extras)))
|
||||
return false;
|
||||
|
||||
uint16_t commentLength = extras[0];
|
||||
|
||||
if (commentLength > m_header.m_headerSize - sizeWithOnlyNameAndPasswordInfo - 4)
|
||||
return false;
|
||||
|
||||
|
||||
m_commentSize = commentLength;
|
||||
m_commentPos = reader.GetPosition();
|
||||
|
||||
if (commentLength)
|
||||
{
|
||||
if (reader.SeekCurrent(commentLength))
|
||||
return false;
|
||||
}
|
||||
|
||||
sizeWithCommentData += commentLength + 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_commentSize = 0;
|
||||
m_commentPos = 0;
|
||||
}
|
||||
|
||||
if (!reader.SeekCurrent(m_header.m_headerSize - sizeWithCommentData))
|
||||
return false;
|
||||
|
||||
if (!reader.ReadExact(&m_annex1, sizeof(m_annex1)))
|
||||
return false;
|
||||
|
||||
if (m_header.m_version == 1)
|
||||
{
|
||||
if (!reader.SeekCurrent(22))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!reader.SeekCurrent(18))
|
||||
return false;
|
||||
}
|
||||
|
||||
m_hasResourceFork = ((m_annex1.m_unknown1 & 0x01) != 0);
|
||||
|
||||
if (m_hasResourceFork)
|
||||
{
|
||||
if (!reader.ReadExact(&m_resourceForkDesc, sizeof(m_resourceForkDesc)))
|
||||
return false;
|
||||
|
||||
if (m_resourceForkDesc.m_passwordDataLength_dirNumFilesLow)
|
||||
{
|
||||
m_resourceForkPasswordData.resize(m_resourceForkDesc.m_passwordDataLength_dirNumFilesLow);
|
||||
if (!reader.ReadExact(&m_resourceForkPasswordData[0], m_resourceForkDesc.m_passwordDataLength_dirNumFilesLow))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_isDirectory)
|
||||
{
|
||||
int numFiles = (m_header.m_dataForkDesc.m_algorithm_dirNumFilesHigh << 8) | (m_header.m_dataForkDesc.m_passwordDataLength_dirNumFilesLow);
|
||||
|
||||
m_children.resize(numFiles);
|
||||
for (int i = 0; i < numFiles; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
if (!reader.SeekStart(m_children[i - 1].m_header.m_nextEntryOffset))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_children[i].Read(reader))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_hasResourceFork)
|
||||
{
|
||||
m_resForkPos = reader.GetPosition();
|
||||
m_dataForkPos = m_resForkPos + m_resourceForkDesc.m_compressedSize;
|
||||
}
|
||||
else
|
||||
m_dataForkPos = reader.GetPosition();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static ArchiveItemList *ConvertToItemList(const std::vector<StuffIt5Block> &blocks);
|
||||
|
||||
static void ConvertItem(const StuffIt5Block &block, ArchiveItem &item)
|
||||
{
|
||||
item.m_isDirectory = block.m_isDirectory;
|
||||
|
||||
if (block.m_isDirectory)
|
||||
item.m_children = ConvertToItemList(block.m_children);
|
||||
else
|
||||
{
|
||||
item.m_macProperties.m_creationDate = block.m_header.m_creationDate;
|
||||
item.m_macProperties.m_modifiedDate = block.m_header.m_modificationDate;
|
||||
memcpy(item.m_macProperties.m_fileCreator, block.m_annex1.m_fileCreator, 4);
|
||||
memcpy(item.m_macProperties.m_fileType, block.m_annex1.m_fileType, 4);
|
||||
item.m_macProperties.m_finderFlags = block.m_annex1.m_finderFlags;
|
||||
|
||||
item.m_dataForkDesc.m_filePosition = block.m_dataForkPos;
|
||||
item.m_dataForkDesc.m_compressedSize = block.m_header.m_dataForkDesc.m_compressedSize;
|
||||
item.m_dataForkDesc.m_uncompressedSize = block.m_header.m_dataForkDesc.m_uncompressedSize;
|
||||
item.m_dataForkDesc.m_compressionMethod = StuffItCommon::ResolveCompressionMethod(block.m_header.m_dataForkDesc.m_algorithm_dirNumFilesHigh);
|
||||
|
||||
if (block.m_hasResourceFork)
|
||||
{
|
||||
item.m_resourceForkDesc.m_filePosition = block.m_resForkPos;
|
||||
item.m_resourceForkDesc.m_compressedSize = block.m_resourceForkDesc.m_compressedSize;
|
||||
item.m_resourceForkDesc.m_uncompressedSize = block.m_resourceForkDesc.m_uncompressedSize;
|
||||
item.m_resourceForkDesc.m_compressionMethod = StuffItCommon::ResolveCompressionMethod(block.m_resourceForkDesc.m_algorithm_dirNumFilesHigh);
|
||||
}
|
||||
}
|
||||
|
||||
item.m_fileNameUTF8 = block.m_filename;
|
||||
|
||||
item.m_commentDesc.m_compressedSize = block.m_commentSize;
|
||||
item.m_commentDesc.m_uncompressedSize = block.m_commentSize;
|
||||
item.m_commentDesc.m_filePosition = block.m_commentPos;
|
||||
item.m_commentDesc.m_compressionMethod = CompressionMethods::kNone;
|
||||
}
|
||||
|
||||
static ArchiveItemList *ConvertToItemList(const std::vector<StuffIt5Block> &blocks)
|
||||
{
|
||||
ArchiveItemList *list = new ArchiveItemList();
|
||||
const size_t numItems = blocks.size();
|
||||
|
||||
list->m_items.resize(numItems);
|
||||
for (size_t i = 0; i < numItems; i++)
|
||||
{
|
||||
ConvertItem(blocks[i], list->m_items[i]);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
bool StuffIt5Parser::Check(IFileReader &reader)
|
||||
{
|
||||
char signatureBytes[80];
|
||||
|
||||
if (reader.FileSize() < sizeof(signatureBytes))
|
||||
return false;
|
||||
|
||||
if (!reader.SeekStart(0))
|
||||
return false;
|
||||
|
||||
if (!reader.ReadExact(signatureBytes, sizeof(signatureBytes)))
|
||||
return false;
|
||||
|
||||
const char *match = "StuffIt (c)1997-\xFF\xFF\xFF\xFF Aladdin Systems, Inc., http://www.aladdinsys.com/StuffIt/\x0d\x0a";
|
||||
|
||||
const char *checkByte = signatureBytes;
|
||||
while (*match && (*checkByte == *match || *match == '\377'))
|
||||
{
|
||||
match++;
|
||||
checkByte++;
|
||||
}
|
||||
|
||||
return (*match) == '\0';
|
||||
}
|
||||
|
||||
ArchiveItemList *StuffIt5Parser::Parse(IFileReader &reader)
|
||||
{
|
||||
reader.SeekStart(0);
|
||||
|
||||
StuffIt5Header header;
|
||||
if (!reader.Read(&header, sizeof(header)))
|
||||
return nullptr;
|
||||
|
||||
uint16_t numRootDirEntries = header.m_numRootDirEntries;
|
||||
|
||||
if (!reader.SeekStart(header.m_rootDirFirstEntryOffset))
|
||||
return nullptr;
|
||||
|
||||
std::vector<StuffIt5Block> rootDirBlocks;
|
||||
rootDirBlocks.resize(numRootDirEntries);
|
||||
|
||||
for (int i = 0; i < numRootDirEntries; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
reader.SeekStart(rootDirBlocks[i - 1].m_header.m_nextEntryOffset);
|
||||
|
||||
if (!rootDirBlocks[i].Read(reader))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ConvertToItemList(rootDirBlocks);
|
||||
}
|
10
unpacktool/StuffIt5Parser.h
Normal file
10
unpacktool/StuffIt5Parser.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "IArchiveParser.h"
|
||||
|
||||
class StuffIt5Parser : public IArchiveParser
|
||||
{
|
||||
public:
|
||||
bool Check(IFileReader &reader) override;
|
||||
ArchiveItemList *Parse(IFileReader &reader) override;
|
||||
};
|
366
unpacktool/StuffItArsenicDecompressor.cpp
Normal file
366
unpacktool/StuffItArsenicDecompressor.cpp
Normal file
@@ -0,0 +1,366 @@
|
||||
#include "StuffItArsenicDecompressor.h"
|
||||
#include "CRC.h"
|
||||
#include "CSInputBuffer.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <crtdbg.h>
|
||||
|
||||
|
||||
|
||||
static const uint16_t RandomizationTable[] =
|
||||
{
|
||||
0xee, 0x56, 0xf8, 0xc3, 0x9d, 0x9f, 0xae, 0x2c,
|
||||
0xad, 0xcd, 0x24, 0x9d, 0xa6, 0x101, 0x18, 0xb9,
|
||||
0xa1, 0x82, 0x75, 0xe9, 0x9f, 0x55, 0x66, 0x6a,
|
||||
0x86, 0x71, 0xdc, 0x84, 0x56, 0x96, 0x56, 0xa1,
|
||||
0x84, 0x78, 0xb7, 0x32, 0x6a, 0x3, 0xe3, 0x2,
|
||||
0x11, 0x101, 0x8, 0x44, 0x83, 0x100, 0x43, 0xe3,
|
||||
0x1c, 0xf0, 0x86, 0x6a, 0x6b, 0xf, 0x3, 0x2d,
|
||||
0x86, 0x17, 0x7b, 0x10, 0xf6, 0x80, 0x78, 0x7a,
|
||||
0xa1, 0xe1, 0xef, 0x8c, 0xf6, 0x87, 0x4b, 0xa7,
|
||||
0xe2, 0x77, 0xfa, 0xb8, 0x81, 0xee, 0x77, 0xc0,
|
||||
0x9d, 0x29, 0x20, 0x27, 0x71, 0x12, 0xe0, 0x6b,
|
||||
0xd1, 0x7c, 0xa, 0x89, 0x7d, 0x87, 0xc4, 0x101,
|
||||
0xc1, 0x31, 0xaf, 0x38, 0x3, 0x68, 0x1b, 0x76,
|
||||
0x79, 0x3f, 0xdb, 0xc7, 0x1b, 0x36, 0x7b, 0xe2,
|
||||
0x63, 0x81, 0xee, 0xc, 0x63, 0x8b, 0x78, 0x38,
|
||||
0x97, 0x9b, 0xd7, 0x8f, 0xdd, 0xf2, 0xa3, 0x77,
|
||||
0x8c, 0xc3, 0x39, 0x20, 0xb3, 0x12, 0x11, 0xe,
|
||||
0x17, 0x42, 0x80, 0x2c, 0xc4, 0x92, 0x59, 0xc8,
|
||||
0xdb, 0x40, 0x76, 0x64, 0xb4, 0x55, 0x1a, 0x9e,
|
||||
0xfe, 0x5f, 0x6, 0x3c, 0x41, 0xef, 0xd4, 0xaa,
|
||||
0x98, 0x29, 0xcd, 0x1f, 0x2, 0xa8, 0x87, 0xd2,
|
||||
0xa0, 0x93, 0x98, 0xef, 0xc, 0x43, 0xed, 0x9d,
|
||||
0xc2, 0xeb, 0x81, 0xe9, 0x64, 0x23, 0x68, 0x1e,
|
||||
0x25, 0x57, 0xde, 0x9a, 0xcf, 0x7f, 0xe5, 0xba,
|
||||
0x41, 0xea, 0xea, 0x36, 0x1a, 0x28, 0x79, 0x20,
|
||||
0x5e, 0x18, 0x4e, 0x7c, 0x8e, 0x58, 0x7a, 0xef,
|
||||
0x91, 0x2, 0x93, 0xbb, 0x56, 0xa1, 0x49, 0x1b,
|
||||
0x79, 0x92, 0xf3, 0x58, 0x4f, 0x52, 0x9c, 0x2,
|
||||
0x77, 0xaf, 0x2a, 0x8f, 0x49, 0xd0, 0x99, 0x4d,
|
||||
0x98, 0x101, 0x60, 0x93, 0x100, 0x75, 0x31, 0xce,
|
||||
0x49, 0x20, 0x56, 0x57, 0xe2, 0xf5, 0x26, 0x2b,
|
||||
0x8a, 0xbf, 0xde, 0xd0, 0x83, 0x34, 0xf4, 0x17
|
||||
};
|
||||
|
||||
|
||||
// Arithmetic decoder model
|
||||
|
||||
void StuffItArsenicDecompressor::InitializeArithmeticModel(ArithmeticModel *model,int firstsymbol,int lastsymbol,int increment,int frequencylimit)
|
||||
{
|
||||
model->increment=increment;
|
||||
model->frequencylimit=frequencylimit;
|
||||
model->numsymbols=lastsymbol-firstsymbol+1;
|
||||
for(int i=0;i<model->numsymbols;i++) model->symbols[i].symbol=i+firstsymbol;
|
||||
|
||||
ResetArithmeticModel(model);
|
||||
}
|
||||
|
||||
void StuffItArsenicDecompressor::ResetArithmeticModel(ArithmeticModel *model)
|
||||
{
|
||||
model->totalfrequency=model->increment*model->numsymbols;
|
||||
for(int i=0;i<model->numsymbols;i++) model->symbols[i].frequency=model->increment;
|
||||
}
|
||||
|
||||
void StuffItArsenicDecompressor::IncreaseArithmeticModelFrequency(ArithmeticModel *model,int symindex)
|
||||
{
|
||||
model->symbols[symindex].frequency+=model->increment;
|
||||
model->totalfrequency+=model->increment;
|
||||
|
||||
if(model->totalfrequency>model->frequencylimit)
|
||||
{
|
||||
model->totalfrequency=0;
|
||||
for(int i=0;i<model->numsymbols;i++)
|
||||
{
|
||||
model->symbols[i].frequency++;
|
||||
model->symbols[i].frequency>>=1;
|
||||
model->totalfrequency+=model->symbols[i].frequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Arithmetic decoder
|
||||
|
||||
#define NumBits 26
|
||||
#define One (1<<(NumBits-1))
|
||||
#define Half (1<<(NumBits-2))
|
||||
|
||||
bool StuffItArsenicDecompressor::InitializeArithmeticDecoder(ArithmeticDecoder *decoder,CSInputBuffer *input)
|
||||
{
|
||||
unsigned int initialCode;
|
||||
if (!CSInputNextLongBitString(input, NumBits, initialCode))
|
||||
return false;
|
||||
decoder->input=input;
|
||||
decoder->range=One;
|
||||
decoder->code = initialCode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StuffItArsenicDecompressor::ReadNextArithmeticCode(ArithmeticDecoder *decoder,int symlow,int symsize,int symtot)
|
||||
{
|
||||
int renorm_factor=decoder->range/symtot;
|
||||
int lowincr=renorm_factor*symlow;
|
||||
|
||||
decoder->code-=lowincr;
|
||||
if(symlow+symsize==symtot) decoder->range-=lowincr;
|
||||
else decoder->range=symsize*renorm_factor;
|
||||
|
||||
while(decoder->range<=Half)
|
||||
{
|
||||
decoder->range<<=1;
|
||||
|
||||
unsigned int bit;
|
||||
if (!CSInputNextBit(decoder->input, bit))
|
||||
return false;
|
||||
|
||||
decoder->code = (decoder->code << 1) | bit;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StuffItArsenicDecompressor::NextArithmeticSymbol(ArithmeticDecoder *decoder,ArithmeticModel *model,int &outSymbol)
|
||||
{
|
||||
int frequency=decoder->code/(decoder->range/model->totalfrequency);
|
||||
int cumulative=0,n;
|
||||
for(n=0;n<model->numsymbols-1;n++)
|
||||
{
|
||||
if(cumulative+model->symbols[n].frequency>frequency) break;
|
||||
cumulative+=model->symbols[n].frequency;
|
||||
}
|
||||
|
||||
if (!ReadNextArithmeticCode(decoder, cumulative, model->symbols[n].frequency, model->totalfrequency))
|
||||
return false;
|
||||
|
||||
IncreaseArithmeticModelFrequency(model,n);
|
||||
|
||||
outSymbol = model->symbols[n].symbol;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StuffItArsenicDecompressor::NextArithmeticBitString(ArithmeticDecoder *decoder,ArithmeticModel *model,int bits,int &outBits)
|
||||
{
|
||||
int res=0;
|
||||
for (int i = 0; i < bits; i++)
|
||||
{
|
||||
int sym;
|
||||
if (!NextArithmeticSymbol(decoder, model, sym))
|
||||
return false;
|
||||
|
||||
if (sym)
|
||||
res |= 1 << i;
|
||||
}
|
||||
outBits = res;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StuffItArsenicDecompressor::StuffItArsenicDecompressor()
|
||||
: block(NULL)
|
||||
, transform(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
StuffItArsenicDecompressor::~StuffItArsenicDecompressor()
|
||||
{
|
||||
delete[] block;
|
||||
delete[] transform;
|
||||
}
|
||||
|
||||
bool StuffItArsenicDecompressor::Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize)
|
||||
{
|
||||
if (!InitializeArithmeticDecoder(&decoder, input))
|
||||
return false;
|
||||
|
||||
InitializeArithmeticModel(&initialmodel,0,1,1,256);
|
||||
InitializeArithmeticModel(&selectormodel,0,10,8,1024);
|
||||
InitializeArithmeticModel(&mtfmodel[0],2,3,8,1024);
|
||||
InitializeArithmeticModel(&mtfmodel[1],4,7,4,1024);
|
||||
InitializeArithmeticModel(&mtfmodel[2],8,15,4,1024);
|
||||
InitializeArithmeticModel(&mtfmodel[3],16,31,4,1024);
|
||||
InitializeArithmeticModel(&mtfmodel[4],32,63,2,1024);
|
||||
InitializeArithmeticModel(&mtfmodel[5],64,127,2,1024);
|
||||
InitializeArithmeticModel(&mtfmodel[6],128,255,1,1024);
|
||||
|
||||
int bits;
|
||||
if (!NextArithmeticBitString(&decoder, &initialmodel, 8, bits) || bits != 'A')
|
||||
return false;
|
||||
if (!NextArithmeticBitString(&decoder, &initialmodel, 8, bits) || bits != 's')
|
||||
return false;
|
||||
|
||||
if (!NextArithmeticBitString(&decoder, &initialmodel, 4, blockbits))
|
||||
return false;
|
||||
|
||||
blockbits+=9;
|
||||
blocksize=1<<blockbits;
|
||||
numbytes=bytecount=0;
|
||||
repeat=0;
|
||||
|
||||
delete[] block;
|
||||
block=new uint8_t[blocksize];
|
||||
|
||||
crc=0xffffffff;
|
||||
compcrc=0;
|
||||
|
||||
// check first end marker
|
||||
int eobsym;
|
||||
if (!NextArithmeticSymbol(&decoder, &initialmodel, eobsym))
|
||||
return false;
|
||||
|
||||
endofblocks = (eobsym != 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StuffItArsenicDecompressor::ReadBlock()
|
||||
{
|
||||
ResetMTFDecoder(&mtf);
|
||||
|
||||
if (!NextArithmeticSymbol(&decoder, &initialmodel, randomized))
|
||||
return false;
|
||||
if (!NextArithmeticBitString(&decoder, &initialmodel, blockbits, transformindex))
|
||||
return false;
|
||||
numbytes=0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int sel;
|
||||
if (!NextArithmeticSymbol(&decoder, &selectormodel, sel))
|
||||
return false;
|
||||
|
||||
if(sel==0||sel==1) // zero counting
|
||||
{
|
||||
int zerostate=1,zerocount=0;
|
||||
while(sel<2)
|
||||
{
|
||||
if(sel==0) zerocount+=zerostate;
|
||||
else if(sel==1) zerocount+=2*zerostate;
|
||||
zerostate*=2;
|
||||
if (!NextArithmeticSymbol(&decoder, &selectormodel, sel))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (numbytes + zerocount > blocksize)
|
||||
return false;
|
||||
|
||||
memset(&block[numbytes],DecodeMTF(&mtf,0),zerocount);
|
||||
numbytes+=zerocount;
|
||||
}
|
||||
|
||||
int symbol;
|
||||
if(sel==10)
|
||||
break;
|
||||
else if(sel==2)
|
||||
symbol=1;
|
||||
else
|
||||
{
|
||||
if (!NextArithmeticSymbol(&decoder, &mtfmodel[sel - 3], symbol))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (numbytes >= blocksize)
|
||||
return false;
|
||||
|
||||
block[numbytes++] = DecodeMTF(&mtf, symbol);
|
||||
}
|
||||
|
||||
if (transformindex >= numbytes)
|
||||
return false;
|
||||
|
||||
ResetArithmeticModel(&selectormodel);
|
||||
for(int i=0;i<7;i++) ResetArithmeticModel(&mtfmodel[i]);
|
||||
|
||||
int endMarkerSym;
|
||||
if (!NextArithmeticSymbol(&decoder, &initialmodel, endMarkerSym))
|
||||
return false;
|
||||
|
||||
if(endMarkerSym) // end marker
|
||||
{
|
||||
int compcrcsym;
|
||||
if (!NextArithmeticBitString(&decoder, &initialmodel, 32, compcrcsym))
|
||||
return false;
|
||||
compcrc = compcrcsym;
|
||||
endofblocks=true;
|
||||
}
|
||||
|
||||
free(transform);
|
||||
transform = new uint32_t[numbytes];
|
||||
CalculateInverseBWT(transform,block,numbytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StuffItArsenicDecompressor::ReadBytes(void *dest, size_t numBytes)
|
||||
{
|
||||
while (numBytes)
|
||||
{
|
||||
uint8_t outbyte;
|
||||
|
||||
if (repeat)
|
||||
{
|
||||
repeat--;
|
||||
outbyte = last;
|
||||
}
|
||||
else
|
||||
{
|
||||
retry:
|
||||
if (bytecount >= numbytes)
|
||||
{
|
||||
if (endofblocks)
|
||||
return false;
|
||||
|
||||
if (!this->ReadBlock())
|
||||
return false;
|
||||
|
||||
bytecount = 0;
|
||||
count = 0;
|
||||
last = 0;
|
||||
|
||||
randindex = 0;
|
||||
randcount = RandomizationTable[0];
|
||||
}
|
||||
|
||||
transformindex = transform[transformindex];
|
||||
int byte = block[transformindex];
|
||||
|
||||
if (randomized&&randcount == bytecount)
|
||||
{
|
||||
byte ^= 1;
|
||||
randindex = (randindex + 1) & 255;
|
||||
randcount += RandomizationTable[randindex];
|
||||
}
|
||||
|
||||
bytecount++;
|
||||
|
||||
if (count == 4)
|
||||
{
|
||||
count = 0;
|
||||
if (byte == 0) goto retry;
|
||||
repeat = byte - 1;
|
||||
outbyte = last;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (byte == last) count++;
|
||||
else { count = 1; last = byte; }
|
||||
outbyte = byte;
|
||||
}
|
||||
}
|
||||
|
||||
crc = XADCRC(crc, outbyte, XADCRCTable_edb88320);
|
||||
|
||||
uint8_t *destBytes = static_cast<uint8_t*>(dest);
|
||||
destBytes[0] = outbyte;
|
||||
|
||||
numBytes--;
|
||||
dest = destBytes + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
65
unpacktool/StuffItArsenicDecompressor.h
Normal file
65
unpacktool/StuffItArsenicDecompressor.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "IDecompressor.h"
|
||||
#include "BWT.h"
|
||||
|
||||
|
||||
class StuffItArsenicDecompressor : public IDecompressor
|
||||
{
|
||||
public:
|
||||
StuffItArsenicDecompressor();
|
||||
~StuffItArsenicDecompressor() override;
|
||||
|
||||
bool Reset(CSInputBuffer *reader, size_t compressedSize, size_t decompressedSize) override;
|
||||
bool ReadBytes(void *dest, size_t numBytes) override;
|
||||
|
||||
private:
|
||||
struct ArithmeticSymbol
|
||||
{
|
||||
int symbol;
|
||||
int frequency;
|
||||
};
|
||||
|
||||
struct ArithmeticModel
|
||||
{
|
||||
int totalfrequency;
|
||||
int increment;
|
||||
int frequencylimit;
|
||||
|
||||
int numsymbols;
|
||||
ArithmeticSymbol symbols[128];
|
||||
};
|
||||
|
||||
struct ArithmeticDecoder
|
||||
{
|
||||
CSInputBuffer *input;
|
||||
int range, code;
|
||||
};
|
||||
|
||||
ArithmeticModel initialmodel, selectormodel, mtfmodel[7];
|
||||
ArithmeticDecoder decoder;
|
||||
MTFState mtf;
|
||||
|
||||
int blockbits, blocksize;
|
||||
uint8_t *block;
|
||||
bool endofblocks;
|
||||
|
||||
int numbytes, bytecount, transformindex;
|
||||
uint32_t *transform;
|
||||
|
||||
int randomized, randcount, randindex;
|
||||
|
||||
int repeat, count, last;
|
||||
|
||||
uint32_t crc, compcrc;
|
||||
|
||||
static void ResetArithmeticModel(ArithmeticModel *model);
|
||||
static void InitializeArithmeticModel(ArithmeticModel *model, int firstsymbol, int lastsymbol, int increment, int frequencylimit);
|
||||
static void IncreaseArithmeticModelFrequency(ArithmeticModel *model, int symindex);
|
||||
static bool InitializeArithmeticDecoder(ArithmeticDecoder *decoder, CSInputBuffer *input);
|
||||
static bool ReadNextArithmeticCode(ArithmeticDecoder *decoder, int symlow, int symsize, int symtot);
|
||||
static bool NextArithmeticSymbol(ArithmeticDecoder *decoder, ArithmeticModel *model, int &outSym);
|
||||
static bool NextArithmeticBitString(ArithmeticDecoder *decoder, ArithmeticModel *model, int bits, int &outBitString);
|
||||
|
||||
bool ReadBlock();
|
||||
};
|
31
unpacktool/StuffItCommon.cpp
Normal file
31
unpacktool/StuffItCommon.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "StuffItCommon.h"
|
||||
|
||||
|
||||
CompressionMethod_t StuffItCommon::ResolveCompressionMethod(int stuffItMethod)
|
||||
{
|
||||
switch (stuffItMethod)
|
||||
{
|
||||
case 0:
|
||||
return CompressionMethods::kNone;
|
||||
case 1:
|
||||
return CompressionMethods::kStuffItRLE90;
|
||||
case 2:
|
||||
return CompressionMethods::kStuffItLZW;
|
||||
case 3:
|
||||
return CompressionMethods::kStuffItHuffman;
|
||||
case 5:
|
||||
return CompressionMethods::kStuffItLZAH;
|
||||
case 6:
|
||||
return CompressionMethods::kStuffItFixedHuffman;
|
||||
case 8:
|
||||
return CompressionMethods::kStuffItMW;
|
||||
case 13:
|
||||
return CompressionMethods::kStuffIt13;
|
||||
case 14:
|
||||
return CompressionMethods::kStuffIt14;
|
||||
case 15:
|
||||
return CompressionMethods::kStuffItArsenic;
|
||||
default:
|
||||
return CompressionMethods::kUnknown;
|
||||
}
|
||||
}
|
8
unpacktool/StuffItCommon.h
Normal file
8
unpacktool/StuffItCommon.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "ArchiveDescription.h"
|
||||
|
||||
namespace StuffItCommon
|
||||
{
|
||||
CompressionMethod_t ResolveCompressionMethod(int stuffItMethod);
|
||||
}
|
75
unpacktool/StuffItHuffmanDecompressor.cpp
Normal file
75
unpacktool/StuffItHuffmanDecompressor.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "StuffItHuffmanDecompressor.h"
|
||||
|
||||
StuffItHuffmanDecompressor::StuffItHuffmanDecompressor()
|
||||
: input(nullptr)
|
||||
, code(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
StuffItHuffmanDecompressor::~StuffItHuffmanDecompressor()
|
||||
{
|
||||
delete code;
|
||||
}
|
||||
|
||||
bool StuffItHuffmanDecompressor::Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize)
|
||||
{
|
||||
this->input = input;
|
||||
code = nullptr;
|
||||
|
||||
return this->resetByteStream();
|
||||
}
|
||||
|
||||
bool StuffItHuffmanDecompressor::resetByteStream()
|
||||
{
|
||||
delete code;
|
||||
code = XADPrefixCode::prefixCode();
|
||||
|
||||
code->startBuildingTree();
|
||||
if (!parseTree())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StuffItHuffmanDecompressor::parseTree()
|
||||
{
|
||||
unsigned int firstBit;
|
||||
if (!CSInputNextBit(input, firstBit))
|
||||
return false;
|
||||
|
||||
if (firstBit == 1)
|
||||
{
|
||||
unsigned int nextBits;
|
||||
if (!CSInputNextBitString(input, 8, nextBits))
|
||||
return false;
|
||||
code->makeLeafWithValue(nextBits);
|
||||
}
|
||||
else
|
||||
{
|
||||
code->startZeroBranch();
|
||||
if (!parseTree())
|
||||
return false;
|
||||
|
||||
code->startOneBranch();
|
||||
if (!parseTree())
|
||||
return false;
|
||||
|
||||
code->finishBranches();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StuffItHuffmanDecompressor::ReadBytes(void *dest, size_t numBytes)
|
||||
{
|
||||
for (size_t i = 0; i < numBytes; i++)
|
||||
{
|
||||
int sym;
|
||||
if (!CSInputNextSymbolUsingCode(input, code, sym))
|
||||
return false;
|
||||
|
||||
static_cast<uint8_t*>(dest)[i] = sym;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
21
unpacktool/StuffItHuffmanDecompressor.h
Normal file
21
unpacktool/StuffItHuffmanDecompressor.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "IDecompressor.h"
|
||||
#include "PrefixCode.h"
|
||||
|
||||
class StuffItHuffmanDecompressor : public IDecompressor
|
||||
{
|
||||
public:
|
||||
StuffItHuffmanDecompressor();
|
||||
~StuffItHuffmanDecompressor();
|
||||
|
||||
bool Reset(CSInputBuffer *input, size_t compressedSize, size_t decompressedSize) override;
|
||||
bool ReadBytes(void *dest, size_t numBytes) override;
|
||||
|
||||
private:
|
||||
CSInputBuffer *input;
|
||||
XADPrefixCode *code;
|
||||
|
||||
bool parseTree();
|
||||
bool resetByteStream();
|
||||
};
|
240
unpacktool/StuffItParser.cpp
Normal file
240
unpacktool/StuffItParser.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
#include "StuffItParser.h"
|
||||
|
||||
#include "IFileReader.h"
|
||||
#include "UPByteSwap.h"
|
||||
|
||||
#include "PLBigEndian.h"
|
||||
#include "CRC.h"
|
||||
#include "ArchiveDescription.h"
|
||||
#include "StuffItCommon.h"
|
||||
#include "StringCommon.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
bool StuffItParser::Check(IFileReader &reader)
|
||||
{
|
||||
if (!reader.SeekStart(0))
|
||||
return false;
|
||||
|
||||
uint8_t bytes[14];
|
||||
|
||||
if (!reader.ReadExact(bytes, sizeof(bytes)))
|
||||
return false;
|
||||
|
||||
|
||||
if (ParseUInt32BE(bytes + 10) == 0x724c6175)
|
||||
{
|
||||
if (ParseUInt32BE(bytes) == 0x53495421)
|
||||
return true;
|
||||
|
||||
// Installer archives?
|
||||
if (bytes[0] == 'S'&&bytes[1] == 'T')
|
||||
{
|
||||
if (bytes[2] == 'i' && (bytes[3] == 'n' || (bytes[3] >= '0'&&bytes[3] <= '9'))) return true;
|
||||
else if (bytes[2] >= '0'&&bytes[2] <= '9'&&bytes[3] >= '0'&&bytes[3] <= '9') return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define SITFH_COMPRMETHOD 0 /* xadUINT8 rsrc fork compression method */
|
||||
#define SITFH_COMPDMETHOD 1 /* xadUINT8 data fork compression method */
|
||||
#define SITFH_FNAMESIZE 2 /* xadUINT8 filename size */
|
||||
#define SITFH_FNAME 3 /* xadUINT8 31 byte filename */
|
||||
#define SITFH_FNAME_CRC 34 /* xadUINT16 crc of filename + size */
|
||||
|
||||
#define SITFH_UNK 36 /* xadUINT16 unknown, always 0x0986? */
|
||||
#define SITFH_RSRCLONG 38 /* xadUINT32 unknown rsrc fork value */
|
||||
#define SITFH_DATALONG 42 /* xadUINT32 unknown data fork value */
|
||||
#define SITFH_DATACHAR 46 /* xadUINT8 unknown data (yes, data) fork value */
|
||||
#define SITFH_RSRCCHAR 47 /* xadUINT8 unknown rsrc fork value */
|
||||
#define SITFH_CHILDCOUNT 48 /* xadUINT16 number of items in dir */
|
||||
#define SITFH_PREVOFFS 50 /* xadUINT32 offset of previous entry */
|
||||
#define SITFH_NEXTOFFS 54 /* xadUINT32 offset of next entry */
|
||||
#define SITFH_PARENTOFFS 58 /* xadUINT32 offset of parent entry */
|
||||
#define SITFH_CHILDOFFS 62 /* xadINT32 offset of first child entry, -1 for file entries */
|
||||
|
||||
#define SITFH_FTYPE 66 /* xadUINT32 file type */
|
||||
#define SITFH_CREATOR 70 /* xadUINT32 file creator */
|
||||
#define SITFH_FNDRFLAGS 74 /* xadUINT16 Finder flags */
|
||||
#define SITFH_CREATIONDATE 76 /* xadUINT32 creation date */
|
||||
#define SITFH_MODDATE 80 /* xadUINT32 modification date */
|
||||
#define SITFH_RSRCLENGTH 84 /* xadUINT32 decompressed rsrc length */
|
||||
#define SITFH_DATALENGTH 88 /* xadUINT32 decompressed data length */
|
||||
#define SITFH_COMPRLENGTH 92 /* xadUINT32 compressed rsrc length */
|
||||
#define SITFH_COMPDLENGTH 96 /* xadUINT32 compressed data length */
|
||||
#define SITFH_RSRCCRC 100 /* xadUINT16 crc of rsrc fork */
|
||||
#define SITFH_DATACRC 102 /* xadUINT16 crc of data fork */
|
||||
|
||||
#define SITFH_RSRCPAD 104 /* xadUINT8 rsrc padding bytes for encryption */
|
||||
#define SITFH_DATAPAD 105 /* xadUINT8 data padding bytes for encryption */
|
||||
#define SITFH_DATAUNK1 106 /* xadUINT8 unknown data value, always 0? */
|
||||
#define SITFH_DATAUNK2 107 /* xadUINT8 unknown data value, always 4 for encrypted? */
|
||||
#define SITFH_RSRCUNK1 108 /* xadUINT8 unknown rsrc value, always 0? */
|
||||
#define SITFH_RSRCUNK2 109 /* xadUINT8 unknown rsrc value, always 4 for encrypted? */
|
||||
|
||||
#define SITFH_HDRCRC 110 /* xadUINT16 crc of file header */
|
||||
#define SIT_FILEHDRSIZE 112
|
||||
|
||||
#define StuffItEncryptedFlag 0x80 // password protected bit
|
||||
#define StuffItStartFolder 0x20 // start of folder
|
||||
#define StuffItEndFolder 0x21 // end of folder
|
||||
#define StuffItFolderContainsEncrypted 0x10 // folder contains encrypted items bit
|
||||
#define StuffItMethodMask (~StuffItEncryptedFlag)
|
||||
#define StuffItFolderMask (~(StuffItEncryptedFlag|StuffItFolderContainsEncrypted))
|
||||
|
||||
struct StuffItHeader
|
||||
{
|
||||
uint8_t m_signature[4];
|
||||
BEUInt16_t m_numRootFiles;
|
||||
BEUInt32_t m_totalSize;
|
||||
uint8_t m_signature2[4];
|
||||
uint8_t m_version;
|
||||
uint8_t m_reserved;
|
||||
BEUInt32_t m_headerSize; // In version 1, this is absent, header size is always 22
|
||||
BEUInt16_t m_crc;
|
||||
};
|
||||
|
||||
void ParseMacFileInfo(ArchiveItem &item, const uint8_t *header, IFileReader::FilePos_t filePos)
|
||||
{
|
||||
item.m_macProperties.m_creationDate = ParseUInt32BE(header + SITFH_CREATIONDATE);
|
||||
item.m_macProperties.m_modifiedDate = ParseUInt32BE(header + SITFH_MODDATE);
|
||||
memcpy(item.m_macProperties.m_fileCreator, header + SITFH_CREATOR, 4);
|
||||
memcpy(item.m_macProperties.m_fileType, header + SITFH_FTYPE, 4);
|
||||
item.m_macProperties.m_finderFlags = ParseUInt16BE(header + SITFH_FNDRFLAGS);
|
||||
}
|
||||
|
||||
ArchiveItemList *StuffItParser::Parse(IFileReader &reader)
|
||||
{
|
||||
if (!reader.SeekStart(0))
|
||||
return nullptr;
|
||||
|
||||
IFileReader::FilePos_t base = reader.GetPosition();
|
||||
|
||||
StuffItHeader archiveHeader;
|
||||
if (!reader.ReadExact(&archiveHeader, sizeof(archiveHeader)))
|
||||
return nullptr;
|
||||
|
||||
uint32_t headerSize = 0;
|
||||
|
||||
if (archiveHeader.m_version == 0)
|
||||
return nullptr;
|
||||
else if (archiveHeader.m_version == 1)
|
||||
headerSize = sizeof(archiveHeader);
|
||||
else
|
||||
headerSize = archiveHeader.m_headerSize;
|
||||
|
||||
if (headerSize < sizeof(archiveHeader))
|
||||
return nullptr;
|
||||
|
||||
reader.SeekCurrent(headerSize - sizeof(archiveHeader));
|
||||
|
||||
IFileReader::FilePos_t totalsize = archiveHeader.m_totalSize;
|
||||
if (static_cast<IFileReader::FilePos_t>(totalsize) > static_cast<IFileReader::FilePos_t>(reader.FileSize()))
|
||||
return nullptr;
|
||||
|
||||
ArchiveItemList rootList;
|
||||
|
||||
std::vector<ArchiveItemList*> folderStack;
|
||||
folderStack.push_back(&rootList);
|
||||
|
||||
while (reader.GetPosition() + SIT_FILEHDRSIZE <= totalsize + base)
|
||||
{
|
||||
uint8_t header[SIT_FILEHDRSIZE];
|
||||
if (!reader.ReadExact(header, SIT_FILEHDRSIZE))
|
||||
return nullptr;
|
||||
|
||||
if (ParseUInt16BE(header + SITFH_HDRCRC) == XADCalculateCRC(0, header, 110, XADCRCTable_a001))
|
||||
{
|
||||
int resourcelength = ParseUInt32BE(header + SITFH_RSRCLENGTH);
|
||||
int resourcecomplen = ParseUInt32BE(header + SITFH_COMPRLENGTH);
|
||||
int datalength = ParseUInt32BE(header + SITFH_DATALENGTH);
|
||||
int datacomplen = ParseUInt32BE(header + SITFH_COMPDLENGTH);
|
||||
int datamethod = header[SITFH_COMPDMETHOD];
|
||||
int resourcemethod = header[SITFH_COMPRMETHOD];
|
||||
int datapadding = header[SITFH_DATAPAD];
|
||||
int resourcepadding = header[SITFH_RSRCPAD];
|
||||
|
||||
int namelen = header[SITFH_FNAMESIZE];
|
||||
if (namelen > 31) namelen = 31;
|
||||
|
||||
const uint8_t fileNameLength = header[SITFH_FNAMESIZE];
|
||||
if (fileNameLength > SITFH_FNAME_CRC - SITFH_FNAME)
|
||||
return false;
|
||||
|
||||
std::vector<uint8_t> utf8FileName;
|
||||
StringCommon::ConvertMacRomanFileName(utf8FileName, header + SITFH_FNAME, fileNameLength);
|
||||
|
||||
IFileReader::FilePos_t start = reader.GetPosition();
|
||||
|
||||
ArchiveItemList *stackTop = folderStack.back();
|
||||
stackTop->m_items.push_back(ArchiveItem());
|
||||
|
||||
ArchiveItem &addedItem = stackTop->m_items.back();
|
||||
addedItem.m_fileNameUTF8 = std::move(utf8FileName);
|
||||
|
||||
if ((datamethod&StuffItFolderMask) == StuffItStartFolder ||
|
||||
(resourcemethod&StuffItFolderMask) == StuffItStartFolder)
|
||||
{
|
||||
addedItem.m_isDirectory = true;
|
||||
ParseMacFileInfo(addedItem, header, start);
|
||||
|
||||
if ((datamethod&StuffItFolderContainsEncrypted) != 0 ||
|
||||
(resourcemethod&StuffItFolderContainsEncrypted) != 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
addedItem.m_children = new ArchiveItemList();
|
||||
folderStack.push_back(addedItem.m_children);
|
||||
}
|
||||
else if ((datamethod&StuffItFolderMask) == StuffItEndFolder ||
|
||||
(resourcemethod&StuffItFolderMask) == StuffItEndFolder)
|
||||
{
|
||||
if (folderStack.size() == 1)
|
||||
return nullptr;
|
||||
|
||||
stackTop->m_items.pop_back();
|
||||
|
||||
folderStack.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseMacFileInfo(addedItem, header, start);
|
||||
|
||||
if (resourcelength)
|
||||
{
|
||||
if (resourcemethod & StuffItEncryptedFlag)
|
||||
return nullptr;
|
||||
|
||||
addedItem.m_resourceForkDesc.m_compressedSize = resourcecomplen;
|
||||
addedItem.m_resourceForkDesc.m_uncompressedSize = resourcelength;
|
||||
addedItem.m_resourceForkDesc.m_filePosition = start;
|
||||
addedItem.m_resourceForkDesc.m_compressionMethod = StuffItCommon::ResolveCompressionMethod(resourcemethod & StuffItMethodMask);
|
||||
}
|
||||
|
||||
if (datalength)
|
||||
{
|
||||
if (datamethod & StuffItEncryptedFlag)
|
||||
return nullptr;
|
||||
|
||||
addedItem.m_dataForkDesc.m_compressedSize = resourcecomplen;
|
||||
addedItem.m_dataForkDesc.m_uncompressedSize = datalength;
|
||||
addedItem.m_dataForkDesc.m_filePosition = start + resourcecomplen;
|
||||
addedItem.m_dataForkDesc.m_compressionMethod = StuffItCommon::ResolveCompressionMethod(datamethod & StuffItMethodMask);
|
||||
}
|
||||
|
||||
if (!reader.SeekCurrent(resourcecomplen + datacomplen))
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (folderStack.size() != 1)
|
||||
return nullptr;
|
||||
|
||||
return new ArchiveItemList(std::move(rootList));
|
||||
}
|
10
unpacktool/StuffItParser.h
Normal file
10
unpacktool/StuffItParser.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "IArchiveParser.h"
|
||||
|
||||
class StuffItParser : public IArchiveParser
|
||||
{
|
||||
public:
|
||||
bool Check(IFileReader &reader) override;
|
||||
ArchiveItemList *Parse(IFileReader &reader) override;
|
||||
};
|
45
unpacktool/UPByteSwap.h
Normal file
45
unpacktool/UPByteSwap.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ByteSwap.h"
|
||||
|
||||
inline uint32_t ParseUInt32BE(const uint8_t *bytes)
|
||||
{
|
||||
return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
|
||||
}
|
||||
|
||||
inline int32_t ParseInt32BE(const uint8_t *bytes)
|
||||
{
|
||||
return static_cast<int32_t>(ParseUInt32BE(bytes));
|
||||
}
|
||||
|
||||
inline uint16_t ParseUInt16BE(const uint8_t *bytes)
|
||||
{
|
||||
return (bytes[0] << 8) | bytes[1];
|
||||
}
|
||||
|
||||
inline int16_t ParseInt16BE(const uint8_t *bytes)
|
||||
{
|
||||
return static_cast<int16_t>(ParseInt16BE(bytes));
|
||||
}
|
||||
|
||||
inline uint32_t ParseUInt32LE(const uint8_t *bytes)
|
||||
{
|
||||
return (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
|
||||
}
|
||||
|
||||
inline int32_t ParseInt32LE(const uint8_t *bytes)
|
||||
{
|
||||
return static_cast<int32_t>(ParseUInt32LE(bytes));
|
||||
}
|
||||
|
||||
inline uint16_t ParseUInt16LE(const uint8_t *bytes)
|
||||
{
|
||||
return (bytes[1] << 8) | bytes[0];
|
||||
}
|
||||
|
||||
inline int16_t ParseInt16LE(const uint8_t *bytes)
|
||||
{
|
||||
return static_cast<int16_t>(ParseInt16LE(bytes));
|
||||
}
|
566
unpacktool/unpacktool.cpp
Normal file
566
unpacktool/unpacktool.cpp
Normal file
@@ -0,0 +1,566 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "IArchiveParser.h"
|
||||
#include "IFileReader.h"
|
||||
#include "StuffItParser.h"
|
||||
#include "StuffIt5Parser.h"
|
||||
#include "CompactProParser.h"
|
||||
|
||||
#include "UTF8.h"
|
||||
#include "UTF16.h"
|
||||
|
||||
#include "ArchiveDescription.h"
|
||||
#include "IDecompressor.h"
|
||||
|
||||
#include "NullDecompressor.h"
|
||||
#include "RLE90Decompressor.h"
|
||||
#include "LZWDecompressor.h"
|
||||
#include "StuffIt13Decompressor.h"
|
||||
#include "StuffItHuffmanDecompressor.h"
|
||||
#include "StuffItArsenicDecompressor.h"
|
||||
#include "CompactProRLEDecompressor.h"
|
||||
#include "CompactProLZHRLEDecompressor.h"
|
||||
|
||||
#include "CSInputBuffer.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class CFileReader final : public IFileReader
|
||||
{
|
||||
public:
|
||||
explicit CFileReader(FILE *f);
|
||||
|
||||
size_t Read(void *buffer, size_t sz);
|
||||
size_t FileSize() const override;
|
||||
|
||||
bool SeekStart(FilePos_t pos) override;
|
||||
bool SeekCurrent(FilePos_t pos) override;
|
||||
bool SeekEnd(FilePos_t pos) override;
|
||||
FilePos_t GetPosition() const override;
|
||||
|
||||
private:
|
||||
FILE *m_file;
|
||||
long m_size;
|
||||
};
|
||||
|
||||
|
||||
CFileReader::CFileReader(FILE *f)
|
||||
: m_file(f)
|
||||
{
|
||||
fseek(f, 0, SEEK_END);
|
||||
m_size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
size_t CFileReader::Read(void *buffer, size_t sz)
|
||||
{
|
||||
return fread(buffer, 1, sz, m_file);
|
||||
}
|
||||
|
||||
size_t CFileReader::FileSize() const
|
||||
{
|
||||
return static_cast<size_t>(m_size);
|
||||
}
|
||||
|
||||
bool CFileReader::SeekStart(FilePos_t pos)
|
||||
{
|
||||
return !_fseeki64(m_file, pos, SEEK_SET);
|
||||
}
|
||||
|
||||
bool CFileReader::SeekCurrent(FilePos_t pos)
|
||||
{
|
||||
return !_fseeki64(m_file, pos, SEEK_CUR);
|
||||
}
|
||||
|
||||
bool CFileReader::SeekEnd(FilePos_t pos)
|
||||
{
|
||||
return !_fseeki64(m_file, pos, SEEK_END);
|
||||
}
|
||||
|
||||
IFileReader::FilePos_t CFileReader::GetPosition() const
|
||||
{
|
||||
return _ftelli64(m_file);
|
||||
}
|
||||
|
||||
StuffItParser g_stuffItParser;
|
||||
StuffIt5Parser g_stuffIt5Parser;
|
||||
CompactProParser g_compactProParser;
|
||||
|
||||
std::string ConvertWStringToUTF8(const wchar_t *str)
|
||||
{
|
||||
size_t strLength = wcslen(str);
|
||||
|
||||
std::string result;
|
||||
|
||||
for (size_t i = 0; i < strLength; )
|
||||
{
|
||||
size_t charsDigested = 0;
|
||||
uint32_t codePoint = 0;
|
||||
uint8_t asUTF8[4];
|
||||
if (!PortabilityLayer::UTF16Processor::DecodeCodePoint(reinterpret_cast<const uint16_t*>(str) + i, strLength - i, charsDigested, codePoint))
|
||||
return "";
|
||||
|
||||
i += charsDigested;
|
||||
|
||||
size_t bytesEmitted = 0;
|
||||
PortabilityLayer::UTF8Processor::EncodeCodePoint(asUTF8, bytesEmitted, codePoint);
|
||||
|
||||
result.append(reinterpret_cast<const char*>(asUTF8), bytesEmitted);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring ConvertUTF8ToWString(const char *str)
|
||||
{
|
||||
size_t strLength = strlen(str);
|
||||
|
||||
std::wstring result;
|
||||
|
||||
for (size_t i = 0; i < strLength; )
|
||||
{
|
||||
size_t charsDigested = 0;
|
||||
uint32_t codePoint = 0;
|
||||
uint16_t asUTF16[4];
|
||||
if (!PortabilityLayer::UTF8Processor::DecodeCodePoint(reinterpret_cast<const uint8_t*>(str) + i, strLength - i, charsDigested, codePoint))
|
||||
return L"";
|
||||
|
||||
i += charsDigested;
|
||||
|
||||
size_t codePointsEmitted = 0;
|
||||
PortabilityLayer::UTF16Processor::EncodeCodePoint(asUTF16, codePointsEmitted, codePoint);
|
||||
|
||||
result.append(reinterpret_cast<const wchar_t*>(asUTF16), codePointsEmitted);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FILE *fopen_utf8(const char *path, const char *options)
|
||||
{
|
||||
std::wstring pathUTF16 = ConvertUTF8ToWString(path);
|
||||
std::wstring optionsUTF16 = ConvertUTF8ToWString(options);
|
||||
|
||||
return _wfopen(pathUTF16.c_str(), optionsUTF16.c_str());
|
||||
}
|
||||
|
||||
int mkdir_utf8(const char *path)
|
||||
{
|
||||
std::wstring pathUTF16 = ConvertUTF8ToWString(path);
|
||||
return _wmkdir(pathUTF16.c_str());
|
||||
}
|
||||
|
||||
void TerminateDirectoryPath(std::string &path)
|
||||
{
|
||||
const size_t length = path.length();
|
||||
|
||||
if (length == 0)
|
||||
path.append("\\");
|
||||
else
|
||||
{
|
||||
const char lastChar = path[path.length() - 1];
|
||||
if (lastChar != '\\' && lastChar != '/')
|
||||
path.append("\\");
|
||||
}
|
||||
}
|
||||
|
||||
std::string LegalizeWindowsFileName(const std::string &path)
|
||||
{
|
||||
const size_t length = path.length();
|
||||
|
||||
std::string legalizedPath;
|
||||
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
const char c = path[i];
|
||||
bool isLegalChar = true;
|
||||
if (c >= '\0' && c <= 31)
|
||||
isLegalChar = false;
|
||||
else if (c == '<' || c == '>' || c == ':' || c == '\"' || c == '/' || c == '\\' || c == '|' || c == '?' || c == '*')
|
||||
isLegalChar = false;
|
||||
else if (c == ' ' || c == '.')
|
||||
{
|
||||
if (i == length - 1)
|
||||
isLegalChar = false;
|
||||
}
|
||||
|
||||
if (isLegalChar)
|
||||
legalizedPath.append(&c, 1);
|
||||
else
|
||||
{
|
||||
const char *hexChars = "0123456789abcdef";
|
||||
char legalizedCharacter[3];
|
||||
legalizedCharacter[0] = '$';
|
||||
legalizedCharacter[1] = hexChars[(c >> 4) & 0xf];
|
||||
legalizedCharacter[2] = hexChars[c & 0xf];
|
||||
|
||||
legalizedPath.append(legalizedCharacter, 3);
|
||||
}
|
||||
}
|
||||
|
||||
const char *bannedNames[] =
|
||||
{
|
||||
"CON",
|
||||
"PRN",
|
||||
"AUX",
|
||||
"NUL",
|
||||
"COM1",
|
||||
"COM2",
|
||||
"COM3",
|
||||
"COM4",
|
||||
"COM5",
|
||||
"COM6",
|
||||
"COM7",
|
||||
"COM8",
|
||||
"COM9",
|
||||
"LPT1",
|
||||
"LPT2",
|
||||
"LPT3",
|
||||
"LPT4",
|
||||
"LPT5",
|
||||
"LPT6",
|
||||
"LPT7",
|
||||
"LPT8",
|
||||
"LPT9"
|
||||
};
|
||||
|
||||
const size_t numBannedNames = sizeof(bannedNames) / sizeof(bannedNames[0]);
|
||||
|
||||
for (size_t i = 0; i < numBannedNames; i++)
|
||||
{
|
||||
const size_t banLength = strlen(bannedNames[i]);
|
||||
const size_t legalizedPathLength = legalizedPath.length();
|
||||
|
||||
bool isThisBannedName = false;
|
||||
if (legalizedPathLength >= banLength)
|
||||
{
|
||||
bool startsWithBannedName = true;
|
||||
for (size_t ci = 0; ci < banLength; ci++)
|
||||
{
|
||||
int charDelta = bannedNames[i][ci] - legalizedPath[ci];
|
||||
if (charDelta != 0 && charDelta != ('A' - 'a'))
|
||||
{
|
||||
startsWithBannedName = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (startsWithBannedName)
|
||||
{
|
||||
if (legalizedPathLength == banLength)
|
||||
{
|
||||
legalizedPath.append("$");
|
||||
break;
|
||||
}
|
||||
else if (legalizedPath[banLength] == '.')
|
||||
{
|
||||
legalizedPath = legalizedPath.substr(0, banLength) + "$" + legalizedPath.substr(banLength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (legalizedPath.length() == 0)
|
||||
legalizedPath = "$";
|
||||
|
||||
return legalizedPath;
|
||||
}
|
||||
|
||||
void MakeIntermediateDirectories(const std::string &path)
|
||||
{
|
||||
size_t l = path.length();
|
||||
|
||||
for (size_t i = 0; i < l; i++)
|
||||
{
|
||||
if (path[i] == '/' || path[i] == '\\')
|
||||
mkdir_utf8(path.substr(0, i).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int RecursiveExtractFiles(int depth, ArchiveItemList *itemList, const std::string &path, IFileReader &reader);
|
||||
|
||||
int ExtractSingleFork(const ArchiveCompressedChunkDesc &chunkDesc, const std::string &path, IFileReader &reader)
|
||||
{
|
||||
if (chunkDesc.m_uncompressedSize == 0)
|
||||
return 0;
|
||||
|
||||
if (!reader.SeekStart(chunkDesc.m_filePosition))
|
||||
{
|
||||
fprintf(stderr, "Could not seek to input position\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *metadataF = fopen_utf8(path.c_str(), "wb");
|
||||
if (!metadataF)
|
||||
{
|
||||
fprintf(stderr, "Could not open output file %s\n", path.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
IDecompressor *decompressor = nullptr;
|
||||
switch (chunkDesc.m_compressionMethod)
|
||||
{
|
||||
case CompressionMethods::kNone:
|
||||
decompressor = new NullDecompressor();
|
||||
break;
|
||||
case CompressionMethods::kStuffItRLE90:
|
||||
decompressor = new RLE90Decompressor();
|
||||
break;
|
||||
case CompressionMethods::kStuffItLZW:
|
||||
decompressor = new LZWDecompressor(0x8e);
|
||||
break;
|
||||
case CompressionMethods::kStuffItHuffman:
|
||||
decompressor = new StuffItHuffmanDecompressor();
|
||||
break;
|
||||
case CompressionMethods::kStuffIt13:
|
||||
decompressor = new StuffIt13Decompressor();
|
||||
break;
|
||||
case CompressionMethods::kStuffItArsenic:
|
||||
decompressor = new StuffItArsenicDecompressor();
|
||||
break;
|
||||
case CompressionMethods::kCompactProRLE:
|
||||
decompressor = new CompactProRLEDecompressor();
|
||||
break;
|
||||
case CompressionMethods::kCompactProLZHRLE:
|
||||
decompressor = new CompactProLZHRLEDecompressor(0x1fff0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!decompressor)
|
||||
{
|
||||
fprintf(stderr, "Could not decompress file %s, compression method %i is not implemented\n", path.c_str(), static_cast<int>(chunkDesc.m_compressionMethod));
|
||||
fclose(metadataF);
|
||||
return -1;
|
||||
}
|
||||
|
||||
CSInputBuffer *input = CSInputBufferAlloc(&reader, 2048);
|
||||
|
||||
if (!input)
|
||||
{
|
||||
fprintf(stderr, "Could not decompress file %s, buffer init failed\n", path.c_str());
|
||||
delete decompressor;
|
||||
fclose(metadataF);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!decompressor->Reset(input, chunkDesc.m_compressedSize, chunkDesc.m_uncompressedSize))
|
||||
{
|
||||
fprintf(stderr, "Could not decompress file %s, decompression init failed\n", path.c_str());
|
||||
CSInputBufferFree(input);
|
||||
delete decompressor;
|
||||
fclose(metadataF);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size_t kDecompressionBufferSize = 4096;
|
||||
uint8_t decompressionBuffer[kDecompressionBufferSize];
|
||||
size_t decompressedBytesRemaining = chunkDesc.m_uncompressedSize;
|
||||
|
||||
while (decompressedBytesRemaining > 0)
|
||||
{
|
||||
size_t decompressAmount = decompressedBytesRemaining;
|
||||
if (decompressAmount > kDecompressionBufferSize)
|
||||
decompressAmount = kDecompressionBufferSize;
|
||||
|
||||
if (!decompressor->ReadBytes(decompressionBuffer, decompressAmount))
|
||||
{
|
||||
fprintf(stderr, "Could not decompress file %s, byte read failed\n", path.c_str());
|
||||
CSInputBufferFree(input);
|
||||
delete decompressor;
|
||||
fclose(metadataF);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fwrite(decompressionBuffer, 1, decompressAmount, metadataF) != decompressAmount)
|
||||
{
|
||||
fprintf(stderr, "Could not decompress file %s, write failed\n", path.c_str());
|
||||
CSInputBufferFree(input);
|
||||
delete decompressor;
|
||||
fclose(metadataF);
|
||||
return -1;
|
||||
}
|
||||
|
||||
decompressedBytesRemaining -= decompressAmount;
|
||||
}
|
||||
|
||||
delete decompressor;
|
||||
CSInputBufferFree(input);
|
||||
fclose(metadataF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExtractFile(const ArchiveItem &item, const std::string &path, IFileReader &reader)
|
||||
{
|
||||
PortabilityLayer::MacFilePropertiesSerialized mfps;
|
||||
mfps.Serialize(item.m_macProperties);
|
||||
|
||||
std::string metadataPath = (path + ".gpf");
|
||||
std::string dataPath = (path + ".gpd");
|
||||
std::string resPath = (path + ".gpr");
|
||||
|
||||
FILE *metadataF = fopen_utf8(metadataPath.c_str(), "wb");
|
||||
if (!metadataF)
|
||||
{
|
||||
fprintf(stderr, "Could not open metadata output file %s", metadataPath.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fwrite(mfps.m_data, 1, PortabilityLayer::MacFilePropertiesSerialized::kSize, metadataF) != PortabilityLayer::MacFilePropertiesSerialized::kSize)
|
||||
{
|
||||
fprintf(stderr, "A problem occurred writing metadata");
|
||||
fclose(metadataF);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(metadataF);
|
||||
|
||||
int returnCode = ExtractSingleFork(item.m_dataForkDesc, dataPath, reader);
|
||||
if (returnCode)
|
||||
return returnCode;
|
||||
|
||||
returnCode = ExtractSingleFork(item.m_resourceForkDesc, resPath, reader);
|
||||
if (returnCode)
|
||||
return returnCode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExtractItem(int depth, const ArchiveItem &item, const std::string &dirPath, IFileReader &reader)
|
||||
{
|
||||
std::string path(reinterpret_cast<const char*>(item.m_fileNameUTF8.data()), item.m_fileNameUTF8.size());
|
||||
|
||||
for (int i = 0; i < depth; i++)
|
||||
printf(" ");
|
||||
|
||||
fputws(ConvertUTF8ToWString(path.data()).c_str(), stdout);
|
||||
printf("\n");
|
||||
|
||||
path = LegalizeWindowsFileName(path);
|
||||
|
||||
path = dirPath + path;
|
||||
|
||||
if (item.m_isDirectory)
|
||||
{
|
||||
mkdir_utf8(path.c_str());
|
||||
|
||||
path.append("\\");
|
||||
|
||||
int returnCode = RecursiveExtractFiles(depth + 1, item.m_children, path, reader);
|
||||
if (returnCode)
|
||||
return returnCode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return ExtractFile(item, path, reader);
|
||||
}
|
||||
|
||||
int RecursiveExtractFiles(int depth, ArchiveItemList *itemList, const std::string &path, IFileReader &reader)
|
||||
{
|
||||
const std::vector<ArchiveItem> &items = itemList->m_items;
|
||||
|
||||
const size_t numChildren = items.size();
|
||||
for (size_t i = 0; i < numChildren; i++)
|
||||
{
|
||||
int returnCode = ExtractItem(depth, items[i], path, reader);
|
||||
if (returnCode)
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unpackMain(int argc, const char **argv)
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: unpacktool <archive file> <destination>");
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *inputArchive = fopen_utf8(argv[1], "rb");
|
||||
|
||||
if (!inputArchive)
|
||||
{
|
||||
fprintf(stderr, "Could not open input archive");
|
||||
return -1;
|
||||
}
|
||||
|
||||
CFileReader reader(inputArchive);
|
||||
|
||||
IArchiveParser *parsers[] =
|
||||
{
|
||||
&g_compactProParser,
|
||||
&g_stuffItParser,
|
||||
&g_stuffIt5Parser
|
||||
};
|
||||
|
||||
ArchiveItemList *archiveItemList = nullptr;
|
||||
|
||||
printf("Reading archive...\n");
|
||||
|
||||
for (IArchiveParser *parser : parsers)
|
||||
{
|
||||
if (parser->Check(reader))
|
||||
{
|
||||
archiveItemList = parser->Parse(reader);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!archiveItemList)
|
||||
{
|
||||
fprintf(stderr, "Failed to open archive");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Decompressing files...\n");
|
||||
|
||||
std::string currentPath = argv[2];
|
||||
TerminateDirectoryPath(currentPath);
|
||||
|
||||
MakeIntermediateDirectories(currentPath);
|
||||
|
||||
int returnCode = RecursiveExtractFiles(0, archiveItemList, currentPath, reader);
|
||||
|
||||
delete archiveItemList;
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
LPWSTR *szArglist;
|
||||
int nArgs;
|
||||
int i;
|
||||
|
||||
szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
|
||||
|
||||
std::vector<std::string> utf8ArgStrings;
|
||||
std::vector<const char *> utf8Args;
|
||||
|
||||
utf8ArgStrings.resize(nArgs);
|
||||
utf8Args.resize(nArgs);
|
||||
|
||||
for (int i = 0; i < nArgs; i++)
|
||||
{
|
||||
utf8ArgStrings[i] = ConvertWStringToUTF8(szArglist[i]);
|
||||
utf8Args[i] = utf8ArgStrings[i].c_str();
|
||||
}
|
||||
|
||||
const char **args = nullptr;
|
||||
if (nArgs)
|
||||
args = &utf8Args[0];
|
||||
|
||||
return unpackMain(nArgs, args);
|
||||
}
|
||||
|
193
unpacktool/unpacktool.vcxproj
Normal file
193
unpacktool/unpacktool.vcxproj
Normal file
@@ -0,0 +1,193 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{A778D062-DE76-49F6-8D05-EB26852DD605}</ProjectGuid>
|
||||
<RootNamespace>unpacktool</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\PortabilityLayer.props" />
|
||||
<Import Project="..\Common.props" />
|
||||
<Import Project="..\MacRomanConversion.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\PortabilityLayer.props" />
|
||||
<Import Project="..\Common.props" />
|
||||
<Import Project="..\MacRomanConversion.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\PortabilityLayer.props" />
|
||||
<Import Project="..\Common.props" />
|
||||
<Import Project="..\MacRomanConversion.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\PortabilityLayer.props" />
|
||||
<Import Project="..\Common.props" />
|
||||
<Import Project="..\MacRomanConversion.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ArchiveDescription.cpp" />
|
||||
<ClCompile Include="BWT.cpp" />
|
||||
<ClCompile Include="CompactProLZHDecompressor.cpp" />
|
||||
<ClCompile Include="CompactProLZHRLEDecompressor.cpp" />
|
||||
<ClCompile Include="CompactProParser.cpp" />
|
||||
<ClCompile Include="CompactProRLEDecompressor.cpp" />
|
||||
<ClCompile Include="CRC.cpp" />
|
||||
<ClCompile Include="CSInputBuffer.cpp" />
|
||||
<ClCompile Include="DecompressorProxyReader.cpp" />
|
||||
<ClCompile Include="LZSSDecompressor.cpp" />
|
||||
<ClCompile Include="LZW.cpp" />
|
||||
<ClCompile Include="LZWDecompressor.cpp" />
|
||||
<ClCompile Include="NullDecompressor.cpp" />
|
||||
<ClCompile Include="PrefixCode.cpp" />
|
||||
<ClCompile Include="RLE90Decompressor.cpp" />
|
||||
<ClCompile Include="StringCommon.cpp" />
|
||||
<ClCompile Include="StuffIt13Decompressor.cpp" />
|
||||
<ClCompile Include="StuffIt5Parser.cpp" />
|
||||
<ClCompile Include="StuffItArsenicDecompressor.cpp" />
|
||||
<ClCompile Include="StuffItCommon.cpp" />
|
||||
<ClCompile Include="StuffItHuffmanDecompressor.cpp" />
|
||||
<ClCompile Include="StuffItParser.cpp" />
|
||||
<ClCompile Include="unpacktool.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ArchiveDescription.h" />
|
||||
<ClInclude Include="BWT.h" />
|
||||
<ClInclude Include="CompactProLZHDecompressor.h" />
|
||||
<ClInclude Include="CompactProLZHRLEDecompressor.h" />
|
||||
<ClInclude Include="CompactProParser.h" />
|
||||
<ClInclude Include="CompactProRLEDecompressor.h" />
|
||||
<ClInclude Include="DecompressorProxyReader.h" />
|
||||
<ClInclude Include="LZSSDecompressor.h" />
|
||||
<ClInclude Include="LZW.h" />
|
||||
<ClInclude Include="LZWDecompressor.h" />
|
||||
<ClInclude Include="NullDecompressor.h" />
|
||||
<ClInclude Include="PrefixCode.h" />
|
||||
<ClInclude Include="RLE90Decompressor.h" />
|
||||
<ClInclude Include="StringCommon.h" />
|
||||
<ClInclude Include="StuffIt13Decompressor.h" />
|
||||
<ClInclude Include="StuffItCommon.h" />
|
||||
<ClInclude Include="StuffItHuffmanDecompressor.h" />
|
||||
<ClInclude Include="UPByteSwap.h" />
|
||||
<ClInclude Include="CRC.h" />
|
||||
<ClInclude Include="CSInputBuffer.h" />
|
||||
<ClInclude Include="IArchiveParser.h" />
|
||||
<ClInclude Include="IDecompressor.h" />
|
||||
<ClInclude Include="IFileReader.h" />
|
||||
<ClInclude Include="StuffIt5Parser.h" />
|
||||
<ClInclude Include="StuffItArsenicDecompressor.h" />
|
||||
<ClInclude Include="StuffItParser.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MacRomanConversion\MacRomanConversion.vcxproj">
|
||||
<Project>{07351a8e-1f79-42c9-bbab-31f071eaa99e}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\PortabilityLayer\PortabilityLayer.vcxproj">
|
||||
<Project>{6ec62b0f-9353-40a4-a510-3788f1368b33}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
168
unpacktool/unpacktool.vcxproj.filters
Normal file
168
unpacktool/unpacktool.vcxproj.filters
Normal file
@@ -0,0 +1,168 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BWT.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CRC.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CSInputBuffer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StuffItArsenicDecompressor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StuffIt5Parser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="unpacktool.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ArchiveDescription.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StuffItParser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StuffItCommon.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="NullDecompressor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RLE90Decompressor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LZWDecompressor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LZW.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StuffItHuffmanDecompressor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PrefixCode.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StuffIt13Decompressor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LZSSDecompressor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CompactProParser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StringCommon.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CompactProRLEDecompressor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DecompressorProxyReader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CompactProLZHRLEDecompressor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CompactProLZHDecompressor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="StuffItParser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ArchiveDescription.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BWT.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CRC.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CSInputBuffer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IArchiveParser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IDecompressor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IFileReader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StuffIt5Parser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StuffItArsenicDecompressor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UPByteSwap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StuffItCommon.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NullDecompressor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RLE90Decompressor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LZWDecompressor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LZW.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StuffItHuffmanDecompressor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PrefixCode.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StuffIt13Decompressor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LZSSDecompressor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CompactProParser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StringCommon.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CompactProRLEDecompressor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DecompressorProxyReader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CompactProLZHRLEDecompressor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CompactProLZHDecompressor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Reference in New Issue
Block a user