74 lines
3.1 KiB
Markdown
74 lines
3.1 KiB
Markdown
# PySedonac
|
|
|
|
Pure-Python `.sax` -> `.sab` encoder. No Java, no JRE.
|
|
|
|
Ported from the Sedona 1.2 runtime library — `sedona/src/sedona/src/sedona/offline/OfflineApp.java`
|
|
and friends, **not** from `sedonac/` (that's the language compiler; app file conversion lives in the
|
|
runtime lib).
|
|
|
|
Verified byte-identical to `sedonac.exe` on a 189-component / 217-link app across 18 kits.
|
|
|
|
## Usage
|
|
|
|
python sab.py <app.sax> <sedona_home> <out.sab>
|
|
|
|
`sedona_home` is the directory holding `manifests/` — kit manifests are required, since slot ids and
|
|
types are resolved from them.
|
|
|
|
## Verify
|
|
|
|
python verify.py [sedona_home]
|
|
|
|
Compares our output against the checked-in `test_normal_sedonac.sab`. Pass a sedona home containing
|
|
`bin/sedonac.exe` and call `check(sax)` with no reference to diff against a live sedonac run instead.
|
|
|
|
## Format
|
|
|
|
Big-endian. **No padding, no alignment** — `Buf.bigEndian = true`, `checkAlignment = false`. The
|
|
`align()`/`pad()` helpers exist on `Buf` but the app encoder never calls them.
|
|
|
|
"sapp" (i4 0x73617070) | version (i4 0x0003)
|
|
schema: u1 kitCount, then per kit: cstr name, i4 checksum
|
|
u2 maxId
|
|
components (ascending id, NOT tree order), each:
|
|
u2 id | u1 kitId | u1 typeId
|
|
cstr name | u2 parentId | u2 firstChildId | u2 nextSiblingId (0xffff = none)
|
|
config prop values in slot-id order, bare - no names, no ids
|
|
u1 ';'
|
|
u2 0xffff
|
|
links: u2 fromComp | u1 fromSlot | u2 toComp | u1 toSlot
|
|
u2 0xffff
|
|
u1 '.'
|
|
|
|
Values: `bool` -> u1 (0/1, **2 = null**) | `byte` -> u1 | `short` -> u2 | `int` -> i4 | `long` -> i8 |
|
|
`float` -> f4 | `double` -> f8 | `Buf` -> u2 len + raw bytes | `asStr` -> u2 (len+1) + ASCII + NUL.
|
|
|
|
Slot flags (`SlotManifest.flagsToString`): `a` = action, `c` = config, `s` = asStr, `o` = operator.
|
|
|
|
## Three things that will bite you
|
|
|
|
**Kit order is normative.** `sys` at index 0, everything else alphabetical (`Schema.sortKits`). Kit id
|
|
is the position in that sorted list, and every component record references it. Document order of the
|
|
`<schema>` block in the SAX is irrelevant.
|
|
|
|
**Slot ids come from flattening.** `Type.resolveSlots` inherits base slots then appends declared ones;
|
|
the resulting index is the id written into links. An **action override** reuses the inherited id rather
|
|
than appending (`Type.addSlot`) — get that wrong and every subsequent slot id shifts.
|
|
|
|
**Missing prop is not null.** When a `<prop>` is absent the value is the manifest's `default`
|
|
attribute, or failing that `Value.defaultForType` -> **zero**. `null` (NaN, `0x7fc00000` for float)
|
|
only when the SAX literally says `val="null"`. This was the single bug between "same length" and
|
|
"byte identical".
|
|
|
|
## Not covered
|
|
|
|
Only what `test_normal.sax` exercises is proven. Untested: action overrides, `Buf`-typed props
|
|
(base64 path is written but unexercised), non-ASCII strings (Sedona `Str` is ASCII-only and will
|
|
raise), and apps whose component ids exceed the 256-entry lookup table.
|
|
|
|
## Alternative
|
|
|
|
If a JRE is present, `sedona/bin/sedonac.exe <file.sax|.sab>` converts either direction off the file
|
|
extension, and additionally validates the app (schema resolution, RAM/FLASH sizing) in a way this
|
|
encoder does not.
|