From e4dbcf0429d6ed8f28ed414982bc1ead96c0e1b6 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Mon, 11 May 2026 21:34:02 -0600 Subject: [PATCH] docs/program-format: document Remarks-table layout + resolution path The previous "What we don't yet know" entry for RemarkID -> RemarkText is now closed. Replaced the placeholder note with the actual format (clsPrograms.ReadRemarks layout) and a Python usage example showing how to resolve a Program's remark_id against PcaAccount.remarks. Removed the "RemarkID -> RemarkText lookup" line from the "what we don't yet know" list. --- src/content/docs/reference/program-format.mdx | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/content/docs/reference/program-format.mdx b/src/content/docs/reference/program-format.mdx index e8e189e..e1e58fd 100644 --- a/src/content/docs/reference/program-format.mdx +++ b/src/content/docs/reference/program-format.mdx @@ -45,8 +45,35 @@ When `prog_type == REMARK` (4) the bytes at offsets 1-4 hold a single | 1-4 | `remark_id` | BE u32 | | 5-13 | (unused, typically zero) | bytes | -The lookup from `remark_id` to the user-visible remark string lives in a -separate table on disk that we have not yet reverse-engineered. +The lookup from `remark_id` to the user-visible remark text lives in a +separate **Remarks table** further down the `.pca` body. Layout +(`clsPrograms.ReadRemarks`, clsPrograms.cs:148-168): + +``` +[u32 LE _RemarksNextID] +[u32 LE count] +[ for each entry: + [u32 LE remark_id] + [u16 LE text_length][text_length bytes UTF-8] +] +``` + +To reach the Remarks table from end-of-Connection, the walker skips +nine fixed-shape Description blocks (one per object family, each +`[u32 count] + count × 33 bytes`); see +`clsHAC.cs:8055-8079`. The Python parser puts the resolved dict on +`PcaAccount.remarks`: + +```python +from omni_pca.pca_file import parse_pca_file, KEY_EXPORT +from omni_pca.programs import ProgramType, iter_defined + +acct = parse_pca_file("Account.pca", key=KEY_EXPORT) +for p in iter_defined(acct.programs): + if p.prog_type == ProgramType.REMARK: + text = acct.remarks.get(p.remark_id, "") + print(f"slot {p.slot}: remark {p.remark_id} = {text!r}") +``` ## Enums @@ -208,9 +235,6 @@ pass. So is the multi-record clausal encoding hinted at by the the `WHEN / AT / EVERY / AND / OR / THEN` ProgType values — so we can't yet say whether they reference adjacent slots, use extra bytes within a single slot, or live in some separate clause table. -- **RemarkID → RemarkText lookup.** The remark-text table on disk has - not been located; `RemarkID` decoded fine, but we can't resolve it - to a string today. - **`DoubleProgramConditional` capability flag.** We hardcode 14-byte records for the Omni Pro II. Other models may use the 12-byte form. Locating where the panel advertises the flag (and which non-OPII