mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-30 07:37:38 +00:00
doomgeneric compiled to wasm32-wasi plus the Freedoom Phase 1 IWAD, packed into one self-contained index.html that publishes as a NIP-5D napplet and runs inside Amethyst's existing sandbox and CSP unchanged. - build.sh: pinned wasi-sdk, Freedoom and doomgeneric; reproducible artifact hash. IWAD=/TITLE= swap in another IWAD. - The page supplies a WASI in-memory file system, the frame buffer, keyboard, touch and gamepad input, Web Audio sound effects, a small MIDI synth for the music, and keeps saves in window.napplet.storage. - doomgeneric.patch makes the screen melt non-blocking and clears the frame buffer before the first melt. - doom.test.mjs plays it in headless Chromium inside the production shell.html, shim.js and CSP: boot, music, gameplay, save and reload through storage, denied and undeclared storage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NQ76C3uhMzzWRfB6NNxYzf
56 lines
1.8 KiB
Python
Executable File
56 lines
1.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Packs doom.wasm and an IWAD into the single self-contained index.html a NIP-5D napplet needs.
|
|
|
|
A napplet is one HTML artifact with no network (CSP connect-src 'none'), so both binaries go in
|
|
the page as gzip+base64. Output is byte-for-byte reproducible: gzip carries no timestamp or name.
|
|
|
|
usage: assemble.py TEMPLATE WASM WAD OUT [TITLE]
|
|
"""
|
|
|
|
import base64
|
|
import gzip
|
|
import hashlib
|
|
import os
|
|
import sys
|
|
from html import escape as html_escape
|
|
|
|
# Amethyst downloads a napplet artifact only up to this size (NappletBlobHttp.MAX_BLOB_BYTES).
|
|
HOST_ARTIFACT_LIMIT = 20 * 1024 * 1024
|
|
RESX, RESY = 320, 200
|
|
|
|
|
|
def packed(path):
|
|
with open(path, "rb") as f:
|
|
data = f.read()
|
|
return base64.b64encode(gzip.compress(data, compresslevel=9, mtime=0)).decode("ascii")
|
|
|
|
|
|
def main(template, wasm, wad, out, title="Freedoom"):
|
|
with open(template, encoding="utf-8") as f:
|
|
html = f.read()
|
|
replacements = {
|
|
"__WASM_GZ_BASE64__": packed(wasm),
|
|
"__WAD_GZ_BASE64__": packed(wad),
|
|
"__WAD_NAME__": os.path.basename(wad).lower(),
|
|
"__TITLE__": html_escape(title),
|
|
"__RESX__": str(RESX),
|
|
"__RESY__": str(RESY),
|
|
}
|
|
for placeholder, value in replacements.items():
|
|
if placeholder not in html:
|
|
sys.exit(f"template is missing {placeholder}")
|
|
html = html.replace(placeholder, value)
|
|
data = html.encode("utf-8")
|
|
with open(out, "wb") as f:
|
|
f.write(data)
|
|
|
|
print(f"{out}: {len(data):,} bytes, sha256 {hashlib.sha256(data).hexdigest()}")
|
|
if len(data) > HOST_ARTIFACT_LIMIT:
|
|
sys.exit(f"over Amethyst's {HOST_ARTIFACT_LIMIT:,}-byte napplet artifact limit")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) not in (5, 6):
|
|
sys.exit(__doc__)
|
|
main(*sys.argv[1:])
|