Content.bin Part B decompresser

From WiiBrew
Jump to navigation Jump to search
// Copyright 2008 Arcnor <arcnorj@yahoo.es>
// Licensed under the terms of the GNU GPL, version 2
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt

#include <fstream>
#include <iostream>

using namespace std;

int Decompress(const char *fileBuf, const unsigned int bufSize, char **newBuffer, unsigned int &newSize) {
	// Check if it's uncompressible
	unsigned char tempbyte;
	unsigned int header = 0;
	if ((*(unsigned int *)fileBuf) == (*(unsigned int *)"LZ77")) {
		// Some LZ77 files have a F7 byte after 'LZ77' that indicates size to be
		// decompressed after it, and size of the compressed data. So, in the end,
		// the header is 0x0A bytes long ;)
		if ((unsigned char)fileBuf[4] == 0xF7)
			header = 0x0A;
		else
			header = 0x04;
	}
	tempbyte = fileBuf[0 + header];
	if (tempbyte != 0x10) {
		throw -1;
	}

	// Take the 4 bytes (unlike NDS)
	newSize = *(unsigned int *)&fileBuf[1 + header];

	// New file buffer (1 byte bigger, in case we're decompressing text, to have it zero-terminated)
	*newBuffer = new char[newSize + 1];
	char *buffer = *newBuffer;

	memset(buffer, 0x00, newSize);

	// Command bit mask
	unsigned char commandMask;
	unsigned char commandSize;
	unsigned int commandOffset;
	unsigned int bufPos = 0;
	
	unsigned int pos = 4 + header + 1;
	while (pos < bufSize) {
		commandMask = fileBuf[pos++];
		for (int j = 0x80; j > 0; j = j >> 1) {
			if (pos < bufSize) {
				if ((commandMask & j) == 0) {
					buffer[bufPos++] = fileBuf[pos++];
				} else { // Command
					commandSize = fileBuf[pos++];
					commandOffset = (int)(unsigned char)fileBuf[pos++] | (int)(unsigned char)(commandSize & 0x0F) << 8;
					commandSize = (commandSize >> 4) + 3;
	
					// FIXME: When found a strange offset, just fill it with 'X'
					if (commandOffset > bufPos) {
						for (int k = 0; k < commandSize; k++) {
							buffer[bufPos++] = 'X';
						}	
					} else {
						for (int k = 0; k < commandSize; k++) {
							buffer[bufPos] = buffer[bufPos - commandOffset - 1];
							bufPos++;
						}
					}
				}
			} else { // if (pos < bufsize)
				break;
			}
		}
	}
	return 0;
}

int main(int argc, char *argv[]) {
	if (argc < 2) {
		cout << "Usage: " << argv[0] << " <XXXX_02_gameinfo.bin>" << endl;
		return 1;
	}

	ifstream fin(argv[1], ifstream::in);

	// Get buffer size
	fin.seekg(0, ios_base::end);
	unsigned int bufSize = (unsigned int)fin.tellg() - 0x20;

	// Fill buffer
	fin.seekg(0x20, ios_base::beg);
	char *buf = new char[bufSize];
	fin.read(buf, bufSize);

	fin.close();

	unsigned int newSize;
	char *newBuf;
	Decompress(buf, bufSize, &newBuf, newSize);

	ofstream fout("DecompGameInfo.bin", ofstream::out);
	fout.write(newBuf, newSize);
	fout.close();
}

See Also

LZ77