128 lines
No EOL
3.6 KiB
C
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;
|
|
} |