[Docs] Land the PPC / Xbox 360 export reference tables

These were sitting in stash@{0} ("On canary_experimental: doc", 2026-04-11) as
untracked files only -- no tracked change was ever stashed with them, so they
were one `git stash drop` away from being lost:

  docs/ppc_instructions.{json,md}   PPC instruction reference
  docs/xbox360_exports.{json,md}    XAM / xboxkrnl export tables
  docs/xex2_format.md               XEX2 container format
  tools/generate_export_docs.py     the generator behind them

xex2_format.md in particular is the spec a static XEX reader would need to
pull the title's XACH achievement table and .rdata tables off the disc.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-08-17 18:10:16 +02:00
parent 55c47b17ca
commit 7a682d7cf4
6 changed files with 51938 additions and 0 deletions

View File

@@ -0,0 +1,338 @@
#!/usr/bin/env python3
"""Generate documentation of all Xbox 360 system exports in Xenia Canary.
Parses the export table .inc files and implementation .cc files to produce
a comprehensive listing of all exports with their implementation status.
Usage:
python3 tools/generate_export_docs.py
"""
import json
import pathlib
import re
from collections import OrderedDict
REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent
# --- Configuration ---
TABLE_FILES = {
"xboxkrnl": REPO_ROOT / "src/xenia/kernel/xboxkrnl/xboxkrnl_table.inc",
"xam": REPO_ROOT / "src/xenia/kernel/xam/xam_table.inc",
"xbdm": REPO_ROOT / "src/xenia/kernel/xbdm/xbdm_table.inc",
}
IMPL_DIRS = {
"xboxkrnl": REPO_ROOT / "src/xenia/kernel/xboxkrnl",
"xam": REPO_ROOT / "src/xenia/kernel/xam",
"xbdm": REPO_ROOT / "src/xenia/kernel/xbdm",
}
MODULE_NAMES = {
"xboxkrnl": "xboxkrnl.exe",
"xam": "xam.xex",
"xbdm": "xbdm.xex",
}
MODULE_MAP = {"XBOXKRNL": "xboxkrnl", "XAM": "xam", "XBDM": "xbdm"}
# Regex patterns
XE_EXPORT_RE = re.compile(
r"XE_EXPORT\(\s*(\w+)\s*,\s*(0x[0-9A-Fa-f]+)\s*,\s*(\w+)\s*,\s*k(\w+)\s*\)"
)
DECLARE_RE = re.compile(
r"DECLARE_(XBOXKRNL|XAM|XBDM)_EXPORT\d*\(\s*(\w+)\s*,"
r"\s*k(\w+)\s*,\s*(.+?)\)\s*;",
re.DOTALL,
)
TAG_RE = re.compile(r"k(\w+)")
SHIM_RE = re.compile(r'SHIM_SET_MAPPING\(\s*"[^"]+"\s*,\s*(\w+)\s*,')
VAR_MAPPING_RE = re.compile(r"ordinals::(\w+)")
EXPORT_KVAR_RE = re.compile(r"EXPORT_KVAR\((\w+)\)")
def parse_table_files():
"""Phase A: Parse all XE_EXPORT entries from *_table.inc files."""
exports = OrderedDict()
for module, path in TABLE_FILES.items():
content = path.read_text()
for match in XE_EXPORT_RE.finditer(content):
mod, ordinal_hex, name, typ = match.groups()
exports[(mod, name)] = {
"module": mod,
"ordinal": int(ordinal_hex, 16),
"ordinal_hex": ordinal_hex.upper().replace("0X", "0x"),
"name": name,
"type": "variable" if typ == "Variable" else "function",
"status": "not_implemented",
"tags": [],
"category": "",
"source_file": "",
}
return exports
def scan_declare_macros(exports):
"""Phase B: Scan DECLARE_*_EXPORT macros in .cc files."""
for mod_key, dir_path in IMPL_DIRS.items():
for cc_file in sorted(dir_path.glob("*.cc")):
content = cc_file.read_text()
for match in DECLARE_RE.finditer(content):
mod_prefix, name, category, tags_str = match.groups()
module = MODULE_MAP[mod_prefix]
tags = TAG_RE.findall(tags_str)
key = (module, name)
if key in exports:
entry = exports[key]
entry["category"] = category
entry["tags"] = tags
entry["source_file"] = str(
cc_file.relative_to(REPO_ROOT)
)
if "Implemented" in tags:
entry["status"] = "implemented"
elif "Stub" in tags:
entry["status"] = "stub"
elif "Sketchy" in tags:
entry["status"] = "sketchy"
else:
entry["status"] = "stub"
def scan_legacy_patterns(exports):
"""Phase C: Handle SHIM_SET_MAPPING and SetVariableMapping."""
# SHIM_SET_MAPPING in xboxkrnl_strings.cc
strings_file = IMPL_DIRS["xboxkrnl"] / "xboxkrnl_strings.cc"
if strings_file.exists():
content = strings_file.read_text()
for match in SHIM_RE.finditer(content):
name = match.group(1)
key = ("xboxkrnl", name)
if key in exports:
exports[key]["status"] = "implemented"
exports[key]["source_file"] = str(
strings_file.relative_to(REPO_ROOT)
)
if not exports[key]["tags"]:
exports[key]["tags"] = ["Implemented"]
# SetVariableMapping / EXPORT_KVAR in xboxkrnl_module.cc
module_file = IMPL_DIRS["xboxkrnl"] / "xboxkrnl_module.cc"
if module_file.exists():
content = module_file.read_text()
for match in VAR_MAPPING_RE.finditer(content):
name = match.group(1)
key = ("xboxkrnl", name)
if key in exports:
exports[key]["status"] = "implemented"
exports[key]["source_file"] = str(
module_file.relative_to(REPO_ROOT)
)
if not exports[key]["tags"]:
exports[key]["tags"] = ["Implemented"]
for match in EXPORT_KVAR_RE.finditer(content):
name = match.group(1)
key = ("xboxkrnl", name)
if key in exports:
exports[key]["status"] = "implemented"
exports[key]["source_file"] = str(
module_file.relative_to(REPO_ROOT)
)
if not exports[key]["tags"]:
exports[key]["tags"] = ["Implemented"]
def compute_statistics(exports):
"""Compute per-module statistics."""
stats = {}
for module in TABLE_FILES:
module_exports = [e for e in exports.values() if e["module"] == module]
stats[module] = {
"total": len(module_exports),
"implemented": sum(
1 for e in module_exports if e["status"] == "implemented"
),
"stub": sum(1 for e in module_exports if e["status"] == "stub"),
"sketchy": sum(
1 for e in module_exports if e["status"] == "sketchy"
),
"not_implemented": sum(
1
for e in module_exports
if e["status"] == "not_implemented"
),
"functions": sum(
1 for e in module_exports if e["type"] == "function"
),
"variables": sum(
1 for e in module_exports if e["type"] == "variable"
),
}
return stats
def generate_markdown(exports, stats):
"""Generate the Markdown documentation."""
lines = []
lines.append("# Xbox 360 System Exports - Xenia Canary")
lines.append("")
lines.append(
"Auto-generated by `tools/generate_export_docs.py`. "
"Do not edit manually."
)
lines.append("")
# Overall summary
lines.append("## Summary")
lines.append("")
total_all = sum(s["total"] for s in stats.values())
impl_all = sum(s["implemented"] for s in stats.values())
stub_all = sum(s["stub"] for s in stats.values())
sketchy_all = sum(s["sketchy"] for s in stats.values())
not_impl_all = sum(s["not_implemented"] for s in stats.values())
lines.append(
f"**Total exports: {total_all}** | "
f"Implemented: {impl_all} | "
f"Stub: {stub_all} | "
f"Sketchy: {sketchy_all} | "
f"Not implemented: {not_impl_all}"
)
lines.append("")
lines.append(
"| Module | File | Total | Implemented | Stub | Sketchy | Not Implemented | Functions | Variables |"
)
lines.append(
"|--------|------|------:|------------:|-----:|--------:|----------------:|----------:|----------:|"
)
for module in TABLE_FILES:
s = stats[module]
lines.append(
f"| {module} | {MODULE_NAMES[module]} | {s['total']} | "
f"{s['implemented']} | {s['stub']} | {s['sketchy']} | "
f"{s['not_implemented']} | {s['functions']} | {s['variables']} |"
)
lines.append("")
# Per-module tables
for module in TABLE_FILES:
s = stats[module]
lines.append(f"## {module} ({MODULE_NAMES[module]})")
lines.append("")
lines.append(
f"Total: {s['total']} | "
f"Implemented: {s['implemented']} | "
f"Stub: {s['stub']} | "
f"Sketchy: {s['sketchy']} | "
f"Not implemented: {s['not_implemented']}"
)
lines.append("")
lines.append(
"| Ordinal | Name | Type | Status | Category | Source |"
)
lines.append(
"|--------:|------|------|--------|----------|--------|"
)
module_exports = sorted(
[e for e in exports.values() if e["module"] == module],
key=lambda e: e["ordinal"],
)
for e in module_exports:
ordinal = f"0x{e['ordinal']:03X}"
status_icon = {
"implemented": "implemented",
"stub": "stub",
"sketchy": "sketchy",
"not_implemented": "-",
}.get(e["status"], e["status"])
source = e["source_file"]
if source:
# Show just the filename for brevity
source = pathlib.Path(source).name
lines.append(
f"| {ordinal} | {e['name']} | {e['type']} | "
f"{status_icon} | {e['category']} | {source} |"
)
lines.append("")
return "\n".join(lines)
def generate_json(exports, stats):
"""Generate the JSON documentation."""
data = {
"description": "Xbox 360 system exports implemented in Xenia Canary",
"generator": "tools/generate_export_docs.py",
"statistics": stats,
"modules": {},
}
for module in TABLE_FILES:
module_exports = sorted(
[e for e in exports.values() if e["module"] == module],
key=lambda e: e["ordinal"],
)
data["modules"][module] = {
"file": MODULE_NAMES[module],
"exports": module_exports,
}
return json.dumps(data, indent=2)
def main():
print("Parsing export tables...")
exports = parse_table_files()
print(f" Found {len(exports)} exports across {len(TABLE_FILES)} modules")
print("Scanning DECLARE_*_EXPORT macros...")
scan_declare_macros(exports)
print("Scanning legacy registration patterns...")
scan_legacy_patterns(exports)
stats = compute_statistics(exports)
for module, s in stats.items():
print(
f" {module}: {s['total']} total, "
f"{s['implemented']} implemented, "
f"{s['stub']} stub, "
f"{s['sketchy']} sketchy, "
f"{s['not_implemented']} not implemented"
)
# Generate outputs
docs_dir = REPO_ROOT / "docs"
docs_dir.mkdir(exist_ok=True)
md_path = docs_dir / "xbox360_exports.md"
md_content = generate_markdown(exports, stats)
md_path.write_text(md_content)
print(f"Written: {md_path.relative_to(REPO_ROOT)}")
json_path = docs_dir / "xbox360_exports.json"
json_content = generate_json(exports, stats)
json_path.write_text(json_content)
print(f"Written: {json_path.relative_to(REPO_ROOT)}")
if __name__ == "__main__":
main()