state("Towers-Win64-Shipping") {} state("JTTSP") {} state("JTTSP", "Epic Games") { bool isLoading: "JTTSP.exe", 0x03AAF7F0, 0x8, 0x20, 0x148, 0x2A8, 0x80, 0x210, 0x1C0; } state("Towers-Win64-Shipping", "Steam") { bool isLoading: "Towers-Win64-Shipping.exe", 0x03B317F0, 0x8, 0x128, 0x2A8, 0x50, 0x20, 0xC30, 0x1C0; } state("Towers-Win64-Shipping", "GOG 1.0.9") { bool isLoading: "Towers-Win64-Shipping.exe", 0x03B967F0, 0x8, 0x128, 0xB90, 0x228, 0x16F8, 0xE0, 0x1688; } state("Towers-Win64-Shipping", "GOG 1.0.10") { bool isLoading: "Towers-Win64-Shipping.exe", 0x03B967F0, 0x8, 0x128, 0xB48, 0xE0, 0xC30, 0x80, 0x1C0; } startup { settings.Add("ResetGroup", true, "Reset"); settings.Add("ResetOnMainMenu", false, "Reset on Main Menu", "ResetGroup"); settings.Add("SplitGroup", true, "Splits"); settings.Add("SingleSplit", false, "Single Split (Placeholder)", "SplitGroup"); if (timer.CurrentTimingMethod == TimingMethod.RealTime) { var timingMessage = MessageBox.Show( "This Game Removes Loads with (Game Time)\n" + "LiveSplit is Currently set to Show Real Time (RTA).\n" + "Would you like to set the timing method to Game Time?", "Journey to the Savage Planet (JTTSP) Autosplitter", MessageBoxButtons.YesNo ); if (timingMessage == DialogResult.Yes) { timer.CurrentTimingMethod = TimingMethod.GameTime; } } vars.StartArmed = false; vars.RunStarted = false; vars.CurrentIsInFrontEnd = false; vars.PreviousIsInFrontEnd = false; vars.Ready = false; vars.PlayerStatus = (byte)0; vars.OldPlayerStatus = (byte)0; vars.WaitingState = (byte)0; vars.OldWaitingState = (byte)0; vars.CinematicState = (byte)0; vars.OldCinematicState = (byte)0; vars.ScriptedMenuState = (byte)0; vars.OldScriptedMenuState = (byte)0; vars.HasTakenControl = (byte)0; vars.CurrentGameModeClass = 0L; vars.FrontEndGameModeClass = 0L; } init { vars.StartArmed = false; vars.RunStarted = false; vars.CurrentIsInFrontEnd = false; vars.PreviousIsInFrontEnd = false; vars.Ready = false; string md5Hash; using (var md5 = System.Security.Cryptography.MD5.Create()) using (var stream = File.Open(modules.First().FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { md5Hash = string.Concat(md5.ComputeHash(stream).Select(x => x.ToString("X2"))); } switch (md5Hash) { case "2EDA192546747F75352571F507D729B5": version = "GOG 1.0.9"; break; case "D30428E963EFFC2255B8BDA13AA627D9": version = "GOG 1.0.10"; break; case "DE33449F9F6F60581B75FD78A0752456": version = "Steam"; break; case "1730FC7FACA5F1392B544CCC15A382D7": version = "Epic Games"; break; default: version = "Unknown"; break; } var scan = new SignatureScanner(game, modules.First().BaseAddress, modules.First().ModuleMemorySize); var gWorldTargetA = new SigScanTarget(3, "48 8B 1D ?? ?? ?? ?? 48 85 DB 74 ?? 41 B0 01"); IntPtr gWorldRef = scan.Scan(gWorldTargetA); if (gWorldRef == IntPtr.Zero) { var gWorldTargetB = new SigScanTarget(3, "48 8B 05 ?? ?? ?? ?? 48 3B C8 48 85 C0 74 05"); gWorldRef = scan.Scan(gWorldTargetB); } if (gWorldRef == IntPtr.Zero && version == "Steam") { vars.GWorld = (IntPtr)((long)modules.First().BaseAddress + 0x42C05B8); } else if (gWorldRef != IntPtr.Zero) { int relativeOffset = memory.ReadValue(gWorldRef); vars.GWorld = gWorldRef + relativeOffset + 4; } else { throw new Exception("GWorld pointer not found, retrying..."); } vars.Ready = true; } update { if (!vars.Ready) return false; vars.PreviousIsInFrontEnd = vars.CurrentIsInFrontEnd; vars.OldPlayerStatus = vars.PlayerStatus; vars.OldWaitingState = vars.WaitingState; vars.OldCinematicState = vars.CinematicState; vars.OldScriptedMenuState = vars.ScriptedMenuState; try { IntPtr world = memory.ReadValue((IntPtr)vars.GWorld); if (world == IntPtr.Zero) return false; IntPtr gameState = memory.ReadValue(world + 0x148); IntPtr gameMode = memory.ReadValue(world + 0x140); IntPtr gameInstance = memory.ReadValue(world + 0x190); vars.CurrentGameModeClass = gameMode != IntPtr.Zero ? memory.ReadValue(gameMode + 0x10) : 0L; vars.FrontEndGameModeClass = gameInstance != IntPtr.Zero ? memory.ReadValue(gameInstance + 0x120) : 0L; if (gameState != IntPtr.Zero) { IntPtr playerStatus = gameState + 0x5C0; vars.PlayerStatus = memory.ReadValue(playerStatus + 0x18); vars.WaitingState = memory.ReadValue(playerStatus + 0x32); vars.CinematicState = memory.ReadValue(playerStatus + 0x33); vars.ScriptedMenuState = memory.ReadValue(playerStatus + 0x34); vars.HasTakenControl = memory.ReadValue(playerStatus + 0x3E); } else { vars.PlayerStatus = 0; vars.WaitingState = 0; vars.CinematicState = 0; vars.ScriptedMenuState = 0; vars.HasTakenControl = 0; } } catch { return false; } vars.CurrentIsInFrontEnd = vars.CurrentGameModeClass != 0 && vars.FrontEndGameModeClass != 0 && vars.CurrentGameModeClass == vars.FrontEndGameModeClass; if (vars.PreviousIsInFrontEnd && !vars.CurrentIsInFrontEnd) { vars.StartArmed = true; } if (vars.CurrentIsInFrontEnd && !vars.RunStarted) { vars.StartArmed = false; } return true; } start { // The run should begin once we have left the front end and the intro // handoff back to gameplay has actually completed. bool leftIntroState = (vars.OldPlayerStatus == 7 && vars.PlayerStatus == 2) || (vars.OldWaitingState != 0 && vars.WaitingState == 0) || (vars.OldCinematicState != 0 && vars.CinematicState == 0) || (vars.OldScriptedMenuState != 0 && vars.ScriptedMenuState == 0); return vars.StartArmed && !vars.CurrentIsInFrontEnd && leftIntroState && vars.HasTakenControl != 0 && vars.WaitingState == 0 && vars.CinematicState == 0 && vars.ScriptedMenuState == 0; } onStart { vars.StartArmed = false; vars.RunStarted = true; } split { return false; } reset { if (!settings["ResetOnMainMenu"] || !vars.RunStarted) return false; return !vars.PreviousIsInFrontEnd && vars.CurrentIsInFrontEnd; } onReset { vars.StartArmed = false; vars.RunStarted = false; } exit { vars.StartArmed = false; vars.RunStarted = false; vars.CurrentIsInFrontEnd = false; vars.PreviousIsInFrontEnd = false; vars.Ready = false; } isLoading { if (version == "Unknown") return false; return current.isLoading; }