add "fast" compression mode

This commit is contained in:
Devin Acker 2013-06-05 19:50:07 -04:00
parent b19637c39a
commit c9e004e18b
3 changed files with 44 additions and 29 deletions

View file

@ -16,8 +16,8 @@
#endif
uint8_t rotate (uint8_t);
rle_t rle_check (uint8_t*, uint8_t*, uint32_t);
backref_t ref_search (uint8_t*, uint8_t*, uint32_t);
rle_t rle_check (uint8_t*, uint8_t*, uint32_t, int);
backref_t ref_search (uint8_t*, uint8_t*, uint32_t, int);
uint16_t write_backref (uint8_t*, uint16_t, backref_t);
uint16_t write_rle (uint8_t*, uint16_t, rle_t);
uint16_t write_raw (uint8_t*, uint16_t, uint8_t*, uint16_t);
@ -26,7 +26,7 @@ uint16_t write_raw (uint8_t*, uint16_t, uint8_t*, uint16_t);
// unpacked/packed are 65536 byte buffers to read/from write to,
// inputsize is the length of the uncompressed data.
// Returns the size of the compressed data in bytes.
size_t pack(uint8_t *unpacked, uint32_t inputsize, uint8_t *packed) {
size_t pack(uint8_t *unpacked, uint32_t inputsize, uint8_t *packed, int fast) {
// current input/output positions
uint32_t inpos = 0;
uint32_t outpos = 0;
@ -43,9 +43,9 @@ size_t pack(uint8_t *unpacked, uint32_t inputsize, uint8_t *packed) {
while (inpos < inputsize) {
// check for a potential back reference
backref = ref_search(unpacked, unpacked + inpos, inputsize);
backref = ref_search(unpacked, unpacked + inpos, inputsize, fast);
// and for a potential RLE
rle = rle_check(unpacked, unpacked + inpos, inputsize);
rle = rle_check(unpacked, unpacked + inpos, inputsize, fast);
// if the backref is a better candidate, use it
if (backref.size > 3 && backref.size > rle.size) {
@ -183,11 +183,6 @@ size_t unpack(uint8_t *packed, uint8_t *unpacked) {
unpacked[outpos++] = unpacked[offset - i];
inpos += 2;
break;
default:
printf("Error: Unsupported method %i near 0x%06x\n", command, inpos);
exit(-1);
}
// keep track of how many times each compression method is used
@ -235,10 +230,9 @@ uint8_t rotate (uint8_t i) {
}
// Searches for possible RLE compressed data.
// Currently only does 8-bit RLE and sequence RLE (since 16-bit can be handled decently
// using the LZ77-like backrefs.)
// start and current are positions within the uncompressed input stream
rle_t rle_check (uint8_t *start, uint8_t *current, uint32_t insize) {
// start and current are positions within the uncompressed input stream.
// fast enables faster compression by ignoring sequence RLE.
rle_t rle_check (uint8_t *start, uint8_t *current, uint32_t insize, int fast) {
rle_t candidate = { 0, 0, 0 };
int size;
@ -275,6 +269,9 @@ rle_t rle_check (uint8_t *start, uint8_t *current, uint32_t insize) {
debug("\trle_check: found new candidate (size = %d, method = %d)\n", candidate.size, candidate.method);
}
// fast mode: don't use sequence RLE
if (fast) return candidate;
// check for possible sequence RLE
for (size = 0; current + size < start + insize; size++)
if (current[size] != (current[0] + size)) break;
@ -294,8 +291,9 @@ rle_t rle_check (uint8_t *start, uint8_t *current, uint32_t insize) {
}
// Searches for the best possible back reference.
// start and current are positions within the uncompressed input stream
backref_t ref_search (uint8_t *start, uint8_t *current, uint32_t insize) {
// start and current are positions within the uncompressed input stream.
// fast enables fast mode which only uses regular forward references
backref_t ref_search (uint8_t *start, uint8_t *current, uint32_t insize, int fast) {
backref_t candidate = { 0, 0, 0 };
uint16_t size;
@ -316,6 +314,9 @@ backref_t ref_search (uint8_t *start, uint8_t *current, uint32_t insize) {
debug("\tref_search: found new candidate (offset: %4x, size: %d, method = %d)\n", candidate.offset, candidate.size, candidate.method);
}
// fast mode: forward references only
if (fast) continue;
// now repeat the check with the bit rotation method
for (size = 0; size <= LONG_RUN_SIZE && current + size < start + insize; size++)
if (pos[size] != rotate(current[size])) break;

View file

@ -40,7 +40,7 @@ typedef struct {
method_e method;
} rle_t;
size_t pack (uint8_t *unpacked, size_t inputsize, uint8_t *packed);
size_t pack (uint8_t *unpacked, size_t inputsize, uint8_t *packed, int fast);
size_t unpack (uint8_t *packed, uint8_t *unpacked);
size_t unpack_from_file (FILE *file, unsigned int offset, uint8_t *unpacked);

36
inhal.c
View file

@ -17,14 +17,16 @@
int main (int argc, char **argv) {
printf("inhal - %s %s\nby Devin Acker (Revenant)\n\n", __DATE__, __TIME__);
if (argc != 4) {
if (argc < 4) {
printf("To insert compressed data into a ROM:\n");
printf("%s infile romfile offset\n", argv[0]);
printf("%s [-fast] infile romfile offset\n", argv[0]);
printf("To write compressed data to a new file:\n");
printf("%s -n infile outfile\n", argv[0]);
printf("%s [-fast] -n infile outfile\n\n", argv[0]);
printf("\nExample:\n%s test.chr kirbybowl.sfc 0x70000\n", argv[0]);
printf("Running with the -fast switch increases compression speed at the expense of size.\n");
printf("\nExample:\n%s -fast test.chr kirbybowl.sfc 0x70000\n", argv[0]);
printf("%s -n test.chr test-packed.bin\n\n", argv[0]);
printf("offset can be in either decimal or hex.\n");
exit(-1);
@ -32,16 +34,28 @@ int main (int argc, char **argv) {
FILE *infile, *outfile;
int fileoffset;
int newfile = 0;
int fast = 0;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-n"))
newfile = 1;
else if (!strcmp(argv[i], "-fast"))
fast = 1;
}
if (fast)
printf("Fast compression enabled.\n");
// check for -n switch
if (!strcmp(argv[1], "-n")) {
if (newfile) {
fileoffset = 0;
infile = fopen(argv[2], "rb");
outfile = fopen(argv[3], "wb");
infile = fopen(argv[argc - 2], "rb");
outfile = fopen(argv[argc - 1], "wb");
} else {
fileoffset = strtol(argv[3], NULL, 0);
infile = fopen(argv[1], "rb");
outfile = fopen(argv[2], "r+b");
fileoffset = strtol(argv[argc - 1], NULL, 0);
infile = fopen(argv[argc - 3], "rb");
outfile = fopen(argv[argc - 2], "r+b");
}
if (!infile) {
@ -74,7 +88,7 @@ int main (int argc, char **argv) {
// compress the file
clock_t time = clock();
outputsize = pack(unpacked, inputsize, packed);
outputsize = pack(unpacked, inputsize, packed, fast);
time = clock() - time;
// write the compressed data to the file