aboutsummaryrefslogtreecommitdiff
path: root/crates/fparkan-script/tests/handler2_native_oracle.cs
blob: f632c688cad72a8146cab5625cbdffcf2c24feb7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

internal static class Handler2NativeOracle {
    private const uint LoadWithAlteredSearchPath = 0x00000008;
    private const int RefreshRecordRva = 0x5070;
    private const string ExpectedSha256 = "c19cd6cd31cd2896f8c4ba0f0e700dd3408aec8182daaf4863d98a7d233a2abc";

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern IntPtr LoadLibraryEx(string fileName, IntPtr file, uint flags);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool FreeLibrary(IntPtr module);

    [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
    private delegate void RefreshRecord(IntPtr record);

    private static int Main() {
        if (IntPtr.Size != 4) {
            Console.Error.WriteLine("Build this oracle with an x86 target; ai.dll is 32-bit.");
            return 2;
        }

        string dllPath = Environment.GetEnvironmentVariable("FPARKAN_ORIGINAL_AI_DLL");
        if (String.IsNullOrEmpty(dllPath) || !File.Exists(dllPath)) {
            Console.Error.WriteLine("Set FPARKAN_ORIGINAL_AI_DLL to the original GOG ai.dll.");
            return 3;
        }

        string actualSha256;
        using (SHA256 sha256 = SHA256.Create())
        using (FileStream input = File.OpenRead(dllPath)) {
            byte[] digest = sha256.ComputeHash(input);
            StringBuilder hex = new StringBuilder(digest.Length * 2);
            foreach (byte value in digest) hex.Append(value.ToString("x2"));
            actualSha256 = hex.ToString();
        }
        if (actualSha256 != ExpectedSha256) {
            Console.Error.WriteLine("Unexpected ai.dll SHA-256: " + actualSha256);
            return 4;
        }

        IntPtr module = LoadLibraryEx(dllPath, IntPtr.Zero, LoadWithAlteredSearchPath);
        if (module == IntPtr.Zero) {
            Console.Error.WriteLine("LoadLibraryEx failed: " + Marshal.GetLastWin32Error());
            return 5;
        }

        try {
            var refresh = (RefreshRecord)Marshal.GetDelegateForFunctionPointer(
                IntPtr.Add(module, RefreshRecordRva), typeof(RefreshRecord));
            IntPtr record = Marshal.AllocHGlobal(100);
            try {
                for (int offset = 0; offset < 100; offset += 4) Marshal.WriteInt32(record, offset, 0);
                Marshal.WriteInt32(record, 0x24, 4);
                Marshal.WriteInt32(record, 0x28, 4);
                AssertRefresh(refresh, record, 8);
                AssertRefresh(refresh, record, 12);

                Marshal.WriteInt32(record, 0x24, unchecked((int)UInt32.MaxValue));
                Marshal.WriteInt32(record, 0x28, unchecked((int)UInt32.MaxValue));
                AssertRefresh(refresh, record, UInt32.MaxValue - 1);
                AssertRefresh(refresh, record, UInt32.MaxValue - 2);
            }
            finally { Marshal.FreeHGlobal(record); }
        }
        finally { FreeLibrary(module); }
        return 0;
    }

    private static void AssertRefresh(RefreshRecord refresh, IntPtr record, uint expectedCounter) {
        uint savedIncrement = unchecked((uint)Marshal.ReadInt32(record, 0x28));
        refresh(record);
        uint actualCounter = unchecked((uint)Marshal.ReadInt32(record, 0x24));
        uint actualIncrement = unchecked((uint)Marshal.ReadInt32(record, 0x28));
        Console.WriteLine("record[+0x24]=0x{0:X8} record[+0x28]=0x{1:X8}",
            actualCounter, actualIncrement);
        if (actualCounter != expectedCounter || actualIncrement != savedIncrement) {
            throw new InvalidOperationException(String.Format(
                "Expected +0x24=0x{0:X8}, +0x28 unchanged at 0x{1:X8}.",
                expectedCounter, savedIncrement));
        }
    }
}