KAZIK/tools/tests/check_audio_bindings.py
Игорь Федосеев ca62db53e9 feat: implement native billiards, scratch tickets, first-person dice, and city wake scene
- Billiards: add native Russian Pyramid & American Eight-Ball pool with physics prediction, controls, and regression tests
- Scratch Tickets: add dedicated counter, mat presentation, elliptical coin scraping, 4 ticket types, and payout effects
- First-Person Dice: add native dice scene, hand anchor frames, throwing animation, and table perspective
- City Wake & Weather: add arrival cutscene, alley geometry, weather/rain effects, and lighter/rocker animation
- World Dressing: add hall paintings, furniture packs, hall lighting, and scene regression tests
- Build & Tooling: update build.bat for modern MSVC toolchain, update launcher hash, and refresh shipping binaries
2026-10-16 12:37:40 +03:00

82 lines
4.1 KiB
Python

"""Verify the native/legacy file mappings and Windows MCI decoders without playing audio."""
import ctypes
from pathlib import Path
import re
ROOT = Path(__file__).resolve().parents[2]
def asm_paths():
for file in [ROOT / "src_graphics/audio_data.inc", ROOT / "src_graphics/casino_actions_data.inc",
ROOT / "src_graphics/scratch_data.inc"]:
definitions = {}
current = None
for line in file.read_text(encoding="ascii").splitlines():
match = re.match(r"(\w+) db (.+)$", line)
continuation = re.match(r"\s+db (.+)$", line)
if match:
current = match[1]
definitions[current] = match[2]
elif continuation and current:
definitions[current] += "," + continuation[1]
else:
current = None
for symbol, payload in definitions.items():
chunks = re.findall(r'"[^"]*"|\d+', payload)
raw = b"".join(c[1:-1].encode("ascii") if c.startswith('"') else bytes([int(c)]) for c in chunks)
name = raw.rstrip(b"\0").decode("cp1251")
if name.endswith(".wav"):
yield symbol, name
def main():
native = dict(asm_paths())
assert native["soundCards"].endswith("playing_cards_deck_flick_through_001_20485.wav")
assert native["soundCoin"].endswith("Звук монеты брошенной в воздух и упавшей на поверхность.wav")
assert native["soundRoulette"].endswith("roulette-wheel-throw-2.wav")
assert native["rrDrySound3D"].endswith("revolverdryfirec.wav")
assert native["rrFireSound3D"].endswith("b05e1ac8be34248.wav")
assert native["soundClick"] != native["rrDrySound3D"]
assert native["soundDiceRoll"] != native["soundCards"]
assert len({native[name] for name in ("scrScrapeSound3D","scrPaperSound3D","scrPayoutSound3D")})==3
engine = (ROOT / "casino3d.inc").read_text(encoding="cp1251")
assert "invoke PlaySoundA, OFFSET sound" not in engine
if "LaunchClassicGame PROC" in engine:
assert "call InitSounds" in engine.split("run_game_body:")[1].split("LaunchClassicGame ENDP")[0]
else:
# Native-only builds intentionally removed the terminal launch path.
assert "run_game_body:" not in engine
assert "invoke PlayCasinoSound3D, OFFSET soundDiceRoll" in engine
audio = (ROOT / "src_graphics/audio_code.inc").read_text(encoding="ascii")
assert "invoke PlaySoundA, ebx, 0, eax" in audio
assert "20003h" in audio # SND_FILENAME | SND_NODEFAULT | SND_ASYNC
legacy = (ROOT / "poker.asm").read_text(encoding="cp1251")
recordings = set(native.values())
recordings.update(re.findall(r'open "(assets\\audio\\[^"\r\n]+\.wav)" type waveaudio', legacy))
checked = 0
for name in sorted(recordings):
if name.endswith("gman-wakeup.wav") and not (ROOT / name).exists():
continue # Optional future voice-over.
path = ROOT / name
assert path.is_file(), f"Missing audio: {name}"
command = f'open "{path}" type waveaudio alias audiotest'
error = ctypes.windll.winmm.mciSendStringW(command, None, 0, None)
assert error == 0, f"MCI cannot open {name}: {error}"
try:
error = ctypes.windll.winmm.mciSendStringW("set audiotest time format milliseconds", None, 0, None)
assert error == 0
length = ctypes.create_unicode_buffer(64)
error = ctypes.windll.winmm.mciSendStringW("status audiotest length", length, 64, None)
assert error == 0 and int(length.value) > 0
finally:
ctypes.windll.winmm.mciSendStringW("close audiotest", None, 0, None)
if ctypes.windll.kernel32.GetACP() == 1251:
error = ctypes.windll.winmm.mciSendStringA(command.encode("cp1251"), None, 0, None)
assert error == 0, f"MCI A/CP1251 cannot open {name}: {error}"
ctypes.windll.winmm.mciSendStringA(b"close audiotest", None, 0, None)
checked += 1
print(f"PASS: {checked} WAV files decoded; native and legacy mappings verified.")
if __name__ == "__main__":
main()