User:Effect2/readRMPP.c
From Just Solve the File Format Problem
// "Effect2" at fileformats.archiveteam.org. CC0 (http://creativecommons.org/about/cc0).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <endian.h>
#include <inttypes.h>
#include <errno.h>
typedef struct {
uint32_t typecode; // Always readable in ASCII, as far as has been observed
uint32_t length;
uint32_t seq;
uint32_t offset;
} HeaderEntry;
void freadParanoid(void *ptr, size_t size, size_t nmemb, FILE *stream) {
assert(fread(ptr, size, nmemb, stream) == nmemb);
}
uint32_t readint32_tFromStream(FILE* stream) { // Works for both int and uint, as endianness conversion doesn't care about that
uint32_t orig;
uint32_t i;
freadParanoid(&orig, sizeof(int32_t), 1, stream);
i = le32toh(orig);
return i;
}
// Assumes that the cursor is already in the right place.
long readcftc(FILE* stream, HeaderEntry** returnedEntries) {
char type[4];
freadParanoid(type, 4, 1, stream);
assert(memcmp("cftc", type, 4) == 0);
uint32_t len = readint32_tFromStream(stream);
uint32_t seq = readint32_tFromStream(stream);
assert(seq == 0);
assert(len % 4 == 0);
long numEntries = len / (4 * 4); // 4 fields of 4 bytes each
HeaderEntry* entries = malloc(sizeof(HeaderEntry) * numEntries);
long numActualEntries = 0;
for (long i = 0; i < numEntries; i ++) {
HeaderEntry entry;
entry.typecode = readint32_tFromStream(stream);
entry.length = readint32_tFromStream(stream);
entry.seq = readint32_tFromStream(stream);
entry.offset = readint32_tFromStream(stream);
if (entry.typecode != 0) {
entries[numActualEntries] = entry;
numActualEntries ++;
}
}
*returnedEntries = entries; // This will probably leave some (from samples, it's going to be much less than a kilobyte) of uninitialized memory in the array, but properly behaving code should never read into it
return numActualEntries;
}
int main(int argc, char** argv) {
if (argc != 3) {
printf("Usage: [program] [command] [target file]\n");
printf("command can be a combination of:\n");
printf(" l - List sections in file\n");
printf(" d - Dump sections to the current directory, named offset.type\n");
printf(" f - when using d, Fix the dib sections by removing the 2 empty bytes at the start. Does not remove the space at the end of the file extension.\n");
return 0;
}
int doList = (strchr(argv[1], 'l') != NULL);
int doDump = (strchr(argv[1], 'd') != NULL);
int doFixDibs = (strchr(argv[1], 'f') != NULL);
FILE* f = fopen(argv[2], "r");
if (f == NULL) {
printf("Could not open file, errno %d\n", errno);
return 2;
}
char magic[12];
freadParanoid(magic, 12, 1, f);
if (!(memcmp(magic, "RIFF ", 4) == 0 && memcmp(magic + 8, "RMMP ", 4) == 0)) {
printf("Not an RMMP file\n");
fclose(f);
return 2;
}
HeaderEntry* entries;
long h = readcftc(f, &entries);
if (doList) {
printf("File contains %d entries.\n", h);
}
for (int i = 0; i < h; i ++) {
char* tc = (char*) &(entries[i].typecode);
if (doList) {
printf("%c%c%c%c : length: % 7d seq: % 5d offset: % 7d\n", tc[0], tc[1], tc[2], tc[3], entries[i].length, entries[i].seq, entries[i].offset);
}
if (doDump) {
fseek(f, entries[i].offset + 12, SEEK_SET);
char* tc = (char*) &(entries[i].typecode);
char name[30];
snprintf(name, 30, "%d.%c%c%c%c", entries[i].offset, tc[0], tc[1], tc[2], tc[3]);
FILE* outf = fopen(name, "w");
fseek(f, entries[i].offset + 12, SEEK_SET);
if (doFixDibs && memcmp(tc, "dib ", 4) == 0) {
fseek(f, 2, SEEK_CUR);
}
uint8_t* raw = malloc(entries[i].length);
fread(raw, entries[i].length, 1, f);
fwrite(raw, entries[i].length, 1, outf);
free(raw);
fclose(outf);
}
}
}