"""Create a self-contained ZIP64 release, including the offline model.""" from pathlib import Path import argparse import hashlib import json import zipfile ROOT = Path(__file__).resolve().parents[2] def sha(path): with path.open('rb') as source: return hashlib.file_digest(source, 'sha256').hexdigest() def main(): parser = argparse.ArgumentParser() parser.add_argument('--check-only', action='store_true') args = parser.parse_args() manifest = json.loads((ROOT / 'ai/model.json').read_text('utf8')) model = ROOT / 'ai' / manifest['file'] assert model.stat().st_size == manifest['bytes'] assert sha(model) == manifest['sha256'] game = ROOT / 'bin/poker.exe' assert b'ai\\fortune-engine.exe' in game.read_bytes(), 'game must include the native AI integration' assert sha(game) == sha(ROOT / 'poker.exe') == sha(ROOT / 'build/graphics/poker.exe') assert sha(game).upper() in (ROOT / 'build/AssemblerCasinoLauncher.cs').read_text('utf8') files = [(ROOT / 'dist/Assembler Casino.exe', 'Assembler Casino.exe'), (game, 'bin/poker.exe')] files += [(p, p.relative_to(ROOT).as_posix()) for p in sorted((ROOT / 'assets').rglob('*')) if p.is_file()] files += [(p, p.relative_to(ROOT).as_posix()) for p in sorted((ROOT / 'ai').rglob('*')) if p.is_file() and p.suffix != '.part'] assert (ROOT / 'ai/fortune-engine.exe', 'ai/fortune-engine.exe') in files assert (ROOT / 'ai/licenses/Qwen.txt', 'ai/licenses/Qwen.txt') in files assert (ROOT / 'ai/licenses/llama.cpp.txt', 'ai/licenses/llama.cpp.txt') in files if args.check_only: print(f'PASS: complete offline bundle, pinned model hash, matching game and launcher; {len(files)} files') return output = ROOT / 'dist/Assembler Casino Offline.zip' with zipfile.ZipFile(output, 'w', compression=zipfile.ZIP_DEFLATED, compresslevel=1, allowZip64=True) as archive: for path, name in files: archive.write(path, name, compress_type=zipfile.ZIP_STORED if path == model else zipfile.ZIP_DEFLATED) with zipfile.ZipFile(output) as archive: entry = archive.getinfo('ai/' + manifest['file']) assert entry.file_size == manifest['bytes'] assert len(archive.infolist()) == len(files) assert archive.testzip() is None, 'release archive failed its CRC check' print(f'Created self-contained ZIP64 bundle: {output} ({output.stat().st_size / 1e9:.2f} GB)') if __name__ == '__main__': main()