/**
 ** MULTI-GAME DISC MAKER FOR GCOS Wii EDITION v0.3
 ** ===============================================
 **/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include "GCM.H"
#define HEADER_LEN	64
#define	MAX_GAMES		32
#define SWAP32(A) ((((A) & 0xff000000) >> 24) | (((A) & 0x00ff0000) >> 8) | (((A) & 0x0000ff00) << 8) | (((A) & 0x000000ff) << 24)) 

static unsigned char header[]={
	0x47,0x47,0x43,0x4f,0x53,0x44,0x56,0x44,0x01,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0xc2,0x33,0x9f,0x3d,0x47,0x43,0x4f,0x53,0x20,0x4d,0x75,
	0x6c,0x74,0x69,0x47,0x61,0x6d,0x65,0x20,0x44,0x56,0x44,0x20,0x28,
	0x43,0x29,0x20,0x47,0x43,0x4f,0x53,0x20,0x54,0x45,0x41,0x4d,0x4d
};

unsigned long gameOffsets[MAX_GAMES]; //endian swapped shite for win32
unsigned long gameOffsetsnorm[MAX_GAMES]; //big endian
void banner() {
	fprintf(stdout, "\n  ============================================== \n"
					  "      GCOS MULTI SELFBOOT GAMEDISC MAKER v0.3    \n"
					  "             (Now with Audio Fix!)               \n"
					  "  ============================================== \n\n");
					  
}

void usage(char *prog) {
	fprintf(stderr, "  Usage  : %s <output> <region> <game1> <game2> <etc>\n"
					"  Example: %s multidisc.gcm PAL zelda.gcm mario.gcm\n"
					"  Example: %s multidisc.gcm JAP zelda.gcm mario.gcm\n"
					"  Example: %s multidisc.gcm USA zelda.gcm mario.gcm\n\n", prog,prog,prog,prog);
}

int main(int argc, char *argv[]) {
	FILE *fileOut, *fileIn;
	struct stat fileInfo;
	unsigned long curOffset = 0x40000, oldOffset = 0x00;
	unsigned char readBuf[2 * 2048];
	unsigned int numGames = 0, nBytes = 0;
	int i = 0, j = 0, ok = 0;
	unsigned long fsize = 0x40000;
	unsigned long long finalOffset = 0;

	banner();
	
	if (argc < 3) {
		usage(argv[0]);
		return -1;
	}
	
	memset(gameOffsets, 0x00, (MAX_GAMES * 4));
	
	for (i = 3; i < argc; i++) {
		if (stat(argv[i], &fileInfo) == -1) {
			printf("ERROR: File not found! (%s)\n", argv[i]);
			return -1;
		}
		
		gameOffsetsnorm[i - 3] = curOffset;
		gameOffsets[i - 3] = SWAP32(curOffset);
		numGames++;
		
	// DEBUG    printf("GAME %d - SIZE: %08X - OFFSET: %08X - %s\n", i, fileInfo.st_size, SWAP32(curOffset), argv[i]);

		oldOffset = curOffset;
		
		curOffset += (fileInfo.st_size);
		curOffset += (0x40000 - (curOffset % 0x20000));
	}

	if ((fileOut = fopen(argv[1], "wb")) == NULL) {
		fprintf(stderr, "    - ERROR opening output file! (%s)\n\n", argv[1]);
		return -1;
	}

	// Write Header
	fwrite(header, 64, 1, fileOut);
	
	// Write Offsets
	for (i = 0; i < numGames; i++) {
		fwrite(&gameOffsets[i], 4, 1, fileOut);	
	}
	
	// Modify Data for Region prior to writing
	if (strncmp((const char*)argv[2], "PAL", 3) == 0)
	{
		printf("  Setting ISO to PAL\n");
		GCMdata[0x45b] = 0x02;
		ok = 1;
	}
	if (strncmp((const char*)argv[2], "USA", 3) == 0)
	{
		printf("  Setting ISO to USA\n");
		GCMdata[0x45b] = 0x01;
		ok = 1;
	}
	if (strncmp((const char*)argv[2], "JAP", 3) == 0)
	{
		printf("  Setting ISO to JAP\n");
		GCMdata[0x45b] = 0x00;
		ok = 1;
	}
	if(ok == 0)
	{
		printf("  Specify Region!\n");
		usage(argv[0]);
		return -1;
	}
	// Write GCOS in there for self-boot
	fwrite(GCMdata+64+(numGames*4), GCMDATA_LEN, 1, fileOut);
	
	// Write dummy data up to just before first GCM
	for (i = 0; i < (0x40000 - ((numGames * 4) + 64 + GCMDATA_LEN)); i++) {
		fputc(0x00, fileOut);
	}
	
	// Add Games
	for (i = 0; i < numGames; i++) {
		printf("\n [+] Adding \"%s\" .. ", argv[3 + i]);	fflush(stdout);
		fileIn = fopen64(argv[3 + i], "rb");

		nBytes = 0;
		
		while(!feof(fileIn)) {
				memset(readBuf, 0x00, (2 * 2048));		
				fread(readBuf, 2 * 2048, 1, fileIn);
				fwrite(readBuf, 2 * 2048, 1, fileOut);
				nBytes += (2 * 2048);
		}

		fclose(fileIn);
		fsize = nBytes + gameOffsetsnorm[i];
		gameOffsetsnorm[numGames] = gameOffsetsnorm[numGames-1] + nBytes;
		gameOffsetsnorm[numGames] += (0x20000 - (gameOffsetsnorm[numGames] % 0x20000));
		printf("DONE! (Wrote %d bytes)\n", nBytes);


			/* DEBUG 
			printf("    - FileSize: 0x%08X nBytes: %08X\n",fsize,nBytes+0x20000);
			printf("    - PADDING with: 0x%08X bytes at 0x%08X\n", (gameOffsetsnorm[i+1] - fsize), nBytes+0x20000);
			*/
			// Add proper amount of padding ..
			for (j = 0; j < gameOffsetsnorm[i+1] - fsize; j++) {
				fputc(0xFF, fileOut);
			}
			/* DEBUG 
			fseek(fileOut, 0, SEEK_END);
    		fsize = ftell(fileOut);
			printf("    - FileSize: 0x%08X nBytes: %08X\n\n",fsize,nBytes+0x20000);
			*/
	}

	
	fclose(fileOut);
	

	printf("DONE!\n");
	
	return 0;
	
}
