state("Avowed-WinGDK-Shipping"){}
state("Avowed-Win64-Shipping"){}

startup
{
    vars.Log = (Action<object>)((output) => print("[Avowed ASL] " + output));

    if (timer.CurrentTimingMethod == TimingMethod.RealTime)
    {
        DialogResult dbox = MessageBox.Show(timer.Form,
            "Avowed uses load removed time.\nWould you like to switch LiveSplit's timing method to that?",
            "LiveSplit | Avowed ASL",
            MessageBoxButtons.YesNo);

        if (dbox == DialogResult.Yes)
        {
            timer.CurrentTimingMethod = TimingMethod.GameTime;
        }
    }
}

init
{
    var scn = new SignatureScanner(game, game.MainModule.BaseAddress, game.MainModule.ModuleMemorySize);
    var syncLoadTrg = new SigScanTarget(5, "89 43 60 8B 05") { OnFound = (p, s, ptr) => ptr + 0x4 + game.ReadValue<int>(ptr) };
    var syncLoadCounter = scn.Scan(syncLoadTrg);
    var localPlayerTrg = new SigScanTarget(3, "48 89 35 ?? ?? ?? ?? 0F 10 0D") { OnFound = (p, s, ptr) => ptr + 0x4 + game.ReadValue<int>(ptr) };
    var localPlayer = scn.Scan(localPlayerTrg);
    var namePoolTrg = new SigScanTarget(7, "8B D9 74 ?? 48 8D 15 ?? ?? ?? ?? EB") { OnFound = (p, s, ptr) => ptr + 0x4 + game.ReadValue<int>(ptr) };
    var namePool = scn.Scan(namePoolTrg);

    if(syncLoadCounter == IntPtr.Zero || localPlayer == IntPtr.Zero || namePool == IntPtr.Zero)
    {
        throw new Exception("One or more base pointers not found - retrying");
    }

    vars.Log("Sync Load Counter: 0x"+syncLoadCounter.ToString("X8"));
    vars.Log("Local Player: 0x"+localPlayer.ToString("X8"));
    vars.Log("Name Pool: 0x"+namePool.ToString("X8"));

    vars.FNameToString = (Func<ulong, string>)(fName =>
    {
        var number   = (fName & 0xFFFFFFFF00000000) >> 0x20;
        var chunkIdx = (fName & 0x00000000FFFF0000) >> 0x10;
        var nameIdx  = (fName & 0x000000000000FFFF) >> 0x00;
        var chunk = game.ReadPointer(namePool + 0x10 + (int)chunkIdx * 0x8);
        var nameEntry = chunk + (int)nameIdx * 0x2;
        var length = game.ReadValue<short>(nameEntry) >> 6;
        var name = game.ReadString(nameEntry + 0x2, length);
        return number == 0 ? name : name + "_" + number;
    });

    vars.Watchers = new MemoryWatcherList
    {
        new MemoryWatcher<int>(new DeepPointer(syncLoadCounter)) { Name = "syncLoadCount" },
        new MemoryWatcher<IntPtr>(new DeepPointer(localPlayer, 0x78, 0x80, 0x220)) { Name = "loadingWidget"},
        new MemoryWatcher<ulong>(new DeepPointer(localPlayer, 0x78, 0x78, 0x18)) { Name = "worldFName"},
    };

    vars.Watchers.UpdateAll(game);
    var worldFName = (ulong)vars.Watchers["worldFName"].Current;
    current.world = old.world = vars.FNameToString(worldFName);
    vars.startAfterLoad = false;
}

update
{
    vars.Watchers.UpdateAll(game);

    var worldFName = (ulong)vars.Watchers["worldFName"].Current;
    current.world = worldFName != 0x0 ? vars.FNameToString(worldFName) : old.world;

    var showingLoadingscreen = (vars.Watchers["loadingWidget"].Current != IntPtr.Zero) && current.world != "MainMenu";
    var isSyncLoading = vars.Watchers["syncLoadCount"].Current > 0;
    current.loading = isSyncLoading || showingLoadingscreen;
}

isLoading
{
    return current.loading;
}

start
{
    if(current.world == "ADR_07_PRO" && old.world == "MainMenu")
    {
        vars.startAfterLoad = true;
    }

    if(vars.startAfterLoad && !current.loading)
    {
        return true;
    }
}
onStart
{
    timer.IsGameTimePaused = current.loading;
    vars.startAfterLoad = false;
}

exit
{
    timer.IsGameTimePaused = true;
}