#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#ifdef __FreeBSD__
  #include <sys/types.h>
  #include <sys/sysctl.h>
#endif
#include "Compiler.h"
#include "DiskImage.h"
#include <time.h>

#ifdef _WIN64
#include <Dbghelp.h>
#include <psapi.h>
#endif

#ifdef _WIN32
bool GetProductAndVersion(char* strProductName, char* strProductVersion)
{
	// get the filename of the executable containing the version resource
	TCHAR szFilename[MAX_PATH + 1] = { 0 };
	if (GetModuleFileName(NULL, szFilename, MAX_PATH) == 0)
	{
		return false;
	}

	// allocate a block of memory for the version info
	DWORD dummy;
	DWORD dwSize = GetFileVersionInfoSize(szFilename, &dummy);
	if (dwSize == 0)
	{
		return false;
	}

	BYTE* data = new BYTE[dwSize];

	// load the version info
	if (!GetFileVersionInfo(szFilename, NULL, dwSize, data))
	{
		return false;
	}

	// get the name and version strings
	LPVOID pvProductName = NULL;
	unsigned int iProductNameLen = 0;
	LPVOID pvProductVersion = NULL;
	unsigned int iProductVersionLen = 0;

	// replace "040904e4" with the language ID of your resources
	if (!VerQueryValueA(&data[0], "\\StringFileInfo\\000904b0\\ProductName", &pvProductName, &iProductNameLen) ||
		!VerQueryValueA(&data[0], "\\StringFileInfo\\000904b0\\ProductVersion", &pvProductVersion, &iProductVersionLen))
	{
		return false;
	}


	strcpy_s(strProductName, 100, (LPCSTR)pvProductName);
	strcpy_s(strProductVersion, 100, (LPCSTR)pvProductVersion);

	return true;
}
#endif

void writeHelp(void)
{
	printf("-v  : verbose output for diagnostics\n");
	printf("-v2 : more verbose output\n");
	printf("-h  : print help\n");
	printf("-i  : additional include paths\n");
	printf("-ii : set default include path\n");
	printf("-o  : optional output file name\n");
	printf("-rt : alternative runtime library, replaces the crt.c(or empty for none)\n");
	printf("-e  : execute the result in the integrated emulator\n");
	printf("-ep : execute and profile the result in the integrated emulator\n");
	printf("-bc : create byte code for all functions\n");
	printf("-n  : create pure native code for all functions(now default)\n");
	printf("-d  : define a symbol(e.g.NOFLOAT or NOLONG to avoid float / long code in printf)\n");
	printf("-D  : define a symbol in a gcc compliant way(e.g. - D NAME = VALUE)\n");
	printf("-O1 or -O : default optimizations\n");
	printf("-O0 : disable optimizations\n");
	printf("-O2 : more aggressive speed optimizations including auto inline of small functions\n");
	printf("-O3 : aggressive optimization for speed\n");
	printf("-Os : optimize for size\n");
	printf("-Oi : enable auto inline of small functions(part of O2 / O3)\n");
	printf("-Oa : optimize inline assembler(part of O2 / O3)\n");
	printf("-Oa : optimize inline assembler(part of O2 / O3)\n");
	printf("-Oz : enable auto placement of global variables in zero page(part of O3)\n");
	printf("-Op : optimize constant parameters\n");
	printf("-Oo : optimize size using \"outliner\" (extract repeated code sequences into functions)\n");
	printf("-Ox : optimize pointer arithmetic by blocking shorter arrays to not cross page boundaries\n");
	printf("-g  : create source level debug info and add source line numbers to asm listing\n");
	printf("-gp : create source level debug info and add source line numbers to asm listing and static profile data\n");
	printf("-tf : target format, may be prg, crt or bin\n");
	printf("-tm : target machine\n");
	printf("-d64 : create a d64 disk image\n");
	printf("-f  : add a binary file to the disk image\n");
	printf("-fz : add a compressed binary file to the disk image\n");
	printf("-fi : sector skip for data files on disk image\n");
	printf("-xz : extended zero page usage, more zero page space, but no return to basic\n");
	printf("-cid : cartridge type ID, used by vice emulator\n");
	printf("-csub : cartridge sub type\n");
	printf("-cname : cartridge name\n");
	printf("-pp : compile in C++ mode\n");
	printf("-strict : use strict ANSI C parsing(no C++ goodies)\n");
	printf("-psci : use PETSCII encoding for all strings without prefix\n");
	printf("-rmp : generate error files: .error.map and .error.asm when linker fails\n");
}

int main2(int argc, const char** argv)
{
	InitDeclarations();
	InitAssembler();

	if (argc > 1)
	{
		char	basePath[200], crtPath[200], includePath[200], targetPath[200], diskPath[200];
		char	strProductName[100], strProductVersion[200];
		int		dataFileInterleave = 10;

#ifdef _WIN32
			GetProductAndVersion(strProductName, strProductVersion);

			DWORD length = ::GetModuleFileNameA(NULL, basePath, sizeof(basePath));

#else
		strcpy(strProductName, "oscar64");
		strcpy(strProductVersion, "1.32.272");

#ifdef __APPLE__
		uint32_t length = sizeof(basePath);

		_NSGetExecutablePath(basePath, &length);
		length = strlen(basePath);
#else
#ifdef __FreeBSD__
		size_t length = 200;
		int oid[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
		if (sysctl((const int *)&oid[0], 4, basePath, &length, NULL, 0) < 0) {
		  length = 0;
		}
#else
		int length = readlink("/proc/self/exe", basePath, sizeof(basePath));

		//		strcpy(basePath, argv[0]);
		//		int length = strlen(basePath);
#endif
#endif
#endif
		while (length > 0 && basePath[length - 1] != '/' && basePath[length - 1] != '\\')
			length--;

		if (length > 0)
		{
			length--;
			while (length > 0 && basePath[length - 1] != '/' && basePath[length - 1] != '\\')
				length--;
		}

		basePath[length] = 0;

		Compiler* compiler = new Compiler();

		compiler->mCompilerOptions |= COPT_NATIVE;

		Location	loc;

		GrowingArray<const char*>	dataFiles(nullptr);
		GrowingArray<bool>			dataFileCompressed(false);

		bool		emulate = false, profile = false, customCRT = false, asserts = false, iorange = false, showHelp = false, hasSources = false;
		int			trace = 0;

		compiler->mPreprocessor->AddPath(basePath);
		strcpy_s(includePath, basePath);
		strcat_s(includePath, "include");

		targetPath[0] = 0;
		diskPath[0] = 0;

		char	targetFormat[20];
		strcpy_s(targetFormat, "prg");

		char	targetMachine[20];
		strcpy_s(targetMachine, "c64");

		compiler->AddDefine(Ident::Unique("__OSCAR64C__"), "1");
		compiler->AddDefine(Ident::Unique("__STDC__"), "1");
		compiler->AddDefine(Ident::Unique("__STDC_VERSION__"), "199901L");

		bool	defining = false;

		for (int i = 1; i < argc; i++)
		{
			const char* arg = argv[i];
			if (defining)
			{
				defining = false;

				char	def[100];
				int i = 0;
				while (arg[i] && arg[i] != '=')
				{
					def[i] = arg[i];
					i++;
				}
				def[i] = 0;
				if (arg[i] == '=')
					compiler->AddDefine(Ident::Unique(def), _strdup(arg + i + 1));
				else
					compiler->AddDefine(Ident::Unique(def), "");
			}
			else if (arg[0] == '-')
			{
				if (arg[1] == 'i' && arg[2] == '=')
				{
					compiler->mPreprocessor->AddPath(arg + 3);
				}
				else if (arg[1] == 'i' && arg[2] == 'i' && arg[3] == '=')
				{
					strcpy_s(includePath, arg + 4);
				}
				else if (arg[1] == 'f' && arg[2] == '=')
				{
					dataFiles.Push(arg + 3);
					dataFileCompressed.Push(false);
				}
				else if (arg[1] == 'f' && arg[2] == 'z' && arg[3] == '=')
				{
					dataFiles.Push(arg + 4);
					dataFileCompressed.Push(true);
				}
				else if (arg[1] == 'f' && arg[2] == 'i' && arg[3] == '=')
				{
					dataFileInterleave = atoi(arg + 4);
				}
				else if (arg[1] == 'o' && arg[2] == '=')
				{
					strcpy_s(targetPath, arg + 3);
				}
				else if (arg[1] == 'r' && arg[2] == 't' && arg[3] == '=')
				{
					strcpy_s(crtPath, arg + 4);
					customCRT = true;
				}
				else if (arg[1] == 'd' && arg[2] == '6' && arg[3] == '4' && arg[4] == '=')
				{
					strcpy_s(diskPath, arg + 5);
				}
				else if (arg[1] == 't' && arg[2] == 'f' && arg[3] == '=')
				{
					strcpy_s(targetFormat, arg + 4);
				}
				else if (arg[1] == 't' && arg[2] == 'm' && arg[3] == '=')
				{
					strcpy_s(targetMachine, arg + 4);
				}
				else if (arg[1] == 'c' && arg[2] == 'i' && arg[3] == 'd' && arg[4] == '=')
				{
					char	cid[10];
					strcpy_s(cid, arg + 5);
					compiler->mCartridgeID = atoi(cid);
				}
				else if (arg[1] == 'c' && arg[2] == 's' && arg[3] == 'u' && arg[4] == 'b' && arg[5] == '=')
				{
					char	cid[10];
					strcpy_s(cid, arg + 6);
					compiler->mCartridgeSubType = atoi(cid);
				}
				else if (arg[1] == 'c' && arg[2] == 'n' && arg[3] == 'a' && arg[4] == 'm' && arg[5] == 'e' && arg[6] == '=')
				{
					strcpy_s(compiler->mCartridgeName, arg + 7);
				}
				else if (arg[1] == 'n' && arg[2] == 0)
				{
					compiler->mCompilerOptions |= COPT_NATIVE;
				}
				else if (arg[1] == 'b' && arg[2] == 'c' && arg[3] == 0)
				{
					compiler->mCompilerOptions &= ~COPT_NATIVE;
				}
				else if (arg[1] == 'p' && arg[2] == 's' && arg[3] == 'c' && arg[4] == 'i' && arg[5] == 0)
				{
					compiler->mCompilerOptions |= COPT_PETSCII;
					compiler->AddDefine(Ident::Unique("__PETSCII__"), "1");
				}
				else if (arg[1] == 'O')
				{
					if (arg[2] == '0' && !arg[3])
						compiler->mCompilerOptions &= ~(COPT_OPTIMIZE_ALL);
					else if (arg[2] == '1' && !arg[3] || arg[2] == 0)
						compiler->mCompilerOptions |= COPT_OPTIMIZE_DEFAULT;
					else if (arg[2] == '2' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_SPEED;
					else if (arg[2] == '3' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_ALL;
					else if (arg[2] == 's' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_SIZE;
					else if (arg[2] == 'a' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_ASSEMBLER;
					else if (arg[2] == 'i' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_AUTO_INLINE;
					else if (arg[2] == 'i' && arg[3] == 'i' && !arg[4])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_AUTO_INLINE_ALL;
					else if (arg[2] == 'z' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_AUTO_ZEROPAGE;
					else if (arg[2] == 'p' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_CONST_PARAMS;
					else if (arg[2] == 'g' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_GLOBAL;
					else if (arg[2] == 'm' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_MERGE_CALLS;
					else if (arg[2] == 'o' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_OUTLINE;
					else if (arg[2] == 'x' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_PAGE_CROSSING;
					else if (arg[2] == 'M' && !arg[3])
						compiler->mCompilerOptions |= COPT_OPTIMIZE_SELF_MOD;
					else
						compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid command line argument", arg);
				}
				else if (arg[1] == 'e')
				{
					emulate = true;
					int i = 2;
					while (arg[i])
					{
						if (arg[i] == 'p')
							profile = true;
						else if (arg[i] == 't')
							trace = 2;
						else if (arg[i] == 'b')
							trace = 1;
						else if (arg[i] == 'a')
							asserts = true;
						else if (arg[i] == 'i')
							iorange = true;
						i++;
					}
				}
				else if (arg[1] == 'D' && !arg[2])
				{
					defining = true;
				}
				else if (arg[1] == 'd' || arg[1] == 'D')
				{
					char	def[100];
					int i = 2;
					while (arg[i] && arg[i] != '=')
					{
						def[i - 2] = arg[i];
						i++;
					}
					def[i - 2] = 0;
					if (arg[i] == '=')
						compiler->AddDefine(Ident::Unique(def), _strdup(arg + i + 1));
					else
						compiler->AddDefine(Ident::Unique(def), "");
				}
				else if (arg[1] == 'g' && !arg[2])
				{
					compiler->mCompilerOptions |= COPT_DEBUGINFO;
				}
				else if (arg[1] == 'g' && arg[2] == 'p' && !arg[3])
				{
					compiler->mCompilerOptions |= COPT_DEBUGINFO | COPT_PROFILEINFO;
				}
				else if (arg[1] == 'h' && !arg[2])
				{
					showHelp = true;
				}
				else if (arg[1] == 'v')
				{
					compiler->mCompilerOptions |= COPT_VERBOSE;
					if (arg[2] == '1')
						;
					else if (arg[2] == '2')
						compiler->mCompilerOptions |= COPT_VERBOSE2;
					else if (arg[2] == '3')
						compiler->mCompilerOptions |= COPT_VERBOSE2 | COPT_VERBOSE3;
					else if (arg[2])
						compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid command line argument", arg);
				}
				else if (arg[1] == 'x' && arg[2] == 'z' && !arg[3])
				{
					compiler->mCompilerOptions |= COPT_EXTENDED_ZERO_PAGE;
				}
				else if (arg[1] == 'p' && arg[2] == 'p' && !arg[3])
				{
					compiler->mCompilerOptions |= COPT_CPLUSPLUS;
					compiler->AddDefine(Ident::Unique("__cplusplus"), "1");
					compiler->AddDefine(Ident::Unique("__cplusplus__"), "1");
				}
				else if (arg[1] == 's' && arg[2] == 't' && arg[3] == 'r' && arg[4] == 'i' && arg[5] == 'c' && arg[6] == 't' && !arg[7])
				{
					compiler->mCompilerOptions |= COPT_STRICT;
					compiler->AddDefine(Ident::Unique("__strict__"), "1");
					}
				else if (arg[1] == 'r' && arg[2] == 'm' && arg[3] == 'p' && !arg[4])
				{
					compiler->mCompilerOptions |= COPT_ERROR_FILES;
				}
				else
					compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid command line argument", arg);
			}
			else
			{
				if (!targetPath[0])
					strcpy_s(targetPath, argv[i]);

				ptrdiff_t n = strlen(argv[i]);
				if (n > 4 && argv[i][n - 4] == '.' && argv[i][n - 3] == 'c' && argv[i][n - 2] == 'p' && argv[i][n - 1] == 'p')
				{
					compiler->mCompilerOptions |= COPT_CPLUSPLUS;
					compiler->AddDefine(Ident::Unique("__cplusplus"), "1");
					compiler->AddDefine(Ident::Unique("__cplusplus__"), "1");
				}

				compiler->mCompilationUnits->AddUnit(loc, argv[i], nullptr);

				hasSources = true;
			}
		}

		if (compiler->mCompilerOptions & COPT_NATIVE)
		{
			compiler->AddDefine(Ident::Unique("OSCAR_NATIVE_ALL"), "1");
		}


		// REMOVE ME
		// compiler->mCompilerOptions |= COPT_OPTIMIZE_GLOBAL;
		// REMOVE ME

		char	basicStart[10];
		strcpy_s(basicStart, "0x0801");

		if (!strcmp(targetMachine, "c64"))
		{
			compiler->mTargetMachine = TMACH_C64;
			compiler->AddDefine(Ident::Unique("__C64__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "c128"))
		{
			strcpy_s(basicStart, "0x1c01");
			compiler->mTargetMachine = TMACH_C128;
			compiler->AddDefine(Ident::Unique("__C128__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "c128b"))
		{
			strcpy_s(basicStart, "0x1c01");
			compiler->mTargetMachine = TMACH_C128B;
			compiler->AddDefine(Ident::Unique("__C128B__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "c128e"))
		{
			strcpy_s(basicStart, "0x1c01");
			compiler->mTargetMachine = TMACH_C128E;
			compiler->AddDefine(Ident::Unique("__C128E__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "vic20"))
		{
			strcpy_s(basicStart, "0x1001");
			compiler->mTargetMachine = TMACH_VIC20;
			compiler->AddDefine(Ident::Unique("__VIC20__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "vic20+3"))
		{
			strcpy_s(basicStart, "0x0401");
			compiler->mTargetMachine = TMACH_VIC20_3K;
			compiler->AddDefine(Ident::Unique("__VIC20__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "vic20+8"))
		{
			strcpy_s(basicStart, "0x1201");
			compiler->mTargetMachine = TMACH_VIC20_8K;
			compiler->AddDefine(Ident::Unique("__VIC20__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "vic20+16"))
		{
			strcpy_s(basicStart, "0x1201");
			compiler->mTargetMachine = TMACH_VIC20_16K;
			compiler->AddDefine(Ident::Unique("__VIC20__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "vic20+24"))
		{
			strcpy_s(basicStart, "0x1201");
			compiler->mTargetMachine = TMACH_VIC20_24K;
			compiler->AddDefine(Ident::Unique("__VIC20__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "pet"))
		{
			strcpy_s(basicStart, "0x0401");
			compiler->mTargetMachine = TMACH_PET_8K;
			compiler->AddDefine(Ident::Unique("__CBMPET__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "pet16"))
		{
			strcpy_s(basicStart, "0x0401");
			compiler->mTargetMachine = TMACH_PET_16K;
			compiler->AddDefine(Ident::Unique("__CBMPET__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "pet32"))
		{
			strcpy_s(basicStart, "0x0401");
			compiler->mTargetMachine = TMACH_PET_32K;
			compiler->AddDefine(Ident::Unique("__CBMPET__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "plus4"))
		{
			strcpy_s(basicStart, "0x1001");
			compiler->mTargetMachine = TMACH_PLUS4;
			compiler->AddDefine(Ident::Unique("__PLUS4__"), "1");
			compiler->AddDefine(Ident::Unique("__CBM__"), "1");
		}
		else if (!strcmp(targetMachine, "mega65"))
		{
			strcpy_s(basicStart, "0x2001");		
			compiler->mTargetMachine = TMACH_MEGA65;
			compiler->AddDefine(Ident::Unique("__MEGA65__"), "1");
		}
		else if (!strcmp(targetMachine, "x16"))
		{
			strcpy_s(basicStart, "0x0801");
			compiler->mTargetMachine = TMACH_X16;
			compiler->AddDefine(Ident::Unique("__X16__"), "1");
		}
		else if (!strcmp(targetMachine, "nes"))
		{
			compiler->mTargetMachine = TMACH_NES;
		}
		else if (!strcmp(targetMachine, "nes_nrom_h"))
		{
			compiler->mTargetMachine = TMACH_NES_NROM_H;
		}
		else if (!strcmp(targetMachine, "nes_nrom_v"))
		{
			compiler->mTargetMachine = TMACH_NES_NROM_V;
		}
		else if (!strcmp(targetMachine, "nes_mmc1"))
		{
			compiler->mTargetMachine = TMACH_NES_MMC1;
		}
		else if (!strcmp(targetMachine, "nes_mmc3"))
		{
			compiler->mTargetMachine = TMACH_NES_MMC3;
		}
		else if (!strcmp(targetMachine, "atari"))
		{
			compiler->mTargetMachine = TMACH_ATARI;
			compiler->AddDefine(Ident::Unique("__ATARI__"), "1");
		}
		else
			compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid target machine option", targetMachine);


		if (compiler->mTargetMachine >= TMACH_NES && compiler->mTargetMachine <= TMACH_NES_MMC3)
		{
			compiler->mCompilerOptions |= COPT_TARGET_NES;
			compiler->mCompilerOptions |= COPT_EXTENDED_ZERO_PAGE;
			compiler->mCompilerOptions |= COPT_NATIVE;
			compiler->AddDefine(Ident::Unique("OSCAR_TARGET_NES"), "1");
			switch (compiler->mTargetMachine)
			{
			default:
			case TMACH_NES:
			case TMACH_NES_NROM_H:
			case TMACH_NES_NROM_V:
				break;
			case TMACH_NES_MMC1:
				compiler->AddDefine(Ident::Unique("__NES_MMC1__"), "1");
				break;
			case TMACH_NES_MMC3:
				compiler->AddDefine(Ident::Unique("__NES_MMC3__"), "1");
				break;
			}
			compiler->AddDefine(Ident::Unique("__NES__"), "1");
		}
		else if (!strcmp(targetFormat, "prg"))
		{
			compiler->mCompilerOptions |= COPT_TARGET_PRG;
			compiler->AddDefine(Ident::Unique("OSCAR_TARGET_PRG"), "1");
			compiler->AddDefine(Ident::Unique("OSCAR_BASIC_START"), basicStart);
		}
		else if (!strcmp(targetFormat, "crt"))
		{
			compiler->mCompilerOptions |= COPT_TARGET_CRT_EASYFLASH;
			compiler->mCartridgeID = 0x0020;

			compiler->AddDefine(Ident::Unique("OSCAR_TARGET_CRT_EASYFLASH"), "1");
		}
		else if (!strcmp(targetFormat, "crt16"))
		{
			compiler->mCompilerOptions |= COPT_TARGET_CRT16;
			compiler->AddDefine(Ident::Unique("OSCAR_TARGET_CRT16"), "1");
		}
		else if (!strcmp(targetFormat, "crt8"))
		{
			compiler->mCompilerOptions |= COPT_TARGET_CRT8;
			compiler->AddDefine(Ident::Unique("OSCAR_TARGET_CRT8"), "1");
		}
		else if (!strcmp(targetFormat, "bin"))
		{
			compiler->mCompilerOptions |= COPT_TARGET_BIN;
			compiler->AddDefine(Ident::Unique("OSCAR_TARGET_BIN"), "1");
		}
		else if (!strcmp(targetFormat, "lzo"))
		{
			compiler->mCompilerOptions |= COPT_TARGET_LZO;
			compiler->AddDefine(Ident::Unique("OSCAR_TARGET_LZO"), "1");
			compiler->AddDefine(Ident::Unique("OSCAR_BASIC_START"), basicStart);
		}
		else
			compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid target format option", targetFormat);

		if (showHelp)
			writeHelp();

		strcpy_s(compiler->mVersion, strProductVersion);

		if (compiler->mCompilerOptions & COPT_VERBOSE)
		{
			printf("Starting %s %s\n", strProductName, strProductVersion);
		}
		DiskImage* d64;

		if (diskPath[0] != '\0')
			d64 = new DiskImage(diskPath);
		else
			d64 = nullptr;

		if (compiler->mErrors->mErrorCount == 0 && (customCRT || hasSources))
		{
			compiler->RemoveErrorFile(targetPath);

			{
				char dstring[100], tstring[100];
				time_t now = time(NULL);
				struct tm t;
#ifdef _WIN32
				localtime_s(&t, &now);
#else
				localtime_r(&now, &t);
#endif

				strftime(dstring, sizeof(tstring) - 1, "\"%b %d %Y\"", &t);
				strftime(tstring, sizeof(dstring) - 1, "\"%H:%M:%S\"", &t);

				compiler->AddDefine(Ident::Unique("__DATE__"), _strdup(dstring));
				compiler->AddDefine(Ident::Unique("__TIME__"), _strdup(tstring));
			}

			if (compiler->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE)
				compiler->AddDefine(Ident::Unique("OSCAR_OPTIMIZE_SIZE"), "1");


			// Add runtime module

			compiler->mPreprocessor->AddPath(includePath);

			if (hasSources && !customCRT)
			{
				FILE* crtFile;
				char crtFileNamePath[FILENAME_MAX];
				crtFileNamePath[FILENAME_MAX - 1] = '\0';
				strcpy_s(crtFileNamePath, includePath);
				strcat_s(crtFileNamePath, "/crt.h");

				if (fopen_s(&crtFile, crtFileNamePath, "r"))
				{
					strcpy_s(crtFileNamePath, basePath);
					strcat_s(crtFileNamePath, "include/oscar64/crt.h");

					if (!fopen_s(&crtFile, crtFileNamePath, "r")) {
						strcpy_s(includePath, basePath);
						strcat_s(includePath, "include/oscar64/");
						compiler->mPreprocessor->AddPath(includePath);
					}
					else
					{
						printf("Could not locate Oscar64 includes under %s\n", basePath);
						return 20;
					}
				}
				fclose(crtFile);

				strcpy_s(crtPath, includePath);
				strcat_s(crtPath, "/crt.c");
			}

			if (crtPath[0] != '\0')
				compiler->mCompilationUnits->AddUnit(loc, crtPath, nullptr);

			if (compiler->mCompilerOptions & COPT_TARGET_LZO)
			{
				compiler->BuildLZO(targetPath);
			}
			else if (compiler->ParseSource() && compiler->GenerateCode())
			{
				compiler->WriteOutputFile(targetPath, d64);


				if (emulate)
					compiler->ExecuteCode(profile, trace, asserts, iorange);
			}
			else if (compiler->mCompilerOptions & COPT_ERROR_FILES)
			{
				compiler->WriteErrorFile(targetPath);
			}
		}

		if (compiler->mErrors->mErrorCount == 0 && d64 != nullptr)
		{
			for (int i = 0; i < dataFiles.Size(); i++)
			{
				if (!d64->WriteFile(dataFiles[i], dataFileCompressed[i], dataFileInterleave))
				{
					printf("Could not embed disk file %s\n", dataFiles[i]);
					return 20;
				}
			}

			if (!d64->WriteImage(diskPath))
			{
				printf("Could not write disk image %s\n", diskPath);
				return 20;
			}
		}

		if (compiler->mErrors->mErrorCount != 0)
			return 20;
	}
	else
	{
		printf("oscar64 [-h] {-i=includePath} [-o=output.prg] [-rt=runtime.c] [-tf=target] [-tm=machine] [-e] [-n] [-g] [-O(0|1|2|3)] [-pp] {-dSYMBOL[=value]} [-v] [-d64=diskname] {-f[z]=file.xxx} {source.c}\n");

		return 0;
	}

	return 0;
}


#ifdef WIN32
#ifndef _DEBUG
int seh_filter(unsigned int code, struct _EXCEPTION_POINTERS* info)
{

#ifdef _WIN64

#pragma warning( push )
#pragma warning( disable : 4996)

	char buff[256];
	switch (info->ExceptionRecord->ExceptionCode)
	{
	case EXCEPTION_ACCESS_VIOLATION:
		sprintf(buff, "Addressing Error[Permissions do not allow this access]");
		break;

	case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
		sprintf(buff, "Addressing Error[Array Bounds Exceeded]");
		break;

	case EXCEPTION_BREAKPOINT:
		sprintf(buff, "Breakpoint Instruction Encountered");
		break;

	case EXCEPTION_DATATYPE_MISALIGNMENT:
		sprintf(buff, "Addressing Error[Data Access Misaligned]");
		break;

	case EXCEPTION_FLT_DENORMAL_OPERAND:
		sprintf(buff, "Arithmetic Exception[Float Denormal Operand]");
		break;

	case EXCEPTION_FLT_DIVIDE_BY_ZERO:
		sprintf(buff, "Arithmetic Exception[Float Divide-by-zero]");
		break;

	case EXCEPTION_FLT_INEXACT_RESULT:
		sprintf(buff, "Arithmetic Exception[Float Inexact Result]");
		break;

	case EXCEPTION_FLT_INVALID_OPERATION:
		sprintf(buff, "Arithmetic Exception[Float Invalid Operation]");
		break;

	case EXCEPTION_FLT_OVERFLOW:
		sprintf(buff, "Arithmetic Exception[Float Overflow]");
		break;

	case EXCEPTION_FLT_STACK_CHECK:
		sprintf(buff, "Arithmetic Exception[Float Stack Check]");
		break;

	case EXCEPTION_FLT_UNDERFLOW:
		sprintf(buff, "Arithmetic Exception[Float Underflow]");
		break;

	case EXCEPTION_ILLEGAL_INSTRUCTION:
		sprintf(buff, "Illegal Instruction");
		break;

	case EXCEPTION_IN_PAGE_ERROR:
		sprintf(buff, "Memory Paging Error");
		break;

	case EXCEPTION_INT_DIVIDE_BY_ZERO:
		sprintf(buff, "Arithmetic Exception[Integer Divide-by-zero]");
		break;

	case EXCEPTION_INT_OVERFLOW:
		sprintf(buff, "Arithmetic Exception[Integer Overflow]");
		break;

	case EXCEPTION_INVALID_DISPOSITION:
		sprintf(buff, "Exception Handler Invalid Disposition");
		break;

	case EXCEPTION_NONCONTINUABLE_EXCEPTION:
		sprintf(buff, "Thread Attempted To Continue When Not Allowed");
		break;

	case EXCEPTION_PRIV_INSTRUCTION:
		sprintf(buff, "Illegal Instruction[Privileged Op code]");
		break;

	case EXCEPTION_SINGLE_STEP:
		sprintf(buff, "Single Step Executed");
		break;

	case EXCEPTION_STACK_OVERFLOW:
		sprintf(buff, "Stack Fault[overfolow]");
		break;

	default:
		sprintf(buff, "Unknown code 0x%-8.8lx", info->ExceptionRecord->ExceptionCode);
		break;
	}

	printf("%s at location 0x%016llx\n", buff, info->ContextRecord->Rip);

	DWORD  err;
	HANDLE hProcess;
	ULONG64	buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64) + 20];
	PSYMBOL_INFO		pSymbol = (PSYMBOL_INFO)buffer;
	IMAGEHLP_LINE64		line = { sizeof(IMAGEHLP_LINE64) };
	DWORD64 displacement;
	DWORD	loffset;

	SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
	hProcess = GetCurrentProcess();

	BOOL printSymbols = SymInitialize(hProcess, NULL, TRUE);
	if (!printSymbols)
	{
		// SymInitialize failed
		err = GetLastError();
		printf("SymInitialize returned error : %d\n", err);
		return EXCEPTION_EXECUTE_HANDLER;
	}

	STACKFRAME64 stackFrame = { 0 };
	CONTEXT context = *info->ContextRecord;
	stackFrame.AddrPC.Offset = info->ContextRecord->Rip;
	stackFrame.AddrPC.Segment = 0;
	stackFrame.AddrPC.Mode = AddrModeFlat;
	stackFrame.AddrFrame.Offset = info->ContextRecord->Rbp;
	stackFrame.AddrFrame.Segment = 0;
	stackFrame.AddrFrame.Mode = AddrModeFlat;
	stackFrame.AddrFrame.Offset = info->ContextRecord->Rsp;
	stackFrame.AddrStack.Mode = AddrModeFlat;
	stackFrame.AddrStack.Segment = 0;

	while (StackWalk64(IMAGE_FILE_MACHINE_AMD64,
		GetCurrentProcess(),
		GetCurrentThread(),
		&stackFrame,
		&context,
		NULL, /* PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine */
		SymFunctionTableAccess64,
		SymGetModuleBase64,
		NULL /* PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress */
	))
	{
		if (true)
		{
			printf("...StackFrame\n");
			printf("......PC address 0x%016llx\n", stackFrame.AddrPC.Offset);
			printf("......Module address 0x%016llx\n", SymGetModuleBase64(GetCurrentProcess(), stackFrame.AddrPC.Offset));
			printf("......Return address 0x%016llx\n", stackFrame.AddrReturn.Offset);
			printf("......Frame address 0x%016llx\n", stackFrame.AddrFrame.Offset);
			printf("......Stack address 0x%016llx\n", stackFrame.AddrStack.Offset);
			printf("......Param[0] 0x%016llx\n", stackFrame.Params[0]);
			printf("......Param[1] 0x%016llx\n", stackFrame.Params[1]);
			printf("......Param[2] 0x%016llx\n", stackFrame.Params[2]);
			printf("......Param[3] 0x%016llx\n", stackFrame.Params[3]);
		}
		pSymbol->MaxNameLen = MAX_SYM_NAME;
		pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);;

		if (SymFromAddr(hProcess, stackFrame.AddrPC.Offset, &displacement, pSymbol))
		{
			if (SymGetLineFromAddr64(hProcess, stackFrame.AddrPC.Offset, &loffset, &line))
			{
				printf("...Procedure %s:0x%-8.8lx LINE %s.%d\n", pSymbol->Name, (DWORD)displacement, line.FileName, line.LineNumber);
			}
			else
			{
				printf("...Procedure %s:0x%-8.8lx\n", pSymbol->Name, (DWORD)displacement);
			}
		}
		else
		{
			err = GetLastError();
			printf("Call to SymEnumSymbolsForAddr failed, error = %d (0x%-8.8x)\n", err, err);
		}
	}
	printf("...Returning EXCEPTION_EXECUTE_HANDLER\n");

#pragma warning( pop )

//	printf("oscar64 crashed. %08x %08llx", info->ExceptionRecord->ExceptionCode, (uint64)(info->ExceptionRecord->ExceptionAddress));
#else
	printf("oscar64 crashed. %08x %08x", info->ExceptionRecord->ExceptionCode, (uint32)(info->ExceptionRecord->ExceptionAddress));
#endif
	return EXCEPTION_EXECUTE_HANDLER;
}
#endif
#endif

int main(int argc, const char** argv)
{
#if 1
#ifdef _WIN32
#ifndef __GNUC__
#ifndef _DEBUG
	__try 
	{
#endif
#endif
#endif
#endif
		return main2(argc, argv);

#if 1
#ifdef _WIN32
#ifndef __GNUC__
#ifndef _DEBUG
}
	__except (seh_filter(GetExceptionCode(), GetExceptionInformation()))
	{
		return 30;
	}
#endif
#endif
#endif
#endif
}
