User:Effect2/readRMPP.c

From Just Solve the File Format Problem
Jump to: navigation, search
// "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 - 4);
			fread(raw, entries[i].length - 4, 1, f);
			fwrite(raw, entries[i].length - 4, 1, outf);
			free(raw);
			fclose(outf);
		}
	}
}

Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox