#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "uw2files.H"
namespace UW2 {
DataFile::DataFile(const char* fname):
data_(0)
{
int fd = open(fname, O_RDONLY);
if(fd >= 0) {
len_ = lseek(fd, 0, SEEK_END);
unsigned char* bytes = new unsigned char[len_];
if(lseek(fd, 0, SEEK_SET))
throw errno;
if(read(fd, bytes, len_) != len_)
throw errno;
close(fd);
data_ = bytes;
return;
}
throw errno;
}
DataFile::~DataFile()
{
if(data_)
delete[] data_;
}
ArkFile::ArkFile(const char* fname):
DataFile(fname)
{
nblocks_ = u16(0);
blocks_ = new const unsigned char*[nblocks_];
len_ = new size_t[nblocks_];
for(int i=0; i<nblocks_; i++) {
blocks_[i] = 0;
len_[i] = 0;
}
}
ArkFile::~ArkFile()
{
// free, normally, but this is a singleton so who cares
}
const unsigned char* ArkFile::block(int m)
{
if(m<0 || m>=nblocks_)
return 0;
if(!blocks_[m]) {
size_t offs = u32(2+m*4);
if(!offs)
return 0;
size_t len = u32(2+(nblocks_+nblocks_+m)*4);
if(u32(2+(nblocks_+m)*4) & 2) {
len_[m] = u32(offs);
unsigned char* buf = new unsigned char[len_[m]];
blocks_[m] = buf;
size_t end = offs + len;
len = 0;
offs += 4;
while(offs<end) {
int bits = u8(offs++);
for(int i=0; i<8 && offs<end; i++, bits>>=1)
if(bits&1)
buf[len++] = u8(offs++);
else {
int ro = u8(offs++);
int rc = u8(offs++);
ro |= (rc&0xF0)<<4;
rc = (rc&0x0F) + 3;
if(ro & 0x800)
ro |= ~0x7FF; // sign extend from 12 bits
ro += 18;
while(ro+0x1000 < len)
ro += 0x1000;
// printf("run: %d from %d at %d\n", rc, ro, len);
if(ro<0)
ro = len+ro;
while(rc--)
buf[len++] = buf[ro++];
}
}
} else {
blocks_[m] = data()+offs;
len_[m] = len;
}
}
return blocks_[m];
}
StringFile::Page::~Page()
{
if(str) {
for(int i=0; i<num; i++)
if(str[i])
delete[] str[i];
delete[] str;
}
}
struct hnode_ {
unsigned char sym;
unsigned char parent;
unsigned char zero;
unsigned char one;
};
StringFile::StringFile(const char* fname):
nump_(0), pages_(0)
{
DataFile df(fname);
const hnode_* hn = reinterpret_cast<const hnode_*>(df.data()+2);
size_t nn = df.u16(0);
size_t np = df.u16(2+nn*4);
size_t pibase = 4+nn*4;
size_t nump = 0;
for(int p=0; p<np; p++) {
int pn = df.u16(pibase+p*6)+1;
if(pn>nump)
nump = pn;
}
pages_ = new Page[nump_ = nump];
//printf("%d huffman nodes, %d pages (max %d)\n", nn, np, nump);
for(int p=0; p<np; p++) {
int pn = df.u16(pibase+p*6);
size_t pbase = df.u32(pibase+p*6+2);
int ns = df.u16(pbase);
pages_[pn].str = new const char* [pages_[pn].num = ns+1];
memset(pages_[pn].str, 0, ns*sizeof(const char*));
for(int sn=0; sn<ns; sn++) {
char pad[4096];
char* d = pad;
const unsigned char* s = df.data() + pbase + (ns+1)*2 + df.u16(pbase+sn*2+2);
int bits = 0;
int left = 0;
int node = nn - 1;
for(;;) {
if(!left) {
bits = *s++;
left = 8;
}
node = (bits&0x80)? hn[node].one: hn[node].zero;
bits <<= 1;
--left;
if(hn[node].zero==0xFF && hn[node].one==0xFF) {
if(hn[node].sym=='|')
break;
if(hn[node].sym >= ' ')
*d++ = hn[node].sym;
else if(hn[node].sym == '\n')
*d++ = '~';
node = nn - 1;
}
}
*d = 0;
if(d > pad) {
char* s = new char[d-pad+1];
strcpy(s, pad);
pages_[pn].str[sn] = s;
//printf("%03x/%-3d : %s\n", pn, sn, pad);
}
}
}
}
StringFile::~StringFile()
{
if(pages_)
delete[] pages_;
}
const char* StringFile::operator () (int pn, int sn)
{
if(sn<0 || pn<0 || pn>=nump_ || sn>=pages_[pn].num)
return 0;
return pages_[pn].str[sn];
}
}; // namespace UW2