// Auto Splitter for Jetpack
// by InsaneJetman
// v1.0

state("DOSBOX")
{
    string26 Regular    : 0x193A1A0, 0x626F4;
    string26 Shareware  : 0x193A1A0, 0x63114;
    string26 Christmas  : 0x193A1A0, 0x62314;
    string26 Alpha      : 0x193A1A0, 0x1A514;
    string8  Program    : 0x193A1A0, 0x01918;
}

startup
{
    settings.Add("gem_split", false, "Gem Splits");
    settings.Add("treasure_split", false, "Treasure Splits");
    settings.Add("death_reset", false, "Reset on Death");
}

init
{
    vars.DOSBoxVersions = new Dictionary<string, int> {
        { "0, 74, 3, 0", 0x193C370 },
        { "0, 74, 2, 0", 0x193E370 },
        { "0, 74, 0, 0", 0x193A1A0 }
    };

    int MemoryOffset = 0;
    vars.MemoryStart = IntPtr.Zero;
    if (vars.DOSBoxVersions.TryGetValue(modules.First().FileVersionInfo.FileVersion, out MemoryOffset)) {
        vars.MemoryStart = modules.First().BaseAddress;
        vars.MemoryStart = IntPtr.Add(vars.MemoryStart, MemoryOffset);
    } else {
        throw new InvalidOperationException("Unsupported DOSBox version");
    }

    vars.DOSBoxWatchers = new MemoryWatcherList {
        new MemoryWatcher<ushort>(new DeepPointer(vars.MemoryStart, 0x1871)) { Name = "ProgramSegment" },
        new StringWatcher(new DeepPointer(vars.MemoryStart, 0x1918), 8) { Name = "ProgramName" }
    };
    vars.JetScanWatchers = new MemoryWatcherList {
        new StringWatcher(new DeepPointer(vars.MemoryStart, 0x626F4), 26) { Name = "Regular" },
        new StringWatcher(new DeepPointer(vars.MemoryStart, 0x61FC4), 26) { Name = "Shareware v1.0" },
        new StringWatcher(new DeepPointer(vars.MemoryStart, 0x62DD4), 26) { Name = "Shareware v1.1" },
        new StringWatcher(new DeepPointer(vars.MemoryStart, 0x61C64), 26) { Name = "Shareware v1.2" },
        new StringWatcher(new DeepPointer(vars.MemoryStart, 0x63104), 26) { Name = "Shareware v1.3" },
        new StringWatcher(new DeepPointer(vars.MemoryStart, 0x63114), 26) { Name = "Shareware v1.4" },
        new StringWatcher(new DeepPointer(vars.MemoryStart, 0x62314), 26) { Name = "Christmas" },
        new StringWatcher(new DeepPointer(vars.MemoryStart, 0x1A514), 26) { Name = "Alpha" }
    };

    vars.doInit = (Action)(() => {
        vars.JetpackVersion = "None";
        vars.JetpackWatchers = new MemoryWatcherList {};
        vars.DOSBoxWatchers.UpdateAll(game);
        if (vars.DOSBoxWatchers["ProgramSegment"].Current != 0 && vars.DOSBoxWatchers["ProgramName"].Current == "JETPACK") {
            vars.JetScanWatchers.UpdateAll(game);
            if (vars.JetScanWatchers["Regular"].Current == " WELCOME TO    JETPACK!   ") {
                vars.JetpackVersion = "Regular";
                vars.JetpackWatchers.Add(new MemoryWatcher<int>  (new DeepPointer(vars.MemoryStart, 0x21D19)) { Name = "Score" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x21D69)) { Name = "Level" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x23294)) { Name = "GameReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x232AC)) { Name = "GameGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x232C8)) { Name = "GameDirection" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x23234)) { Name = "EditReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x2324C)) { Name = "EditGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x23268)) { Name = "EditDirection" });
            } else if (vars.JetScanWatchers["Shareware v1.0"].Current == " WELCOME TO    JETPACK!   ") {
                vars.JetpackVersion = "Shareware v1.0";
                vars.JetpackWatchers.Add(new MemoryWatcher<int>  (new DeepPointer(vars.MemoryStart, 0x21B17)) { Name = "Score" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x21B47)) { Name = "Level" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x2320C)) { Name = "GameReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x23224)) { Name = "GameGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x23240)) { Name = "GameDirection" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x2316C)) { Name = "EditReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x23184)) { Name = "EditGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x231A0)) { Name = "EditDirection" });
            } else if (vars.JetScanWatchers["Shareware v1.1"].Current == " WELCOME TO    JETPACK!   ") {
                vars.JetpackVersion = "Shareware v1.1";
                vars.JetpackWatchers.Add(new MemoryWatcher<int>  (new DeepPointer(vars.MemoryStart, 0x228B7)) { Name = "Score" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x22907)) { Name = "Level" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x23FF2)) { Name = "GameReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x2400A)) { Name = "GameGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x24026)) { Name = "GameDirection" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x23F92)) { Name = "EditReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x23FAA)) { Name = "EditGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x23FC6)) { Name = "EditDirection" });
            } else if (vars.JetScanWatchers["Shareware v1.2"].Current == " WELCOME TO    JETPACK!   ") {
                vars.JetpackVersion = "Shareware v1.2";
                vars.JetpackWatchers.Add(new MemoryWatcher<int>  (new DeepPointer(vars.MemoryStart, 0x21737)) { Name = "Score" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x21787)) { Name = "Level" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x22E86)) { Name = "GameReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x22E9A)) { Name = "GameGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x22EB6)) { Name = "GameDirection" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x22E26)) { Name = "EditReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x22E3A)) { Name = "EditGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x22E56)) { Name = "EditDirection" });
            } else if (vars.JetScanWatchers["Shareware v1.3"].Current == " WELCOME TO    JETPACK!   ") {
                vars.JetpackVersion = "Shareware v1.3";
                vars.JetpackWatchers.Add(new MemoryWatcher<int>  (new DeepPointer(vars.MemoryStart, 0x22727)) { Name = "Score" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x22777)) { Name = "Level" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x23CFE)) { Name = "GameReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x23D1A)) { Name = "GameGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x23D36)) { Name = "GameDirection" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x23C9E)) { Name = "EditReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x23CBA)) { Name = "EditGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x23CD6)) { Name = "EditDirection" });
            } else if (vars.JetScanWatchers["Shareware v1.4"].Current == " WELCOME TO    JETPACK!   ") {
                vars.JetpackVersion = "Shareware v1.4";
                vars.JetpackWatchers.Add(new MemoryWatcher<int>  (new DeepPointer(vars.MemoryStart, 0x22737)) { Name = "Score" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x22787)) { Name = "Level" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x23D0E)) { Name = "GameReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x23D2A)) { Name = "GameGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x23D46)) { Name = "GameDirection" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x23CAE)) { Name = "EditReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x23CCA)) { Name = "EditGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x23CE6)) { Name = "EditDirection" });
            } else if (vars.JetScanWatchers["Christmas"].Current == "   ROOKIE       SANTA     ") {
                vars.JetpackVersion = "Christmas";
                vars.JetpackWatchers.Add(new MemoryWatcher<int>  (new DeepPointer(vars.MemoryStart, 0x217B7)) { Name = "Score" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x21807)) { Name = "Level" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x22E72)) { Name = "GameReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x22E8A)) { Name = "GameGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x22EA6)) { Name = "GameDirection" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x22E12)) { Name = "EditReady" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x22E2A)) { Name = "EditGo" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x22E46)) { Name = "EditDirection" });
            } else if (vars.JetScanWatchers["Alpha"].Current == "   BLANKET                ") {
                vars.JetpackVersion = "Alpha";
                vars.JetpackWatchers.Add(new MemoryWatcher<int>  (new DeepPointer(vars.MemoryStart, 0x19264)) { Name = "Score" });
                vars.JetpackWatchers.Add(new MemoryWatcher<short>(new DeepPointer(vars.MemoryStart, 0x19284)) { Name = "Level" });
                vars.JetpackWatchers.Add(new MemoryWatcher<int>  (new DeepPointer(vars.MemoryStart, 0x122F9E5)) { Name = "Playing" });
                vars.JetpackWatchers.Add(new MemoryWatcher<byte> (new DeepPointer(vars.MemoryStart, 0x19349)) { Name = "Door" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x2150E)) { Name = "GameDirection" });
                vars.JetpackWatchers.Add(new MemoryWatcher<uint> (new DeepPointer(vars.MemoryStart, 0x2149A)) { Name = "EditDirection" });
            } else {
                vars.JetpackVersion = "Unknown";
            }
            vars.JetpackWatchers.UpdateAll(game);
        }
        vars.State = -1;
        vars.Score = 0;
        vars.Level = 0;
        vars.ExpectLevelUp = false;
        vars.FirstScan = true;
    });
    vars.doInit();
}

update
{
    if (vars.JetpackVersion != "None") {
        vars.DOSBoxWatchers.UpdateAll(game);
        if (vars.DOSBoxWatchers["ProgramSegment"].Current != 0 && vars.DOSBoxWatchers["ProgramName"].Current == "JETPACK")
            vars.JetpackWatchers.UpdateAll(game);
        else
            vars.JetpackVersion = "None";
    }

    vars.OldState = vars.State;
    vars.OldScore = vars.Score;
    vars.OldLevel = vars.Level;
    Func<uint, bool> EvalDirection = (direction) => {
        short UpDown = (short)(direction & 0xFFFF);
        short LeftRight = (short)(direction >> 16);
        return UpDown >= -1 && UpDown <= 1 && LeftRight >= -1 && LeftRight <= 1;
    };

    switch ((string)vars.JetpackVersion) {
    case "Regular":
    case "Shareware v1.0":
    case "Shareware v1.1":
    case "Shareware v1.2":
    case "Shareware v1.3":
    case "Shareware v1.4":
    case "Christmas":
        if (vars.JetpackWatchers["GameReady"].Current == -1) {
            vars.State = vars.JetpackWatchers["GameGo"].Current;
            if (vars.OldState != 0 && vars.State == 0 && !EvalDirection(vars.JetpackWatchers["GameDirection"].Current)) vars.State = -1;
        } else if (vars.JetpackWatchers["EditReady"].Current == -1) {
            vars.State = vars.JetpackWatchers["EditGo"].Current;
            if (vars.OldState != 0 && vars.State == 0 && !EvalDirection(vars.JetpackWatchers["EditDirection"].Current)) vars.State = -1;
        } else vars.State = -1;
        vars.Score = vars.JetpackWatchers["Score"].Current;
        vars.Level = vars.JetpackWatchers["Level"].Current;
        break;
    case "Alpha":
        if (vars.JetpackWatchers["Playing"].Current == 0x007A7A00) {
            if (vars.JetpackWatchers["Playing"].Old == 0x007A7A00) {
                if (vars.OldState == 1) {
                    if (EvalDirection(((vars.JetpackWatchers["EditDirection"].Old & 0xFFFF) == 0x482D)?
                                      (vars.JetpackWatchers["EditDirection"].Current):
                                      (vars.JetpackWatchers["GameDirection"].Current))) vars.State = 0;
                } else if (vars.OldState == 0 && vars.JetpackWatchers["Door"].Current == 69) vars.State = 4;
            } else vars.State = 1;
        } else {
            vars.State = -1;
        }
        vars.Score = vars.JetpackWatchers["Score"].Current;
        vars.Level = vars.JetpackWatchers["Level"].Current;
        break;
    case "Unknown":
    case "None":
    default:
        vars.doInit();
        vars.Split = false;
        vars.Reset = settings["death_reset"];
        return true;
    }

    vars.Split = false;
    if (vars.State == 4 && vars.OldState == 0) {
        vars.Split = true;
        vars.ExpectLevelUp = true;
    }
    if (vars.Level == vars.OldLevel + 1) {
        if (!vars.ExpectLevelUp) vars.Split = true;
        vars.ExpectLevelUp = false;
    }

    int DeltaScore = vars.Score - vars.OldScore;
    if (DeltaScore == 5) {
        if (settings["gem_split"]) vars.Split = true;
    }
    else if (DeltaScore > 5) {
        if (settings["treasure_split"]) vars.Split = true;
    }

    vars.Reset = false;
    if (settings["death_reset"] && vars.State != 0 && vars.State != 4 && vars.OldState == 0) {
        vars.Reset = true;
    }

    if (vars.FirstScan) { vars.FirstScan = false; return false; }
}

start
{
    return vars.State == 0 && vars.OldState != 0;
}

split
{
    return vars.Split;
}

isLoading
{
    return vars.State != 0;
}

reset
{
    return vars.Reset;
}