Files
rijads 1de921685f First boot on the console: the title screen at 1080p
- Runtime: stdio output goes to the log file and the kernel log (the console's printf writes
  nowhere visible); exit ends the title through sceSystemServiceLoadExec (the exit system call
  gets it killed with SIGSYS); realpath on stat (the libc's fails with EPERM, which broke libc++'s
  relative and with it OpenRCT2's case-insensitive file lookups); threads get 2 MiB of stack (the
  console's default is 64 KiB); folder listing keeps nullfs entries, whose inode is 0.
- The game relinks when the runtime changes.
- main loads the keyboard and IME dialog modules (their imports are null otherwise), selects
  SDL's software renderer, and writes a first-run config with the interface at 2x.
- The documentation (changelog) is packaged into the data folder.
- Console tests: plan lines list, stat, realpath and arg; the harness logs the renderer and how
  much of each captured frame is lit. tools/klog.py records the kernel log; tools/deploy.py
  --eboot uploads only the executable.
2026-10-04 20:00:06 +02:00

89 lines
3.3 KiB
Python

"""Uploads the built title folder to /data/homebrew on the PS5, or prints a file from the console.
uv run --no-project python tools/deploy.py upload dist/PPSA99728
uv run --no-project python tools/deploy.py --rct2 DIR also upload an RCT2 folder as rct2/
uv run --no-project python tools/deploy.py --eboot upload only eboot.bin
uv run --no-project python tools/deploy.py cat PATH print a console file (e.g. the log)
Uses the standalone PS5Upload engine from dev/ps5/tools/push_ffpkg.py, so the PS5Upload desktop
app can stay closed. The PS5Upload helper payload must be running on the console. Uploading adds
and replaces files but never deletes. --rct2 is for testing until the title can import the game
data itself; only Data, ObjData, Scenarios, Tracks and Landscapes are sent.
"""
import argparse
import hashlib
import shutil
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT.parent / "tools"))
import ps5 # noqa: E402
import push_ffpkg as push # noqa: E402
TITLE = "PPSA99728"
APP = ROOT / "dist" / TITLE
DEST = f"/data/homebrew/{TITLE}"
RCT2_FOLDERS = ("Data", "ObjData", "Scenarios", "Tracks", "Landscapes")
def transfer(source, dest):
digest = hashlib.md5(dest.encode())
for f in sorted(source.rglob("*")):
if f.is_file():
digest.update(f"{f.relative_to(source)}|{f.stat().st_size}|{f.stat().st_mtime_ns}".encode())
r = push.http("POST", "/api/transfer/dir",
{"addr": push.XFER, "tx_id": digest.hexdigest(), "dest_root": dest, "src_dir": str(source)})
job = r.get("job_id")
if not job:
sys.exit(f"transfer did not start: {r}")
while True:
j = push.http("GET", f"/api/jobs/{job}")
status = j.get("status") or j.get("job", {}).get("status")
if status in ("done", "failed"):
break
time.sleep(2)
if status != "done":
sys.exit(f"upload failed: {j}")
def upload(rct2, eboot_only):
if not (APP / "eboot.bin").exists():
sys.exit(f"{APP} is missing: run make package first")
push.LOG = ROOT / "build" / "deploy.log"
push.LOG.parent.mkdir(exist_ok=True)
push.start_engine()
push.ensure_helper()
if eboot_only:
staging = ROOT / "build" / "deploy-eboot"
staging.mkdir(exist_ok=True)
shutil.copy2(APP / "eboot.bin", staging / "eboot.bin")
transfer(staging, DEST)
else:
transfer(APP, DEST)
if rct2:
missing = [name for name in RCT2_FOLDERS if not (rct2 / name).is_dir()]
if missing:
sys.exit(f"{rct2} lacks {', '.join(missing)}")
for name in RCT2_FOLDERS:
transfer(rct2 / name, f"{DEST}/rct2/{name}")
names = sorted(e["name"] for e in ps5.list_dir(DEST)["entries"])
print(f"uploaded to {DEST}: {', '.join(names)}")
def cat(path):
sys.stdout.buffer.write(ps5.read(path))
if __name__ == "__main__":
if len(sys.argv) == 3 and sys.argv[1] == "cat":
cat(sys.argv[2])
sys.exit()
ap = argparse.ArgumentParser(usage=__doc__)
ap.add_argument("--rct2", type=Path, help="RCT2 folder to upload as rct2/")
ap.add_argument("--eboot", action="store_true", help="upload only eboot.bin")
args = ap.parse_args()
upload(args.rct2, args.eboot)