Add native Durak, poker, blackjack, roulette, Buckshot and billiards scenes with animation assets and regression tests. Improve arrival, city, hall, profiles, shops, audio and exit handling; remove the portable C game core and refresh shipping executables.
95 lines
4.6 KiB
Python
95 lines
4.6 KiB
Python
"""Verify that shipping card handlers match the native regression-test handlers."""
|
|
import struct
|
|
from pathlib import Path
|
|
|
|
from capstone import Cs, CS_ARCH_X86, CS_MODE_32
|
|
from capstone.x86_const import X86_OP_IMM
|
|
|
|
|
|
class Coff:
|
|
def __init__(self, path):
|
|
self.data = path.read_bytes()
|
|
_, count, _, symbol_at, total, optional, _ = struct.unpack_from("<HHIIIHH", self.data)
|
|
strings = symbol_at + total * 18
|
|
self.symbols = {}
|
|
self.functions = {}
|
|
index = 0
|
|
while index < total:
|
|
at = symbol_at + index * 18
|
|
zero, offset = struct.unpack_from("<II", self.data, at)
|
|
if zero == 0:
|
|
end = self.data.index(0, strings + offset)
|
|
name = self.data[strings + offset:end].decode("ascii")
|
|
else:
|
|
name = self.data[at:at + 8].split(b"\0")[0].decode("ascii")
|
|
value, section, kind, storage, aux = struct.unpack_from("<IhHBB", self.data, at + 8)
|
|
self.symbols[index] = name
|
|
if kind == 0x20 and storage == 2 and section > 0:
|
|
self.functions[name] = (section, value)
|
|
index += 1 + aux
|
|
self.sections = {}
|
|
for section in range(1, count + 1):
|
|
at = 20 + optional + (section - 1) * 40
|
|
size, raw, reloc, _, reloc_count = struct.unpack_from("<IIIIH", self.data, at + 16)
|
|
entries = [struct.unpack_from("<IIH", self.data, reloc + i * 10)
|
|
for i in range(reloc_count)]
|
|
self.sections[section] = (raw, size, entries)
|
|
|
|
def signature(self, name):
|
|
section, start = self.functions[name]
|
|
raw, size, entries = self.sections[section]
|
|
end = min([value for sec, value in self.functions.values()
|
|
if sec == section and value > start] or [size])
|
|
relocations = {at: (self.symbols[symbol], kind) for at, symbol, kind in entries
|
|
if start <= at < end}
|
|
targets = {value: symbol for symbol, (sec, value) in self.functions.items()
|
|
if sec == section}
|
|
decoder = Cs(CS_ARCH_X86, CS_MODE_32)
|
|
decoder.detail = True
|
|
result = []
|
|
for ins in decoder.disasm(self.data[raw + start:raw + end], start):
|
|
code = bytearray(ins.bytes)
|
|
refs = []
|
|
for at, reference in relocations.items():
|
|
if ins.address <= at < ins.address + ins.size:
|
|
offset = at - ins.address
|
|
code[offset:offset + 4] = b"\0" * 4
|
|
refs.append((offset, reference))
|
|
target = None
|
|
if ins.mnemonic == "call" and not refs and ins.operands[0].type == X86_OP_IMM:
|
|
target = targets[ins.operands[0].imm]
|
|
code[ins.imm_offset:ins.imm_offset + ins.imm_size] = b"\0" * ins.imm_size
|
|
result.append((bytes(code), tuple(refs), target))
|
|
assert sum(len(code) for code, _, _ in result) == end - start, name
|
|
return result
|
|
|
|
|
|
def main():
|
|
root = Path(__file__).resolve().parents[2]
|
|
tested = Coff(root / "build/actions/actions-test.obj")
|
|
shipped = Coff(root / "build/graphics/poker.obj")
|
|
functions = ("CardTableMouse3D", "LeaveCardTable3D", "RequestCardExit3D",
|
|
"CardTableEscape3D", "HandleCardExit3D", "RenderCardExit3D",
|
|
"HandleNativePoker3D", "HandleNativeBlackjack3D",
|
|
"RenderNativePoker3D", "RenderNativeBlackjack3D", "StartCasino3D",
|
|
"InitNativeDurak3D", "StartNativeDurak3D", "DurakBeginBout3D",
|
|
"DurakCommitFlight3D", "DurakResolveBout3D", "DurakAITurn3D",
|
|
"DurakClampSelection3D", "DurakMouse3D",
|
|
"HandleNativeDurak3D", "RenderNativeDurak3D")
|
|
for name in functions:
|
|
suffix = "@4" if name == "CardTableMouse3D" else "@0"
|
|
symbol = "_" + name + suffix
|
|
assert tested.signature(symbol) == shipped.signature(symbol), name
|
|
for name, size in (("NativeDurakSettle3D", 4), ("DurakCanTransfer3D", 8),
|
|
("DurakCanPlay3D", 12), ("DurakPlay3D", 12),
|
|
("DurakTake3D", 4), ("DurakFinish3D", 4),
|
|
("DurakRefill3D", 4), ("DurakCardValue3D", 4),
|
|
("DurakFindMove3D", 8), ("DurakMoveSelection3D", 4),
|
|
("DurakFlightPoint3D", 8), ("DrawNativeCard3D", 20)):
|
|
symbol = f"_{name}@{size}"
|
|
assert tested.signature(symbol) == shipped.signature(symbol), name
|
|
print("PASS: shipping engine loop and all poker/blackjack/Durak input, rules, animation and exit handlers match native-tested code")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|