using System.Runtime.InteropServices;
namespace ChangeT50.Adapter;
///
/// Native Change.dll ABI assumptions reconstructed from the supplied vendor document.
///
public static class ChangeAbi
{
public const ushort ImageFileMachineI386 = 0x014c;
public const int NativePack = 8;
public static readonly string[] RequiredExports =
[
"CapDBConnect",
"CapDBDiscon",
"CapDownBlacklist",
"CapDownBlacklistCount",
"CapDownCustomerInfo",
"CapGetCustomerByNum",
"CapGetCustomerCount",
"CapGetCustomerList",
"CapGetDepCount",
"CapGetDepList",
"CapGetNBCardInfo",
"CapNBQueryCard",
"CapSetNBCardInfo",
"CapUpload",
"CloseCom",
"OpenCom"
];
public static IReadOnlyList ValidateManagedLayouts()
{
var errors = new List();
CheckSize(68, errors);
CheckSize(120, errors);
CheckSize(320, errors);
CheckSize(268, errors);
var psamIdOffset = Marshal.OffsetOf(nameof(UploadInfoNative.PsamId)).ToInt32();
if (psamIdOffset != 96)
{
errors.Add($"UploadInfoNative.PsamId offset is {psamIdOffset}; expected 96.");
}
return errors;
}
public static void EnsureNativeLoadEnvironment()
{
if (!OperatingSystem.IsWindows())
{
throw new PlatformNotSupportedException(
"Change.dll is a Windows PE32 library. Native loading must run on Windows.");
}
if (RuntimeInformation.ProcessArchitecture != Architecture.X86)
{
throw new BadImageFormatException(
$"Change.dll requires an x86 process; current process is {RuntimeInformation.ProcessArchitecture}.");
}
var layoutErrors = ValidateManagedLayouts();
if (layoutErrors.Count != 0)
{
throw new TypeLoadException(string.Join(" ", layoutErrors));
}
}
private static void CheckSize(int expected, ICollection errors) where T : struct
{
var actual = Marshal.SizeOf();
if (actual != expected)
{
errors.Add($"{typeof(T).Name} size is {actual}; expected {expected}.");
}
}
}
[StructLayout(LayoutKind.Sequential, Pack = ChangeAbi.NativePack)]
public unsafe struct CustomerInfoNative
{
public int CardClass;
public int CustomerId;
public int CardNumber;
public int CardSerialNumber;
public int Status;
public int SubType;
public int TotalAmountCents;
public int MainWalletBalanceCents;
public int MainWalletOperationCount;
public int SubsidyWalletBalanceCents;
public int SubsidyWalletOperationCount;
public fixed byte CardAsn[24];
}
[StructLayout(LayoutKind.Sequential, Pack = ChangeAbi.NativePack)]
public unsafe struct UploadInfoNative
{
public int TerminalId;
public int CustomerId;
public int CardNumber;
public int CardSerialNumber;
public int CardClass;
public fixed byte CardAsn[24];
public int TotalAmountCents;
public int ManagementFeeCents;
public int WalletNumber;
public int OperationAmountCents;
public int RemainingBalanceCents;
public int OperationCount;
public fixed byte TradeTime[24];
public long PsamId;
public int PsamTradeNumber;
public int Tac;
public int Result;
}
[StructLayout(LayoutKind.Sequential, Pack = ChangeAbi.NativePack)]
public unsafe struct DownInfoNative
{
public fixed byte Department[200];
public fixed byte StudentNumber[60];
public fixed byte Name[40];
public int Sex;
public int CustomerId;
public int CardNumber;
public int Status;
public int CardSerialNumber;
}
[StructLayout(LayoutKind.Sequential, Pack = ChangeAbi.NativePack)]
public unsafe struct DownDepartmentInfoNative
{
public fixed byte DepartmentCode1[6];
public fixed byte DepartmentCode2[6];
public fixed byte DepartmentCode3[8];
public fixed byte DepartmentCode4[8];
public fixed byte DepartmentName1[60];
public fixed byte DepartmentName2[60];
public fixed byte DepartmentName3[60];
public fixed byte DepartmentName4[60];
}