/*
 * sf_megaextract: An unpacker SimFarm's datafiles.
 *
 * Copyright (c) 2020, David Gow <david@davidgow.net>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * See:
 * 	https://davidgow.net/hacks/sf_megaextract.html
 * 
 * To compile: 	cc -o sf_megaextract sf_megaextract.c
 *
 * To run:	cd <path to SimFarm>
 * 		# MEGADATA.IDX and MEGADATA.EEA present
 * 		./sf_megaextract
 */


#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>


struct megaidx_entry
{
	char filename[12];
	uint32_t unk;
	uint32_t end;
};

#ifndef _WIN32
#include <dirent.h>
#include <sys/stat.h>
#include <strings.h>

/* Case-insensitive fopen() for files in current directory*/
FILE *fcaseopen(const char *filename, const char *mode)
{
	/* Quickly check to see if the file exists with the current case. */
	struct stat fileStat;
	if (!stat(filename, &fileStat))
	{
		return fopen(filename, mode);
	}

	DIR *current_dir = opendir(".");

	/* Search the current directory for matching names. */
	for (struct dirent *dir_entry = readdir(current_dir); dir_entry; dir_entry = readdir(current_dir))
	{
		if (!strcasecmp(dir_entry->d_name, filename))
		{
			/* We've found our file! */
			FILE *f = fopen(dir_entry->d_name, mode);
			closedir(current_dir);
			return f;
		}
	}

	/* We didn't find a matching file. */
	closedir(current_dir);
	return NULL;
}

#else

/* On Windows, it's case-insensitive anyway. */
FILE *fcaseopen(const char *filename, const char *mode)
{
	return fopen(filename, mode);
}

#endif

int main()
{
	int success_count = 0;
	int total_count = 0;
	FILE *index = fcaseopen("MEGADATA.IDX", "rb");
	if (!index)
	{
		fprintf(stderr, "Error: Couldn't open MEGADATA.IDX!\n");
		return -1;
	}
	FILE *datafile = fcaseopen("MEGADATA.EEA", "rb");
	if (!datafile)
	{
		fclose(index);
		fprintf(stderr, "Error: Couldn't open MEGADATA.EEA!\n");
		return -2;
	}
	struct megaidx_entry entry = {0};
	struct megaidx_entry last_entry = {0};
	fread(&entry, sizeof(entry), 1, index);
	do
	{
		last_entry = entry;
		fread(&entry, sizeof(entry), 1, index);
		total_count++;
		printf("Extracting: \"%s\"...\n",last_entry.filename);
		FILE *outfile = fopen(last_entry.filename, "wb");
		if (!outfile)
		{
			fprintf(stderr, "Error: Couldn't open \"%s\"!\n", last_entry.filename);
			continue;
		}
		char *data = malloc(entry.end - last_entry.end);
		fread(data, entry.end - last_entry.end, 1, datafile);
		fwrite(data, entry.end - last_entry.end, 1, outfile);
		fclose(outfile);
		free(data);
		success_count++;
	}
	while (strcmp(last_entry.filename, entry.filename));
	fclose(datafile);
	fclose(index);
	printf("Extracted %d/%d files.\n", success_count, total_count);
	if (success_count != total_count)
		return -3;
	return 0;
}
