// Made by Nikoheart & TheDementedSalad
// Big shoutouts to the Ero for assistance within the Items logic and splitting
// Shoutouts to Rumii & Hntd for their assistance within for all the efforts of finding some of the values needed
// Extra shoutout to Rumii for adding code to finally fix the cutscene issues
state("ProjectNeon-Win64-Shipping", "1.0.0.648-20240516") { 
	byte loading : 0x56062E4;
	byte results : 0x55B7848;
}

state("ProjectNeon-Win64-Shipping", "1.0.1.649-20240527") { 
	byte loading : 0x539D138;
	byte results : 0x55B8848;
}

startup
{
	Assembly.Load(File.ReadAllBytes("Components/asl-help")).CreateInstance("Basic");
	vars.Helper.GameName = "RKGK / Rakugaki";

	dynamic[,] _settings =
	{
		{ "Levels", true, "Levels", null },
			{ "Stage 1", true, "Stage 1", "Levels" },
				{ "Level_1_2", true, "Stage 1 - Urban Rampart", "Stage 1" },
                { "Level_1_3", true, "Stage 1 - Obsidian Street", "Stage 1" },
				{ "Level_1_4", true, "Stage 1 - Capital Square", "Stage 1" },
				{ "Level_1_5", true, "Stage 1 - Mr. Buff", "Stage 1" },
				{ "Level_2_6", true, "Bonus Level - Surf.EXE", "Stage 1" },
			{ "Stage 2", true, "Stage 2", "Levels" },
				{ "Level_2_2", true, "Stage 2 - Assembly Floor", "Stage 2" },
                { "Level_2_3", true, "Stage 2 - Manufacturing Bay", "Stage 2" },
				{ "Level_2_4", true, "Stage 2 - Heat Sink", "Stage 2"},
                { "Level_2_5", true, "Stage 2 - Sunflower Noodle", "Stage 2" },
                { "Level_3_4", true, "Bonus Level - Evade.EXE", "Stage 2" },
				{ "Level_6_3", true, "Stage 2 - Waste Disposal Facility", "Stage 2" },
			{ "Stage 3", true, "Stage 3", "Levels" },
				{ "Level_3_2", true, "Stage 3 - Violet Zone", "Stage 3" },
				{ "Level_3_3", true, "Stage 3 - Crimson Zone", "Stage 3" },
				{ "Level_3_5", true, "Stage 3 - Apex Tower", "Stage 3" },
				{ "Level_3_6", true, "Stage 3 - Top Floors", "Stage 3" },
                { "Level_3_7", true, "Stage 3 - Tentacle Tangle", "Stage 3" },
				{ "Level_4_8", true, "Bonus Level - Focus.EXE", "Stage 3" },
				{ "Level_6_4", true, "Stage 3 - Concrete Playground", "Stage 3" },
			{ "Stage 4", true, "Stage 4", "Levels" },
				{ "Level_4_2", true, "Stage 4 - Dawn Station", "Stage 4" },
				{ "Level_4_3", true, "Stage 4 - Industrial Water Park", "Stage 4" },
				{ "Level_4_4", true, "Stage 4 - Chokepoint", "Stage 4" },
				{ "Level_4_6", true, "Stage 4 - Clockwork Heart", "Stage 4" },
				{ "Level_4_7", true, "Stage 4 - Machine Onslaught", "Stage 4" },
				{ "Level_5_7", true, "Bonus Level - Blindfold.EXE", "Stage 4" },
				{ "Level_6_2", true, "Stage 4 - Waterrise", "Stage 4" },
			{ "Stage 5", true, "Stage 5", "Levels" },
				{ "Level_5_2", true, "Stage 5 - Gravity's Sinkhole", "Stage 5" },
				{ "Level_5_3", true, "Stage 5 - Misterium", "Stage 5" },
				{ "Level_5_4", true, "Stage 5 - Glassfall", "Stage 5" },
				{ "Level_5_5", true, "Stage 5 - Leap of Faith", "Stage 5" },
				{ "Level_5_6", true, "Stage 5 - War-O", "Stage 5" },
				{ "Level_6_7", true, "Bonus Level - Minotaur.EXE", "Stage 5" },
				{ "Level_6_5", true, "Stage 5 - Stairway to the Sky", "Stage 5" },
			{ "Stage 6", true, "Stage 6", "Levels"},
				{ "Level_7_16", true, "Stage 6 - The Digital Depths", "Stage 6" },
				{ "Level_7_3", true, "Stage 6 - The Flying Bastion", "Stage 6" },
				{ "Level_7_4", true, "Stage 6 - Inner Highway", "Stage 6" },
				{ "Level_7_5", true, "Stage 6 - Gray Inferno", "Stage 6" },
				{ "Level_7_6", true, "Stage 6 - Mega Buff", "Stage 6" },
				{ "Level_7_7", true, "Bonus Level - Minotaur.EXE", "Stage 6" },
		{ "AutoReset", false, "Auto Reset when returning to Main Menu", null },
	};

	vars.Helper.Settings.Create(_settings);
	vars.CompletedSplits = new HashSet<string>();
	vars.stopwatch = null;
}

init
{
	byte[] exeMD5HashBytes = new byte[0];
	using (var md5 = System.Security.Cryptography.MD5.Create())
    {
        using (var s = File.Open(modules.First().FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            exeMD5HashBytes = md5.ComputeHash(s);
        }
    }

	var MD5Hash = exeMD5HashBytes.Select(x => x.ToString("X2")).Aggregate((a, b) => a + b);
    vars.MD5Hash = MD5Hash;
    print("MD5: " + MD5Hash);

	switch(MD5Hash){
		case "C0851CDE0F095EC05124EFF834C10617" :
			version = "1.0.0.648-20240516";
			break;
		case "C6A54A424739CF2C086E14F855C18928" :
			version = "1.0.1.649-20240527";
			break;
		default:
			version = "Unknown Version";
            MessageBox.Show(timer.Form,
                "RKGK / Rakugaki Autosplitter Error:\n\n"
                + "This autosplitter does not support this game version.\n"
                + "Please contact Nikoheart (@nikoheart on Discord)\n"
                + "with the following string and the game's version number.\n\n"
                + "MD5Hash: " + MD5Hash + "\n\n"
                + "Defaulting to the most recent known memory addesses...",
                  "RKGK / Rakugaki Autosplitter Error",
                  MessageBoxButtons.OK,
                  MessageBoxIcon.Error);
			break;
	}

	IntPtr gEngine = vars.Helper.ScanRel(3, "48 89 05 ?? ?? ?? ?? 48 85 c9 74 ?? e8 ?? ?? ?? ?? 48 8d 4d");
	IntPtr fNames = vars.Helper.ScanRel(13, "89 5C 24 ?? 89 44 24 ?? 74 ?? 48 8D 15");

	// if (gWorld == IntPtr.Zero || gEngine == IntPtr.Zero || fNames == IntPtr.Zero)
    if (gEngine == IntPtr.Zero || fNames == IntPtr.Zero)
	{
		const string Msg = "Not all required addresses could be found by scanning.";
		throw new Exception(Msg);
	}

	// GWorld.Name
	vars.Helper["GWorldName"] = vars.Helper.Make<ulong>(gEngine, 0x780, 0x78, 0x18);
	
	vars.FNameToString = (Func<ulong, string>)(fName =>
	{
		var nameIdx = (fName & 0x000000000000FFFF) >> 0x00;
		var chunkIdx = (fName & 0x00000000FFFF0000) >> 0x10;
		var number = (fName & 0xFFFFFFFF00000000) >> 0x20;

		IntPtr chunk = vars.Helper.Read<IntPtr>(fNames + 0x10 + (int)chunkIdx * 0x8);
		IntPtr entry = chunk + (int)nameIdx * sizeof(short);

		int length = vars.Helper.Read<short>(entry) >> 6;
		string name = vars.Helper.ReadString(length, ReadStringType.UTF8, entry + sizeof(short));

		return number == 0 ? name : name + "_" + number;
	});

	current.World = "";
}

update
{
	vars.Helper.Update();
	vars.Helper.MapPointers();

	var world = vars.FNameToString(current.GWorldName);
	if (!string.IsNullOrEmpty(world) && world != "None")
		current.World = world;
	if (old.World != current.World) vars.Log("GWorldName: " + current.World.ToString());
	if (old.World != current.World) vars.stopwatch = Stopwatch.StartNew();
}

onStart
{
	vars.stopwatch = Stopwatch.StartNew();
	vars.CompletedSplits.Clear();

	// This makes sure the timer always starts at 0.00
	timer.IsGameTimePaused = true;
}

start
{
	return old.World == "MainMenu" && current.World == "Level_1_2";
}

split
{	
	if (current.World != "Level_7_6")
	{
		if (old.World != current.World && (current.World != "MainMenu" || current.World != "Hideout") && settings.ContainsKey(current.World)
		&& (!vars.CompletedSplits.Contains(current.World)))
		{
			vars.stopwatch = Stopwatch.StartNew();
			vars.CompletedSplits.Add(current.World);
			return true;
		}
	}
	else if (current.World == "Level_7_6")
	{
		if (settings.ContainsKey(current.World) && (!vars.CompletedSplits.Contains(current.World))
			&& old.results == 0 && current.results == 1 && vars.stopwatch.ElapsedMilliseconds >= 30000)
		{
			vars.stopwatch = Stopwatch.StartNew();
			vars.CompletedSplits.Add(current.World);
			return true;
		}
	}
}

isLoading
{
	return current.loading == 0;
}

reset
{
	
	return settings["AutoReset"] && old.World != "MainMenu" && current.World == "MainMenu";
}

exit
{
	//pauses timer if the game crashes
	timer.IsGameTimePaused = true;
}