/* ReC98
 * -----
 * C++ reimplementation of master.lib's PAR packfile format, including the
 * modifications for TH01
 */

#include "th01/th01.h"
#include <ctype.h>

#define FN_LEN 13
#define MAX_NUM_FILES 64
#define CACHE_SIZE 0x100

typedef struct {
	char type[2]; // •• if RLE-compressed
	char aux; // Always 3, unused
	char fn[FN_LEN];
	int32_t packsize;
	int32_t orgsize;
	int32_t offset; // of the file data within the entire archive
	int32_t reserved; // Always zero
} pf_header_t;

#pragma option -Z -a1

pf_header_t *arc_pfs;
pf_header_t *file_pf;
int file_num;
int arc_num_files;
char file_compressed;
char arc_key;

char *file_data;
char *cache;
char arc_fn[FN_LEN];
unsigned int file_pos;
unsigned int cache_bytes_read;

void pascal arc_open(const char *fn)
{
	int i, c;

	arc_pfs = new pf_header_t[MAX_NUM_FILES];
	file_ropen(fn);
	for(i = 0; i < FN_LEN; i++) {
		arc_fn[i] = fn[i];
		if(fn[i] == 0) {
			break;
		}
	}
	file_read(arc_pfs, sizeof(pf_header_t) * MAX_NUM_FILES);
	file_close();
	for(i = 0; i < MAX_NUM_FILES; i++) {
		if(arc_pfs[i].type[0] == 0) {
			break;
		}
		for(c = 0; c < FN_LEN; c++) {
			if(arc_pfs[i].fn[c] == 0) {
				break;
			}
			arc_pfs[i].fn[c] = ~arc_pfs[i].fn[c];
		}
	}
	arc_num_files = i;
}

void pascal arc_close(void)
{
	delete[] arc_pfs;
}

int pascal near at_pos_of(const char *fn)
{
	int i;
	for(i = 0; i < FN_LEN; i++) {
		if(arc_pfs[file_num].fn[i] != toupper(fn[i])) {
			return 0;
		}
		if(fn[i] == 0) {
			return 1;
		}
	}
	return 0;
}

// Get it? En*crap*tion?
void pascal near crapt(char *buf, size_t size)
{
	size_t i;
	for(i = 0; i < size; i++) {
		buf[i] ^= arc_key;
	}
}

unsigned char pascal near cache_next(void)
{
	char b;
	if(cache_bytes_read == 0) {
		file_read(cache, CACHE_SIZE);
	}
	b = cache[cache_bytes_read];
	b ^= arc_key;
	cache_bytes_read++;
	if(cache_bytes_read >= CACHE_SIZE) {
		cache_bytes_read = 0;
	}
	return b;
}

void pascal near unrle(unsigned int input_size)
{
	unsigned char var_1;
	unsigned char runs;
	unsigned char var_3;
	long bytes_read = 0;
	long bytes_written = 0;
	#define NEXT() \
		cache_next(); \
		bytes_read++;
	var_3 = NEXT();
	while(input_size > bytes_read) {
		do {
			file_data[bytes_written] = var_3;
			var_1 = var_3;
			bytes_written++;
			var_3 = NEXT();
		} while(var_1 != var_3);
		file_data[bytes_written] = var_3;
		while(1) {
			bytes_written++;
			runs = NEXT();
			while(runs > 0) {
				file_data[bytes_written] = var_1;
				bytes_written++;
				runs--;
			}
			var_3 = NEXT();
			if(var_3 != var_1) {
				break;
			}
			file_data[bytes_written] = var_1;
		}
	}
	#undef NEXT()
}

void pascal arc_file_load(const char *fn)
{
	const char rle_type[] = {"••"};
	int i;

	file_num = 0;
	for(i = 0; i < arc_num_files; i++) {
		if(at_pos_of(fn)) {
			break;
		}
		file_num++;
	}
	file_pf = &arc_pfs[file_num];
	file_ropen(arc_fn);
	file_seek(file_pf->offset, SEEK_SET);
	if(file_pf->type[0] == rle_type[0] && file_pf->type[1] == rle_type[1]) {
		file_compressed = 1;
	} else {
		file_compressed = 0;
	}
	file_pos = 0;
	file_data = new char[file_pf->orgsize];
	if(file_compressed) {
		cache = new char[CACHE_SIZE];
		cache_bytes_read = 0;
		unrle(file_pf->packsize);
		delete[] cache;
	} else {
		file_read(file_data, file_pf->packsize);
		crapt(file_data, file_pf->packsize);
	}
	file_close();
}

void pascal arc_file_get(char *buf, size_t size)
{
	char *p = buf;
	size_t i;
	for(i = 0; i < size; i++) {
		if(file_pos >= file_pf->orgsize) {
			break;
		}
		p[i] = file_data[file_pos];
		file_pos++;
	}
}

void pascal arc_file_seek(char pos)
{
	file_pos = pos;
}

void pascal arc_file_free(void)
{
	delete[] file_data;
}

int pascal arc_file_size(void)
{
	return file_pf->orgsize;
}