kaki-rsc/main.c
2025-04-26 00:53:17 +00:00

128 lines
No EOL
3.6 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <zlib.h>
// Struct to hold one decompressed block and its file offset
typedef struct {
uint32_t offset;
unsigned char *data;
size_t size;
} Block;
// Decompress a zlib-compressed buffer 'in' of length 'in_len'.
// On success, *out and *out_len hold the decompressed data (malloc'd) and length.
// Returns 0 on success, non-zero on error.
int decompress_data(const unsigned char *in, size_t in_len,
unsigned char **out, size_t *out_len) {
z_stream strm = {0};
if (inflateInit(&strm) != Z_OK) return -1;
strm.next_in = (Bytef *)in;
strm.avail_in = in_len;
size_t buf_size = 4096;
*out = malloc(buf_size);
if (!*out) { inflateEnd(&strm); return -1; }
*out_len = 0;
int ret;
do {
// Ensure we have room
if (*out_len + 4096 > buf_size) {
buf_size *= 2;
unsigned char *tmp = realloc(*out, buf_size);
if (!tmp) { free(*out); inflateEnd(&strm); return -1; }
*out = tmp;
}
strm.next_out = *out + *out_len;
strm.avail_out = buf_size - *out_len;
ret = inflate(&strm, Z_NO_FLUSH);
if (ret != Z_OK && ret != Z_STREAM_END) {
inflateEnd(&strm);
return -1;
}
*out_len = buf_size - strm.avail_out;
} while (ret != Z_STREAM_END);
inflateEnd(&strm);
return 0;
}
int compare_blocks(const void *a, const void *b) {
const Block *ba = a;
const Block *bb = b;
return (ba->offset < bb->offset) ? -1 : (ba->offset > bb->offset);
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <rsc-file>\n", argv[0]);
return 1;
}
const char *path = argv[1];
FILE *f = fopen(path, "rb");
if (!f) {
fprintf(stderr, "Cannot open file '%s'\n", path);
return 1;
}
Block *blocks = NULL;
size_t block_count = 0;
while (1) {
// Record the start of this block
long start = ftell(f);
if (start < 0) break;
// Read 4-byte little-endian length header
uint8_t len_bytes[4];
if (fread(len_bytes, 1, 4, f) != 4) break; // EOF or error
uint32_t comp_len = (uint32_t)len_bytes[0]
| ((uint32_t)len_bytes[1] << 8)
| ((uint32_t)len_bytes[2] << 16)
| ((uint32_t)len_bytes[3] << 24);
// Read compressed payload
unsigned char *comp = malloc(comp_len);
if (!comp || fread(comp, 1, comp_len, f) != comp_len) {
free(comp);
break;
}
// Decompress
unsigned char *decomp = NULL;
size_t decomp_len = 0;
if (decompress_data(comp, comp_len, &decomp, &decomp_len) != 0) {
fprintf(stderr, "Decompression failed at offset %ld\n", start);
free(comp);
continue;
}
free(comp);
// Store block
blocks = realloc(blocks, (block_count + 1) * sizeof(Block));
blocks[block_count].offset = (uint32_t)start;
blocks[block_count].data = decomp;
blocks[block_count].size = decomp_len;
block_count++;
}
fclose(f);
// Sort by offset to restore original order
qsort(blocks, block_count, sizeof(Block), compare_blocks);
// Output each block's text (assumes UTF-8) to stdout, separator to stderr
for (size_t i = 0; i < block_count; i++) {
fwrite(blocks[i].data, 1, blocks[i].size, stdout);
fprintf(stderr, "----------------------------------------\n");
free(blocks[i].data);
}
free(blocks);
return 0;
}