import hashlib
import json
import struct
import sys
from pathlib import Path
MAGIC = bytes([0x89, 0x41, 0x4C, 0x43, 0x0D, 0x0A, 0x1A, 0x0A])
DOMAIN = b"alice/container/v1"
HERE = Path(__file__).resolve().parent
def sha(b: bytes) -> bytes:
return hashlib.sha256(b).digest()
def be64(n: int) -> bytes:
return struct.pack(">Q", n)
def build(semantics: bytes, sections, minor=0, major=1):
assert len(semantics) == 32
header = MAGIC + struct.pack("<HHI", major, minor, 0) + semantics + struct.pack("<Q", len(sections))
offset = len(header) + 56 * len(sections)
table = b""
body = b""
for tag, critical, payload in sections:
assert len(tag) == 4
table += tag + struct.pack("<HHQQ", 1 if critical else 0, 0, offset, len(payload)) + sha(payload)
offset += len(payload)
body += payload
data = header + table + body
data += sha(data)
cid = sha(be64(len(DOMAIN)) + DOMAIN + be64(len(header)) + header + be64(len(table)) + table)
return data, cid
def sref(rows):
out = struct.pack("<Q", len(rows))
for target, kind, builder in rows:
out += target + kind + struct.pack("<H", len(builder)) + builder
return out
def lids(rows):
out = struct.pack("<Q", len(rows))
for index, law_id, semantics in rows:
out += struct.pack("<Q", index) + law_id + semantics
return out
def big_payload():
return bytes((i * 31 + 7) % 256 for i in range(1 << 20))
def legacy_alice_zip(major, minor, file_type, engine, payload_type, payload, original_size, original_hash):
if (major, minor) >= (1, 1):
header = struct.pack(
"<9sBBBBB Q Q 32s I", b"ALICE_ZIP", major, minor, file_type, engine, payload_type,
original_size, len(payload), original_hash, 0)
else:
header = struct.pack(
"<9sBBBB Q Q 32s I", b"ALICE_ZIP", major, minor, file_type, engine,
original_size, len(payload), original_hash, 0)
return header + payload
SEM = bytes([0x11]) * 32
def scenes():
shape = b"shape-bytes-0123456789"
world = b"world-state"
shape_sha = sha(shape)
return {
"empty": (SEM, []),
"one": (SEM, [(b"RAW_", True, b"hello")]),
"dup_tag": (SEM, [(b"RAW_", True, b"a"), (b"RAW_", True, b"b"), (b"PROV", False, b"note")]),
"zero_len": (SEM, [(b"RESD", True, b"")]),
"unknown_noncritical": (SEM, [(b"RAW_", True, b"x"), (b"ZZZZ", False, b"keep me")]),
"bundle": (SEM, [
(b"SHAP", True, shape),
(b"PHYS", True, world),
(b"SREF", True, sref([
(shape_sha, b"SHAP", b"alice-sdf/compiled-field/v1"),
(shape_sha, b"SHAP", b"alice-sdf/compiled-field/v1"),
])),
(b"LIDS", True, lids([(1, bytes([0xAB]) * 32, SEM)])),
]),
}
def main():
golden = {}
for name, (sem, secs) in scenes().items():
data, cid = build(sem, secs)
(HERE / f"{name}.bin").write_bytes(data)
golden[name] = {"id": cid.hex(), "file_sha256": sha(data).hex(), "len": len(data)}
data, cid = build(SEM, [(b"RAW_", True, big_payload())])
golden["big"] = {"id": cid.hex(), "file_sha256": sha(data).hex(), "len": len(data)}
legacy_payload = b"\x00legacy-payload\xff"
original_hash = sha(b"original data")
(HERE / "legacy_v1.alice").write_bytes(
legacy_alice_zip(1, 0, 0x05, 0, 0, legacy_payload, 13, original_hash))
(HERE / "legacy_v2.alice").write_bytes(
legacy_alice_zip(1, 1, 0x01, 3, 0x30, legacy_payload, 13, original_hash))
(HERE / "legacy_major2.alice").write_bytes(
legacy_alice_zip(2, 0, 0x01, 0, 0x00, legacy_payload, 13, original_hash))
(HERE / "legacy_unknown_payload.alice").write_bytes(
legacy_alice_zip(1, 1, 0x01, 0, 0x7F, legacy_payload, 13, original_hash))
(HERE / "golden.json").write_text(json.dumps(golden, indent=1, sort_keys=True) + "\n")
print(f"wrote {len(golden)} golden entries", file=sys.stderr)
if __name__ == "__main__":
main()