Restore backward lookback function, add -reverse option to LZ compression command

This commit is contained in:
Rachel 2024-10-23 20:49:54 -07:00
parent fc527bccbd
commit b934160afb
3 changed files with 63 additions and 29 deletions

81
lz.c
View File

@ -71,7 +71,58 @@ fail:
FATAL_ERROR("Fatal error while decompressing LZ file.\n");
}
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance)
static void FindBestBlockForwards(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize)
{
int blockStart = srcPos < 0x1000 ? 0 : srcPos - 0x1000;
while (blockStart != srcPos) {
int blockSize = 0;
while (blockSize < 18
&& srcPos + blockSize < srcSize
&& src[blockStart + blockSize] == src[srcPos + blockSize])
blockSize++;
if (blockSize > *outBestBlockSize
&& srcPos - blockStart >= minDistance) {
*outBestBlockDistance = srcPos - blockStart;
*outBestBlockSize = blockSize;
if (blockSize == 18)
break;
}
blockStart++;
}
}
static void FindBestBlockBackwards(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize)
{
int blockDistance = minDistance;
while (blockDistance <= srcPos && blockDistance <= 0x1000) {
int blockStart = srcPos - blockDistance;
int blockSize = 0;
while (blockSize < 18
&& srcPos + blockSize < srcSize
&& src[blockStart + blockSize] == src[srcPos + blockSize])
blockSize++;
if (blockSize > *outBestBlockSize) {
*outBestBlockDistance = blockDistance;
*outBestBlockSize = blockSize;
if (blockSize == 18)
break;
}
blockDistance++;
}
}
typedef void (*FindBestBlockFunc)(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize);
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration)
{
if (srcSize <= 0)
goto fail;
@ -94,7 +145,7 @@ unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize,
int srcPos = 0;
int destPos = 4;
int lookbackStart = 0;
FindBestBlockFunc FindBestBlock = forwardIteration ? FindBestBlockForwards : FindBestBlockBackwards;
for (;;) {
unsigned char *flags = &dest[destPos++];
@ -104,42 +155,18 @@ unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize,
int bestBlockDistance = 0;
int bestBlockSize = 0;
int blockStart = lookbackStart;
while (blockStart != srcPos) {
int blockSize = 0;
FindBestBlock(src, srcPos, srcSize, minDistance, &bestBlockDistance, &bestBlockSize);
while (blockSize < 18
&& srcPos + blockSize < srcSize
&& src[blockStart + blockSize] == src[srcPos + blockSize])
blockSize++;
if (blockSize > bestBlockSize
&& srcPos - blockStart >= minDistance) {
bestBlockDistance = srcPos - blockStart;
bestBlockSize = blockSize;
if (blockSize == 18)
break;
}
blockStart++;
}
int lookbackJump = 0;
if (bestBlockSize >= 3) {
*flags |= (0x80 >> i);
srcPos += bestBlockSize;
lookbackJump = srcPos - lookbackStart > 0x1000 ? bestBlockSize : 0;
bestBlockSize -= 3;
bestBlockDistance--;
dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8);
dest[destPos++] = (unsigned char)bestBlockDistance;
} else {
lookbackJump = (int)(srcPos - lookbackStart > 0x1000);
dest[destPos++] = src[srcPos++];
}
lookbackStart += lookbackJump;
if (srcPos == srcSize) {
// Pad to multiple of 4 bytes.

4
lz.h
View File

@ -3,7 +3,9 @@
#ifndef LZ_H
#define LZ_H
#include "stdbool.h"
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance);
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration);
#endif // LZ_H

7
main.c
View File

@ -955,6 +955,7 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
{
int overflowSize = 0;
int minDistance = 2; // default, for compatibility with LZ77UnCompVram()
bool forwardIteration = true;
for (int i = 3; i < argc; i++)
{
@ -986,6 +987,10 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
if (minDistance < 1)
FATAL_ERROR("LZ min search distance must be positive.\n");
}
else if (strcmp(option, "-reverse") == 0)
{
forwardIteration = false;
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
@ -1002,7 +1007,7 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
unsigned char *buffer = ReadWholeFileZeroPadded(inputPath, &fileSize, overflowSize);
int compressedSize;
unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize, minDistance);
unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize, minDistance, forwardIteration);
compressedData[1] = (unsigned char)fileSize;
compressedData[2] = (unsigned char)(fileSize >> 8);