Add native assembly scenes and assets for Slots (777), Coin flip, and European Roulette; add 3D hall furniture packs and prompts; improve Russian UI strings in CP1251; update launcher hash and rebuild shipping binaries.
94 lines
4.4 KiB
Python
94 lines
4.4 KiB
Python
"""Offline assertion breakpoint in the native test executable, not game code."""
|
|
import ctypes as c
|
|
import struct
|
|
|
|
from capstone import Cs, CS_ARCH_X86, CS_MODE_32
|
|
from debug_native_crash import K, ROOT, Startup, Process, Event
|
|
from check_card_exit_build import Coff
|
|
|
|
|
|
def main():
|
|
obj = Coff(ROOT / "build/actions/actions-test.obj")
|
|
sec, start = obj.functions["_RunWheelSceneTest3D@0"]
|
|
raw, size, _ = obj.sections[sec]
|
|
end = min(v for s, v in obj.functions.values() if s == sec and v > start)
|
|
instructions = list(Cs(CS_ARCH_X86, CS_MODE_32).disasm(obj.data[raw + start:raw + end], start))
|
|
trap_offset = instructions[-6].address
|
|
assert instructions[-6].mnemonic == "mov"
|
|
symbols = {}
|
|
_, _, _, table, total, _, _ = struct.unpack_from("<HHIIIHH", obj.data)
|
|
index = 0
|
|
while index < total:
|
|
value, section, _, _, aux = struct.unpack_from("<IhHBB", obj.data, table + index * 18 + 8)
|
|
if section == 2:
|
|
symbols[obj.symbols[index]] = value
|
|
index += aux + 1
|
|
pe = (ROOT / "build/actions/actions-test.exe").read_bytes()
|
|
header = struct.unpack_from("<I", pe, 60)[0]
|
|
count = struct.unpack_from("<H", pe, header + 6)[0]
|
|
opt = struct.unpack_from("<H", pe, header + 20)[0]
|
|
sections = header + 24 + opt
|
|
data_rva = None
|
|
for i in range(count):
|
|
at = sections + i * 40
|
|
if pe[at:at + 8].rstrip(b"\0") == b".data":
|
|
data_rva = struct.unpack_from("<I", pe, at + 12)[0]
|
|
assert data_rva
|
|
K.WriteProcessMemory.argtypes = [c.c_void_p, c.c_void_p, c.c_void_p, c.c_size_t, c.c_void_p]
|
|
K.TerminateProcess.argtypes = [c.c_void_p, c.c_uint32]
|
|
start_info, process = Startup(), Process()
|
|
start_info.cb, start_info.flags, start_info.show = c.sizeof(start_info), 1, 0
|
|
executable = str(ROOT / "build/actions/actions-test.exe")
|
|
command = c.create_unicode_buffer('"' + executable + '"')
|
|
if not K.CreateProcessW(executable, command, None, None, False, 3, None,
|
|
str(ROOT), c.byref(start_info), c.byref(process)):
|
|
raise c.WinError(c.get_last_error())
|
|
base = trap = 0
|
|
|
|
def memory(address, size):
|
|
buffer, read = c.create_string_buffer(size), c.c_size_t()
|
|
assert K.ReadProcessMemory(process.process, address, buffer, size, c.byref(read))
|
|
return buffer.raw[:read.value]
|
|
|
|
while True:
|
|
event = Event()
|
|
assert K.WaitForDebugEvent(c.byref(event), 120000)
|
|
data, status = bytes(event.data), 0x10002
|
|
if event.code == 3:
|
|
base = struct.unpack_from("<Q", data, 24)[0]
|
|
trap = base + 0x1000 + trap_offset
|
|
assert K.WriteProcessMemory(process.process, trap, b"\xcc", 1, None)
|
|
elif event.code == 1:
|
|
exception = struct.unpack_from("<I", data)[0]
|
|
address = struct.unpack_from("<Q", data, 16)[0]
|
|
if exception in (0x80000003, 0x4000001F) and address == trap:
|
|
thread = K.OpenThread(0x48, False, event.tid)
|
|
context = c.create_string_buffer(716)
|
|
struct.pack_into("<I", context, 0, 0x10007)
|
|
assert K.Wow64GetThreadContext(thread, context)
|
|
regs = dict(zip(("edi", "esi", "ebx", "edx", "ecx", "eax", "ebp", "eip", "cs", "flags", "esp"),
|
|
struct.unpack_from("<11I", context, 156)))
|
|
print("ASSERTION REGISTERS:", regs, flush=True)
|
|
print("LOCALS:", struct.unpack("<7I", memory(regs["ebp"] - 28, 28)), flush=True)
|
|
for name in ("wheelArtReady3D", "wheelStake3D", "wheelReturn3D", "wheelResult3D",
|
|
"wheelResultSlot3D", "wheelBallAngle3D", "bankroll", "rngSeed3D", "roulState"):
|
|
print(name, struct.unpack("<I", memory(base + data_rva + symbols[name], 4))[0], flush=True)
|
|
K.CloseHandle(thread)
|
|
K.TerminateProcess(process.process, 1)
|
|
elif exception not in (0x80000003, 0x4000001F):
|
|
status = 0x80010001
|
|
elif event.code == 5:
|
|
print("PROCESS EXIT:", struct.unpack_from("<I", data)[0], flush=True)
|
|
K.ContinueDebugEvent(event.pid, event.tid, status)
|
|
break
|
|
elif event.code == 6:
|
|
handle = struct.unpack_from("<Q", data)[0]
|
|
if handle:
|
|
K.CloseHandle(handle)
|
|
K.ContinueDebugEvent(event.pid, event.tid, status)
|
|
K.CloseHandle(process.thread)
|
|
K.CloseHandle(process.process)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|