mirror of
https://github.com/spoopyghosty0/frameport.git
synced 2026-10-06 10:00:36 +02:00
Python core + Flet GUI + CLI that turns the manual Steam Frame porting work into a pipeline: scan dumps, analyze, suggest patches (overport + Frame fixes, catalog of 34 verified recipes), build, static-validate, install over SSH via a Frame-side agent, add Steam shortcuts with artwork, headless launch test and log triage. Includes the native sources (FrameBridge adapter, VrApi bridge patches, GL shim), prebuilt artifacts, docs (playbook, runtime notes) and a parity harness (34/34 games reproduce the known-good builds).
93 lines
3.4 KiB
Python
93 lines
3.4 KiB
Python
import os
|
|
import struct
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def isolated_home(tmp_path, monkeypatch):
|
|
"""Never touch the real user data dir (tools, keystores, library) from tests."""
|
|
monkeypatch.setenv("FRAMEPORT_HOME", str(tmp_path / "home"))
|
|
yield
|
|
|
|
|
|
# ---------------------------------------------------------------------------------------------- AXML builder
|
|
def build_axml(elements, strings_extra=()):
|
|
"""elements: list of ("start", name, [(attr, kind, value)]) / ("end", name). kind: "str" | "bool".
|
|
Produces a UTF-16 string-pool binary XML like aapt does (enough for our editor)."""
|
|
ns = "http://schemas.android.com/apk/res/android"
|
|
pool = []
|
|
|
|
def idx(s):
|
|
if s not in pool:
|
|
pool.append(s)
|
|
return pool.index(s)
|
|
|
|
idx(ns)
|
|
for e in elements:
|
|
idx(e[1])
|
|
if e[0] == "start":
|
|
for a, kind, v in e[2]:
|
|
idx(a)
|
|
if kind == "str":
|
|
idx(v)
|
|
for s in strings_extra:
|
|
idx(s)
|
|
data = b""
|
|
offsets = []
|
|
for s in pool:
|
|
offsets.append(len(data))
|
|
data += struct.pack("<H", len(s)) + s.encode("utf-16-le") + b"\0\0"
|
|
data += b"\0" * (-len(data) % 4)
|
|
hsz = 28
|
|
sstart = hsz + 4 * len(pool)
|
|
pool_chunk = struct.pack("<HHIIIIII", 0x0001, hsz, sstart + len(data), len(pool), 0, 0, sstart, 0)
|
|
pool_chunk += struct.pack(f"<{len(pool)}I", *offsets) + data
|
|
body = b""
|
|
for e in elements:
|
|
if e[0] == "start":
|
|
attrs = b""
|
|
for a, kind, v in e[2]:
|
|
if kind == "str":
|
|
attrs += struct.pack("<IIIHBBI", idx(ns), idx(a), idx(v), 8, 0, 0x03, idx(v))
|
|
else:
|
|
attrs += struct.pack("<IIIHBBI", idx(ns), idx(a), 0xFFFFFFFF, 8, 0, 0x12, 0xFFFFFFFF if v else 0)
|
|
ext = struct.pack("<IIHHHHHH", 0xFFFFFFFF, idx(e[1]), 20, 20, len(e[2]), 0, 0, 0)
|
|
chunk = struct.pack("<HHIII", 0x0102, 16, 16 + len(ext) + len(attrs), 1, 0xFFFFFFFF) + ext + attrs
|
|
else:
|
|
chunk = struct.pack("<HHIII", 0x0103, 16, 24, 1, 0xFFFFFFFF) + struct.pack("<II", 0xFFFFFFFF, idx(e[1]))
|
|
body += chunk
|
|
total = 8 + len(pool_chunk) + len(body)
|
|
return struct.pack("<HHI", 0x0003, 8, total) + pool_chunk + body
|
|
|
|
|
|
@pytest.fixture
|
|
def quest_manifest():
|
|
return build_axml([
|
|
("start", "manifest", [("package", "str", "com.example.questgame")]),
|
|
("start", "uses-permission", [("name", "str", "com.oculus.permission.USE_SCENE")]),
|
|
("end", "uses-permission"),
|
|
("start", "uses-permission", [("name", "str", "android.permission.INTERNET")]),
|
|
("end", "uses-permission"),
|
|
("start", "application", [("debuggable", "bool", True)]),
|
|
("start", "activity", [("name", "str", "com.example.Main")]),
|
|
("start", "intent-filter", []),
|
|
("start", "action", [("name", "str", "android.intent.action.MAIN")]),
|
|
("end", "action"),
|
|
("start", "category", [("name", "str", "android.intent.category.INFO")]),
|
|
("end", "category"),
|
|
("end", "intent-filter"),
|
|
("end", "activity"),
|
|
("end", "application"),
|
|
("end", "manifest"),
|
|
])
|
|
|
|
|
|
def games_dir():
|
|
d = os.environ.get("FRAMEPORT_GAMES")
|
|
return Path(d) if d else None
|