aboutsummaryrefslogtreecommitdiff
path: root/crates/fparkan-script/tests/handler2_native_oracle.cs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-10-20 00:31:26 +0300
committerValentin Popov <valentin@popov.link>2026-10-20 00:31:26 +0300
commita970974eb086372dc292a69b52b97b343da41e30 (patch)
treef46e41c2fe6edc6f31e62095b687a8fe946ed39c /crates/fparkan-script/tests/handler2_native_oracle.cs
parentda0672bdf04b497df064e685bd1e2da9f9c4d2ca (diff)
downloadfparkan-a970974eb086372dc292a69b52b97b343da41e30.tar.xz
fparkan-a970974eb086372dc292a69b52b97b343da41e30.zip
fix(script): preserve scheduler counter increment
Diffstat (limited to 'crates/fparkan-script/tests/handler2_native_oracle.cs')
-rw-r--r--crates/fparkan-script/tests/handler2_native_oracle.cs87
1 files changed, 87 insertions, 0 deletions
diff --git a/crates/fparkan-script/tests/handler2_native_oracle.cs b/crates/fparkan-script/tests/handler2_native_oracle.cs
new file mode 100644
index 0000000..f632c68
--- /dev/null
+++ b/crates/fparkan-script/tests/handler2_native_oracle.cs
@@ -0,0 +1,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));
+ }
+ }
+}