You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Unknown OCX properties that reference binary blobs are dropped on save #60
When an unrecognised property references a blob in a companion binary — UnknownPicture = "Form1.frx":0A00 — both the text line and the blob are logged and dropped rather than preserved.
Full fidelity needs a RawFrxEntry concept (offset → raw bytes) threaded through FrxDeserializer → FormDefinition → FrxSerializer, so unknown blobs are re-emitted at their original offsets and the referencing property lines are stored alongside UnknownRawPropertyLines and re-emitted verbatim.
The hard part is offset bookkeeping: interleaving changed known-property blobs with fixed-offset unknown blobs. A practical approach is to re-emit all blobs in their original order, giving changed known blobs a new offset and updating their property text, while unknown offsets stay put.
Part of the round-trip epic #21; the contract it fails is in openspec/specs/serialization-round-trip/spec.md ("Content the IDE does not understand SHALL survive a round-trip").
A design pass on this, recorded here so it is not re-derived. Nothing is built.
Purpose
Content HexIDE cannot interpret is carried through a save unchanged, with citations that correctly
address it. That is all. It requires no understanding of any OCX format, is immune to obfuscation, and is
testable today.
This is a gap against a requirement that already exists — "Content the IDE does not understand SHALL
survive a round-trip" — not a new capability. Unknown text is already preserved; binary is the half that
was never finished.
The one change to the approach above
The issue body suggests re-emitting all blobs in original order "while unknown offsets stay put". That
constraint comes only from re-emitting the unknown property line verbatim — which pins the offset,
because the offset is in the line. Drop that and the hard part largely evaporates:
Preserve every record in original order. Rewrite every citation, known and unknown alike, to wherever
its record landed.
Nothing changed → identical order and sizes → identical offsets, byte-identical both halves, for free.
Something changed → offsets shift coherently, and every citation still addresses its own record.
Rewriting a citation inside an otherwise-opaque line is cheap: FrxDeserializer.IsFrxReference already
identifies those lines, and the format is a fixed "name.frx":HHHH. That is container syntax, which
HexIDE necessarily already understands — not control format. The distinction runs through every decision
below.
This also satisfies #148's invariant by construction rather than by refusing, which is why this issue
retires read-only forms instead of creating them. The tests from #151 become its acceptance criteria
unchanged.
Decisions
question
decision
Understand OCX formats?
No. Preservation needs no comprehension, and comprehension is unbounded and defeated by obfuscation.
The axis
Direction, not provenance. Preserve for save (universal); interpret for display (opportunistic, read-only). Not first-party vs third-party — an in-box control and an obfuscated commercial one round-trip identically.
Reading vs writing
A reader that is 95% right is useful; a writer that is 95% right destroys files. Never re-encode. Replacing content is fine; round-tripping through a decoder is not.
Editing unmodelled content
Carried, not edited — and principled rather than pragmatic: the editing UI for an OCX property is the control's own property page, which is out of scope, so there is nothing to edit with.
Platform
Pass-through is the floor everywhere. Delegating to a real control is an enhancement available when one is registered — which is not merely a Windows/elsewhere split, since opening someone else's project on Windows without their controls hits the same wall.
Byte-identity
The container's obligation, not the control's. See #154.
Prior art
twinBASIC supports OCX binaries, but its forms engine is Windows-bounded and COM-native, so it delegates — it never understands a format. That solution cannot move to its planned cross-platform
engine, which will hit this same wall. There is no prior art for the cross-platform case.
Split out
A control HexIDE does not model is invisible in the designer #157 — an unmodelled control is invisible in the designer. This issue is what makes that dangerous: today the read-only gate hides it; afterwards such a form edits freely with an invisible control in it.
Now part of the in-box ActiveX controls epic, #1081. Its foundation issue, #1082, settles this as part of the design note that comes before any of the controls are built.
This follows the comment. For a form nobody changed, the two give the same bytes. They differ only once a modelled blob changes size. Then a verbatim line pins its record to an offset that may no longer be free, while a rewritten citation follows its record. So this rewrites the whole "name.frx":HHHH token, the file name included, which Save As needs.
Concretely.
The companion is laid out in the order the .frm cites its records, as now.
Every citation is written as a placeholder, and resolved once the whole .frm is written. That covers known properties, carried unknown ones, and the lines of a preserved unmodelled control. So the order can't drift from the text, as the List/ItemData order once did.
A citation that doesn't resolve still holds the form unsaveable, as today.
The real case is Forms/Web Browser.frm in VB6's Template tree. Its ImageList cites six pictures, and it is the one VB6-authored file in the corpus that doesn't round-trip (27/28). It should come back byte for byte.
#157 lands separately. Once this lands, Web Browser.frm opens, edits and saves, with its ImageList and Toolbar invisible in the designer. That is already true of any form whose unmodelled controls cite nothing, and nothing is damaged. #157 says it is "best landed with" this, not before it; it should come next.
Activity
The round-trip numbers were five commits stale (#128)
Save As is refused too, and refused before the picker (hexide-io#143)
Save As is refused too, and refused before the picker (#145)
A form opened without its .frx is held read-only instead of stripped (#…
imojenisys commentedon Sep 1, 2026
Design settled (2026-09-01) — not started
A design pass on this, recorded here so it is not re-derived. Nothing is built.
Purpose
Content HexIDE cannot interpret is carried through a save unchanged, with citations that correctly
address it. That is all. It requires no understanding of any OCX format, is immune to obfuscation, and is
testable today.
This is a gap against a requirement that already exists — "Content the IDE does not understand SHALL
survive a round-trip" — not a new capability. Unknown text is already preserved; binary is the half that
was never finished.
The one change to the approach above
The issue body suggests re-emitting all blobs in original order "while unknown offsets stay put". That
constraint comes only from re-emitting the unknown property line verbatim — which pins the offset,
because the offset is in the line. Drop that and the hard part largely evaporates:
its record landed.
Rewriting a citation inside an otherwise-opaque line is cheap:
FrxDeserializer.IsFrxReferencealreadyidentifies those lines, and the format is a fixed
"name.frx":HHHH. That is container syntax, whichHexIDE necessarily already understands — not control format. The distinction runs through every decision
below.
This also satisfies #148's invariant by construction rather than by refusing, which is why this issue
retires read-only forms instead of creating them. The tests from #151 become its acceptance criteria
unchanged.
Decisions
Prior art
twinBASIC supports OCX binaries, but its forms engine is Windows-bounded and COM-native, so it
delegates — it never understands a format. That solution cannot move to its planned cross-platform
engine, which will hit this same wall. There is no prior art for the cross-platform case.
Split out
vb6.exeoracle to an OCX-bearing form, blocked on this.PictureBoxrenders no picture at all today (independent bug).Say which party owes byte-identity (#154)
A control VB6 cannot load renders as an empty box, not a crossed one (#…
The round-trip numbers were five commits stale (#128)
Save As is refused too, and refused before the picker (#145)
A form opened without its .frx is held read-only instead of stripped (#…
Say which party owes byte-identity (#154)
A control VB6 cannot load renders as an empty box, not a crossed one (#…
imojenisys commentedon Oct 15, 2026
Now part of the in-box ActiveX controls epic, #1081. Its foundation issue, #1082, settles this as part of the design note that comes before any of the controls are built.
imojenisys commentedon Oct 22, 2026
In progress on branch
fix/60-carry-unknown-frx-blobs(hexide-frm).Which design. Two are on record, and they disagree after an edit:
This follows the comment. For a form nobody changed, the two give the same bytes. They differ only once a modelled blob changes size. Then a verbatim line pins its record to an offset that may no longer be free, while a rewritten citation follows its record. So this rewrites the whole
"name.frx":HHHHtoken, the file name included, which Save As needs.Concretely.
.frmcites its records, as now..frmis written. That covers known properties, carried unknown ones, and the lines of a preserved unmodelled control. So the order can't drift from the text, as the List/ItemData order once did.The real case is
Forms/Web Browser.frmin VB6's Template tree. Its ImageList cites six pictures, and it is the one VB6-authored file in the corpus that doesn't round-trip (27/28). It should come back byte for byte.#157 lands separately. Once this lands,
Web Browser.frmopens, edits and saves, with its ImageList and Toolbar invisible in the designer. That is already true of any form whose unmodelled controls cite nothing, and nothing is damaged. #157 says it is "best landed with" this, not before it; it should come next.An unknown control's .frx records survive a save, cited where they la…
Batch 38: #2025, #2028, #2031, #2034, #2036, #2037, #2046, #2047, #2044…