Skip to content

Unknown OCX properties that reference binary blobs are dropped on save #60

Closed

Description

@imojenisys

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").

Activity

  1. imojenisys commented on Sep 1, 2026

    @imojenisys
    ContributorAuthor

    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:

    • 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

  2. added
    mediumT-shirt size, never time. An open design decision AND a coupled fix. Anchor: PR #242.
    serializationReading and writing VB6's own files. Ends once a record's bytes are correct in memory.
    silently-wrongWrong output shown as right, a dropped or misplaced write, an accepted no-op, a file VB6 refuses.
    bugSomething isn't working
    and removed
    enhancementNew feature or request
    on Sep 20, 2026
  3. imojenisys commented on Oct 15, 2026

    @imojenisys
    ContributorAuthor

    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.

  4. self-assigned this
    on Oct 22, 2026
  5. imojenisys commented on Oct 22, 2026

    @imojenisys
    ContributorAuthor

    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":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.

  6. moved this from Todo to In Progress in HexIDE — next upon Oct 22, 2026
  7. moved this from In Progress to Done in HexIDE — next upon Oct 22, 2026
  8. added a commit that references this issue on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingmediumT-shirt size, never time. An open design decision AND a coupled fix. Anchor: PR #242.serializationReading and writing VB6's own files. Ends once a record's bytes are correct in memory.silently-wrongWrong output shown as right, a dropped or misplaced write, an accepted no-op, a file VB6 refuses.

Type

No type

Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions