#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#ifdef __APPLE__
#include <mach-o/dyld.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

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.263");

#ifdef __APPLE__
		uint32_t length = sizeof(basePath);

		_NSGetExecutablePath(basePath, &length);
		length = strlen(basePath);
#else
		int length = readlink("/proc/self/exe", basePath, sizeof(basePath));

		//		strcpy(basePath, argv[0]);
		//		int length = strlen(basePath);
#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;
		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] == '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
						compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid command line argument", arg);
				}
				else if (arg[1] == 'e')
				{
					emulate = true;
					if (arg[2] == 'p')
						profile = true;
					else if (arg[2] == 't')
						trace = 2;
					else if (arg[2] == 'b')
						trace = 1;
					else if (arg[2] == 'a')
						asserts = true;
				}
				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] == '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);
			}
		}

		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 (compiler->mErrors->mErrorCount == 0)
		{
			strcpy_s(compiler->mVersion, strProductVersion);

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

			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 (!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"))
						strcat_s(includePath, "include/oscar64/");
					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])
				compiler->mCompilationUnits->AddUnit(loc, crtPath, nullptr);

			if (compiler->mCompilerOptions & COPT_TARGET_LZO)
			{
				compiler->BuildLZO(targetPath);
			}
			else if (compiler->ParseSource() && compiler->GenerateCode())
			{
				DiskImage* d64 = nullptr;

				if (diskPath[0])
					d64 = new DiskImage(diskPath);

				compiler->WriteOutputFile(targetPath, d64);

				if (d64)
				{
					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 (emulate)
					compiler->ExecuteCode(profile, trace, asserts);
			}
			else if (compiler->mCompilerOptions & COPT_ERROR_FILES)
			{
				compiler->WriteErrorFile(targetPath);
			}
		}

		if (compiler->mErrors->mErrorCount != 0)
			return 20;
	}
	else
	{
		printf("oscar64 {-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
}
