mirror of
https://github.com/fallout2-ce/fallout2-ce.git
synced 2026-07-27 16:47:11 -07:00
245 lines
7.5 KiB
C++
245 lines
7.5 KiB
C++
// NOTE: Functions in these module are somewhat different from what can be seen
|
|||
|
|
// in IDA because of two new helper functions that deal with incoming bits. I
|
||
|
|
// bet something like these were implemented via function-like macro in the
|
||
|
|
// same manner zlib deals with bits. The pattern is so common in this module so
|
||
|
|
// I made an exception and extracted it into separate functions to increase
|
||
|
|
// readability.
|
||
|
|
|
||
|
|
#include "fo1_dat_lzss.h"
|
||
|
|
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
namespace fallout {
|
||
|
|
|
||
|
|
static inline bool lzss_fill_decode_buffer(FILE* stream);
|
||
|
|
static inline bool lzss_read_decode_byte(FILE* stream, unsigned char* value);
|
||
|
|
static inline bool lzss_decode_chunk_to_buf(unsigned int type, FILE* stream, unsigned char** dest, unsigned int* length);
|
||
|
|
|
||
|
|
constexpr size_t lzssDecodeBufferSize = 1024;
|
||
|
|
constexpr unsigned int lzssRingBufferSize = 4116;
|
||
|
|
constexpr unsigned int lzssRingBufferMask = 0xFFF;
|
||
|
|
constexpr unsigned int lzssDecodeTriggerWindow = 16;
|
||
|
|
constexpr unsigned int lzssMatchLengthBias = 3;
|
||
|
|
constexpr unsigned char lzssInitialRingFill = ' ';
|
||
|
|
constexpr int lzssInitialRingIndex = 4078;
|
||
|
|
|
||
|
|
// 0x6B0860
|
||
|
|
static unsigned char decode_buffer[lzssDecodeBufferSize];
|
||
|
|
|
||
|
|
// 0x6B0C60
|
||
|
|
static unsigned char* decode_buffer_position;
|
||
|
|
|
||
|
|
// 0x6B0C64
|
||
|
|
static unsigned int decode_bytes_left;
|
||
|
|
|
||
|
|
// 0x6B0C68
|
||
|
|
static int ring_buffer_index;
|
||
|
|
|
||
|
|
// 0x6B0C6C
|
||
|
|
static unsigned char* decode_buffer_end;
|
||
|
|
|
||
|
|
// 0x6B0C70
|
||
|
|
static unsigned char ring_buffer[lzssRingBufferSize];
|
||
|
|
|
||
|
|
// 0x4CA260
|
||
|
|
int lzss_decode_to_buf(FILE* in, unsigned char* dest, unsigned int length)
|
||
|
|
{
|
||
|
|
unsigned char* curr;
|
||
|
|
unsigned char byte;
|
||
|
|
|
||
|
|
curr = dest;
|
||
|
|
memset(ring_buffer, lzssInitialRingFill, lzssInitialRingIndex);
|
||
|
|
ring_buffer_index = lzssInitialRingIndex;
|
||
|
|
decode_buffer_end = decode_buffer;
|
||
|
|
decode_buffer_position = decode_buffer;
|
||
|
|
decode_bytes_left = length;
|
||
|
|
|
||
|
|
while (length > 16) {
|
||
|
|
if (!lzss_read_decode_byte(in, &byte)) {
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
length -= 1;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x01, in, &curr, &length)) return -1;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x02, in, &curr, &length)) return -1;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x04, in, &curr, &length)) return -1;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x08, in, &curr, &length)) return -1;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x10, in, &curr, &length)) return -1;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x20, in, &curr, &length)) return -1;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x40, in, &curr, &length)) return -1;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x80, in, &curr, &length)) return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
do {
|
||
|
|
if (length == 0) break;
|
||
|
|
|
||
|
|
if (!lzss_read_decode_byte(in, &byte)) {
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
length -= 1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x01, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x02, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x04, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x08, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x10, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x20, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x40, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x80, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
|
||
|
|
if (!lzss_read_decode_byte(in, &byte)) {
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
length -= 1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x01, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x02, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x04, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x08, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x10, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x20, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x40, in, &curr, &length)) return -1;
|
||
|
|
|
||
|
|
if (length == 0) break;
|
||
|
|
if (!lzss_decode_chunk_to_buf(byte & 0x80, in, &curr, &length)) return -1;
|
||
|
|
} while (0);
|
||
|
|
|
||
|
|
return curr - dest;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline bool lzss_fill_decode_buffer(FILE* stream)
|
||
|
|
{
|
||
|
|
size_t bytes_to_read;
|
||
|
|
size_t bytes_read;
|
||
|
|
|
||
|
|
if (decode_bytes_left != 0 && decode_buffer_end - decode_buffer_position <= lzssDecodeTriggerWindow) {
|
||
|
|
if (decode_buffer_position == decode_buffer_end) {
|
||
|
|
decode_buffer_end = decode_buffer;
|
||
|
|
} else {
|
||
|
|
memmove(decode_buffer, decode_buffer_position, decode_buffer_end - decode_buffer_position);
|
||
|
|
decode_buffer_end = decode_buffer + (decode_buffer_end - decode_buffer_position);
|
||
|
|
}
|
||
|
|
|
||
|
|
decode_buffer_position = decode_buffer;
|
||
|
|
|
||
|
|
bytes_to_read = lzssDecodeBufferSize - (decode_buffer_end - decode_buffer);
|
||
|
|
if (bytes_to_read > decode_bytes_left) {
|
||
|
|
bytes_to_read = decode_bytes_left;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (bytes_to_read == 0) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bytes_read = fread(decode_buffer_end, 1, bytes_to_read, stream);
|
||
|
|
if (bytes_read != bytes_to_read) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
decode_buffer_end += bytes_read;
|
||
|
|
decode_bytes_left -= bytes_read;
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline bool lzss_read_decode_byte(FILE* stream, unsigned char* value)
|
||
|
|
{
|
||
|
|
if (!lzss_fill_decode_buffer(stream)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (decode_buffer_position >= decode_buffer_end) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
*value = *decode_buffer_position++;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline bool lzss_decode_chunk_to_buf(unsigned int type, FILE* stream, unsigned char** dest, unsigned int* length)
|
||
|
|
{
|
||
|
|
unsigned char low;
|
||
|
|
unsigned char high;
|
||
|
|
int dict_offset;
|
||
|
|
int dict_index;
|
||
|
|
int chunk_length;
|
||
|
|
int index;
|
||
|
|
|
||
|
|
if (type != 0) {
|
||
|
|
if (*length < 1) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
unsigned char byte;
|
||
|
|
if (!lzss_read_decode_byte(stream, &byte)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
*length -= 1;
|
||
|
|
*(*dest) = byte;
|
||
|
|
ring_buffer[ring_buffer_index] = *(*dest)++;
|
||
|
|
ring_buffer_index += 1;
|
||
|
|
ring_buffer_index &= lzssRingBufferMask;
|
||
|
|
} else {
|
||
|
|
if (*length < 2) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
unsigned char bytes[2];
|
||
|
|
if (!lzss_read_decode_byte(stream, &bytes[0]) || !lzss_read_decode_byte(stream, &bytes[1])) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
*length -= 2;
|
||
|
|
low = bytes[0];
|
||
|
|
high = bytes[1];
|
||
|
|
dict_offset = low | ((high & 0xF0) << 4);
|
||
|
|
chunk_length = (high & 0x0F) + lzssMatchLengthBias;
|
||
|
|
|
||
|
|
for (index = 0; index < chunk_length; index++) {
|
||
|
|
dict_index = (dict_offset + index) & lzssRingBufferMask;
|
||
|
|
*(*dest) = ring_buffer[dict_index];
|
||
|
|
ring_buffer[ring_buffer_index] = *(*dest)++;
|
||
|
|
ring_buffer_index += 1;
|
||
|
|
ring_buffer_index &= lzssRingBufferMask;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace fallout
|