Skip to content

Instantly share code, notes, and snippets.

@jessechounard
Last active June 14, 2023 08:37
Show Gist options
  • Select an option

  • Save jessechounard/c3fe77537fd6bd79eab6cd09588522ae to your computer and use it in GitHub Desktop.

Select an option

Save jessechounard/c3fe77537fd6bd79eab6cd09588522ae to your computer and use it in GitHub Desktop.
SpeedrunHelper
// This file is for the speedrun community to use as a base for
// their LiveSplit autosplitter. You can set it up to read the
// memory you've layed out, and they can take it from there.
state("GameName") {
}
startup {
vars.scanTarget = new SigScanTarget(0,
"de c0 ad de de c0 37 13" // tag (0x1337c0dedeadc0de)
);
}
init {
var speedrunHelperPtr = IntPtr.Zero;
foreach (var page in game.MemoryPages()) {
var scanner = new SignatureScanner(game, page.BaseAddress, (int)page.RegionSize);
speedrunHelperPtr = scanner.Scan(vars.scanTarget);
if(speedrunHelperPtr != IntPtr.Zero) {
print("[Autosplitter] GameName.SpeedrunHelper Found : " + speedrunHelperPtr.ToString("X"));
break;
}
}
if(speedrunHelperPtr == IntPtr.Zero) {
throw new Exception("[Autosplitter] Can't find signature");
}
vars.gameStarted = new MemoryWatcher<int>(speedrunHelperPtr + 8);
vars.levelComplete = new MemoryWatcher<int>(speedrunHelperPtr + 12);
vars.reset = new MemoryWatcher<int>(speedrunHelperPtr + 16);
vars.watchers = new MemoryWatcherList() {
vars.gameStarted,
vars.levelComplete,
vars.reset
};
}
update {
vars.watchers.UpdateAll(game);
}
start {
return vars.gameStarted.Current == 1 && vars.gameStarted.Old == 0;
}
split {
return vars.levelComplete.Current == 1 && vars.levelComplete.Old == 0;
}
reset {
return vars.reset.Current == 1 && vars.reset.Old == 0;
}
// This file is for game developers looking to make it easier on
// the speedrun community to make an autosplitter for your game
// The same idea would work in any language that supports fixed
// layout of variables in static memory.
//
// Keep in mind, this is all read only, if someone uses a memory
// editor to write to this memory, it won't change the state of
// your game. So feel free to share any state variables here
// that you think external tool developers might want access to.
// (Score, time, number of lives, health, version number, etc.)
// Just document what's in there and let the community build
// a bunch of cool crap!
namespace GameName
{
[StructLayout(LayoutKind.Sequential)]
public static class SpeedrunHelper
{
public static ulong tag = 0x1337c0dedeadc0de;
public static int gameStarted = 0;
public static int levelComplete = 0;
public static int reset = 0;
}
// in your GameName.Program.Main function
private static void Main()
{
// access the static class ASAP to make sure it's in memory and ready to go
SpeedrunHelper.gameStarted = SpeedrunHelper.levelComplete = SpeedrunHelper.reset = 0;
// your Main() startup code
}
// we changes states in the splitter when we see the variables changing states
// set gameStarted to 0 in the main menu, set it to 1 when they hit play
// set levelComplete to 0 when a new level is launched, set it to 1 when the reach the end
// whenever the player chooses to exit early to the main menu set reset to 1
// set it back to 0 once the game starts
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment