3.1 KiB
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.