Remaster thirteen original sprite and terrain pictures

This commit is contained in:
raptorreplica committed 2026-10-18 14:07:39 +11:00
1 parent f3b30df0f8
commit 05a2eae96b
148 files changed
+330320 -54

No files matched your search

+151
View File
@@ -0,0 +1,151 @@
"""Reproduce a complete SHIP20 family using reviewed RGB and Original-tip steps.
This is a portable dispatcher, not a new pixel algorithm. It calls the unchanged
DSHP complete-family renderer and functional Original retention helper in memory.
All paths in the geometry recipe resolve against the outer recipe's directory.
No artwork approval, engine operation or registration is performed.
"""
from __future__ import annotations
import argparse
import hashlib
import io
import json
from pathlib import Path
import sys
import numpy as np
from PIL import Image, __version__ as PILLOW_VERSION
import hd_other_functional_cleanup as functional
METHOD = "derived-SHIP20-complete4-reviewed-generated-RGB-and-Original-tips-v1"
FUNCTIONAL_SHA = "f8078284e300947aec4d2c9e70cbc7198fa3cb5d34de6b36c07cacc5f3820a84"
ASSETS = ["200c4", "200c5", "200c6", "200c7"]
def digest(payload):
return hashlib.sha256(payload).hexdigest()
def dependency_pins():
path = Path(functional.__file__).resolve()
if digest(path.read_bytes()) != FUNCTIONAL_SHA:
raise ValueError("functional Original implementation changed")
return {"functional_Original_tool": {"path": "tools/hd_other_functional_cleanup.py",
"sha256": FUNCTIONAL_SHA},
**functional.dependency_pins()}
def run_recipe(recipe_path, output):
recipe_path, output = Path(recipe_path).resolve(), Path(output)
if output.exists():
raise ValueError("fresh absent output directory required")
dependencies = dependency_pins()
rb = recipe_path.read_bytes()
recipe = json.loads(rb)
if (type(recipe) is not dict or set(recipe) != {
"format_version", "method", "geometry_recipe", "functional_original_cells",
"expected_output_sha256"}
or type(recipe["format_version"]) is not int or recipe["format_version"] != 1
or recipe["method"] != METHOD):
raise ValueError("exact SHIP20 dispatcher recipe fields/version required")
geometry = recipe["geometry_recipe"]
frames = geometry.get("frames") if type(geometry) is dict else None
if (type(frames) is not list or len(frames) != 4
or any(type(f) is not dict for f in frames)
or [f.get("asset") for f in frames] != ASSETS):
raise ValueError("complete4 exact SHIP20 frame order required")
selected = recipe["functional_original_cells"]
expected = recipe["expected_output_sha256"]
if (type(selected) is not dict or set(selected) != set(ASSETS)
or type(expected) is not dict or set(expected) != set(ASSETS)
or any(type(v) is not str or len(v) != 64
or any(c not in "0123456789abcdef" for c in v)
for v in expected.values())):
raise ValueError("complete4 functional cells and expected PNG hashes required")
# Validate the complete selection before invoking the existing renderer.
sources = []
for frame in frames:
_, sb = functional.base.checked(frame["source"], recipe_path.parent)
w, h, cells = functional.base._indices(sb)
if (w, h) != (48, 56) or frame.get("origin") != [0, 0]:
raise ValueError("exact native48x56/origin0 SHIP20 canvas required")
source = np.asarray(cells, np.uint16).reshape(h, w)
points = selected[frame["asset"]]
if (type(points) is not list or len(points) > 64
or any(type(xy) is not list or len(xy) != 2
or any(type(v) is not int for v in xy)
or not 0 <= xy[0] < w or not 0 <= xy[1] < h
or int(source[xy[1], xy[0]]) not in (82, 83, 84)
for xy in points)
or points != sorted(points)
or len({tuple(xy) for xy in points}) != len(points)):
raise ValueError("sorted unique painted Source yellow82..84 cells required")
sources.append(source)
arrays, geometry_report = functional.base.render_family(geometry, recipe_path.parent)
_, pb = functional.base.checked(geometry["palette"], recipe_path.parent)
p6 = np.frombuffer(pb, np.uint8).reshape(256, 3).astype(np.uint16)
palette = ((p6 << 2) | (p6 >> 4)).astype(np.uint8)
payloads, reports = [], []
for frame, source, rgba in zip(frames, sources, arrays):
asset = frame["asset"]
points = selected[asset]
if points:
out, retention = functional.retain_functional_original(rgba, source, palette, points)
if not np.array_equal(out[:, :, 3], rgba[:, :, 3]):
raise ValueError("functional selection changed SourceScale4 alpha")
else:
out = rgba
retention = {"cells": [], "actual_Original_Scale4_RGBA_samples": 0,
"outside_explicit_footprints_RGBA_exact": True}
encoded = io.BytesIO()
Image.fromarray(out).save(encoded, format="PNG", compress_level=9)
png = encoded.getvalue()
if digest(png) != expected[asset]:
raise ValueError(f"reviewed PNG not reproduced: {asset}")
payloads.append((asset, png))
reports.append({"asset": asset, "functional_Original": retention,
"sha256": digest(png), "RGBA_sha256": digest(out.tobytes())})
# Reject changed input/implementation bytes before the first output write.
if recipe_path.read_bytes() != rb or dependency_pins() != dependencies:
raise ValueError("recipe or implementation changed during reconstruction")
for frame in frames:
functional.base.checked(frame["source"], recipe_path.parent)
functional.base.checked(frame["raw_png"], recipe_path.parent)
functional.base.checked(geometry["palette"], recipe_path.parent)
execution = {"status": "success", "method": METHOD,
"recipe_sha256": digest(rb), "tool_sha256": digest(Path(__file__).read_bytes()),
"source_sha256": {f["asset"]: f["source"]["sha256"] for f in frames},
"parent_raw_sha256": sorted({f["raw_png"]["sha256"] for f in frames}),
"output_sha256": {a: digest(b) for a, b in payloads}}
report = {"execution": execution, "dependencies": dependencies,
"geometry": geometry_report, "frames": reports,
"Original_tip_cells_are_not_generated_HD": True,
"actual_dispatcher_calls": 1, "actual_geometry_renderer_calls": 1,
"actual_pure_retention_calls": sum(bool(selected[a]) for a in ASSETS),
"python_version": sys.version, "numpy_version": np.__version__,
"pillow_version": PILLOW_VERSION,
"scope": "Portable byte-exact replay only; no new art/engine/QA/context approval."}
output.mkdir(parents=True)
for asset, png in payloads:
with (output / f"{asset}-derived.png").open("xb") as stream:
stream.write(png)
with (output / "derived-provenance.json").open("x") as stream:
json.dump(report, stream, indent=2, sort_keys=True)
stream.write("\n")
return report
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("recipe", type=Path)
parser.add_argument("fresh_output", type=Path)
args = parser.parse_args()
print(json.dumps(run_recipe(args.recipe, args.fresh_output)["execution"], sort_keys=True))
if __name__ == "__main__":
main()
+146
View File
@@ -0,0 +1,146 @@
"""Synthetic dispatcher/portable-lineage tests; no real assets or engine calls."""
import copy
import hashlib
import importlib.util
import io
import json
import os
from pathlib import Path
import struct
import sys
import tempfile
import unittest
from unittest.mock import patch
import numpy as np
from PIL import Image
sys.path.insert(0, str(Path(__file__).resolve().parent))
spec = importlib.util.spec_from_file_location("ship_cleanup", Path(__file__).with_name("hd_ship20_cleanup.py"))
c = importlib.util.module_from_spec(spec)
spec.loader.exec_module(c)
class PortableShipDispatcherTests(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.addCleanup(self.tmp.cleanup)
self.root = Path(self.tmp.name)
self.assets = c.ASSETS
self.cells = dict(zip(self.assets, [[], [[14, 25]], [[25, 19]], [[36, 23]]]))
y, x = np.indices((224, 192))
rgba = np.empty((224, 192, 4), np.uint8)
rgba[:, :, :3] = np.stack([(x * 3 + y) % 256, (x + y * 5) % 256,
(x * 7 + y * 11) % 256], axis=-1)
rgba[:, :, 3] = 255
Image.fromarray(rgba).save(self.root / "parent.png")
self.palette_bytes = bytes((np.arange(768) % 64).astype(np.uint8))
(self.root / "palette").write_bytes(self.palette_bytes)
frames = []
for asset in self.assets:
source = np.full((56, 48), 10, np.uint16)
for xx, yy in self.cells[asset]:
source[yy, xx] = 82
payload = bytearray(struct.pack("<5i", 0, 1, 31, 48, 56))
for yy in range(56):
payload.extend(struct.pack("<4i", 0, yy, yy * 48, 48))
payload.extend(bytes(source[yy].tolist()))
payload.extend(struct.pack("<4i", 0, 0, -1, 0))
(self.root / asset).write_bytes(payload)
frames.append(dict(asset=asset, source=self.bind(asset), raw_png=self.bind("parent.png"),
viewport=[0, 0, 192, 224], sampling_offset_native=[0, 0], origin=[0, 0]))
self.geometry = dict(format_version=1, output_scale=4, max_fill_distance_pixels=12,
shared_backing_anchor=1, palette=self.bind("palette"), frames=frames)
# Independent sequential reference: published geometry stage, then the
# unchanged pure Original stage; PNG compression matches the actual CLI.
arrays, _ = c.functional.base.render_family(self.geometry, self.root)
self.reference = {}
for f, array in zip(frames, arrays):
_, sb = c.functional.base.checked(f["source"], self.root)
w, h, indices = c.functional.base._indices(sb)
if self.cells[f["asset"]]:
p6 = np.frombuffer(self.palette_bytes, np.uint8).reshape(256, 3).astype(np.uint16)
palette = ((p6 << 2) | (p6 >> 4)).astype(np.uint8)
array, _ = c.functional.retain_functional_original(
array, np.asarray(indices, np.uint16).reshape(h, w), palette, self.cells[f["asset"]])
buffer = io.BytesIO()
Image.fromarray(array).save(buffer, format="PNG", compress_level=9)
self.reference[f["asset"]] = buffer.getvalue()
self.recipe = dict(format_version=1, method=c.METHOD, geometry_recipe=self.geometry,
functional_original_cells=self.cells,
expected_output_sha256={a: c.digest(b) for a, b in self.reference.items()})
def bind(self, name):
return dict(path=name, sha256=hashlib.sha256((self.root / name).read_bytes()).hexdigest())
def recipe_path(self, recipe=None):
path = self.root / "recipe.json"
path.write_text(json.dumps(self.recipe if recipe is None else recipe))
return path
def test_relative_recipe_from_other_cwd_reproduces_sequential_bytes_and_receipt(self):
rp = self.recipe_path()
before = {p: p.read_bytes() for p in self.root.iterdir()}
old_cwd = Path.cwd()
try:
os.chdir("/")
report = c.run_recipe(rp, self.root / "actual")
finally:
os.chdir(old_cwd)
for asset, expected in self.reference.items():
self.assertEqual((self.root / "actual" / (asset + "-derived.png")).read_bytes(), expected)
self.assertEqual(report["execution"]["tool_sha256"], c.digest(Path(c.__file__).read_bytes()))
self.assertEqual(report["execution"]["parent_raw_sha256"], [self.bind("parent.png")["sha256"]])
self.assertEqual(report["actual_geometry_renderer_calls"], 1)
self.assertEqual(report["actual_pure_retention_calls"], 3)
self.assertEqual(report["frames"][0]["functional_Original"]["actual_Original_Scale4_RGBA_samples"], 0)
self.assertIn("pillow_version", report)
self.assertIn("numpy_version", report)
for p, value in before.items():
self.assertEqual(p.read_bytes(), value)
with self.assertRaisesRegex(ValueError, "fresh absent"):
c.run_recipe(rp, self.root / "actual")
def test_wrong_expected_png_rejected_before_output_mkdir(self):
r = copy.deepcopy(self.recipe)
r["expected_output_sha256"]["200c7"] = "0" * 64
with self.assertRaisesRegex(ValueError, "reviewed PNG not reproduced"):
c.run_recipe(self.recipe_path(r), self.root / "absent")
self.assertFalse((self.root / "absent").exists())
def test_wrong_order_duplicate_bool_unpainted_and_unsorted_cells_reject(self):
cases = []
r = copy.deepcopy(self.recipe)
r["geometry_recipe"]["frames"].reverse()
cases.append(r)
for cells in ([[14, 25], [14, 25]], [[True, 25]], [[0, 0]], [[14, 26], [14, 25]]):
r = copy.deepcopy(self.recipe)
r["functional_original_cells"]["200c5"] = cells
cases.append(r)
for r in cases:
with self.subTest(recipe=r), self.assertRaises(ValueError):
c.run_recipe(self.recipe_path(r), self.root / "absent")
self.assertFalse((self.root / "absent").exists())
def test_corrupt_declared_input_or_dependency_rejects_before_output(self):
r = copy.deepcopy(self.recipe)
r["geometry_recipe"]["frames"][0]["raw_png"]["sha256"] = "0" * 64
with self.assertRaisesRegex(ValueError, "hash mismatch"):
c.run_recipe(self.recipe_path(r), self.root / "absent")
with patch.object(c, "FUNCTIONAL_SHA", "0" * 64), self.assertRaisesRegex(ValueError, "implementation changed"):
c.run_recipe(self.recipe_path(), self.root / "absent")
self.assertFalse((self.root / "absent").exists())
def test_retention_cannot_change_alpha(self):
original = c.functional.retain_functional_original
def wrong_alpha(*args):
out, report = original(*args)
out[0, 0, 3] = 0
return out, report
with patch.object(c.functional, "retain_functional_original", wrong_alpha), self.assertRaisesRegex(ValueError, "alpha"):
c.run_recipe(self.recipe_path(), self.root / "absent")
self.assertFalse((self.root / "absent").exists())
if __name__ == "__main__":
unittest.main(verbosity=2)