Phase 4: Formulardateien und Konvertierung

This commit is contained in:
2026-09-05 08:18:55 +02:00
parent 54488b5b67
commit 05837cd846
18 changed files with 5287 additions and 27 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-04

View File

@@ -0,0 +1,88 @@
# Binäre Beispieldateien
Alle Prüfsummen sind SHA-256 über die dekomprimierte `.FRM`-Datei. Die Dateien
werden wegen ihrer fremden Lizenz nicht in das Repository übernommen; der Test
enthält nur eine Hexdarstellung des 245-Byte-Belegs `new.frm`.
## Microsoft Visual Basic for MS-DOS 1.00 Professional
Quelle: WinWorld-Abbild „Microsoft Visual Basic 1.0 Professional for MS-DOS
(1992) (3.5-1.44mb)", Archiv-SHA-1
`052ac72c28de119573611e09bb4011c9610efb10`; Dateien mit Microsofts
`DECOMP`-kompatiblem KWAJ-Verfahren aus `*.FR$` entpackt.
| Datei | SHA-256 |
|---|---|
| BOOKCARD.FRM | `f24625a21098d02814bb5ecad2a9e655b4f6a3a352dd9af31d7a4e2d5797ad86` |
| BOOKLIST.FRM | `7f3c091a87c899d3df4cbeacd7a507bdcdd0379bc61e3cad29175c4bcb7e0cb2` |
| BOOKLOOK.FRM | `4e7356eecf5f378ae25ca1481260c048eda57614925ce5ecf8318378c8414cd8` |
| BOOKSRCH.FRM | `af19b7b1716efaa1f44990fa90cf94553028c6fed6e09e40db4c2a2963c82ffa` |
| BOOKSTCK.FRM | `7983beaddc47c819922d77c0bfdc269e1033c09887adbdbb37df75bdca670f7d` |
| CALC.FRM | `189eda4d341804c54fe24ac5df696f7d7ceadec936776e89258ce75e78636201` |
| CHECK.FRM | `ed552601aca93c2a773159cb3fe13719560924e9fdaa286468bf3b8e051e52c9` |
| CHRTATTR.FRM | `8b2c86fc0ec3cae126e0c1b4e67a494302b82ad380aae617b4e855e66c1327cf` |
| CHRTDATA.FRM | `64ba285c863cb685c981d13bf0211034140fa2f8cb8d99d6d0911060cb017de8` |
| CHRTDEMO.FRM | `3a24445bd8ca68bb35a3e7ff07595d13c275c4240b5839f8ec5d8b71c15586ea` |
| CHRTFONT.FRM | `99bef5c1d9f578f01bcdbbd84d17213a040311a78affa94b4c275a1d6232e2fc` |
| CHRTTYPE.FRM | `f4f566a96f97274b9b50c0970e33760e0d111656f4fa376660d3d72c517768e6` |
| CLOCK.FRM | `97f75993a7558bfb0dbcb7aa8908731a5537ea6542e36b5ee61a30e32b16d3b4` |
| CMNDLGF.FRM | `7273671ef4a938251472a3a8221347f7717d969a69b6b1bfb6fa27412e26c580` |
| CONTROLP.FRM | `a8bb99de1600d44f74922e1d493c9b54afd5cf08aadfde26b453eedf7695dcee` |
| DEBUG.FRM | `e08348af465f3f8c14187a3b4f79a210669170e84a5884c38ccdf672246ffb1c` |
| FONTDEMO.FRM | `90730abb9eac5d1b3c7a0ed47ebfff086b68942742e3dac1f561bfe64273cb6e` |
| GRAPHICS.FRM | `5890134890a950a34d62fa69e2cd013b5b8233c590d924f42d21c6959b4ecb0f` |
| HELPF.FRM | `4dea528924fdcfad4bd21fef3ff2b6508f010dcca71a8e0c012a1ee6924b06d9` |
| HELPUTIL.FRM | `fe49a8c6086b8f77e77004eb624849647ca740e86d5659dd4a8bf44d5146ea48` |
| NOTEPAD.FRM | `24580d5fb284dee839ca26b03cc755b46d574b32b34896d37479ad9ad4379d40` |
| QLBVIEW.FRM | `358e7babd6b9205341a2e79611fd79c5838b546a327fbb0eefa36c69ef38db97` |
| SEEK.FRM | `2c7bf1df44fe59feef40853fb722ff981824662327eea9c4f8722fe38efb1b68` |
| SETUPMSG.FRM | `4c0826c937a6c1b1737e38719dffc6a73e24a544d04b450fb2ad393b84eb4630` |
| SETUPOPT.FRM | `e832533cd06ddb745656a94b0bf7d04eee9cc8da208cf22ebd3ba52eaba74fc4` |
| SETUPPTH.FRM | `814eeb2c94f11c3ab30b45d9b56a4af8973e79f4b01cd6710653bdd5ac2d0bfe` |
| SETUPSTS.FRM | `56c051320317048fba758e65920dde9878c2bafd37ad3f459c8e7ffb015d9cfd` |
| SPINDEMO.FRM | `259e67a3ce0eb72c2cd19a72b957944f5e1a4269425a254f83861c6175f9c738` |
| TORUS.FRM | `fe645610b661d6cf946e47134602c8bece26d8ff8054d85fcd030c62fbd6c620` |
## `github.com/cout/vbdos`
Quelle: Commit `1cdd2b32b829fe1721d0b6aecc433abc47a96fb6`; die beiden
`misc/mdi/*.zip` wurden vor dem Prüfen entpackt.
| Pfad | SHA-256 |
|---|---|
| graphics/graphics.frm | `5890134890a950a34d62fa69e2cd013b5b8233c590d924f42d21c6959b4ecb0f` |
| microsoft/check.frm | `ed552601aca93c2a773159cb3fe13719560924e9fdaa286468bf3b8e051e52c9` |
| microsoft/notepad.frm | `e11b76f60eb1f1e8fc7fdbc37acebfbae4b9d9d75f3dff77cc26e8cb33d43c97` |
| microsoft/qlbview.frm | `358e7babd6b9205341a2e79611fd79c5838b546a327fbb0eefa36c69ef38db97` |
| microsoft/seek.frm | `2c7bf1df44fe59feef40853fb722ff981824662327eea9c4f8722fe38efb1b68` |
| microsoft/spindemo.frm | `259e67a3ce0eb72c2cd19a72b957944f5e1a4269425a254f83861c6175f9c738` |
| misc/mentors/mentors.frm | `e3ac6a4ea998b6050f9baf066b2ce776dd9f2a7a276a87350a43243340e287fb` |
| misc/mdi/mdi.zip: bargraph.frm | `e44707a7b9ff918760729f520d828189e4bb5bc5eb7fbff0f01fc53ed8fe7530` |
| misc/mdi/mdi.zip: desktop.frm | `63c0ad0fb9f205e56d64879ccd6b00fc140eabc9d8804a10646e15abc6c10c6a` |
| misc/mdi/mdi.zip: draw.frm | `c59e4ccb540bd51f26c9021fa39554e094f70dbab373f21c89e2e62766a4ee6a` |
| misc/mdi/mdi.zip: graph.frm | `78065efb81d31cd4d5cb0e6a8c073686392d255c1142b66864092207b056d597` |
| misc/mdi/mdi.zip: icondraw.frm | `ebcfd115d5439bf3b3531516b2c843e862430b7fb4ea41763fa7767911489025` |
| misc/mdi/mdi.zip: new.frm | `a888365e84a1ed469e16b5aa82a7f09ff9145e83e7aac7ff1b615f3aac8634f4` |
| misc/mdi/mdi.zip: piano.frm | `371ba0ced7597cc3203e34f25b40870ef6da1fc9ab0ef619ce7855d18416112f` |
| misc/mdi/mdi.zip: pingpong.frm | `33e322462efa0b7e0855ad34a9682c5556b13dde18c27c443c4196327776ea0f` |
| misc/mdi/mdi.zip: scribble.frm | `e9aa68bba8e39dde7259c1f3ce1c6aed418ffb191a7c2fab9ca2be6c0edcacce` |
| misc/mdi/mdi2.zip: calc.frm | `b06bdf0baa104c19fc147e47397cd353aa1cfbfcab6989a606b190dda0a93fb4` |
| misc/mdi/mdi2.zip: clock.frm | `632393c79f8b895887f04680f68fbcca1bcd6d005009a7bd42e6d1d192f7351c` |
| misc/mdi/mdi2.zip: cmndlgf.frm | `7273671ef4a938251472a3a8221347f7717d969a69b6b1bfb6fa27412e26c580` |
| misc/mdi/mdi2.zip: controlp.frm | `d4654cd54596a536f16ed828882ff6e3bb231e00430a6037d72c449a71dc24b8` |
| misc/mdi/mdi2.zip: debug.frm | `e08348af465f3f8c14187a3b4f79a210669170e84a5884c38ccdf672246ffb1c` |
| misc/mdi/mdi2.zip: fastedit.frm | `5f5bc1abe48c1d97f864795df7ebbc81a1180cf246c223f94c8781ea1296da9c` |
| misc/mdi/mdi2.zip: mdi.frm | `af7a30b08d8e8d8634019173e7a55d9e27717d3a49f1cd8e2586021d3dcdca01` |
| misc/mdi/mdi2.zip: run.frm | `e7d5cb04168d3db60b3fe98327e206407a088c7d44f6cc73405c80b7644addff` |
| misc/mdi/mdi2.zip: scribble.frm | `c96250f8529dc875deab7bfcb27fd0cfbc46b0959d82ceffcc991ca5b6d9b0d7` |
| misc/mdi/mdi2.zip: wmaster.frm | `53d6caa956ffc6cc714ec13ca4cd76186a209857330eb35c1f4ccd7d463f0f9c` |
| misc/mdi/mdi2.zip: wmsetup.frm | `963f9e8eb45858afc84c4a530146c1da5047472542308c8f53a993067b775522` |
## Konvertierungslauf
`tbc convert-frm` wurde am 2026-09-04 über alle 56 oben aufgeführten Dateien
ausgeführt: 56 konvertiert, 0 strukturelle Abbrüche, 4 konkrete Warnungen. Die
Warnungen betreffen `VSpin.CustomControl` und `HSpin.CustomControl` in den zwei
aufgeführten Kopien von `SPINDEMO.FRM`; beide Objekte werden ausgelassen und
nicht als `Screen` fehlinterpretiert. Jede Warnung nennt den Objektnamen und
die tatsächliche Byteposition.

View File

@@ -0,0 +1,88 @@
# Design — Formulardateien und Konvertierung
## Context
Siehe proposal.md — Why. Ausgangslage: `docs/dateiformate.md` hält das
Schema des Windows-Schwesterprodukts als Vorlage fest und markiert die
exakte Serialisierung als offen. Die Formularbeschreibung, die gelesen
und geschrieben wird, stammt aus `phase-4-objektmodell`; dieser Change
fügt nur Ein- und Ausgabe hinzu.
## Goals / Non-Goals
**Goals:**
- Ein Format, das ein Mensch im Editor bearbeiten kann und das ein
Werkzeug reproduzierbar schreibt.
- Binäre Beispieldateien werden lesbar, ohne dass wir das Binärformat
jemals schreiben.
**Non-Goals:**
- Binärkompatibilität in Schreibrichtung.
- Verlustfreie Übernahme von Eigenschaften, die es bei uns nicht gibt
(etwa reine Windows-Eigenschaften aus Version 2.00).
## Decisions
### D1 — Wir legen die Serialisierung fest und dokumentieren sie als Referenz
Es gibt keine Originalfassung zum Nachbilden. Festgelegt wird das
Sparsamste, das den Rundlauf trägt:
```
VERSION 1.00
Begin Form Form1
Caption = "Beispiel"
Height = 15
Begin CommandButton cmdOK
Caption = "&OK"
End
End
```
- Drei Leerzeichen Einrückung je Ebene, Eigenschaftsname auf 16 Spalten
aufgefüllt, `=` mit zwei Leerzeichen davor und danach — die Optik des
Vorlagenschemas.
- Nur Eigenschaften, die vom Vorgabewert abweichen. Das hält Dateien
klein und macht Vorgabewertänderungen sichtbar, statt sie in
Altbeständen einzufrieren.
- Reihenfolge: alphabetisch wie in der Forms-Referenz je Klasse, nicht die
Einfügereihenfolge — sonst hängt die Datei von der Bedienung des
Designers ab.
### D2 — Rundlauf ist die Prüfung, nicht der Vergleich mit einer Sollddatei
Da das Format unsere Festlegung ist, prüft ein Vergleich gegen eine
handgeschriebene Solldatei nur uns selbst. Aussagekräftig ist der
Rundlauf: lesen, schreiben, byte-vergleichen — und zusätzlich
schreiben, lesen, Beschreibung vergleichen. Beide Richtungen fangen
verschiedene Fehler.
### D3 — Der Binärleser bleibt eigenständig und liest nur
Das Reverse Engineering erschließt den Aufbau aus den Beispieldateien.
Der Leser übersetzt in dieselbe Formularbeschreibung wie der Textleser
und teilt sich mit ihm die Prüfungen. Was er nicht erkennt, bricht ab —
eine „beste Vermutung" hinterließe ein Formular, das anders aussieht als
das Original, ohne dass jemand es merkt.
Unbekannte Eigenschaften aus dem Windows-Erbe werden beim Konvertieren
namentlich gemeldet und übersprungen; die Zusammenfassung nennt sie am
Ende, damit sie nicht in der Ausgabeflut untergehen.
## Risks / Trade-offs
- **Das Binärformat ist nur teilweise erschließbar** → der Befehl bricht
ab, statt zu raten; die dokumentierte Fundstelle sagt, wo es endete.
- **„Nur Abweichungen schreiben" verliert Werte, wenn sich ein
Vorgabewert ändert** → die Vorgabewerte stehen im Inventar und in der
Forms-Referenz; eine Änderung dort ist ein bewusster Vorgang.
- **Version 2.00 kennt Eigenschaften, die wir nicht haben** → beim Lesen
namentlich gemeldet und übersprungen, nicht als Fehler behandelt.
## Open Questions
- Ob eine Formulardatei mehrere Formulare enthalten darf, klärt sich mit
der Projektverwaltung (`.MAK`, Phase 5); bis dahin gilt: ein Formular
je Datei.

View File

@@ -0,0 +1,63 @@
# Phase 4 — Formulardateien (`.FRM`) und Konvertierung
## Why
Formulare müssen sich speichern und laden lassen, sonst gibt es weder
den Formular-Designer der Phase 5 noch den Kompatibilitätstest an den
Programmen des Vorbilds. Das Vorbild kannte zwei Formate: binär
(Standard) und Text. Terminal Basic implementiert nur das Textformat —
das Binärformat ist Nicht-Ziel, seine Dateien müssen aber lesbar werden,
denn die Beispielprojekte des Originalpakets und die Programme aus
`github.com/cout/vbdos` liegen **alle** binär vor.
Ein wörtliches Original-Beispiel einer Text-`.FRM` war nicht auffindbar
(Befund in `docs/dateiformate.md`). Die Serialisierung ist deshalb
festzulegen und zu dokumentieren, statt sie zu rekonstruieren — der
einzige Punkt der Phase, an dem die Leitplanke „Referenzverhalten schlägt
Eleganz" mangels Referenz nicht greift.
Dieser Change hängt weder an den Steuerelementen noch an der
Ereignisschleife: er beschreibt Formulare, er stellt sie nicht dar.
## What Changes
- **Textformat festlegen und dokumentieren**: `VERSION`-Zeile,
verschachtelte `Begin <Klasse> <Name> … End`-Blöcke,
`Eigenschaft = Wert`-Zeilen, danach der BASIC-Code des
Formularmoduls. Festgelegt werden Kopfzeile, Einrückung, Reihenfolge
und die Regel, welche Eigenschaften überhaupt geschrieben werden.
Angenommen werden die Versionen 1.00 und 2.00.
- **Lesen und Schreiben**: Eine `.FRM` wird in eine
Formularbeschreibung gelesen und aus ihr wieder geschrieben; das
erneute Schreiben einer gelesenen Datei MUST dieselbe Datei ergeben.
- **Fehlerhafte Dateien**: Unbekannte Klasse, unbekannte Eigenschaft,
unpassender Wert und unausgeglichene Blöcke werden mit Datei, Zeile
und Name gemeldet, nicht stillschweigend übergangen.
- **`tbc convert-frm`**: Konvertierung binärer `.FRM` (Magic
`FC 08 01 00`) in unser Textformat, als Gegenstück zum
Konvertierungswerkzeug des Vorbilds. Das Binärformat wird per Reverse
Engineering aus den Beispieldateien des Originalpakets und des
cout/vbdos-Repos erschlossen und dokumentiert.
**Non-Goals:** Schreiben des Binärformats; Darstellung oder Ausführung
der beschriebenen Formulare (Changes `phase-4-objektmodell` und
`phase-4-steuerelemente`); der Formular-Designer (Phase 5).
## Capabilities
### New Capabilities
- `forms-dateiformat`: Textformat der Formulardateien — Aufbau, Lesen,
Schreiben, Fehlermeldungen bei fehlerhaften Dateien — sowie die
Konvertierung binärer Formulardateien des Vorbilds.
## Impact
- `crates/tb-ui`: Leser und Schreiber des Textformats auf der
Formularbeschreibung aus `phase-4-objektmodell`.
- `crates/tb-cli`: Unterbefehl `convert-frm`.
- `docs/dateiformate.md`: Das TODO zur Serialisierung wird durch die
festgelegte Fassung ersetzt; das Binärformat wird beschrieben, soweit
erschlossen.
- `tests/`: Beispieldateien und ihre erwartete Formularbeschreibung.
- PLAN.md: Punkte „`.FRM`-Textformat" und „Konvertierungstool".

View File

@@ -0,0 +1,70 @@
## Purpose
Das Formulardateiformat legt fest, wie ein Formular samt seiner
Steuerelemente und seines Codes als Textdatei abgelegt, wieder gelesen
und aus binären Dateien des Vorbilds übernommen wird — die Grundlage
dafür, dass Formulare überhaupt gespeichert und ausgetauscht werden
können.
## ADDED Requirements
### Requirement: Aufbau des Textformats
Eine Formulardatei SHALL aus einer `VERSION`-Zeile, einem
Beschreibungsteil und einem Codeteil bestehen. Der Beschreibungsteil
SHALL aus verschachtelten Blöcken `Begin <Klasse> <Name>``End` mit
Zeilen `Eigenschaft = Wert` bestehen; Zeichenketten stehen in
Anführungszeichen. Der Codeteil SHALL gewöhnlicher Quelltext des
Formularmoduls sein. Die Versionen `1.00` und `2.00` SHALL angenommen
werden; eine andere Version MUST mit Nennung der vorgefundenen Version
abgewiesen werden.
#### Scenario: Formular mit einem Steuerelement
- **WHEN** eine Datei ein `Form`-Blockelement mit einem eingebetteten `CommandButton`-Block und anschließendem `SUB`-Code enthält
- **THEN** entsteht daraus eine Formularbeschreibung mit einem Steuerelement und dem zugehörigen Quelltext
#### Scenario: Unbekannte Version
- **WHEN** die Datei mit `VERSION 3.00` beginnt
- **THEN** wird sie abgewiesen und die Meldung nennt `3.00`
### Requirement: Schreiben ist die Umkehrung des Lesens
Das Schreiben einer gelesenen Formularbeschreibung SHALL dieselbe Datei
ergeben. Geschrieben SHALL nur werden, was vom Vorgabewert abweicht;
Reihenfolge und Einrückung SHALL festgelegt und dokumentiert sein, damit
zwei Läufe dieselbe Datei erzeugen.
#### Scenario: Rundlauf
- **WHEN** eine Formulardatei gelesen und unverändert wieder geschrieben wird
- **THEN** ist die geschriebene Datei byte-gleich zur gelesenen
#### Scenario: Vorgabewerte werden nicht geschrieben
- **WHEN** ein Steuerelement nur Vorgabewerte trägt
- **THEN** enthält sein Block außer `Begin`/`End` keine Eigenschaftszeile
### Requirement: Fehlerhafte Dateien werden benannt
Eine unbekannte Klasse, eine für die Klasse unbekannte Eigenschaft, ein
Wert außerhalb des Wertebereichs und ein unausgeglichener Block MUST je
mit Dateiname, Zeilennummer und dem betroffenen Namen gemeldet werden.
Eine solche Datei MUST NOT teilweise übernommen werden.
#### Scenario: Unbekannte Eigenschaft
- **WHEN** ein `CommandButton`-Block die Zeile `Farbe = 3` enthält
- **THEN** nennt die Meldung Datei, Zeile, `CommandButton` und `Farbe`
#### Scenario: Unausgeglichener Block
- **WHEN** einer Datei ein `End` fehlt
- **THEN** nennt die Meldung die Zeile des offenen `Begin`-Blocks
### Requirement: Konvertierung binärer Formulardateien
Ein Unterbefehl SHALL eine binäre Formulardatei des Vorbilds (Kennung
`FC 08 01 00`) in das Textformat übersetzen. Eine nicht erkannte oder
beschädigte Datei MUST mit Nennung der Fundstelle abgewiesen werden;
eine Teilausgabe MUST NOT entstehen. Der erschlossene Aufbau des
Binärformats SHALL dokumentiert sein.
#### Scenario: Bekannte Beispieldatei
- **WHEN** eine binäre Beispieldatei konvertiert wird
- **THEN** entsteht eine Textdatei, deren Lesen dieselbe Formularbeschreibung ergibt wie die dokumentierte Erwartung
#### Scenario: Fremde Datei
- **WHEN** eine Datei ohne die Kennung übergeben wird
- **THEN** bricht der Befehl mit einer Meldung ab und schreibt keine Ausgabedatei

View File

@@ -0,0 +1,24 @@
## 1. Belege und Festlegung
- [x] 1.1 Vorhandene binäre Beispieldateien aus dem Originalpaket und `github.com/cout/vbdos` sammeln und in `beispieldateien.md` dieses Changes mit Herkunft auflisten; verifiziert durch die Liste mit Prüfsumme je Datei
- [x] 1.2 Serialisierung nach D1 festlegen und in `docs/dateiformate.md` an die Stelle des TODO schreiben; verifiziert durch den Abschnitt mit vollständigem Beispiel
## 2. Textformat lesen und schreiben
- [x] 2.1 Leser für `VERSION`, verschachtelte `Begin`/`End`-Blöcke und Eigenschaftszeilen; verifiziert durch Unit-Tests für ein Formular mit eingebettetem Steuerelement und für eine unbekannte Version
- [x] 2.2 Prüfungen gegen Klasse, Eigenschaftsname und Wertebereich mit Datei, Zeile und Name in der Meldung; verifiziert durch Unit-Tests je Fehlerart, einschließlich unausgeglichenem Block
- [x] 2.3 Codeteil vom Beschreibungsteil trennen und unverändert durchreichen; verifiziert durch einen Test, der den Quelltext byte-gleich zurückgibt
- [x] 2.4 Schreiber nach D1 — Einrückung, Spaltenbreite, Reihenfolge, nur Abweichungen; verifiziert durch einen Test gegen das dokumentierte Beispiel
- [x] 2.5 Rundlauf in beiden Richtungen (D2); verifiziert durch Tests „lesen, schreiben, byte-gleich" und „schreiben, lesen, Beschreibung gleich"
## 3. Binäre Dateien
- [x] 3.1 Aufbau des Binärformats aus den Dateien aus 1.1 erschließen und in `docs/dateiformate.md` beschreiben; verifiziert durch die Beschreibung mit Feldtabelle und Kennung `FC 08 01 00`
- [x] 3.2 Binärleser auf dieselbe Formularbeschreibung wie der Textleser; verifiziert durch einen Test, der eine Beispieldatei liest und gegen die dokumentierte Erwartung prüft
- [x] 3.3 Unbekannte Eigenschaften namentlich melden und überspringen, unerkannte Struktur mit Fundstelle abbrechen; verifiziert durch Unit-Tests für beide Fälle
- [x] 3.4 Unterbefehl `tbc convert-frm` mit Abbruch ohne Teilausgabe; verifiziert durch einen Test, der nach dem Abbruch das Fehlen der Ausgabedatei prüft
## 4. Abnahme
- [x] 4.1 Alle Beispieldateien aus 1.1 konvertieren und die Zusammenfassung der übersprungenen Eigenschaften festhalten; verifiziert durch den Lauf über den gesamten Bestand
- [x] 4.2 `cargo test` grün und `openspec validate phase-4-frm --strict` ohne Befund; verifiziert durch beide Kommandos