ys2-intro/loader/tools/pucrunch/pucrunch.c

3420 lines
82 KiB
C
Raw Permalink Normal View History

2025-11-13 11:07:39 -05:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <time.h>
#define DELTA
#define DELTA_OP +
/* Pucrunch ©1997-2008 by Pasi 'Albert' Ojala, a1bert@iki.fi */
/* Pucrunch is now under LGPL: see the doc for details. */
/* #define BIG */
/*
Define BIG for >64k files.
It will use even more *huge* amounts of memory.
Note:
Although this version uses memory proportionally to the file length,
it is possible to use fixed-size buffers. The LZ77 history buffer
(and backSkip) needs to be as long as is needed, the other buffers
minimally need to be about three times the length of the maximum
LZ77 match. Writing the compressor this way would probably make it a
little slower, and automatic selection of e.g. escape bits might not be
practical.
Adjusting the number of escape bits to adapt to local
changes in the data would be worth investigating.
Also, the memory needed for rle/elr tables could probably be reduced
by using a sparse table implementation. Because of the RLE property
only the starting and ending points (or lengths) need be saved. The
speed should not decrease too much, because the tables are used in
LZ77 string match also.... Wait! Actually no, because the RLE/LZ77
optimize needs to change the RLE lengths inside RLE's...
The elr array can be reduced to half by storing only the byte that
is before a run of bytes if we have the full backSkip table..
Because the lzlen maximum value is 256, we could reduce the table
from unsigned short to unsigned char by encoding 0->0, 2->1, .. 256->255.
lzlen of the value 1 is never used anyway..
*/
#define ENABLE_VERBOSE /* -v outputs the lz77/rle data to stdout */
#define HASH_STAT /* gives statistics about the hash compares */
#define BACKSKIP_FULL /* full backSkip table - enables RESCAN. If */
/* not defined, backSkip only uses max 128kB */
#define RESCAN /* rescans LZ77 matches for a closer match. */
#define HASH_COMPARE /* Use a 3-to-1 hash to skip impossible matches */
/* takes "inbytes" bytes, reduces string compares from 16% to 8% */
const char version[] = "\0$VER: pucrunch 1.14 22-Nov-2008\n";
static int maxGamma = 7, reservedBytes = 2;
static int escBits = 2, escMask = 0xc0;
static int extraLZPosBits = 0, rleUsed = 15;
static int memConfig = 0x37, intConfig = 0x58; /* cli */
/*
-------->
z..zx.....x normal (zz != ee)
e..e value(LEN) value(POSHI+1) 8+b(POSLO) LZ77
e..e 0 (2) 0 (2-256) 8b(POSLO) LZ77
e..e 100 (3) 111111 111111 END of FILE
#ifdef DELTA
e..e 101 (4..) 111111 111111 8b(add) 8b(POSLO) DLZ
#endif
e..e010 n..ne.....e escape + new esc
e..e011 value(LEN) bytecode Short RLE 2..
e..e011 111..111 8b(LENLO) value(LENHI+1) bytecode Long RLE
(values 64.. not used (may not be available) in bytecode)
e..e011 0 0 RLE=2, rank 1 (saves 11.. bit)
e..e011 0 10 x RLE=2, rank 2-3 (saves 9.. bit)
e..e011 0 11 0xx RLE=2, rank 4-7 (saves 7.. bit)
e..e011 0 11 10xxx RLE=2, rank 8-15 (saves 5.. bit)
e..e011 0 11 110xxxx xxxx RLE=2, not ranked
LZ77, len=2 (pos<=256) saves 4 bits (2-bit escape)
LZ77, len=3 saves 10..1 bits (pos 2..15616)
LZ77, len=4 saves 18..9 bits
LZ77, len=5 saves 24..15 bits
RLE, len=2 saves 11..1(..-5) bits (bytecode rank 1..not ranked)
RLE, len=3 saves 15..2 bits
RLE, len=4 saves 23..10 bits
RLE, len=5 saves 29..16 bits
bs: 3505 LZ reference points, 41535 bytes -> 11.85, i.e. 8.4% referenced
1) Short RLE -> gamma + 1 linear bit -> ivanova.run -29 bytes
2) ?? .. no
esc = RLE, with value 1
e..e01 value(1) n..ne.....e escape + new esc
e..e01 value(LEN) bytecode Short RLE 2..
e..e01 111..111 8b(LENLO) value(LENHI+1) bytecode Long RLE
(values 64.. not used (may not be available) in bytecode)
*/
/*
Value:
Elias Gamma Code rediscovered, just the prefix bits are reversed, plus
there is a length limit (1 bit gained for each value in the last group)
; 0000000 not possible
; 0000001 0 1 -6 bits
; 000001x 10 x 2-3 -4 bits
; 00001xx 110 xx 4-7 -2 bits
; 0001xxx 1110 xxx 8-15 +0 bits
; 001xxxx 11110 xxxx 16-31 +2 bits
; 01xxxxx 111110 xxxxx 32-63 +4 bits
; 1xxxxxx 111111 xxxxxx 64-127 +5 bits
*/
#include "pucrunch.h" /* Include the decompressors */
void ListDecompressors(FILE *fp) {
struct FixStruct *dc = &fixStruct[0];
while (dc && dc->code) {
fprintf(fp, "%s\n", dc->name);
dc++;
}
}
struct FixStruct *BestMatch(int type) {
struct FixStruct *dc = &fixStruct[0], *best = NULL;
while (dc && dc->code) {
if ((dc->flags & FIXF_MACHMASK) == (type & FIXF_MACHMASK)) {
/* machine is correct */
/* Require wrap if necessary, allow wrap if not */
/* Require delta matches */
if (((dc->flags & type) & FIXF_MUSTMASK) ==
(type & FIXF_MUSTMASK)) {
/* Haven't found any match or this is better */
if (!best ||
((type & FIXF_WRAP) == (dc->flags & FIXF_WRAP) &&
(!(type & (FIXF_FAST | FIXF_SHORT)) ||
(dc->flags & type & (FIXF_FAST | FIXF_SHORT)))))
best = dc;
/* If requirements match exactly, can return */
/* Assumes that non-wraps are located before wrap versions */
if ((type & (FIXF_FAST | FIXF_SHORT)) ==
(dc->flags & (FIXF_FAST | FIXF_SHORT))) {
return dc;
}
}
}
dc++;
}
return best;
}
int GetHeaderSize(int type, int *deCall) {
struct FixStruct *best;
if (deCall)
*deCall = 0;
if ((type & FIXF_MACHMASK) == 0) {
return 47; /* standalone */
}
best = BestMatch(type);
if (best && deCall) {
int i;
for (i=0; best->fixes[i].type != ftEnd; i++) {
if (best->fixes[i].type == ftDeCall) {
*deCall = best->fixes[i].offset;
break;
}
}
}
return best?best->codeSize:0;
}
int SavePack(int type, unsigned char *data, int size, char *target,
int start, int exec, int escape, unsigned char *rleValues,
int endAddr, int progEnd, int extraLZPosBits, int enable2MHz,
int memStart, int memEnd) {
FILE *fp = NULL;
struct FixStruct *dc;
unsigned char *header;
int i, overlap = 0, stackUsed = 0, ibufferUsed = 0;
if (!data)
return 10;
if (!target)
fp = stdout;
if ((type & FIXF_MACHMASK) == 0) {
/* Save without decompressor */
if (fp || (fp = fopen(target, "wb"))) {
unsigned char head[64];
int cnt = 0;
head[cnt++] = (endAddr + overlap - size) & 0xff; /* INPOS */
head[cnt++] = ((endAddr + overlap - size) >> 8);
head[cnt++] = 'p';
head[cnt++] = 'u';
head[cnt++] = (endAddr - 0x100) & 0xff;
head[cnt++] = ((endAddr - 0x100) >> 8);
head[cnt++] = (escape>>(8-escBits));
head[cnt++] = (start & 0xff); /* OUTPOS */
head[cnt++] = (start >> 8);
head[cnt++] = escBits;
/* head[cnt++] = 8-escBits; */
head[cnt++] = maxGamma + 1;
/* head[cnt++] = (8-maxGamma); */ /* Long RLE */
head[cnt++] = (1<<maxGamma); /* Short/Long RLE */
/* head[cnt++] = (2<<maxGamma)-1; */ /* EOF (maxGammaValue) */
head[cnt++] = extraLZPosBits;
head[cnt++] = (exec & 0xff);
head[cnt++] = (exec >> 8);
head[cnt++] = rleUsed;
for(i = 1; i <= rleUsed; i++) {
head[cnt++] = rleValues[i];
}
fwrite(head, 1, cnt, fp);
fwrite(data, size, 1, fp);
if(fp != stdout)
fclose(fp);
return 0;
}
fprintf(stderr, "Could not open %s for writing\n", target);
return 10;
}
if ((memStart & 0xff) != 1) {
fprintf(stderr, "Misaligned basic start 0x%04x\n", memStart);
return 10;
} else if (memStart > 9999) {
/* The basic line only holds 4 digits.. */
fprintf(stderr, "Too high basic start 0x%04x\n", memStart);
return 10;
}
if (endAddr > memEnd) {
overlap = endAddr - memEnd;
endAddr = memEnd;
/*
Make the decrunch code wrap from $ffff to $004b.
The decrunch code first copies the data that would exceed
$ffff to $004b and then copy the rest of it to end at $ffff.
*/
if (overlap > 22) {
fprintf(stderr, "Warning: data overlap is %d, but only 22 "
"is totally safe!\n", overlap);
fprintf(stderr, "The data from $61 to $%02x is overwritten.\n",
0x4b + overlap);
}
}
if (overlap) {
type |= FIXF_WRAP;
} else {
type &= ~FIXF_WRAP;
}
dc = BestMatch(type);
if (!dc) {
fprintf(stderr, "No matching decompressor found\n");
return 10;
}
header = dc->code;
if (!memStart)
memStart = 0x801;
#ifndef BIG
if (memStart + dc->codeSize - 2 + size > 0xfe00) {
fprintf(stderr, "Packed file's max size is 0x%04x (0x%04x)!\n",
0xfe00-memStart-(dc->codeSize-2), size);
return 10;
}
#endif /* BIG */
for (i=0; dc->fixes[i].type != ftEnd; i++) {
switch (dc->fixes[i].type) {
case ftFastDisable:
if (!enable2MHz) {
header[dc->fixes[i].offset] = 0x2c;
}
break;
case ftOverlap:
header[dc->fixes[i].offset] = overlap ? (overlap-1) : 0;
break;
case ftOverlapLo:
header[dc->fixes[i].offset] =
(memStart+dc->codeSize-2+rleUsed-15+size - overlap) & 0xff;
break;
case ftOverlapHi:
header[dc->fixes[i].offset] =
(memStart+dc->codeSize-2+rleUsed-15+size - overlap) >> 8;
break;
case ftWrapCount:
header[dc->fixes[i].offset] =
(memEnd>>8) - ((endAddr + overlap - size) >> 8); /* wrap point.. */
break;
case ftSizePages:
header[dc->fixes[i].offset] = (size>>8) + 1;
break;
case ftSizeLo:
header[dc->fixes[i].offset] =
(memStart+dc->codeSize-2+rleUsed-15+size-0x100 - overlap) & 0xff;
break;
case ftSizeHi:
header[dc->fixes[i].offset] =
(memStart+dc->codeSize-2+rleUsed-15+size-0x100 - overlap) >> 8;
break;
case ftEndLo:
header[dc->fixes[i].offset] = (endAddr - 0x100) & 0xff;
break;
case ftEndHi:
header[dc->fixes[i].offset] = ((endAddr - 0x100) >> 8);
break;
case ftEscValue:
header[dc->fixes[i].offset] = (escape>>(8-escBits));
break;
case ftOutposLo:
header[dc->fixes[i].offset] = (start & 0xff); /* OUTPOS */
break;
case ftOutposHi:
header[dc->fixes[i].offset] = (start >> 8);
break;
case ftEscBits:
header[dc->fixes[i].offset] = escBits;
break;
case ftEsc8Bits:
header[dc->fixes[i].offset] = 8-escBits;
break;
case ft1MaxGamma:
header[dc->fixes[i].offset] = (1<<maxGamma); /* Short/Long RLE */
break;
case ft8MaxGamma:
header[dc->fixes[i].offset] = (8-maxGamma); /* Long RLE */
break;
case ft2MaxGamma:
header[dc->fixes[i].offset] = (2<<maxGamma)-1; /* EOF (maxGammaValue) */
break;
case ftExtraBits:
header[dc->fixes[i].offset] = extraLZPosBits;
break;
case ftMemConfig:
header[dc->fixes[i].offset] = memConfig;
break;
case ftCli:
header[dc->fixes[i].offset] = intConfig; /* $58/$78 cli/sei; */
break;
case ftExecLo:
header[dc->fixes[i].offset] = (exec & 0xff);
break;
case ftExecHi:
header[dc->fixes[i].offset] = (exec >> 8);
break;
case ftInposLo:
header[dc->fixes[i].offset] = (endAddr + overlap - size) & 0xff; /* INPOS */
break;
case ftInposHi:
header[dc->fixes[i].offset] = ((endAddr + overlap - size) >> 8);
break;
case ftMaxGamma:
header[dc->fixes[i].offset] = maxGamma + 1;
break;
case ftReloc:
if (header[1] != (memStart>>8)) {
header[dc->fixes[i].offset] -= (header[1] - (memStart >> 8));
}
break;
case ftBEndLo:
header[dc->fixes[i].offset] = (progEnd & 0xff);
break;
case ftBEndHi:
header[dc->fixes[i].offset] = (progEnd >> 8);
break;
case ftStackSize:
stackUsed = header[dc->fixes[i].offset];
break;
case ftIBufferSize:
ibufferUsed = header[dc->fixes[i].offset];
break;
default:
break;
}
}
for (i=1; i<=15; i++)
header[dc->codeSize - 15 + i-1] = rleValues[i];
if (header[1] != (memStart>>8)) {
header[1] = (memStart>>8); /* Load address */
header[3] = (memStart>>8); /* Line link */
header[7] = 0x30 + (memStart+12)/1000;
header[8] = 0x30 + ((memStart+12)/100 % 10);
header[9] = 0x30 + ((memStart+12)/10 % 10);
header[10] = 0x30 + ((memStart+12) % 10);
}
fprintf(stderr, "Saving %s\n", dc->name);
if (fp || (fp = fopen(target, "wb"))) {
fwrite(header, 1, dc->codeSize+rleUsed-15, fp);
fwrite(data, size, 1, fp);
if (fp != stdout)
fclose(fp);
} else {
fprintf(stderr, "Could not open %s for writing\n", target);
return 10;
}
if (dc->flags & FIXF_SHORT) {
fprintf(stderr, "%s uses the memory $2d-$30, ", target?target:"");
} else {
fprintf(stderr, "%s uses the memory $2d/$2e, ", target?target:"");
}
if (overlap)
fprintf(stderr, "$4b-$%02x, ", 0x4b + overlap);
else if ((dc->flags & FIXF_WRAP))
fprintf(stderr, "$4b, ");
if (stackUsed)
fprintf(stderr, "$f7-$%x, ", 0xf7 + stackUsed);
if (ibufferUsed)
fprintf(stderr, "$200-$%x, ", 0x200 + ibufferUsed);
fprintf(stderr, "and $%04x-$%04x.\n",
(start < memStart+1) ? start : memStart+1, endAddr-1);
return 0;
}
#ifdef ENABLE_VERBOSE
#define F_VERBOSE (1<<0)
#endif
#define F_STATS (1<<1)
#define F_AUTO (1<<2)
#define F_NOOPT (1<<3)
#define F_AUTOEX (1<<4)
#define F_SKIP (1<<5)
#define F_2MHZ (1<<6)
#define F_AVOID (1<<7)
#define F_DELTA (1<<8)
#define F_NORLE (1<<9)
#define F_UNPACK (1<<14)
#define F_ERROR (1<<15)
#ifndef min
#define min(a,b) ((a<b)?(a):(b))
#endif
#define LRANGE (((2<<maxGamma)-3)*256) /* 0..125, 126 -> 1..127 */
#define MAXLZLEN (2<<maxGamma)
#define MAXRLELEN (((2<<maxGamma)-2)*256) /* 0..126 -> 1..127 */
#define DEFAULT_LZLEN LRANGE
static int lrange, maxlzlen, maxrlelen;
#ifdef BIG
#define OUT_SIZE 2000000
#else
#define OUT_SIZE 65536
#endif /* BIG */
static unsigned char outBuffer[OUT_SIZE];
static int outPointer = 0;
static int bitMask = 0x80;
static void FlushBits(void) {
if (bitMask != 0x80)
outPointer++;
}
static void PutBit(int bit) {
if (bit && outPointer < OUT_SIZE)
outBuffer[outPointer] |= bitMask;
bitMask >>= 1;
if (!bitMask) {
bitMask = 0x80;
outPointer++;
}
}
void PutValue(int value) {
int bits = 0, count = 0;
while (value>1) {
bits = (bits<<1) | (value & 1); /* is reversed compared to value */
value >>= 1;
count++;
PutBit(1);
}
if (count<maxGamma)
PutBit(0);
while (count--) {
PutBit((bits & 1)); /* output is reversed again -> same as value */
bits >>= 1;
}
}
#if 0
int LenValue(int value) {
int count = 0;
while (value>1) {
value >>= 1;
count += 2;
}
if (count<maxGamma)
return count + 1;
return count;
}
void InitValueLen(void) {
}
#else
int RealLenValue(int value) {
int count = 0;
if (value<2) /* 1 */
count = 0;
else if (value<4) /* 2-3 */
count = 1;
else if (value<8) /* 4-7 */
count = 2;
else if (value<16) /* 8-15 */
count = 3;
else if (value<32) /* 16-31 */
count = 4;
else if (value<64) /* 32-63 */
count = 5;
else if (value<128) /* 64-127 */
count = 6;
else if (value<256) /* 128-255 */
count = 7;
if (count<maxGamma)
return 2*count + 1;
return 2*count;
}
static int lenValue[256];
void InitValueLen(void);
void InitValueLen() {
int i;
for (i=1; i<256; i++)
lenValue[i] = RealLenValue(i);
}
#define LenValue(a) (lenValue[a])
#endif
void PutNBits(int byte, int bits) {
while (bits--)
PutBit((byte & (1<<bits)));
}
static int gainedEscaped = 0;
static int gainedRle = 0, gainedSRle = 0, gainedLRle = 0;
static int gainedLz = 0, gainedRlecode = 0;
#ifdef DELTA
static int gainedDLz = 0, timesDLz = 0;
#endif
static int timesEscaped = 0, timesNormal = 0;
static int timesRle = 0, timesSRle = 0, timesLRle = 0;
static int timesLz = 0;
static int lenStat[8][4];
int OutputNormal(int *esc, unsigned char *data, int newesc) {
timesNormal++;
if ((data[0] & escMask) == *esc) {
PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */
PutValue(2-1);
PutBit(1);
PutBit(0);
#if 0
*esc = (*esc + (1<<(8-escBits))) & escMask;
PutNBits(data[0], 8-escBits);
gainedEscaped += 3;
#else
*esc = newesc;
PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */
PutNBits(data[0], 8-escBits);
gainedEscaped += escBits + 3;
#endif
timesEscaped++;
return 1;
}
PutNBits(data[0], 8);
return 0;
}
void OutputEof(int *esc);
void OutputEof(int *esc) {
/* EOF marker */
PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */
PutValue(3-1); /* >1 */
PutValue((2<<maxGamma)-1); /* Maximum value */
/* flush */
FlushBits();
}
static unsigned char rleValues[32] = {1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
static int rleHist[256];
void PutRleByte(int data) {
int index;
for (index = 1; index < 16/*32*/; index++) {
if (data == rleValues[index]) {
if (index==1)
lenStat[0][3]++;
else if (index<=3)
lenStat[1][3]++;
else if (index<=7)
lenStat[2][3]++;
else if (index<=15)
lenStat[3][3]++;
/*else if (index<=31)
lenStat[4][3]++;*/
gainedRlecode += 8 - LenValue(index);
PutValue(index);
return;
}
}
/*fprintf(stderr, "RLECode n: 0x%02x\n", data);*/
PutValue(16/*32*/ + (data>>4/*3*/));
gainedRlecode -= LenValue(16/*32*/+(data>>4/*3*/)) + 4/*3*/;
PutNBits(data, 4/*3*/);
lenStat[4/*5*/][3]++;
/* Note: values 64..127 are not used if maxGamma>5 */
}
#if 0
int LenRleByte(unsigned char data) {
int index;
for (index = 1; index < 16/*32*/; index++) {
if (data == rleValues[index]) {
return LenValue(index);
}
}
return LenValue(16/*32*/ + 0) + 4/*3*/;
}
#else
static unsigned char rleLen[256];
void InitRleLen(void);
void InitRleLen() {
int i;
for (i=0; i<256; i++)
rleLen[i] = LenValue(16/*32*/ + 0) + 4/*3*/;
for (i=1; i<16 /*32*/; i++)
rleLen[rleValues[i]] = LenValue(i);
}
#define LenRleByte(d) (rleLen[d])
#endif
int LenRle(int len, int data) {
int out = 0;
do {
if (len == 1) {
out += escBits + 3 + 8;
len = 0;
} else if (len <= (1<<maxGamma)) {
out += escBits + 3 + LenValue(len-1) + LenRleByte(data);
len = 0;
} else {
int tmp = min(len, maxrlelen);
out += escBits + 3 + maxGamma + 8 +
LenValue(((tmp-1)>>8)+1) + LenRleByte(data);
len -= tmp;
}
} while (len);
return out;
}
int OutputRle(int *esc, unsigned char *data, int rlelen) {
int len = rlelen, tmp;
while (len) {
if (len >= 2 && len <= (1<<maxGamma)) {
/* Short RLE */
if (len==2)
lenStat[0][2]++;
else if (len<=4)
lenStat[1][2]++;
else if (len<=8)
lenStat[2][2]++;
else if (len<=16)
lenStat[3][2]++;
else if (len<=32)
lenStat[4][2]++;
else if (len<=64)
lenStat[5][2]++;
else if (len<=128)
lenStat[6][2]++;
else if (len<=256)
lenStat[6][2]++;
PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */
PutValue(2-1);
PutBit(1);
PutBit(1);
PutValue(len-1);
PutRleByte(*data);
tmp = 8*len -escBits -3 -LenValue(len-1) -LenRleByte(*data);
gainedRle += tmp;
gainedSRle += tmp;
timesRle++;
timesSRle++;
return 0;
}
if (len<3) {
while (len--)
OutputNormal(esc, data, *esc);
return 0;
}
if (len <= maxrlelen) {
/* Run-length encoding */
PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */
PutValue(2-1);
PutBit(1);
PutBit(1);
PutValue((1<<maxGamma) + (((len-1)&0xff)>>(8-maxGamma)));
PutNBits((len-1), 8-maxGamma);
PutValue(((len-1)>>8) + 1);
PutRleByte(*data);
tmp = 8*len -escBits -3 -maxGamma -8 -LenValue(((len-1)>>8)+1)
-LenRleByte(*data);
gainedRle += tmp;
gainedLRle += tmp;
timesRle++;
timesLRle++;
return 0;
}
/* Run-length encoding */
PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */
PutValue(2-1);
PutBit(1);
PutBit(1);
PutValue((1<<maxGamma) + (((maxrlelen-1)&0xff)>>(8-maxGamma)));
PutNBits((maxrlelen-1) & 0xff, 8-maxGamma);
PutValue(((maxrlelen-1)>>8)+1);
PutRleByte(*data);
tmp = 8*maxrlelen -escBits -3 -maxGamma -8
-LenValue(((maxrlelen-1)>>8)+1) -LenRleByte(*data);
gainedRle += tmp;
gainedLRle += tmp;
timesRle++;
timesLRle++;
len -= maxrlelen;
data += maxrlelen;
}
return 0;
}
#ifdef DELTA
/* e..e 101 (4..) 111111 111111 8b(add) 8b(POSLO) DLZ*/
static int LenDLz(int lzlen, int lzpos) {
return escBits + 2*maxGamma + 8 + 8 + LenValue(lzlen-1);
}
static int OutputDLz(int *esc, int lzlen, int lzpos, int add) {
PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */
PutValue(lzlen-1);
PutValue((2<<maxGamma)-1); /* Maximum value */
PutNBits(add, 8);
PutNBits(((lzpos-1) & 0xff) ^ 0xff, 8);
gainedDLz += 8*lzlen -(escBits + LenValue(lzlen-1) + 2*maxGamma + 16);
timesDLz++;
return 4;
}
#endif
static int LenLz(int lzlen, int lzpos) {
if (lzlen==2) {
#if 0
if (lzpos <= 16)
return escBits + 2 + 5;
if (lzpos <= 128)
return escBits + 2 + 8;
#else
if (lzpos <= 256)
return escBits + 2 + 8;
#endif
return 100000;
}
return escBits + 8 + extraLZPosBits +
LenValue(((lzpos-1)>>(8+extraLZPosBits))+1) +
LenValue(lzlen-1);
}
static int OutputLz(int *esc, int lzlen, int lzpos, char *data, int curpos) {
if (lzlen==2)
lenStat[0][1]++;
else if (lzlen<=4)
lenStat[1][1]++;
else if (lzlen<=8)
lenStat[2][1]++;
else if (lzlen<=16)
lenStat[3][1]++;
else if (lzlen<=32)
lenStat[4][1]++;
else if (lzlen<=64)
lenStat[5][1]++;
else if (lzlen<=128)
lenStat[6][1]++;
else if (lzlen<=256)
lenStat[7][1]++;
if (lzlen >= 2 && lzlen <= maxlzlen) {
int tmp;
PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */
tmp = ((lzpos-1)>>(8+extraLZPosBits))+2;
if (tmp==2)
lenStat[0][0]++;
else if (tmp<=4)
lenStat[1][0]++;
else if (tmp<=8)
lenStat[2][0]++;
else if (tmp<=16)
lenStat[3][0]++;
else if (tmp<=32)
lenStat[4][0]++;
else if (tmp<=64)
lenStat[5][0]++;
else if (tmp<=128)
lenStat[6][0]++;
else if (tmp<=256)
lenStat[6][0]++;
if (lzlen==2) {
PutValue(lzlen-1);
PutBit(0);
if (lzpos > 256)
fprintf(stderr,
"Error at %d: lzpos too long (%d) for lzlen==2\n",
curpos, lzpos);
#if 0
if (lzpos <= 16) {
PutBit(0);
PutNBits(((lzpos-1) & 0xff) ^ 0xff, 4);
} else {
PutBit(1);
PutNBits(((lzpos-1) & 0xff) ^ 0xff, 8);
}
#else
PutNBits(((lzpos-1) & 0xff) ^ 0xff, 8);
#endif
} else {
PutValue(lzlen-1);
PutValue( ((lzpos-1) >> (8+extraLZPosBits)) +1);
PutNBits( ((lzpos-1) >> 8), extraLZPosBits);
PutNBits(((lzpos-1) & 0xff) ^ 0xff, 8);
}
gainedLz += 8*lzlen -LenLz(lzlen, lzpos);
timesLz++;
return 3;
}
fprintf(stderr, "Error: lzlen too short/long (%d)\n", lzlen);
return lzlen;
}
static unsigned short *rle, *elr, *lzlen, *lzpos, *lzmlen, *lzmpos;
#ifdef DELTA
static unsigned short *lzlen2, *lzpos2;
#endif
static int *length, inlen;
static unsigned char *indata, *mode, *newesc;
unsigned short *backSkip;
enum MODE {
LITERAL = 0,
LZ77 = 1,
RLE = 2,
#ifdef DELTA
DLZ = 3,
#endif
MMARK = 4
};
static int lzopt = 0;
/* Non-recursive version */
/* NOTE! IMPORTANT! the "length" array length must be inlen+1 */
int OptimizeLength(int optimize) {
int i;
length[inlen] = 0; /* one off the end, our 'target' */
for (i=inlen-1; i>=0; i--) {
int r1 = 8 + length[i+1], r2, r3;
if (!lzlen[i] && !rle[i]
#ifdef DELTA
&& (!lzlen2 || !lzlen2[i])
#endif
) {
length[i] = r1;
mode[i] = LITERAL;
continue;
}
/* If rle>maxlzlen, skip to the start of the rle-maxlzlen.. */
if (rle[i] > maxlzlen && elr[i] > 1) {
int z = elr[i];
i -= elr[i];
r2 = LenRle(rle[i], indata[i]) + length[i+ rle[i]];
if (optimize) {
int ii, mini = rle[i], minv = r2;
int bot = rle[i] - (1<<maxGamma);
if (bot < 2)
bot = 2;
for (ii=mini-1; ii>=bot; ii--) {
int v = LenRle(ii, indata[i]) + length[i + ii];
if (v < minv) {
minv = v;
mini = ii;
}
}
if (minv != r2) {
lzopt += r2 - minv;
rle[i] = mini;
r2 = minv;
}
}
length[i] = r2;
mode[i] = RLE;
for (; z>=0; z--) {
length[i+z] = r2;
mode[i+z] = RLE;
}
continue;
}
r3 = r2 = r1 + 1000; /* r3 >= r2 > r1 */
if (rle[i]) {
r2 = LenRle(rle[i], indata[i]) + length[i+ rle[i]];
if (optimize) {
int ii, mini = rle[i], minv = r2;
#if 0
int bot = rle[i] - (1<<maxGamma);
if (bot < 2)
bot = 2;
for (ii=mini-1; ii>=bot; ii--) {
int v = LenRle(ii, indata[i]) + length[i + ii];
if (v < minv) {
minv = v;
mini = ii;
}
}
#else
/* Check only the original length and all shorter
lengths that are power of two.
Does not really miss many 'minimums' this way,
at least not globally..
Makes the assumption that the Elias Gamma Code is
used, i.e. values of the form 2^n are 'optimal' */
ii = 2;
while (rle[i] > ii) {
int v = LenRle(ii, indata[i]) + length[i + ii];
if (v < minv) {
minv = v;
mini = ii;
}
ii <<= 1;
}
#endif
if (minv != r2) {
/*printf("%05d RL %d %d\n", i, rle[i], mini);*/
lzopt += r2 - minv;
rle[i] = mini;
r2 = minv;
}
}
}
if (lzlen[i]) {
r3 = LenLz(lzlen[i], lzpos[i]) + length[i + lzlen[i]];
if (optimize && lzlen[i]>2) {
int ii, mini = lzlen[i], minv = r3, mino = lzpos[i];
int topLen = LenLz(lzlen[i], lzpos[i])
- LenValue(lzlen[i]-1);
#if 0
int bot = 3;
if (lzpos[i] <= 256)
bot = 2;
for (ii=mini-1; ii>=bot; ii--) {
int v = topLen + LenValue(ii-1) + length[i + ii];
if (v < minv) {
minv = v;
mini = ii;
}
}
#else
/* Check only the original length and all shorter
lengths that are power of two.
Does not really miss many 'minimums' this way,
at least not globally..
Makes the assumption that the Elias Gamma Code is
used, i.e. values of the form 2^n are 'optimal' */
ii = 4;
while (lzlen[i] > ii) {
int v = topLen + LenValue(ii-1) + length[i + ii];
if (v < minv) {
minv = v;
mini = ii;
}
ii <<= 1;
}
/* Then check the max lengths we have found, but
did not originally approve because they seemed
to gain less than the shorter, nearer matches. */
ii = 3;
while (lzmlen[i] >= ii) {
int v = LenLz(ii, lzmpos[i]) + length[i + ii];
if (v < minv) {
minv = v;
mini = ii;
mino = lzmpos[i];
}
ii++;
}
#endif
#ifdef BACKSKIP_FULL
/*
Note:
2-byte optimization checks are no longer done
with the rest, because the equation gives too long
code lengths for 2-byte matches if extraLzPosBits>0.
*/
/* Two-byte rescan/check */
if (backSkip[i] && backSkip[i] <= 256) {
/* There are previous occurrances (near enough) */
int v = LenLz(2, (int)backSkip[i]) + length[i + 2];
if (v < minv) {
minv = v;
mini = 2;
lzlen[i] = mini;
r3 = minv;
lzpos[i] = (int)backSkip[i];
}
}
#endif /* BACKSKIP_FULL */
if (minv != r3 && minv < r2) {
/*
printf("@%05d LZ %d %4x -> %d %4x\n",
i, lzlen[i], lzpos[i], mini, lzpos[i]);
*/
lzopt += r3 - minv;
lzlen[i] = mini;
lzpos[i] = mino;
r3 = minv;
}
}
}
if (r2 <= r1) {
if (r2 <= r3) {
length[i] = r2;
mode[i] = RLE;
} else {
length[i] = r3;
mode[i] = LZ77;
}
} else {
if (r3 <= r1) {
length[i] = r3;
mode[i] = LZ77;
} else {
length[i] = r1;
mode[i] = LITERAL;
}
}
#ifdef DELTA
if (lzlen2 && lzlen2[i] > 3) {
r3 = LenDLz(lzlen2[i], lzpos2[i]) + length[i + lzlen2[i]];
if (r3 < length[i]) {
length[i] = r3;
mode[i] = DLZ;
}
}
#endif
}
return length[0];
}
/*
The algorithm in the OptimizeEscape() works as follows:
1) Only unpacked bytes are processed, they are marked
with MMARK. We proceed from the end to the beginning.
Variable A (old/new length) is updated.
2) At each unpacked byte, one and only one possible
escape matches. A new escape code must be selected
for this case. The optimal selection is the one which
provides the shortest number of escapes to the end
of the file,
i.e. A[esc] = 1+min(A[0], A[1], .. A[states-1]).
For other states A[esc] = A[esc];
If we change escape in this byte, the new escape is
the one with the smallest value in A.
3) The starting escape is selected from the possibilities
and mode 0 is restored to all mode 3 locations.
*/
int OptimizeEscape(int *startEscape, int *nonNormal) {
int i, j, states = (1<<escBits);
long minp = 0, minv = 0, other = 0;
long a[256]; /* needs int/long */
long b[256]; /* Remembers the # of escaped for each */
int esc8 = 8-escBits;
for (i=0; i<256; i++)
b[i] = a[i] = -1;
if (states>256) {
fprintf(stderr, "Escape optimize: only 256 states (%d)!\n",
states);
return 0;
}
/* Mark those bytes that are actually outputted */
for (i=0; i<inlen; ) {
switch (mode[i]) {
#ifdef DELTA
case DLZ:
other++;
i += lzlen2[i];
break;
#endif
case LZ77:
other++;
i += lzlen[i];
break;
case RLE:
other++;
i += rle[i];
break;
/*case LITERAL:*/
default:
mode[i++] = MMARK; /* mark it used so we can identify it */
break;
}
}
for (i=inlen-1; i>=0; i--) {
/* Using a table to skip non-normal bytes does not help.. */
if (mode[i] == MMARK) {
int k = (indata[i] >> esc8);
/* Change the tag values back to normal */
mode[i] = LITERAL;
/*
k are the matching bytes,
minv is the minimum value,
minp is the minimum index
*/
newesc[i] = (minp << esc8);
a[k] = minv + 1;
b[k] = b[minp] + 1;
if (k==minp) {
/* Minimum changed -> need to find a new minimum */
/* a[k] may still be the minimum */
minv++;
for (k=states-1; k>=0; k--) {
if (a[k] < minv) {
minv = a[k];
minp = k;
/*
There may be others, but the first one that
is smaller than the old minimum is equal to
any other new minimum.
*/
break;
}
}
}
}
}
/* Select the best value for the initial escape */
if (startEscape) {
i = inlen; /* make it big enough */
for (j=states-1; j>=0; j--) {
if (a[j] <= i) {
*startEscape = (j << esc8);
i = a[j];
}
}
}
if (nonNormal)
*nonNormal = other;
return b[startEscape ? (*startEscape>>esc8) : 0];
}
/* Initialize the RLE byte code table according to all RLE's found so far */
/* O(n) */
void InitRle(int);
void InitRle(int flags) {
int p, mr, mv, i;
for (i=1; i<16/*32*/; i++) {
mr = -1;
mv = 0;
for (p=0; p<256; p++) {
if (rleHist[p] > mv) {
mv = rleHist[p];
mr = p;
}
}
if (mv>0) {
rleValues[i] = mr;
rleHist[mr] = -1;
} else
break;
}
InitRleLen();
}
/* Initialize the RLE byte code table according to RLE's actually used */
/* O(n) */
void OptimizeRle(int);
void OptimizeRle(int flags) {
int p, mr, mv, i;
if ((flags & F_NORLE)) {
rleUsed = 0;
return;
}
if ((flags & F_STATS))
fprintf(stderr, "RLE Byte Code Re-Tune, RLE Ranks:\n");
for (p=0; p<256; p++)
rleHist[p] = 0;
for (p=0; p<inlen; ) {
switch (mode[p]) {
#ifdef DELTA
case DLZ: /* lz */
p += lzlen2[p];
break;
#endif
case LZ77: /* lz */
p += lzlen[p];
break;
case RLE: /* rle */
rleHist[indata[p]]++;
p += rle[p];
break;
/* case LITERAL:
case MMARK:*/
default:
p++;
break;
}
}
for (i=1; i<16 /*32*/; i++) {
mr = -1;
mv = 0;
for (p=0; p<256; p++) {
if (rleHist[p] > mv) {
mv = rleHist[p];
mr = p;
}
}
if (mv>0) {
rleValues[i] = mr;
if ((flags & F_STATS)) {
fprintf(stderr, " %2d.0x%02x %-3d ", i, mr, mv);
if (!((i - 1) % 6))
fprintf(stderr, "\n");
}
rleHist[mr] = -1;
} else
break;
}
rleUsed = i-1;
if ((flags & F_STATS))
if (((i - 1) % 6)!=1)
fprintf(stderr, "\n");
InitRleLen();
}
static const unsigned char *up_Data;
static int up_Mask, up_Byte;
void up_SetInput(const unsigned char *data) {
up_Data = data;
up_Mask = 0x80;
up_Byte = 0;
}
int up_GetBits(int bits) {
int val = 0;
while (bits--) {
val <<= 1;
if ((*up_Data & up_Mask))
val |= 1;
up_Mask >>= 1;
if (!up_Mask) {
up_Mask = 0x80;
up_Data++;
up_Byte++;
}
}
return val;
}
int up_GetValue(void) {
int i = 0;
while (i<maxGamma) {
if (!up_GetBits(1))
break;
i++;
}
return (1<<i) | up_GetBits(i);
}
int UnPack(int loadAddr, const unsigned char *data, const char *file,
int flags) {
long size, startEsc, endAddr, execAddr, headerSize;
long startAddr, error = 0;
FILE *fp;
int i, overlap;
long timeused = clock();
const char *byteCodeVec;
#define MAXCODES 20
int mismatch[MAXCODES], collect[ftEnd];
struct FixStruct *dc;
/* Search for the right code */
if (data[0] == 'p' && data[1] == 'u') {
/* was saved without decompressor */
int cnt = 2;
endAddr = (data[cnt] | (data[cnt+1]<<8)) + 0x100;
cnt += 2;
startEsc = data[cnt++];
startAddr = data[cnt] | (data[cnt+1] << 8);
cnt += 2;
escBits = data[cnt++];
if (escBits < 0 || escBits > 8) {
fprintf(stderr, "Error: Broken archive, escBits %d.\n",
escBits);
return 20;
}
maxGamma = data[cnt++] - 1;
if (data[cnt++] != (1<<maxGamma) ||
maxGamma < 5 || maxGamma > 7) {
fprintf(stderr, "Error: Broken archive, maxGamma %d.\n",
maxGamma);
return 20;
}
lrange = LRANGE;
maxlzlen = MAXLZLEN;
maxrlelen = MAXRLELEN;
extraLZPosBits = data[cnt++];
if (extraLZPosBits < 0 || extraLZPosBits > 4) {
fprintf(stderr, "Error: Broken archive, extraLZPosBits %d.\n",
extraLZPosBits);
return 20;
}
execAddr = data[cnt] | (data[cnt+1]<<8);
cnt += 2;
rleUsed = data[cnt++];
byteCodeVec = (const char *) &data[cnt - 1];
overlap = 0;
//memConfig = memConfig;
//intConfig = intConfig;
size = endAddr-startAddr;
headerSize = cnt + rleUsed;
endAddr = loadAddr + size;
} else {
for (i=0; fixStruct[i].code && i < MAXCODES; i++) {
int j, maxDiff = 0;
if (fixStruct[i].code[1] != (loadAddr>>8))
maxDiff = 5;
for (j=0; fixStruct[i].fixes[j].type != ftEnd; j++) {
maxDiff++;
}
mismatch[i] = 0;
for (j=2; j<fixStruct[i].codeSize-15; j++) {
if (fixStruct[i].code[j] != data[j-2])
mismatch[i]++;
}
if (mismatch[i] <= maxDiff) {
fprintf(stderr, "Detected %s (%d <= %d)\n",
fixStruct[i].name, mismatch[i], maxDiff);
break;
}
fprintf(stderr, "Not %s (%d > %d)\n",
fixStruct[i].name, mismatch[i], maxDiff);
}
dc = &fixStruct[i];
if (!dc->code) {
fprintf(stderr,
"Error: The file is not compressed with this program.\n");
return 20;
}
if ((loadAddr & 0xff) != 1) {
fprintf(stderr, "Error: Misaligned basic start address 0x%04x\n",
loadAddr);
return 20;
}
/* TODO: check that the decrunch code and load address match. */
error = 0;
for (i=0; i<ftEnd; i++) {
collect[i] = 0;
}
collect[ftMemConfig] = memConfig;
collect[ftCli] = intConfig;
for (i=0; dc->fixes[i].type!=ftEnd; i++) {
collect[dc->fixes[i].type] = data[dc->fixes[i].offset-2];
}
overlap = collect[ftOverlap];
/* TODO: check overlap LO/HI and WrapCount */
maxGamma = collect[ftMaxGamma] - 1;
if (maxGamma < 5 || maxGamma > 7) {
fprintf(stderr, "Error: Broken archive, maxGamma %d.\n",
maxGamma);
return 20;
}
lrange = LRANGE;
maxlzlen = MAXLZLEN;
maxrlelen = MAXRLELEN;
if (collect[ft1MaxGamma] != (1<<maxGamma) ||
collect[ft8MaxGamma] != (8-maxGamma) ||
collect[ft2MaxGamma] != (2<<maxGamma)-1) {
fprintf(stderr,
"Error: Broken archive, maxGamma (%d) mismatch.\n",
maxGamma);
return 20;
}
startEsc = collect[ftEscValue];
startAddr = collect[ftOutposLo] | (collect[ftOutposHi]<<8);
escBits = collect[ftEscBits];
if (escBits < 0 || escBits > 8) {
fprintf(stderr, "Error: Broken archive, escBits %d.\n",
escBits);
return 20;
}
if (collect[ftEsc8Bits] != 8-escBits) {
fprintf(stderr, "Error: Broken archive, escBits (%d) mismatch.\n",
escBits);
return 20;
}
extraLZPosBits = collect[ftExtraBits];
if (extraLZPosBits < 0 || extraLZPosBits > 4) {
fprintf(stderr, "Error: Broken archive, extraLZPosBits %d.\n",
extraLZPosBits);
return 20;
}
endAddr = 0x100 + (collect[ftEndLo] | (collect[ftEndHi]<<8));
size = endAddr - (collect[ftInposLo] | (collect[ftInposHi]<<8));
headerSize = ((collect[ftSizeLo] | (collect[ftSizeHi]<<8))
+ 0x100 - size - loadAddr) & 0xffff;
execAddr = collect[ftExecLo] | (collect[ftExecHi]<<8);
memConfig = collect[ftMemConfig];
intConfig = collect[ftCli];
byteCodeVec = (const char *) &data[dc->codeSize - 32 -2];
rleUsed = 15 - dc->codeSize +2 + headerSize;
}
if ((flags & F_STATS)) {
fprintf(stderr,
"Load 0x%04x, Start 0x%04lx, exec 0x%04lx, %s%s$01=$%02x\n",
loadAddr, startAddr, execAddr,
(intConfig==0x58)?"cli, ":"", (intConfig==0x78)?"sei, ":"",
memConfig);
fprintf(stderr, "Escape bits %d, starting escape 0x%02lx\n",
escBits, (startEsc<<(8-escBits)));
fprintf(stderr,
"Decompressor size %ld, max length %d, LZPOS LO bits %d\n",
headerSize+2, (2<<maxGamma), extraLZPosBits+8);
fprintf(stderr, "rleUsed: %d\n", rleUsed);
}
if (rleUsed > 15) {
fprintf(stderr, "Error: Old archive, rleUsed %d > 15.\n", rleUsed);
return 20;
}
outPointer = 0;
up_SetInput(data + headerSize);
while (!error) {
int sel = startEsc;
#ifndef BIG
if (startAddr + outPointer >= up_Byte + endAddr - size) {
if (!error)
fprintf(stderr, "Error: Target %5ld exceeds source %5ld..\n",
startAddr + outPointer, up_Byte + endAddr - size);
error++;
}
if (up_Byte > size + overlap) {
fprintf(stderr, "Error: No EOF symbol found (%d > %d).\n",
up_Byte, (int) (size + overlap));
error++;
}
#endif /* BIG */
if (escBits)
sel = up_GetBits(escBits);
if (sel == startEsc) {
int lzPos, lzLen = up_GetValue(), i;
#ifdef DELTA
int add = 0;
#endif
if (lzLen != 1) {
int lzPosHi = up_GetValue()-1, lzPosLo;
if (lzPosHi == (2<<maxGamma)-2) {
#ifdef DELTA
/* asm: 25 bytes longer */
if (lzLen > 2) {
add = up_GetBits(8);
lzPos = up_GetBits(8) ^ 0xff;
} else
#endif
break; /* EOF */
} else {
if (extraLZPosBits) {
lzPosHi = (lzPosHi<<extraLZPosBits) |
up_GetBits(extraLZPosBits);
}
lzPosLo = up_GetBits(8) ^ 0xff;
lzPos = (lzPosHi<<8) | lzPosLo;
}
} else {
if (up_GetBits(1)) {
int rleLen, byteCode, byte;
if (!up_GetBits(1)) {
int newEsc = up_GetBits(escBits);
outBuffer[outPointer++] =
(startEsc<<(8-escBits)) | up_GetBits(8-escBits);
/*fprintf(stdout, "%5ld %5ld *%02x\n",
outPointer-1, up_Byte, outBuffer[outPointer-1]);*/
startEsc = newEsc;
if (outPointer >= OUT_SIZE) {
fprintf(stderr, "Error: Broken archive, "
"output buffer overrun at %d.\n",
outPointer);
return 20;
}
continue;
}
rleLen = up_GetValue();
if (rleLen >= (1<<maxGamma)) {
rleLen = ((rleLen-(1<<maxGamma))<<(8-maxGamma)) |
up_GetBits(8-maxGamma);
rleLen |= ((up_GetValue()-1)<<8);
}
byteCode = up_GetValue();
if (byteCode < 16/*32*/) {
byte = byteCodeVec[byteCode];
} else {
byte = ((byteCode-16/*32*/)<<4/*3*/) | up_GetBits(4/*3*/);
}
/*fprintf(stdout, "%5ld %5ld RLE %5d 0x%02x\n", outPointer, up_Byte, rleLen+1,
byte);*/
if (outPointer + rleLen + 1 >= OUT_SIZE) {
fprintf(stderr, "Error: Broken archive, "
"output buffer overrun at %d.\n",
OUT_SIZE);
return 20;
}
for (i=0; i<=rleLen; i++) {
outBuffer[outPointer++] = byte;
}
continue;
}
lzPos = up_GetBits(8) ^ 0xff;
}
/*fprintf(stdout, "%5ld %5ld LZ %3d 0x%04x\n",
outPointer, up_Byte, lzLen+1, lzPos+1);*/
/* outPointer increases in the loop, thus its minimum is here */
if (outPointer - lzPos -1 < 0) {
fprintf(stderr, "Error: Broken archive, "
"LZ copy position underrun at %d (%d). "
"lzLen %d.\n",
outPointer, lzPos+1, lzLen+1);
return 20;
}
if (outPointer + lzLen + 1 >= OUT_SIZE) {
fprintf(stderr, "Error: Broken archive, "
"output buffer overrun at %d.\n",
OUT_SIZE);
return 20;
}
for (i=0; i<=lzLen; i++) {
outBuffer[outPointer] = outBuffer[outPointer - lzPos - 1]
#ifdef DELTA
DELTA_OP add;
#else
;
#endif
outPointer++;
}
} else {
int byte = (sel<<(8-escBits)) | up_GetBits(8-escBits);
/*fprintf(stdout, "%5ld %5ld %02x\n",
outPointer, up_Byte, byte);*/
outBuffer[outPointer++] = byte;
if (outPointer >= OUT_SIZE) {
fprintf(stderr, "Error: Broken archive, "
"output buffer overrun at %d.\n",
outPointer);
return 20;
}
}
}
if (error)
fprintf(stderr, "Error: Target exceeded source %5ld times.\n",
error);
if ((file && (fp = fopen(file, "wb"))) || (fp = stdout)) {
unsigned char tmp[2];
tmp[0] = startAddr & 0xff;
tmp[1] = (startAddr >> 8);
fwrite(tmp, 2, 1, fp);
fwrite(outBuffer, outPointer, 1, fp);
if (fp != stdout)
fclose(fp);
timeused = clock() - timeused;
if (!timeused)
timeused++; /* round upwards */
fprintf(stderr,
"Decompressed %d bytes in %4.2f seconds (%4.2f kB/s)\n",
outPointer,
(double)timeused/CLOCKS_PER_SEC,
(double)CLOCKS_PER_SEC*outPointer/timeused/1024.0);
return error;
}
fprintf(stderr, "Could not open file \"%s\" for writing.\n", file);
return 20;
}
int PackLz77(int lzsz, int flags, int *startEscape,
int endAddr, int memEnd, int type) {
int i, j, outlen, p, headerSize;
int escape;
#ifdef HASH_COMPARE
unsigned char *hashValue;
unsigned char *a;
int k;
#endif /* HASH_COMPARE */
#ifdef BIG
unsigned int *lastPair;
#else
unsigned short *lastPair;
#endif /* BIG */
#ifdef BACKSKIP_FULL
#ifdef RESCAN
int rescan = 0;
#endif /* RESCAN */
#endif /* BACKSKIP_FULL */
#ifdef HASH_STAT
unsigned long compares = 0, hashChecks = 0, hashEqual = 0;
#endif /* HASH_STAT */
if (lzsz < 0 || lzsz > lrange) {
fprintf(stderr, "LZ range must be from 0 to %d (was %d). Set to %d.\n",
lrange, lzsz, lrange);
lzsz = lrange;
}
if (lzsz > 65535) {
fprintf(stderr,
"LZ range must be from 0 to 65535 (was %d). Set to 65535.\n",
lzsz);
lzsz = 65535;
}
if (!lzsz)
fprintf(stderr, "Warning: zero LZ range. Only RLE packing used.\n");
InitRleLen();
length = (int *)calloc(sizeof(int), inlen + 1);
mode = (unsigned char *)calloc(sizeof(unsigned char), inlen);
rle = (unsigned short *)calloc(sizeof(unsigned short), inlen);
elr = (unsigned short *)calloc(sizeof(unsigned short), inlen);
lzlen = (unsigned short *)calloc(sizeof(unsigned short), inlen);
lzpos = (unsigned short *)calloc(sizeof(unsigned short), inlen);
lzmlen = (unsigned short *)calloc(sizeof(unsigned short), inlen);
lzmpos = (unsigned short *)calloc(sizeof(unsigned short), inlen);
#ifdef DELTA
if ((type & FIXF_DLZ)) {
lzlen2 = (unsigned short *)calloc(sizeof(unsigned short), inlen);
lzpos2 = (unsigned short *)calloc(sizeof(unsigned short), inlen);
} else {
lzlen2 = lzpos2 = NULL;
}
#endif
newesc = (unsigned char *)calloc(sizeof(unsigned char), inlen);
#ifdef BACKSKIP_FULL
backSkip = (unsigned short *)calloc(sizeof(unsigned short), inlen);
#else
backSkip = (unsigned short *)calloc(sizeof(unsigned short), 65536);
#endif /* BACKSKIP_FULL */
#ifdef HASH_COMPARE
hashValue = (unsigned char *)malloc(inlen);
#endif /* HASH_COMPARE */
#ifdef BIG
lastPair = (unsigned int *)calloc(sizeof(unsigned int), 256*256);
#else
lastPair = (unsigned short *)calloc(sizeof(unsigned short), 256*256);
#endif /* BIG */
/* error checking */
if (!length || !mode || !rle || !elr || !lzlen || !lzpos ||
!lzmlen || !lzmpos || !newesc || !lastPair || !backSkip
#ifdef DELTA
|| ((type & FIXF_DLZ) && (!lzlen2 || !lzpos2))
#endif
#ifdef HASH_COMPARE
|| !hashValue
#endif /* HASH_COMPARE */
) {
fprintf(stderr, "Memory allocation failed!\n");
goto errorexit;
}
#ifdef HASH_COMPARE
i = 0;
j = 0;
a = indata + inlen;
for (p=inlen-1; p>=0; p--) {
k = j;
j = i;
i = *--a; /* Only one read per position */
/* Without hash: 18.56%, end+middle: 12.68% */
/* hashValue[p] = i*2 ^ j*3 ^ k*5; */ /* 8.56% */
/* hashValue[p] = i ^ j*2 ^ k*3; */ /* 8.85% */
/* hashValue[p] = i + j + k; */ /* 9.33% */
/* hashValue[p] = i + j*2 + k*3; */ /* 8.25% */
/* hashValue[p] = i*2 + j*3 + k*5; */ /* 8.29% */
/* hashValue[p] = i*3 + j*5 + k*7; */ /* 7.95% */
hashValue[p] = i*3 + j*5 + k*7; /* 7.95 % */
}
#endif /* HASH_COMPARE */
/* Detect all RLE and LZ77 jump possibilities */
for (p=0; p<inlen; p++) {
#ifndef BIG
if (!(p & 2047)) {
fprintf(stderr, "\r%d ", p);
fflush(stderr); /* for SAS/C */
}
#endif /* BIG */
/* check run-length code - must be done, LZ77 search needs it! */
if (rle[p] <= 0) {
/*
There are so few RLE's and especially so few
long RLE's that byte-by-byte is good enough.
*/
unsigned char *a = indata + p;
int val = *a++; /* if this were uchar, it would go to stack..*/
int top = inlen - p;
int rlelen = 1;
/* Loop for the whole RLE */
while (rlelen<top && *a++ == (unsigned char)val
#ifdef BIG
&& rlelen < 65535
#endif /* BIG */
) {
rlelen++;
}
#ifdef HASH_STAT
compares += rlelen;
#endif /* HASH_STAT */
if (rlelen>=2) {
rleHist[indata[p]]++;
for (i=rlelen-1; i>=0; i--) {
rle[p+i] = rlelen-i;
elr[p+i] = i; /* For RLE backward skipping */
}
#if 0
if (rlelen>maxlzlen) {
/* Jump over some unnecessary memory references */
p += rlelen - maxlzlen - 1;
continue;
}
#endif
}
}
/* check LZ77 code */
if (p+rle[p]+1<inlen) {
int bot = p - lzsz, maxval, maxpos, rlep = rle[p];
#ifdef HASH_COMPARE
unsigned char hashCompare = hashValue[p];
#else
unsigned char valueCompare = indata[p+2];
#endif /* HASH_COMPARE */
/*
There's always 1 equal byte, although it may
not be marked as RLE.
*/
if (rlep <= 0)
rlep = 1;
if (bot < 0)
bot = 0;
bot += (rlep-1);
/*
First get the shortest possible match (if any).
If there is no 2-byte match, don't look further,
because there can't be a longer match.
*/
i = (int)lastPair[ (indata[p]<<8) | indata[p+1] ] -1;
if (i>=0 && i>=bot) {
/* Got a 2-byte match at least */
maxval = 2;
maxpos = p-i;
/*
A..AB rlep # of A's, B is something else..
Search for bytes that are in p + (rlep-1), i.e.
the last rle byte ('A') and the non-matching one
('B'). When found, check if the rle in the compare
position (i) is long enough (i.e. the same number
of A's at p and i-rlep+1).
There are dramatically less matches for AB than for
AA, so we get a huge speedup with this approach.
We are still guaranteed to find the most recent
longest match there is.
*/
i = (int)lastPair[(indata[p+(rlep-1)]<<8) | indata[p+rlep]] -1;
while (i>=bot /* && i>=rlep-1 */) { /* bot>=rlep-1, i>=bot ==> i>=rlep-1 */
/* Equal number of A's ? */
if (!(rlep-1) || rle[i-(rlep-1)]==rlep) { /* 'head' matches */
/* rlep==1 ==> (rlep-1)==0 */
/* ivanova.run: 443517 rlep==1,
709846 rle[i+1-rlep]==rlep */
/*
Check the hash values corresponding to the last
two bytes of the currently longest match and
the first new matching(?) byte. If the hash
values don't match, don't bother to check the
data itself.
*/
#ifdef HASH_STAT
hashChecks++;
#endif /* HASH_STAT */
if (
#ifdef HASH_COMPARE
hashValue[i+maxval-rlep-1] == hashCompare
#else
indata[i+maxval-rlep+1] == valueCompare
#endif /* HASH_COMPARE */
) {
unsigned char *a = indata + i+2; /* match */
unsigned char *b = indata + p+rlep-1+2;/* curpos */
int topindex = inlen-(p+rlep-1);
/* the 2 first bytes ARE the same.. */
j = 2;
while (j < topindex && *a++==*b++)
j++;
#ifdef HASH_STAT
hashEqual++;
compares += j - 1;
#endif /* HASH_STAT */
if (j + rlep-1 > maxval) {
int tmplen = j+rlep-1, tmppos = p-i+rlep-1;
if (tmplen > maxlzlen)
tmplen = maxlzlen;
if (lzmlen[p] < tmplen) {
lzmlen[p] = tmplen;
lzmpos[p] = tmppos;
}
/* Accept only versions that really are shorter */
if (tmplen*8 - LenLz(tmplen, tmppos) >
maxval*8 - LenLz(maxval, maxpos)) {
maxval = tmplen;
maxpos = tmppos;
#ifdef HASH_COMPARE
hashCompare = hashValue[p+maxval-2];
#else
valueCompare = indata[p+maxval];
#endif /* HASH_COMPARE */
}
#if 0
else {
printf("@%5d %d*8 - LenLz(%d, %4x)==%d < ",
p, tmplen, tmplen, tmppos,
tmplen*8 - LenLz(tmplen, tmppos));
printf("%d*8 - LenLz(%d, %4x)==%d\n",
maxval, maxval, maxpos,
maxval*8 - LenLz(maxval, maxpos));
}
#endif
if (maxval == maxlzlen)
break;
}
}
}
#ifdef BACKSKIP_FULL
if (!backSkip[i])
break; /* No previous occurrances (near enough) */
i -= (int)backSkip[i];
#else
if (!backSkip[i & 0xffff])
break; /* No previous occurrances (near enough) */
i -= (int)backSkip[i & 0xffff];
#endif /* BACKSKIP_FULL */
}
/*
If there is 'A' in the previous position also,
RLE-like LZ77 is possible, although rarely
shorter than real RLE.
*/
if (p && rle[p-1] > maxval) {
maxval = rle[p-1] - 1;
maxpos = 1;
}
/*
Last, try to find as long as possible match
for the RLE part only.
*/
if (maxval < maxlzlen && rlep > maxval) {
bot = p - lzsz;
if (bot < 0)
bot = 0;
/* Note: indata[p] == indata[p+1] */
i = (int)lastPair[indata[p]*257] -1;
while (/* i>= rlep-2 &&*/ i>=bot) {
if (elr[i] + 2 > maxval) {
maxval = min(elr[i] + 2, rlep);
maxpos = p - i + (maxval-2);
if(maxval == rlep)
break; /* Got enough */
}
i -= elr[i];
#ifdef BACKSKIP_FULL
if (!backSkip[i])
break; /* No previous occurrances (near enough) */
i -= (int)backSkip[i];
#else
if (!backSkip[i & 0xffff])
break; /* No previous occurrances (near enough) */
i -= (int)backSkip[i & 0xffff];
#endif /* BACKSKIP_FULL */
}
}
if (p+maxval > inlen) {
fprintf(stderr,
"Error @ %d, lzlen %d, pos %d - exceeds inlen\n",
p, maxval, maxpos);
maxval = inlen - p;
}
if (lzmlen[p] < maxval) {
lzmlen[p] = maxval;
lzmpos[p] = maxpos;
}
if (maxpos<=256 || maxval > 2) {
if (maxpos < 0)
fprintf(stderr, "Error @ %d, lzlen %d, pos %d\n",
p, maxval, maxpos);
lzlen[p] = (maxval<maxlzlen)?maxval:maxlzlen;
lzpos[p] = maxpos;
}
}
}
#ifdef DELTA
/* check LZ77 code again, ROT1..255 */
if ((type & FIXF_DLZ) && /* rle[p]<maxlzlen && */ p+rle[p]+1<inlen) {
int rot;
for (rot = 1; rot < 255/*BUG:?should be 256?*/; rot++) {
int bot = p - /*lzsz*/256, maxval, maxpos, rlep = rle[p];
unsigned char valueCompare = (indata[p+2] DELTA_OP rot) & 0xff;
/*
There's always 1 equal byte, although it may
not be marked as RLE.
*/
if (rlep <= 0)
rlep = 1;
if (bot < 0)
bot = 0;
bot += (rlep-1);
/*
First get the shortest possible match (if any).
If there is no 2-byte match, don't look further,
because there can't be a longer match.
*/
i = (int)lastPair[ (((indata[p] DELTA_OP rot) & 0xff)<<8) |
((indata[p+1] DELTA_OP rot) & 0xff) ] -1;
if (i>=0 && i>=bot) {
/* Got a 2-byte match at least */
maxval = 2;
maxpos = p-i;
/*
A..AB rlep # of A's, B is something else..
Search for bytes that are in p + (rlep-1), i.e.
the last rle byte ('A') and the non-matching one
('B'). When found, check if the rle in the compare
position (i) is long enough (i.e. the same number
of A's at p and i-rlep+1).
There are dramatically less matches for AB than for
AA, so we get a huge speedup with this approach.
We are still guaranteed to find the most recent
longest match there is.
*/
i = (int)lastPair[(((indata[p+(rlep-1)] DELTA_OP rot) & 0xff)<<8) |
((indata[p+rlep] DELTA_OP rot) & 0xff)] -1;
while (i>=bot /* && i>=rlep-1 */) { /* bot>=rlep-1, i>=bot ==> i>=rlep-1 */
/* Equal number of A's ? */
if (!(rlep-1) || rle[i-(rlep-1)]==rlep) { /* 'head' matches */
/* rlep==1 ==> (rlep-1)==0 */
/* ivanova.run: 443517 rlep==1,
709846 rle[i+1-rlep]==rlep */
/*
Check the hash values corresponding to the last
two bytes of the currently longest match and
the first new matching(?) byte. If the hash
values don't match, don't bother to check the
data itself.
*/
#ifdef HASH_STAT
hashChecks++;
#endif /* HASH_STAT */
if (indata[i+maxval-rlep+1] == valueCompare) {
unsigned char *a = indata + i+2; /* match */
unsigned char *b = indata + p+rlep-1+2;/* curpos */
int topindex = inlen-(p+rlep-1);
/* the 2 first bytes ARE the same.. */
j = 2;
while (j < topindex && *a++==((*b++ DELTA_OP rot) & 0xff))
j++;
#ifdef HASH_STAT
hashEqual++;
compares += j - 1;
#endif /* HASH_STAT */
if (j + rlep-1 > maxval) {
int tmplen = j+rlep-1, tmppos = p-i+rlep-1;
if (tmplen > maxlzlen)
tmplen = maxlzlen;
/* Accept only versions that really are shorter */
if (tmplen*8 - LenLz(tmplen, tmppos) >
maxval*8 - LenLz(maxval, maxpos)) {
maxval = tmplen;
maxpos = tmppos;
valueCompare = (indata[p+maxval] DELTA_OP rot) & 0xff;
}
#if 0
else {
printf("@%5d %d*8 - LenLz(%d, %4x)==%d < ",
p, tmplen, tmplen, tmppos,
tmplen*8 - LenLz(tmplen, tmppos));
printf("%d*8 - LenLz(%d, %4x)==%d\n",
maxval, maxval, maxpos,
maxval*8 - LenLz(maxval, maxpos));
}
#endif
if (maxval == maxlzlen)
break;
}
}
}
#ifdef BACKSKIP_FULL
if (!backSkip[i])
break; /* No previous occurrances (near enough) */
i -= (int)backSkip[i];
#else
if (!backSkip[i & 0xffff])
break; /* No previous occurrances (near enough) */
i -= (int)backSkip[i & 0xffff];
#endif /* BACKSKIP_FULL */
}
if (p+maxval > inlen) {
fprintf(stderr,
"Error @ %d, lzlen %d, pos %d - exceeds inlen\n",
p, maxval, maxpos);
maxval = inlen - p;
}
if (maxval > 3 && maxpos <= 256 &&
(maxval > lzlen2[p] ||
(maxval == lzlen2[p] && maxpos < lzpos2[p]))) {
if (maxpos < 0)
fprintf(stderr, "Error @ %d, lzlen %d, pos %d\n",
p, maxval, maxpos);
lzlen2[p] = (maxval<maxlzlen)?maxval:maxlzlen;
lzpos2[p] = maxpos;
}
}
}
if (lzlen2[p] <= lzlen[p] || lzlen2[p] <= rle[p]) {
lzlen2[p] = lzpos2[p] = 0;
}
}
#endif
/* Update the two-byte history ('hash table') &
backSkip ('linked list') */
if (p+1<inlen) {
int index = (indata[p]<<8) | indata[p+1];
int ptr = p - (lastPair[index]-1);
if (ptr > p || ptr > 0xffff)
ptr = 0;
#ifdef BACKSKIP_FULL
backSkip[p] = ptr;
#else
backSkip[p & 0xffff] = ptr;
#endif /* BACKSKIP_FULL */
lastPair[index] = p+1;
}
}
if ((flags & F_NORLE)) {
for (p=1; p<inlen; p++) {
if (rle[p-1]-1 > lzlen[p]) {
lzlen[p] = (rle[p]<maxlzlen)?rle[p]:maxlzlen;
lzpos[p] = 1;
}
}
for (p=0; p<inlen; p++) {
rle[p] = 0;
}
}
fprintf(stderr, "\rChecked: %d \n", p);
fflush(stderr); /* for SAS/C */
/* Initialize the RLE selections */
InitRle(flags);
/* Check the normal bytes / all ratio */
if ((flags & F_AUTO)) {
int mb, mv;
fprintf(stderr, "Selecting the number of escape bits.. ");
fflush(stderr); /* for SAS/C */
/*
Absolute maximum number of escaped bytes with
the escape optimize is 2^-n, where n is the
number of escape bits used.
This worst case happens only on equal-
distributed normal bytes (01230123..).
This is why the typical values are so much smaller.
*/
mb = 0;
mv = 8*OUT_SIZE;
for (escBits=1; escBits<9; escBits++) {
int escaped, other = 0, c;
escMask = (0xff00>>escBits) & 0xff;
/* Find the optimum path for selected escape bits (no optimize) */
OptimizeLength(0);
/* Optimize the escape selections for this path & escBits */
escaped = OptimizeEscape(&escape, &other);
/* Compare value: bits lost for escaping -- bits lost for prefix */
c = (escBits+3)*escaped + other*escBits;
if ((flags & F_STATS)) {
fprintf(stderr, " %d:%d", escBits, c);
fflush(stderr); /* for SAS/C */
}
if (c < mv) {
mb = escBits;
mv = c;
} else {
/* minimum found */
break;
}
if (escBits==4 && (flags & F_STATS))
fprintf(stderr, "\n");
}
if (mb==1) { /* Minimum was 1, check 0 */
int escaped;
escBits = 0;
escMask = 0;
/* Find the optimum path for selected escape bits (no optimize) */
OptimizeLength(0);
/* Optimize the escape selections for this path & escBits */
escaped = OptimizeEscape(&escape, NULL);
if ((flags & F_STATS)) {
fprintf(stderr, " %d:%d", escBits, 3*escaped);
fflush(stderr); /* for SAS/C */
}
if (3*escaped < mv) {
mb = 0;
/* mv = 3*escaped; */
}
}
if ((flags & F_STATS))
fprintf(stderr, "\n");
fprintf(stderr, "Selected %d-bit escapes\n", mb);
escBits = mb;
escMask = (0xff00>>escBits) & 0xff;
}
if (!(flags & F_NOOPT)) {
fprintf(stderr, "Optimizing LZ77 and RLE lengths...");
fflush(stderr); /* for SAS/C */
}
/* Find the optimum path (optimize) */
OptimizeLength((flags & F_NOOPT)?0:1);
if ((flags & F_STATS)) {
if(!(flags & F_NOOPT))
fprintf(stderr, " gained %d units.\n", lzopt/8);
} else
fprintf(stderr, "\n");
if (1 || (flags & F_AUTOEX)) {
long lzstat[5] = {0,0,0,0,0}, i, cur = 0, old = extraLZPosBits;
fprintf(stderr, "Selecting LZPOS LO length.. ");
fflush(stderr); /* for SAS/C */
for (p=0; p<inlen; ) {
switch (mode[p]) {
case LZ77: /* lz */
extraLZPosBits = 0;
lzstat[0] += LenLz(lzlen[p], lzpos[p]);
extraLZPosBits = 1;
lzstat[1] += LenLz(lzlen[p], lzpos[p]);
extraLZPosBits = 2;
lzstat[2] += LenLz(lzlen[p], lzpos[p]);
extraLZPosBits = 3;
lzstat[3] += LenLz(lzlen[p], lzpos[p]);
extraLZPosBits = 4;
lzstat[4] += LenLz(lzlen[p], lzpos[p]);
p += lzlen[p];
break;
#ifdef DELTA
case DLZ:
p += lzlen2[p];
break;
#endif
case RLE: /* rle */
p += rle[p];
break;
default: /* normal */
p++;
break;
}
}
for (i=0; i<5; i++) {
if ((flags & F_STATS))
fprintf(stderr, " %ld:%ld", i + 8, lzstat[i]);
/* first time around (lzstat[0] < lzstat[0]) */
if (lzstat[i] < lzstat[cur])
cur = i;
}
extraLZPosBits = (flags & F_AUTOEX)?cur:old;
if ((flags & F_STATS))
fprintf(stderr, "\n");
fprintf(stderr, "Selected %d-bit LZPOS LO part\n",
extraLZPosBits + 8);
if (cur != old) {
fprintf(stderr,
"Note: Using option -p%ld you may get better results.\n",
cur);
}
/* Find the optimum path (optimize) */
if (extraLZPosBits != old)
OptimizeLength((flags & F_NOOPT)?0:1);
}
if (1) {
long stat[4] = {0,0,0,0};
for (p=0; p<inlen; ) {
switch (mode[p]) {
case LZ77: /* lz */
if ((lzpos[p] >> 8)+1 > (1<<maxGamma))
stat[3]++;
if (lzlen[p] > (1<<maxGamma))
stat[0]++;
p += lzlen[p];
break;
case RLE: /* rle */
if (rle[p] > (1<<(maxGamma-1))) {
if (rle[p] <= (1<<maxGamma))
stat[1]++;
#if 0
else if (rle[p] <= (2<<maxGamma))
stat[2]++;
#endif
}
p += rle[p];
break;
#ifdef DELTA
case DLZ:
p += lzlen2[p];
break;
#endif
default: /* normal */
p++;
break;
}
}
/* TODO: better formula.. */
if (maxGamma < 7 && stat[0] + stat[1] + stat[3] > 10) {
fprintf(stderr,
"Note: Using option -m%d you may get better results.\n",
maxGamma+1);
}
if (maxGamma > 5 && stat[0] + stat[1] + stat[3] < 4) {
fprintf(stderr,
"Note: Using option -m%d you may get better results.\n",
maxGamma-1);
}
}
/* Optimize the escape selections */
OptimizeEscape(&escape, NULL);
if (startEscape)
*startEscape = escape;
OptimizeRle(flags); /* Retune the RLE selections */
#ifdef ENABLE_VERBOSE
if ((flags & F_VERBOSE)) {
int oldEscape = escape;
printf("normal RLE LZLEN LZPOS(absolute)\n\n");
for (p=0; p<inlen; ) {
switch (mode[p]) {
case LZ77:
mode[p - lzpos[p]] |= MMARK; /* Was referred to by lz77 */
p += lzlen[p];
break;
case RLE:
p += rle[p];
break;
#ifdef DELTA
case DLZ:
mode[p - lzpos2[p]] |= MMARK; /* Was referred to by lz77 */
p += lzlen2[p];
break;
#endif
/* case LITERAL:
case MMARK:*/
default:
p++;
break;
}
}
j = 0;
for (p=0; p<inlen; p++) {
switch (mode[p]) {
#ifdef DELTA
case MMARK | DLZ:
case DLZ:
if (j==p) {
printf(">");
j += lzlen2[p];
} else
printf(" ");
if (lzpos2) {
printf(" %04x*%03d*+%02x", lzpos2[p], lzlen2[p],
(indata[p] - indata[p-lzpos2[p]]) & 0xff);
}
printf(" 001 %03d %03d %04x(%04x) %02x %s\n",
rle[p], lzlen[p], lzpos[p], p-lzpos[p], indata[p],
(mode[p] & MMARK)?"#":" ");
break;
#endif
case MMARK | LITERAL:
case LITERAL:
if (j==p) {
printf(">");
} else
printf(" ");
#ifdef DELTA
if (lzpos2) {
printf(" %04x %03d +%02x", lzpos2[p], lzlen2[p],
(indata[p] - indata[p-lzpos2[p]]) & 0xff);
}
#endif
if (j==p) {
printf("*001* %03d %03d %04x(%04x) %02x %s %02x",
rle[p], lzlen[p], lzpos[p], p-lzpos[p], indata[p],
(mode[p] & MMARK)?"#":" ", newesc[p]);
if ((indata[p] & escMask) == escape) {
escape = newesc[p];
printf("«");
}
printf("\n");
j += 1;
} else {
printf("*001* %03d %03d %04x(%04x) %02x %s %02x\n",
rle[p], lzlen[p], lzpos[p], p-lzpos[p], indata[p],
(mode[p] & MMARK)?"#":" ", newesc[p]);
}
break;
case MMARK | LZ77:
case LZ77:
if (j==p) {
printf(">");
j += lzlen[p];
} else
printf(" ");
#ifdef DELTA
if (lzpos2) {
printf(" %04x %03d +%02x", lzpos2[p], lzlen2[p],
(indata[p] - indata[p-lzpos2[p]]) & 0xff);
}
#endif
printf(" 001 %03d *%03d* %04x(%04x) %02x %s",
rle[p], lzlen[p], lzpos[p], p-lzpos[p], indata[p],
(mode[p] & MMARK)?"#":" ");
printf("\n");
break;
case MMARK | RLE:
case RLE:
if (j==p) {
printf(">");
j += rle[p];
} else
printf(" ");
#ifdef DELTA
if (lzpos2) {
printf(" %04x %03d +%02x", lzpos2[p], lzlen2[p],
(indata[p] - indata[p-lzpos2[p]]) & 0xff);
}
#endif
printf(" 001 *%03d* %03d %04x(%04x) %02x %s\n",
rle[p], lzlen[p], lzpos[p], p-lzpos[p], indata[p],
(mode[p] & MMARK)?"#":" ");
break;
default:
j++;
break;
}
mode[p] &= ~MMARK;
}
escape = oldEscape;
}
#endif /* ENABLE_VERBOSE */
/* Perform rescan */
{
int esc = escape;
for (p=0; p<inlen; ) {
switch (mode[p]) {
case LITERAL: /* normal */
if ((indata[p] & escMask) == esc) {
esc = newesc[p];
}
p++;
break;
#ifdef DELTA
case DLZ:
p += lzlen2[p];
break;
#endif
case LZ77: /* lz77 */
#ifdef BACKSKIP_FULL
/* Not possible for smaller backSkip table
(the table is overwritten during previous use) */
#ifdef RESCAN
/* Re-search matches to get the closest one */
if (lzopt && /* If any changes to lengths.. */
lzlen[p] > 2 /*&& lzlen[p] > rle[p]*/) {
int bot = p - lzpos[p] + 1, i;
unsigned short rlep = rle[p];
if (!rlep)
rlep = 1;
if (bot < 0)
bot = 0;
bot += (rlep-1);
i = p - (int)backSkip[p];
while (i>=bot /* && i>=rlep-1 */) {
/* Equal number of A's ? */
if (rlep==1 || rle[i-rlep+1]==rlep) { /* 'head' matches */
unsigned char *a = indata + i+1; /* match */
unsigned char *b = indata + p+rlep-1+1; /* curpos */
int topindex = inlen-(p+rlep-1);
j = 1;
while (j < topindex && *a++==*b++)
j++;
if (j + rlep-1 >= lzlen[p]) {
int tmppos = p-i+rlep-1;
rescan +=
LenLz(lzlen[p], lzpos[p]) -
LenLz(lzlen[p], tmppos);
#if 0
printf("@%d, lzlen %d, pos %04x -> %04x\n",
p, lzlen[p], lzpos[p], tmppos);
for (i=-1; i<=lzlen[p]; i++) {
printf("%02x %02x %02x ",
indata[p+i], indata[p-lzpos[p]+i],
indata[p-tmppos+i]);
}
printf("\n");
#endif
lzpos[p] = tmppos;
break;
}
}
if (!backSkip[i])
break; /* No previous occurrances (near enough) */
i -= (int)backSkip[i];
}
}
#endif /* RESCAN */
#endif /* BACKSKIP_FULL */
p += lzlen[p];
break;
case RLE: /* rle */
p += rle[p];
break;
default: /* Error Flynn :-) */
fprintf(stderr, "Internal error: mode %d\n", mode[p]);
p++;
break;
}
}
}
/* start of output */
for (p=0; p<inlen; ) {
switch (mode[p]) {
case LITERAL: /* normal */
length[p] = outPointer;
OutputNormal(&escape, indata+p, newesc[p]);
p++;
break;
#ifdef DELTA
case DLZ:
for (i=0; i<lzlen2[p]; i++)
length[p+i] = outPointer;
OutputDLz(&escape, lzlen2[p], lzpos2[p],
(indata[p] - indata[p-lzpos2[p]]) & 0xff);
p += lzlen2[p];
break;
#endif
case LZ77: /* lz77 */
for (i=0; i<lzlen[p]; i++)
length[p+i] = outPointer;
OutputLz(&escape, lzlen[p], lzpos[p], (char *) (indata+p-lzpos[p]), p);
p += lzlen[p];
break;
case RLE: /* rle */
for (i=0; i<rle[p]; i++)
length[p+i] = outPointer;
OutputRle(&escape, indata+p, rle[p]);
p += rle[p];
break;
default: /* Error Flynn :-) */
fprintf(stderr, "Internal error: mode %d\n", mode[p]);
p++;
break;
}
}
OutputEof(&escape);
/* xxxxxxxxxxxxxxxxxxx uncompressed */
/* yyyyyyyyyyyyyyyyy compressed */
/* zzzz */
i = inlen;
for (p=0; p<inlen; p++) {
int pos = (inlen - outPointer) + (int)length[p] - p;
i = min(i, pos);
}
if (i<0)
reservedBytes = -i + 2;
else
reservedBytes = 0;
#ifndef BIG
if (type == 0) {
headerSize = 16 + rleUsed;
} else
#endif /* BIG */
{
if (endAddr + reservedBytes + 3 > memEnd) {
type |= FIXF_WRAP;
} else {
type &= ~FIXF_WRAP;
}
headerSize = GetHeaderSize(type, NULL) + rleUsed - 15;
}
outlen = outPointer + headerSize; /* unpack code */
fprintf(stderr, "In: %d, out: %d, ratio: %5.2f%% (%4.2f[%4.2f] b/B)"
", gained: %5.2f%%\n",
inlen, outlen, (double)outlen*100.0/(double)inlen + 0.005,
8.0*(double)outlen/(double)inlen + 0.005,
8.0*(double)(outlen-headerSize+rleUsed+4)/(double)inlen + 0.005,
100.0 - (double)outlen*100.0/(double)inlen + 0.005);
#ifdef DELTA
if ((type & FIXF_DLZ)) {
fprintf(stderr, "Gained RLE: %d (S+L:%d+%d), DLZ: %d, LZ: %d, Esc: %d"
", Decompressor: %d\n",
gainedRle/8, gainedSRle/8, gainedLRle/8, gainedDLz/8,
gainedLz/8, -gainedEscaped/8, -headerSize);
fprintf(stderr, "Times RLE: %d (%d+%d), DLZ: %d, LZ: %d, Esc: %d (normal: %d)"
", %d escape bit%s\n",
timesRle, timesSRle, timesLRle, timesDLz,
timesLz, timesEscaped, timesNormal,
escBits, (escBits==1)?"":"s" );
} else
#endif
{
fprintf(stderr, "Gained RLE: %d (S+L:%d+%d), LZ: %d, Esc: %d"
", Decompressor: %d\n",
gainedRle/8, gainedSRle/8, gainedLRle/8,
gainedLz/8, -gainedEscaped/8, -headerSize);
fprintf(stderr, "Times RLE: %d (%d+%d), LZ: %d, Esc: %d (normal: %d)"
", %d escape bit%s\n",
timesRle, timesSRle, timesLRle,
timesLz, timesEscaped, timesNormal,
escBits, (escBits==1)?"":"s" );
}
if ((flags & F_STATS)) {
const char *ll[] = {"2", "3-4", "5-8", "9-16", "17-32", "33-64",
"65-128", "129-256"};
fprintf(stderr, "(Gained by RLE Code: %d, LZPOS LO Bits %d"
", maxLen: %d, tag bit/prim. %4.2f)\n",
gainedRlecode/8 - rleUsed,
extraLZPosBits + 8,
(2<<maxGamma),
(double)((timesRle+timesLz)*escBits +
timesEscaped*(escBits + 3))/
(double)(timesRle+timesLz+timesNormal) + 0.0049);
fprintf(stderr, " LZPOS HI+2 LZLEN S-RLE RLEcode\n");
fprintf(stderr, " ------------------------------\n");
for (i=0; i<=maxGamma; i++) {
fprintf(stderr, "%-7s %5d %5d", ll[i],
lenStat[i][0], lenStat[i][1]);
if (i<maxGamma)
fprintf(stderr, " %5d", lenStat[i][2]);
else
fprintf(stderr, " -");
if (i<5)
fprintf(stderr, " %5d%s\n", lenStat[i][3], (i==4)?"*":"");
else
fprintf(stderr, " -\n");
}
#ifdef BACKSKIP_FULL
#ifdef RESCAN
fprintf(stderr, "LZ77 rescan gained %d bytes\n", rescan/8);
#endif /* RESCAN */
#endif /* BACKSKIP_FULL */
#ifdef HASH_STAT
#ifdef HASH_COMPARE
fprintf(stderr,
"Hash Checks %ld (%ld, %4.2f%% equal), RLE/LZ compares %ld\n",
hashChecks, hashEqual,
100.0*(double)hashEqual/(double)hashChecks,
compares);
#else
fprintf(stderr,
"Value Checks %ld (%ld, %4.2f%% equal), RLE/LZ compares %ld\n",
hashChecks, hashEqual,
100.0*(double)hashEqual/(double)hashChecks,
compares);
#endif /* HASH_COMPARE */
#endif /* HASH_STAT */
}
errorexit:
if (rle)
free(rle);
if (elr)
free(elr);
if (lzmlen)
free(lzmlen);
if (lzmpos)
free(lzmpos);
if (lzlen)
free(lzlen);
if (lzpos)
free(lzpos);
if (length)
free(length);
if (mode)
free(mode);
if (newesc)
free(newesc);
if (lastPair)
free(lastPair);
if (backSkip)
free(backSkip);
#ifdef HASH_COMPARE
if (hashValue)
free(hashValue);
#endif /* HASH_COMPARE */
return 0;
}
int main(int argc, char *argv[]) {
int n, execAddr = -1, ea = -1, newlen, startAddr = -1, startEscape;
int flags = F_2MHZ, lzlen = -1, buflen;
char *fileIn = NULL, *fileOut = NULL;
FILE *infp;
unsigned char tmp[2];
unsigned long timeused = clock();
int machineType = 64;
char *machineTypeTxt;
int memStart, memEnd;
int type = 0;
lrange = LRANGE;
maxlzlen = MAXLZLEN;
maxrlelen = MAXRLELEN;
InitValueLen();
flags |= (F_AUTO | F_AUTOEX);
for (n=1; n<argc; n++) {
if (!strcmp(argv[n], "-flist")) {
printf("List of Decompressors:\n");
printf("----------------------\n");
ListDecompressors(stdout);
return EXIT_FAILURE;
} else if (!strcmp(argv[n], "-ffast")) {
type |= FIXF_FAST;
} else if (!strcmp(argv[n], "-fnorle")) {
flags |= F_NORLE;
} else if (!strcmp(argv[n], "-fshort")) {
type |= FIXF_SHORT;
} else if (!strcmp(argv[n], "-fbasic")) {
type |= FIXF_BASIC;
#ifdef DELTA
} else if (!strcmp(argv[n], "-fdelta")) {
type |= FIXF_DLZ;
#endif
} else if (!strcmp(argv[n], "+f")) {
flags &= ~F_2MHZ;
} else if (argv[n][0]=='-') {
int i = 1;
char *val, *tmp, c;
long tmpval;
while (argv[n][i]) {
switch (argv[n][i]) {
case 'u':
flags |= F_UNPACK;
break;
case 'd': /* Raw - no loading address */
flags |= F_SKIP;
break;
case 'n': /* noopt, no rle/lzlen optimization */
flags |= F_NOOPT;
break;
case 's':
flags |= F_STATS;
break;
#ifdef ENABLE_VERBOSE
case 'v':
flags |= F_VERBOSE;
break;
#endif /* ENABLE_VERBOSE */
case 'f':
flags |= F_2MHZ;
break;
case 'a':
flags |= F_AVOID;
break;
case 'h':
case '?':
flags |= F_ERROR;
break;
case 'g':
case 'i':
case 'r':
case 'x':
case 'm':
case 'e':
case 'p':
case 'l':
case 'c': /* 64 (C64), 20 (VIC20), 16/4 (C16/Plus4) */
c = argv[n][i]; /* Remember the option */
if (argv[n][i+1]) {
val = argv[n]+i+1;
} else if (n+1 < argc) {
val = argv[n+1];
n++;
} else {
flags |= F_ERROR;
break;
}
i = strlen(argv[n])-1;
if (*val=='$')
tmpval = strtol(val+1, &tmp, 16);
else
tmpval = strtol(val, &tmp, 0);
if (*tmp) {
fprintf(stderr,
"Error: invalid number: \"%s\"\n", val);
flags |= F_ERROR;
break;
}
switch (c) {
case 'r':
lzlen = tmpval;
break;
case 'x':
ea = tmpval;
break;
case 'm':
maxGamma = tmpval;
if (maxGamma < 5 || maxGamma > 7) {
fprintf(stderr, "Max length must be 5..7!\n");
flags |= F_ERROR;
maxGamma = 7;
}
lrange = LRANGE;
maxlzlen = MAXLZLEN;
maxrlelen = MAXRLELEN;
InitValueLen();
break;
case 'e':
escBits = tmpval;
if (escBits < 0 || escBits > 8) {
fprintf(stderr, "Escape bits must be 0..8!\n");
flags |= F_ERROR;
} else
flags &= ~F_AUTO;
escMask = (0xff00>>escBits) & 0xff;
break;
case 'p':
extraLZPosBits = tmpval;
if (extraLZPosBits < 0 || extraLZPosBits > 4) {
fprintf(stderr,
"Extra LZ-pos bits must be 0..4!\n");
flags |= F_ERROR;
} else
flags &= ~F_AUTOEX;
break;
case 'l':
startAddr = tmpval;
if (startAddr < 0 || startAddr > 0xffff) {
fprintf(stderr,
"Load address must be 0..0xffff!\n");
flags |= F_ERROR;
}
break;
case 'c': /* 64 (C64), 20 (VIC20), 16/4 (C16/Plus4) */
machineType = tmpval;
if (machineType != 64 && machineType != 20 &&
machineType != 16 && machineType != 4 &&
machineType != 128 && machineType != 0) {
fprintf(stderr, "Machine must be 64, 20, 16/4, 128!\n");
flags |= F_ERROR;
}
break;
case 'i': /* Interrupt config */
if (tmpval==0) {
intConfig = 0x78; /* sei */
} else {
intConfig = 0x58; /* cli */
}
break;
case 'g': /* Memory configuration */
memConfig = (tmpval & 0xff);
break;
}
break;
default:
fprintf(stderr, "Error: Unknown option \"%c\"\n",
argv[n][i]);
flags |= F_ERROR;
}
i++;
}
} else {
if (!fileIn) {
fileIn = argv[n];
} else if (!fileOut) {
fileOut = argv[n];
} else {
fprintf(stderr, "Only two filenames wanted!\n");
flags |= F_ERROR;
}
}
}
if ((flags & F_ERROR)) {
fprintf(stderr, "Usage: %s [-<flags>] [<infile> [<outfile>]]\n",
argv[0]);
fprintf(stderr,
"\t -flist list all decompressors\n"
"\t -ffast select faster version, if available (longer)\n"
"\t -fshort select shorter version, if available (slower)\n"
"\t -fbasic select version for BASIC programs (for VIC20 and C64)\n"
#ifdef DELTA
"\t -fdelta use delta-lz77 -- shortens some files\n"
#endif
"\t -f enable fast mode for C128 (C64 mode) and C16/+4 (default)\n"
"\t +f disable fast mode for C128 (C64 mode) and C16/+4\n"
"\t c<val> machine: 64 (C64), 20 (VIC20), 16 (C16/+4)\n"
"\t a avoid video matrix (for VIC20)\n"
"\t d data (no loading address)\n"
"\t l<val> set/override load address\n"
"\t x<val> set execution address\n"
"\t e<val> force escape bits\n"
"\t r<val> restrict lz search range\n"
"\t n no RLE/LZ length optimization\n"
"\t s full statistics\n"
#ifdef ENABLE_VERBOSE
"\t v verbose\n"
#endif /* ENABLE_VERBOSE */
"\t p<val> force extralzposbits\n"
"\t m<val> max len 5..7 (2*2^5..2*2^7)\n"
"\t i<val> interrupt enable after decompress (0=disable)\n"
"\t g<val> memory configuration after decompress\n"
"\t u unpack\n");
return EXIT_FAILURE;
}
if (lzlen == -1)
lzlen = DEFAULT_LZLEN;
if (fileIn) {
if (!(infp = fopen(fileIn, "rb"))) {
fprintf(stderr, "Could not open %s for reading!\n", fileIn);
return EXIT_FAILURE;
}
} else {
fprintf(stderr, "Reading from stdin\n");
fflush(stderr); /* for SAS/C */
infp = stdin;
}
if (!(flags & F_SKIP)) {
ssize_t bytes_read = fread(tmp, 1, 2, infp);
(void) bytes_read;
/* Use it only if not overriden by the user */
if (startAddr==-1)
startAddr = tmp[0] + 256*tmp[1];
}
if (startAddr==-1)
startAddr = 0x258;
/* Read in the data */
inlen = 0;
buflen = 0;
indata = NULL;
while (1) {
if (buflen < inlen + lrange) {
unsigned char *tmp = realloc(indata, buflen + lrange);
if (!tmp) {
free(indata);
return 20;
}
indata = tmp;
buflen += lrange;
}
newlen = fread(indata + inlen, 1, lrange, infp);
if (newlen <= 0)
break;
inlen += newlen;
}
if (infp != stdin)
fclose(infp);
if ((flags & F_UNPACK)) {
n = UnPack(startAddr, indata, fileOut, flags);
if (indata)
free(indata);
return n;
}
if (startAddr < 0x258
#ifndef BIG
|| startAddr + inlen -1 > 0xffff
#endif /* BIG */
) {
fprintf(stderr,
"Only programs from 0x0258 to 0xffff can be compressed\n");
fprintf(stderr, "(the input file is from 0x%04x to 0x%04x)\n",
startAddr, startAddr+inlen-1);
if (indata)
free(indata);
return EXIT_FAILURE;
}
switch (machineType) {
case 20:
machineTypeTxt = "VIC20 with 8k or 16k (or 24k) expansion memory";
memStart = 0x1201;
memEnd = 0x4000;
type |= FIXF_VIC20 | FIXF_WRAP;
if (startAddr+inlen > 0x8000) {
fprintf(stderr, "Original file exceeds 0x8000 (0x%04x), "
"not a valid VIC20 file!\n", startAddr+inlen-1);
n = EXIT_FAILURE;
goto errexit;
} else if (startAddr+inlen > 0x6000) {
if (startAddr < 0x1000) {
fprintf(stderr, "Original file exceeds 0x6000 (0x%04x), "
"3kB+24kB memory expansions assumed\n",
startAddr+inlen-1);
machineTypeTxt = "VIC20 with 3k+24k expansion memory";
} else {
fprintf(stderr, "Original file exceeds 0x6000 (0x%04x), "
"24kB memory expansion assumed\n",
startAddr+inlen-1);
machineTypeTxt = "VIC20 with 24k expansion memory";
}
memEnd = 0x8000;
} else if (startAddr+inlen > 0x4000) {
if (startAddr < 0x1000) {
fprintf(stderr, "Original file exceeds 0x4000 (0x%04x), "
"3kB+16kB memory expansion assumed\n",
startAddr+inlen-1);
machineTypeTxt =
"VIC20 with 3k+16k (or 3k+24k) expansion memory";
} else {
fprintf(stderr, "Original file exceeds 0x4000 (0x%04x), "
"16kB memory expansion assumed\n",
startAddr+inlen-1);
machineTypeTxt = "VIC20 with 16k (or 24k) expansion memory";
}
memEnd = 0x6000;
} else if (startAddr+inlen > 0x2000) {
if (startAddr < 0x1000) {
fprintf(stderr, "Original file exceeds 0x2000 (0x%04x), "
"3kB+8kB memory expansion assumed\n",
startAddr+inlen-1);
machineTypeTxt =
"VIC20 with 3k+8k (or 3k+16k, or 3k+24k) expansion memory";
} else {
fprintf(stderr, "Original file exceeds 0x2000 (0x%04x), "
"8kB memory expansion assumed\n",
startAddr+inlen-1);
}
/* memEnd = 0x4000; */
} else {
if (startAddr >= 0x1000 && startAddr < 0x1200) {
fprintf(stderr, "Program for unexpanded VIC detected.\n");
memStart = 0x1001;
memEnd = (flags & F_AVOID)?0x1e00:0x2000;
machineTypeTxt = "VIC20 without expansion memory";
} if (startAddr >= 0x400 && startAddr < 0x1000) {
fprintf(stderr, "Program for 3k-expanded VIC detected.\n");
memStart = 0x0401;
memEnd = (flags & F_AVOID)?0x1e00:0x2000;
machineTypeTxt = "VIC20 with 3k expansion memory";
}
}
break;
case 16:
case 4:
type |= FIXF_C16 | FIXF_WRAP;
if (startAddr+inlen > 0x4000) {
fprintf(stderr, "Original file exceeds 0x4000, 61k RAM assumed\n");
memStart = 0x1001;
memEnd = 0xfd00;
machineTypeTxt = "Plus/4";
} else {
fprintf(stderr, "Program for unexpanded C16 detected.\n");
memStart = 0x1001;
memEnd = 0x4000;
machineTypeTxt = "Commodore 16";
}
break;
case 128:
type |= FIXF_C128 | FIXF_WRAP;
memStart = 0x1c01;
memEnd = 0x10000;
machineTypeTxt = "Commodore 128";
break;
case 0:
type |= 0;
machineTypeTxt = "Without decompressor";
memStart = 0x801;
memEnd = 0x10000;
break;
default: /* C64 */
type |= FIXF_C64 | FIXF_WRAP; /* C64, wrap active */
machineTypeTxt = "Commodore 64";
memStart = 0x801; /* Loading address */
memEnd = 0x10000;
break;
}
if (startAddr <= memStart) {
for (n=memStart-startAddr; n<memStart-startAddr+60; n++) {
if (indata[n]==0x9e) { /* SYS token */
execAddr = 0;
n++;
/* Skip spaces and parens */
while (indata[n]=='(' || indata[n]==' ')
n++;
while (indata[n]>='0' && indata[n]<='9') {
execAddr = execAddr * 10 + indata[n++] - '0';
}
break;
}
}
}
if (ea != -1) {
if (execAddr!=-1 && ea!=execAddr)
fprintf(stderr, "Discarding execution address 0x%04x=%d\n",
execAddr, execAddr);
execAddr = ea;
} else if (execAddr < startAddr || execAddr >= startAddr+inlen) {
if ((type & FIXF_BASIC)) {
execAddr = 0xa7ae;
} else {
fprintf(stderr, "Note: The execution address was not detected "
"correctly!\n");
fprintf(stderr, " Use the -x option to set the execution "
"address.\n");
}
}
fprintf(stderr, "Load address 0x%04x=%d, Last byte 0x%04x=%d\n",
startAddr, startAddr, startAddr+inlen-1, startAddr+inlen-1);
fprintf(stderr, "Exec address 0x%04x=%d\n", execAddr, execAddr);
fprintf(stderr, "New load address 0x%04x=%d\n", memStart, memStart);
if (machineType == 64) {
fprintf(stderr, "Interrupts %s and memory config set to $%02x "
"after decompression\n",
(intConfig==0x58)?"enabled":"disabled", memConfig);
fprintf(stderr, "Runnable on %s\n", machineTypeTxt);
} else if (machineType != 0) {
fprintf(stderr, "Interrupts %s after decompression\n",
(intConfig==0x58)?"enabled":"disabled");
fprintf(stderr, "Runnable on %s\n", machineTypeTxt);
} else {
fprintf(stderr, "Standalone decompressor required\n");
}
#if 0
if ((flags & F_2MHZ))
type |= FIXF_FAST;
#endif
n = PackLz77(lzlen, flags, &startEscape, startAddr + inlen, memEnd, type);
if (!n) {
int endAddr = startAddr + inlen; /* end for uncompressed data */
int hDeCall, progEnd = endAddr;
if (GetHeaderSize(type, &hDeCall) == 0) {
GetHeaderSize(type & ~FIXF_WRAP, &hDeCall);
}
if (machineType != 0 &&
endAddr - ((outPointer + 255) & ~255) < memStart + hDeCall + 3) {
/* would overwrite the decompressor, move a bit upwards */
fprintf(stderr, "$%x < $%x, decompressor overwrite possible, "
"moving upwards\n",
endAddr - ((outPointer + 255) & ~255),
memStart + hDeCall + 3);
endAddr = memStart + hDeCall + 3 + ((outPointer + 255) & ~255);
}
/* Should check that endAddr really is larger than original endaddr! */
#if 0
/* Move the end address for files that got expanded */
if (memStart + hSize + outPointer > endAddr) {
endAddr = memStart + hSize + outPointer;
}
#endif
/* 3 bytes reserved for EOF */
/* bytes reserved for temporary data expansion (escaped chars) */
endAddr += 3 + reservedBytes;
#ifdef BIG
endAddr = 0x10000;
#endif /* BIG */
#ifdef DELTA
if (!timesDLz) {
type &= ~FIXF_DLZ;
}
#endif
SavePack(type, outBuffer, outPointer, fileOut,
startAddr, execAddr, startEscape, rleValues,
endAddr, progEnd, extraLZPosBits, (flags & F_2MHZ)?1:0,
memStart, memEnd);
timeused = clock()-timeused;
if (!timeused)
timeused++;
fprintf(stderr,
"Compressed %d bytes in %4.2f seconds (%4.2f kB/sec)\n",
inlen,
(double)timeused/CLOCKS_PER_SEC,
(double)CLOCKS_PER_SEC*inlen/timeused/1024.0);
}
errexit:
if (indata)
free(indata);
return n;
}