Files
tony-remake/NOTES.md
T

1889 lines
107 KiB
Markdown

# Kellogg's Tony and Friends — Remake-Projekt, Analyse-Notizen
## Ziel
Plattformunabhängiges Remake (Python) von "Kellogg's Tony and Friends" (DOS, 1994),
lauffähig auf x64 ohne DOS/QEMU. Ursprung: Spiel crasht in unserer FreeDOS-VM
(RTM.EXE-Engine vs. QEMU-VGA-Emulation, siehe Projekt "Classic Games" für den
Debug-Marathon dazu) — daher der Wunsch für ein eigenes Remake.
## Quelldateien (aus VM-Disk `dos_shareware` extrahiert, liegen in `raw/`)
- `RTM.EXE` — die eigentliche Spiel-Engine (Runtime), DOS-EXE (MZ-Header), 108 KB.
- `START.EXE` / `SETUP.EXE` — Launcher/Konfigurationsprogramme.
- `KELLOGG.INI` — Soundkarten-Konfig (IRQ/DMA), irrelevant fürs Remake.
- `PCKELL.DAT` — 2,69 MB, **unkomprimiert**. Beginnt mit einer monoton
steigenden 16-Bit-LE-Offset-Tabelle (vermutlich Sprite-/Block-Offsets in
die anhängenden Rohdaten). Enthält laut `SRB.LST` 32 "SRB"-Blöcke
(vermutlich "Sprite Resource Block"). Tabellenstruktur noch nicht final
verstanden — steigt über mehr Einträge als die reinen 32 Top-Level-Files,
also vermutlich verschachtelt (pro Sprite/Frame-Offsets innerhalb jedes
SRB-Blocks). TODO: Tabellenende finden und pro-SRB-Grenzen bestimmen.
- `PCKELL.SET`**ist tatsächlich eine ZIP-Datei** (Signatur `PK\x03\x04`)!
Enthält 3 Einträge:
- `APPEND.LST` (Methode 6 "Imploded", 1945 Bytes entpackt) — Manifest mit
178 Dateinamen für die in `PCKELL.PRE` zusammengefügten Assets.
- `SRB.LST` (Methode 1 "Shrink", 314 Bytes entpackt) — Manifest mit den
32 Dateinamen, die zu `PCKELL.DAT` gehören (1:1 gleiche Basisnamen wie
ein Teil der BOB-Einträge in APPEND.LST, z.B. `TONY.SRB``TONY.BOB`).
- `PCKELL.PRE` (Methode 6 "Imploded", 2.328.934 Bytes entpackt) — die
eigentliche Asset-Sammlung, konkateniert nach der Reihenfolge in
`APPEND.LST`.
**WICHTIG — Kompressions-Falle:** ZIP-Methode 6 "Imploding" ist NICHT
dasselbe wie das "DCL Implode/Explode"-Format aus PKWAREs Data
Compression Library (dafür gibt's `blast.c` in zlib/contrib — hab ich
zuerst versucht, schlägt fehl mit "distance too far back", weil es ein
anderer Algorithmus ist trotz gleichem Namen). Debian/Ubuntus
`unzip` (Info-ZIP, kompiliert MIT Explode+Unshrink-Support) entpackt
beide Methoden aber klaglos — auf `aria-wohnung` funktioniert
`unzip PCKELL.SET` direkt. Das Alpine-`unzip` im Brain-Container
(aria-proxy) kann das NICHT (kein Implode-Support einkompiliert).
## Datei-Typen in APPEND.LST (178 Einträge total)
| Ext | Anzahl | Vermutung |
|------|--------|-----------|
| PCC | 77 | "Compiled Picture" — vermutlich RLE/Palette-komprimiertes Vollbild (Titelscreens, Menüs, Hintergründe) |
| BOB | 32 | "Blob" — Sprite-Grafik-Daten (Spielfiguren, Objekte), 1:1 mit den 32 SRB-Einträgen |
| MAP | 22 | Level-/Karten-Daten |
| ARE | 22 | "Area" — vermutlich Level-Metadaten/Kollisionszonen, im Paar mit MAP (22=22) |
| ICO | 19 | Icons (z.B. für Menüs/HUD) |
| SAM | 3 | Sound-Samples |
| TFX | 3 | vermutlich Musik/Sound-Effekt-Format |
## Nächste Schritte (in dieser Reihenfolge sinnvoll)
1. **PCKELL.PRE aufteilen**: Die 178 Dateien sind laut APPEND.LST hintereinander
konkateniert — es fehlt noch die Größentabelle pro Datei (vermutlich am
Anfang von PCKELL.PRE selbst, ähnlich der Offset-Tabelle in PCKELL.DAT,
oder implizit aus dem Dateiformat jedes Typs ableitbar). Header von
PCKELL.PRE selbst noch nicht angeschaut.
2. **Ein PCC-Bild reverse-engineeren** — vermutlich klassisches Palette+RLE,
das dürfte am leichtesten zu knacken sein (PCC ähnelt oft PCX-Varianten).
3. **BOB/SRB-Sprite-Format verstehen** — Kollisions-Header aus PCKELL.DAT
(die Offset-Tabelle) mit den 32 SRB-Namen abgleichen.
4. **RTM.EXE nicht disassemblieren müssen** — Ziel ist ein sauberes
Python-Reimplement der Spiellogik (Physik/Level/Gegner aus MAP/ARE lesen),
nicht 1:1-Code-Nachbau der DOS-Engine.
## Aufbewahrungsort
Alle Rohdaten + Manifeste liegen unter `raw/` in diesem Projektordner
(`/shared/projects/kellogg_s_remake/raw/`). `raw/extracted/` enthält den
fehlgeschlagenen blast.c-Versuch (kann weg). Entpackte Dateien
(`APPEND.LST`, `SRB.LST`, `PCKELL.PRE` — alle bereits entpackt, nicht mehr
komprimiert) liegen direkt in `raw/`.
## DOSBox-Testumgebung (2026-07-22) — Original läuft jetzt!
**DOSBox 0.74-3 ist auf `aria-wohnung` installiert** (apt). Zweck: das
Original-Spiel als visuelle/funktionale Referenz neben dem Reverse-Engineering
laufen lassen, ohne die kaputte QEMU-VGA-Emulation aus dem "Classic Games"-Crash.
### Fehlende Dateien nachgeliefert
`raw/` hatte urspr. nur `RTM.EXE` + `PCKELL.*`**`START.EXE`, `SETUP.EXE`,
`KELLOGG.INI`, `HDPMI16.EXE` (DPMI-Host) und `DPMI16BI.OVL`** fehlten (waren
nie aus der `dos_shareware`-VM-Disk kopiert worden, nur RTM+PCKELL). Auch ein
`READ.ME` (Original-Textdatei mit Installations-/Troubleshooting-Hinweisen)
lag dort. Alle liegen jetzt in `raw/` (Quelle: `dos_shareware`-Diskimage auf
`aria-wohnung`, Pfad `GAMES/KELLOGG/` — per `qemu-nbd --read-only` gemountet,
VM dafür kurz gestoppt, danach sauber wieder disconnected. **Die VM
`dos_shareware` ist seitdem gestoppt** — bei Bedarf über's Cockpit-Panel oder
`aria-vm boot dos_shareware` wieder hochfahren).
**RTM.EXE ist die eigentliche Engine**, aber sie verweigert den Direktstart
("This loader is invoked and required by Borland tools") — man MUSS über
`START.EXE` gehen, das dann RTM.EXE korrekt aufruft.
### Der Show-Stopper: IRQ-Konflikt (aus dem Original-READ.ME!)
Mit den Original-`KELLOGG.INI`-Werten (`Adr=544 DMA=1 IRQ=7`, Soundblaster)
hängt sich das Spiel nach dem 2. Studio-Logo (Factor 5) in einer Endlosschleife
auf — kompletter schwarzer Screen, DOSBox-Prozess lebt aber (kein Crash,
Freeze). Das READ.ME beschreibt dieses EXAKTE Symptom ("Bild bleibt schwarz")
und empfiehlt IRQ 5 statt 7. Fix:
- `raw/KELLOGG.INI`: `IRQ=5` (statt 7)
- `dosbox.conf` `[sblaster]`-Sektion MUSS matchen: `irq=5` (sonst redet DOSBox's
emulierte SB16 am falschen IRQ vorbei und das Spiel hängt trotzdem)
Mit diesem Fix läuft das Spiel sauber durch: Rauser-Advertainment-Logo →
Factor-5-Logo → Kellogg's-Logo → "Tony & Friends in Kellogg's Land"-Titelbild
→ Weltkarte (Berge/Wald/Schloss mit Drache, alle Charaktere sichtbar:
Tiger/Tony, Toucan, Frosch, Affe/Coco, Bär). Screenshots liegen unter
`/shared/uploads/aria_kellogg_*.png`.
### Funktionierende dosbox.conf
```
[sdl]
fullscreen=false
output=surface
[sblaster]
sbtype=sb16
sbbase=220
irq=5
dma=1
hdma=5
[autoexec]
mount c <pfad-zu-raw>
c:
START.EXE
```
### Screenshot-Workflow (headless, ohne echten Desktop)
`aria-wohnung` hat keinen laufenden X-Server. Screenshots via
`xvfb-run -s '-screen 0 1024x768x24' dosbox ...` + `import -window root`
(ImageMagick) im selben xvfb-run-Prozess. Tasteneingaben simulierbar mit
`xdotool key <taste>` (kein `--window`-Targeting nötig, funktioniert auch
ohne Fenstermanager in Xvfb). Audio läuft im ALSA-nosound-Modus (keine echte
Soundkarte im Container/VM) — DOSBox emuliert die SB16 trotzdem korrekt genug
fürs IRQ-Timing, es kommt nur kein tatsächlicher Ton raus.
### Steuerung (aus READ.ME, für spätere Gameplay-Tests)
Pfeil links/rechts = laufen, Leertaste/Pfeil-hoch = springen, Pfeil-runter =
ducken, ALT = Sonderfunktion/Tür öffnen, P = Pause, ESC = zurück zur Karte
(ohne Leben zu verlieren).
## PCKELL.PRE Container-Format (2026-07-22) — Kernstruktur geknackt
Ziel: die 178 in `APPEND.LST` gelisteten Dateien (32 BOB, 77 PCC, 22 MAP,
22 ARE, 19 ICO, 3 SAM, 3 TFX) sauber aus `PCKELL.PRE` (2.328.934 Bytes,
bereits ZIP-entpackt) raustrennen. Tools liegen in `tools/split_pre.py`
(aktuellster Stand, laeuft auf `aria-wohnung` mit `python3`, braucht
`raw/PCKELL.PRE` + `raw/APPEND.LST` + `raw/SRB.LST`).
### Zwei Header-Varianten je nach Dateityp
**`.BOB`-Eintraege (32x, Sprite-Rohdaten):** kein Tag-Slot, Header direkt
lueckenlos hintereinander:
```
[4-Byte-LE-Laenge][NUL-terminierter Original-DOS-Pfad
"H:\WIWA\ENGINE\GAMEDATA\<NAME>.BOB"][<Laenge> Bytes Rohdaten]
```
Danach folgt variables Zero-Padding (~219-226 Byte beobachtet) bis zum
naechsten Header. **Vollstaendig validiert: alle 32 BOB-Eintraege exakt
gefunden, 0 Namens-Mismatches gegen APPEND.LST.** Robusteste Methode zum
Finden: direkte Byte-Suche nach `H:\WIWA\ENGINE\GAMEDATA\<erwarteter Name>`
im File (nicht raten, den vollen erwarteten Namen pro Eintrag einzeln
suchen — das ist wasserdicht).
**Alle anderen Typen (PCC/MAP/ARE/ICO/SAM/TFX, 146x):** kein eingebetteter
Pfad, dafuer ein **256-Byte-Tag-Slot** VOR dem eigentlichen Header:
```
[Tag-Slot, exakt 256 Byte lang:
1-Byte-Namenslaenge + Dateiname (z.B. 0x0B "CHOCOS7.PCC")
+ Rest mit Speicher-Muell des Kompilier-Tools aufgefuellt]
[bei Tag-Slot-Start + 256: 4-Byte-LE-Laenge][<Laenge> Bytes Rohdaten]
```
Bestaetigt an mehreren Stellen (ANTS.PCC, CHOCOS7.PCC, und der
"Master"-Tag "A.SRB" ganz am Dateianfang vor A.BOB). **Wichtige Falle:**
beim Uebergang von der letzten `.BOB`- zur ersten `.PCC`-Datei (DOORS.BOB
-> ANTS.PCC) ueberlappt der Tag-Slot von ANTS.PCC um ~34 Byte in das, was
man anhand der BOB-Laengenangabe als "Ende von DOORS.BOB" berechnen wuerde.
Grund noch nicht restlos geklaert (vermutlich rundet der Original-Compiler
beim Sektionswechsel BOB->PCC anders). Workaround im Skript: Tag-Suche
zuerst vorwaerts ab erwarteter Position, bei Fehlschlag Fallback mit
300 Byte Sicherheitsabstand rueckwaerts.
**Kuriosum:** Der Tag fuer `.BOB`-Eintraege (nur beim allerersten,
"A.SRB" vor A.BOB) nennt nicht den eigenen Namen, sondern den
`.SRB`-Begleitnamen aus `SRB.LST` (1:1 gleiche Reihenfolge wie die
BOB-Liste in APPEND.LST). Passt zur bekannten SRB<->BOB-Kopplung
(Kollisions-Metadaten in PCKELL.DAT <-> Sprite-Pixeldaten in PCKELL.PRE).
### PCC-Bildformat (erste Erkenntnisse, noch nicht fertig decodiert)
Nutzdaten beginnen mit einem PCX-aehnlichen 16-Byte-Header:
```
Byte 0: 0x0A (Manufacturer-Marker, wie ZSoft PCX)
Byte 1: 0x05 (Version)
Byte 2: 0x01 (Encoding = RLE)
Byte 3: 0x08 (Bits pro Pixel = 8, also 256 Farben)
Byte 4-11: bisher immer 0 gesehen (Xmin/Ymin/Reserved?)
Byte 12-13: Breite (LE16), z.B. 0x0140 = 320
Byte 14-15: Hoehe (LE16), z.B. 0x00C8 = 200
Byte 16+: vermutlich RLE-komprimierte Pixel-Indexdaten (klassisches
PCX-RLE: Byte mit oberen 2 Bits gesetzt (0xC0-0xFF) = Lauflaenge
(Byte&0x3F), gefolgt vom Wert-Byte; sonst literaler Pixel)
```
Farbpalette (256x3 Byte RGB) wahrscheinlich NICHT pro Datei (dafuer waeren
die kleinen Dateien wie ANTS.PCC mit nur 900 Byte total zu klein) — vermutlich
eine globale Palette, die noch gefunden werden muss (Kandidat: irgendwo in
RTM.EXE oder als eigene Ressource). TODO fuer naechste Session.
### PCKELL.PRE Splitter — GELOEST (2026-07-22, ~02:15 Ortszeit)
Der vermeintliche "14%-Drift-Bug" war GAR KEIN Bug im Tag-Matching. Diagnose
(`tools/diag_split.py`, inzwischen geloescht, siehe Git-History falls noetig)
zeigte: jeder der 178 Tags aus `APPEND.LST` wird exakt EINDEUTIG gefunden
(genau 1 Kandidat, Gap durchgehend 0, ausser dem bekannten BOB->PCC-
Uebergang mit -34 Byte). Der Splitter war also die ganze Zeit korrekt —
er endete nur zu frueh, weil **`APPEND.LST` gar nicht alle Eintraege in
`PCKELL.PRE` auflistet.**
Direkt im Anschluss an den letzten `APPEND.LST`-Eintrag (`TITEL2.SAM`,
endet bei `0x1e9aac`) folgen, im EXAKT GLEICHEN Tag-Slot-Format, noch
**3 weitere, nicht gelistete Eintraege**:
```
DPMI16BI.OVL 65312 Byte
RTM.EXE 108686 Byte
START.EXE 148480 Byte
```
Der letzte davon (`START.EXE`) endet bei `0x238966`**exakt** die
Dateigroesse von `PCKELL.PRE`. Damit ist das Container-Format zu 100%
verifiziert, kein einziges Byte bleibt unerklaert. Macht inhaltlich auch
Sinn: `PCKELL.PRE` ist offenbar ein self-contained Redistributable-Paket,
das die komplette DOS-Extender-Runtime (DPMI-Server + Runtime-EXE +
Launcher) gleich mitbuendelt — nicht nur Spiel-Assets.
**Extra-Validierung:** die 3 extrahierten System-Dateien sind
byte-identisch (`cmp`) zu den Einzeldateien, die schon lose in `raw/`
liegen (aus der `dos_shareware`-VM kopiert) — zweite unabhaengige
Bestaetigung, dass die Extraktion korrekt ist.
**`tools/split_pre.py` (finale v7) extrahiert jetzt alle 181 Dateien
fehlerfrei nach `extracted_pre/`:**
```
32 BOB, 77 PCC, 22 MAP, 22 ARE, 19 ICO, 3 SAM, 3 TFX, 1 OVL, 2 EXE
```
Alte Splitter-Zwischenversionen (v1-v6, Diagnose-Scripts) sind auf
`aria-wohnung` (`/home/aria/kellogg_remake/tools/`) aufgeraeumt — nur
noch `split_pre.py` (v7, final) liegt dort, identisch zur Kopie hier im
Projekt unter `tools/split_pre.py`. `extracted_pre/` (2,6 MB, 181 Dateien)
liegt jetzt auch hier im Projekt, nicht nur auf `aria-wohnung`.
### PCC-Header — ergaenzender Fund
Bei `MENU.PCC` sind Byte 4-11 NICHT alle Null (anders als bei `ANTS.PCC`
vermutet) — sie enthalten `Xmax`/`Ymax` (0x013f=319, 0x00c7=199), passend
zum klassischen PCX-Header-Layout `Xmin(2) Ymin(2) Xmax(2) Ymax(2)` bei
Offset 4. Fuer ein 320x200-Bild ab (0,0) macht Xmax=319/Ymax=199 Sinn.
Bei `ANTS.PCC` (kleines 900-Byte-Sprite-Preview-Bild) stehen dort
durchgehend Nullen — vermutlich weil die Original-Autoren dieses Feld
bei kleineren generierten Bildern nicht befuellt haben. Fuer den
PCC-Decoder erstmal ignorierbar, Breite/Hoehe kommt zuverlaessig aus
Offset 12-15.
### Naechste Schritte (Prioritaet) — ALT, siehe unten "PCC-Format GELOEST" fuer aktuellen Stand
~~1. Ein PCC-Bild vollstaendig decodieren~~ ERLEDIGT (siehe unten).
2. BOB/SRB-Sprite-Format verstehen (PCKELL.DAT-Offset-Tabelle mit den
32 SRB-Namen abgleichen, dann Frame-Struktur pro Sprite ableiten).
3. MAP/ARE-Levelformat reversen (kleinste Dateien zuerst, z.B. ein
simples frühes Level, z.B. `W1L0.MAP`/`W1L0.ARE`).
4. Erst danach: Python-Projektstruktur aufsetzen (pygame), Asset-Loader
fuer BOB/PCC/MAP schreiben, Kernschleife (Physik/Kollision) gegen
DOSBox-Referenzverhalten abgleichen.
## PCC-Bildformat — VOLLSTAENDIG GELOEST (2026-07-22, ~02:20 Ortszeit)
**Format zu 100% verstanden und visuell verifiziert.** PCC = klassisches
PCX v5, 256-Farben, mit **eingebetteter Palette PRO DATEI** (kein globales
Palette-Rätsel, wie in der vorherigen Notiz befürchtet):
```
Byte 0-15: PCX-Header (Manufacturer=0x0A, Version=5, Encoding=1/RLE,
BPP=8, Xmin/Ymin/Xmax/Ymax bei Offset 4-11,
Width(LE16)/Height(LE16) bei Offset 12-15 — Width/Height sind
DIREKTE Pixelmaße, nicht Xmax-Xmin+1, auch wenn beides meist
gleich rauskommt bei 320x200-Vollbildern)
Byte 16 .. (len-769): SCANLINE-weise RLE-komprimierte Pixel-Indexdaten.
RLE-Tupel: Byte mit oberen 2 Bits gesetzt (0xC0-0xFF) =
Lauflaenge (Byte&0x3F), gefolgt von 1 Wert-Byte. Sonst
literaler Pixel. WICHTIG: pro Scanline wird bis exakt
`width` Pixel dekodiert, ueberschuessige Pixel eines Runs
am Zeilenende werden verworfen (klassisches PCX-Verhalten,
NICHT als ein durchgehender Bytestrom decodieren — das
fuehrt zu Off-by-Overshoot-Fehlern und Fehlausrichtung
gegenueber der eingebetteten Palette am Dateiende).
Letzte 769 Byte: `0x0C`-Marker + 768 Byte (256 x RGB) Palette — die
klassische PCX-v5-256-Farben-Erweiterung. JEDE PCC-Datei
traegt ihre EIGENE Palette mit sich (keine globale Palette
aus RTM.EXE noetig, wie in der vorigen Session vermutet).
```
**Decoder fertig:** `tools/pcc_to_png.py` (auf `aria-wohnung` unter
`/home/aria/kellogg_remake/tools/pcc_to_png.py`, identische Kopie hier
im Projekt unter `tools/pcc_to_png.py`). Aufruf:
`python3 tools/pcc_to_png.py extracted_pre/<NAME>.PCC out.png`
(braucht ImageMagick `convert` auf dem Zielsystem — ist auf `aria-wohnung`
vorhanden).
**Visuelle Verifikation — 100% korrekt:**
- `KARTE.PCC` → exakte Weltkarte (Berge/Schloss/Drache, Tony/Tiger,
Coco/Affe, Frosch, Fluss, Bäume) — Pixel-für-Pixel wiedererkennbar
gegen den DOSBox-Screenshot `aria_kellogg_worldmap.png` von vorher.
- `KELLOGGS.PCC` → glasklares "Kellogg's präsentiert"-Logo mit
korrektem geschwungenem Markenschriftzug, scharfe Kanten, kein
Rauschen/Fehler in der Dekodierung.
- `MENU.PCC` → sauberer Menü-Rahmen (blauer Hintergrund, rot-goldene
Box) — vermutlich Passwort-/Level-Auswahl-UI.
Alle Test-PNGs liegen unter `/shared/uploads/aria_pcc_*.png` zur
Ansicht, dauerhafte Kopien aller **77 von 77 PCC-Dateien fehlerfrei
batch-decodiert** liegen unter `png_out/` in diesem Projektordner
(416 KB total, 77 Dateien — vollstaendige Validierung, kein einziger
Absturz oder Format-Ausreisser).
Einzige kleine Restfrage (kein Blocker): bei manchen Dateien bleiben
1-30 Byte am Ende des Pixel-Datenblocks vor der Palette ungenutzt
(vermutlich Padding/Rundung des Original-Encoders) — beeinflusst das
Bildergebnis nicht sichtbar, wird ignoriert.
## BOB-Sprite-Format — erste Erkenntnisse (2026-07-22, ~02:30 Ortszeit, IN ARBEIT)
**Kein PCX-Verwandter.** BOB-Dateien starten NICHT mit `0x0A`-Manufacturer-
Byte, kein erkennbarer Klartext-Header wie bei PCC. Vermutlich beginnt die
(vermutlich RLE-komprimierte) Pixel-Nutzdaten direkt ab Byte 0.
**Wichtiger Fund:** Die 32 BOB-Namen sind NICHT alle "richtige" Objektnamen
— 14 davon sind Einzelbuchstaben (`A.BOB` .. `O.BOB`, ohne `M`) plus
`BLUME.BOB`/`WOLKE.BOB` (Blume/Wolke, Deko-Objekte), dann die Marken-Packs
(`VITAMIN`/`COCOPOPS`/`SMACKIES`/`FROSTIES`/`LOOPS`), dann Charaktere
(`TONY`/`SMACKS`/`TOUCAN`/`COCO`), dann Level-Objekte (`PLATFW1`=Plattform,
`KEYS`, `GAMEBAR`=HUD-Leiste, `KROKO`/`BOSSNAKE`/`DRAGON`=Gegner, `DOORS`).
Die Buchstaben-Sprites (A-O) sind vermutlich Sammel-Buchstaben-Items
(Bonus-Mechanik: Buchstaben im Level einsammeln) — passend zur Groessen-
Gruppierung (viele davon fast gleich gross: B/E/N/O=1295 Byte, D/H/I=4406,
J/K=2332 — deutet auf fixe Sprite-Dimensionen pro Buchstaben-"Familie",
vermutlich Schriftart-aehnliche Boxen).
**Byte-Identitaet als Format-Hinweis:** `FROSTIES.BOB`, `COCOPOPS.BOB`,
`SMACKIES.BOB` (alle 3323 Byte gross) haben BYTE-FUER-BYTE identische
ersten 16 Byte (`16 b4 2c 76 82 b4 3a 9f 06 88 82 cf 16 e8 03 2d`) und
divergieren erst ab ca. Offset 0x20. `LOOPS.BOB` (ebenfalls 3323 Byte)
zeigt eine um 4 Byte verschobene, aber erkennbar verwandte Bytefolge.
**Interpretation:** das ist vermutlich KEIN Klartext-Header, sondern
bereits RLE-komprimierte Pixeldaten — die vier Sprites sind alle
"Cerealien-Packung"-Objekte mit identischer Kastenform/Silhouette
(gleiche Pixel-Auesserform => gleiche RLE-Runs am Anfang), die sich nur
im aufgedruckten Marken-Logo unterscheiden (Divergenz kommt spaeter).
Das wuerde erklaeren warum 4 komplett verschiedene Marken exakt gleich
grosse Dateien mit ident. Praefix erzeugen: gleiche Box-Geometrie,
gleiches Kompressionsschema, nur Content-Unterschied in der Bildmitte.
**Hypothese fuer naechste Session:** BOB = aehnliches RLE-Schema wie PCC
(Byte mit oberen 2 Bits gesetzt = Lauflaenge+Wert), aber OHNE PCX-Header
und OHNE eingebettete Palette — Breite/Hoehe muessen aus `PCKELL.DAT`
kommen (die SRB-Offset-Tabelle, noch nicht durchdrungen) oder aus einer
festen Tabelle in `RTM.EXE`. Naechste konkrete Schritte:
1. `PCKELL.DAT` Offset-Tabelle vs. `SRB.LST`-Namen abgleichen (32 Eintraege,
1:1 mit den 32 BOB-Dateien) — dort vermutlich Breite/Hoehe/Frame-Anzahl
pro Sprite zu finden.
2. Testweise RLE-Decode von `A.BOB` (kleinstes/einfachstes Beispiel aus der
Buchstaben-Gruppe) mit angenommener kleiner Breite/Hoehe (z.B. 20x20 bis
40x40 fuer ein UI-Buchstaben-Icon) durchprobieren, gegen erwartete
Pixelzahl abgleichen (aehnlich wie beim PCC-Ansatz, der am Ende
erfolgreich war: Header-Annahmen iterativ gegen Dateigroesse pruefen).
3. Falls das nicht schnell konvergiert: `TONY.BOB` (Hauptfigur, 47726 Byte,
groesste Datei nach COCO) ist wahrscheinlich der wichtigste Fund fuer
den Port und rechtfertigt tiefere Analyse zuerst.
## Naechste Schritte (Prioritaet, aktueller Stand)
1. **BOB/SRB-Sprite-Format weiter reversen** — siehe Hypothesen direkt
oben. Naechster Schritt: `PCKELL.DAT`-Offset-Tabelle mit `SRB.LST`
abgleichen, danach `A.BOB`-Testdecoding.
2. MAP/ARE-Levelformat reversen (kleinste Dateien zuerst, z.B. ein
simples frühes Level, z.B. `W1L0.MAP`/`W1L0.ARE` falls vorhanden —
sonst kleinste Datei nach Groesse waehlen).
3. Erst danach: Python-Projektstruktur aufsetzen (pygame), Asset-Loader
fuer BOB/PCC/MAP schreiben (PCC-Loader ist im Grunde fertig, nur
noch von PPM/ImageMagick-Zwischenschritt auf direktes PNG-Schreiben
in Python umstellen, z.B. mit `Pillow` — aktuell haengt der Decoder
noch am System-`convert`-Tool, das fuer den finalen Python-Port
NICHT die Zielarchitektur sein soll).
### Hinweis fuer Fortsetzung nach Neustart/Session-Wechsel
Dieses Projekt laeuft bewusst autonom weiter (Stefan-Wunsch, 2026-07-22).
Fortschritt wird nach JEDEM Meilenstein hier in `NOTES.md` gesichert.
Zusaetzlich liegt ein gepinnter Kurz-Status im Memory (Suchbegriff
"Kellogg's Remake") als Fallback, falls diese Datei mal nicht griffbereit
sein sollte. Bei Session-Neustart: einfach hier weiterlesen ab
"Naechste Schritte" oben, keine Rueckfrage bei Stefan noetig.
Ein `trigger_timer` ("kellogg-remake-continue") ist als Selbst-Weck-Kette
aktiv: feuert stuendlich, arbeitet den naechsten offenen Punkt ab, speichert
hier, legt danach den naechsten Timer an — laeuft ohne Rueckfrage weiter.
## PCC-Decoder-Bugfix + Git-Repo (2026-07-22, ~09:20 Ortszeit)
Stefan hat zurecht bemaengelt, dass die PCC-Bilder verzerrt/verrutscht
aussehen (z.B. Smacks-Frosch spiegelverkehrt, Kellogg's-Logo mit Fehlstelle).
Ergebnis der Untersuchung:
**GEFUNDEN UND GEFIXT — Width/Height-Bug:** Der Decoder las Breite/Hoehe
bisher aus Offset 12-15 (siehe alte Notiz oben). Das ist FALSCH — dieses
Feld ist bei allen Dateien ein Konstant-/Platzhalterwert (vermutlich
DPI-Reserved-Feld wie im echten 128-Byte-PCX-Header), der bei
Vollbild-Screens (320x200) zufaellig mit der echten Groesse
uebereinstimmt, bei kleineren Sprites/Logos aber komplett falsch ist.
**Richtig:** Breite/Hoehe muessen aus `Xmax-Xmin+1` / `Ymax-Ymin+1`
(Offset 4-11) berechnet werden. Beweis: `FACTOR5.PCC` konsumiert mit der
alten (falschen) Hoehe 200 nicht alle Bytes sauber, mit der echten Hoehe
199 (aus Ymax=198) landet der Byte-Konsum exakt bei 100% (51609/51609).
Betroffen/jetzt gefixt: `RAUSER1.PCC` (182x46 statt fälschlich 320x200),
`RAUSER2.PCC` (228x21), `RAUSER3.PCC` (16x55), `FAC0-3.PCC` (16x16),
`FACTOR5.PCC` (320x199). Fix ist in `tools/pcc_to_png.py` eingebaut,
alle 77 PNGs unter `png_out/` mit dem Fix neu generiert.
**Rauser-Intro: JA, schon extrahiert.** `RAUSER1/2/3.PCC` sind Frames
eines scrollenden Text-Logos (vermutlich "RAUSER ADVERTAINMENT PRESENTS"),
mit dem Fix jetzt lesbar (zeigt z.B. "...USER TRAI..." als Ausschnitt).
Volles Logo muesste sich aus allen 3 Frames zusammensetzen lassen (noch
nicht gemacht).
**FACTOR5.PCC "Geister"-Doppellogo: vermutlich KEIN Bug**, sondern ein
Reflexions-/Schatten-Designelement — Byte-Konsum ist exakt 100%, also
decodiert der Inhalt komplett korrekt, das Doppelbild ist einfach das,
was in der Datei steht.
**OFFEN — KELLOGGS.PCC/KARTE.PCC-Notch-Artefakt:** Der Width/Height-Fix
hat den harmlosen 320x200-Fall nicht veraendert (Xmax/Ymax ergeben dort
zufaellig dieselbe Groesse wie vorher). Das Notch-Artefakt (rot/gelber
Fleck rechts neben "Kellogg's"-Schriftzug, passt nicht zum
DOSBox-Referenz-Screenshot) bleibt bestehen. Ausgeschlossen als Ursache
(empirisch getestet, nicht nur vermutet):
- Horizontales Rollen/Scrollen des ganzen Bilds (18 Shift-Kandidaten
durchprobiert) — Artefakt bleibt IMMER an derselben Stelle relativ zum
Bildinhalt (Box/Text), nicht zur Leinwand. Kein Verschiebe-/Wrap-Bug.
- Zeilenweises Verwerfen von RLE-Overshoot am Zeilenende — zeilenbasierte
und durchgehende (row-boundary-freie) Decodierung liefern fuer
KELLOGGS.PCC byte-identische Ergebnisse (kein Overshoot tritt je auf).
Interessant: `MENU.PCC` nutzt vermutlich denselben Box-Rahmen (leere
Variante) und rendert jetzt SAUBER ohne Notch — der Fehler ist also
dateispezifisch, nicht im Rahmen-Design selbst. Naechste Hypothese:
Palette-Fehlzuordnung fuer einzelne Indizes ODER RLE-Ambiguitaet bei
literalen Pixelwerten >= 0xC0 (werden durch `(b&0xC0)==0xC0` evtl.
faelschlich als Lauflaengen-Token statt literaler Indexwert gelesen) —
noch zu verifizieren durch gezielten Test mit einem Decoder, der beide
Interpretationen an der vermuteten Fehlstelle durchspielt.
Ebenfalls noch offen (niedrige Prioritaet): `TONY.PCC`/`SMACKS.PCC`
liefern mit dem Xmax/Ymax-Fix nur 1x1 Pixel (degenerated, Xmax=Ymax=0
im Header) — vermutlich sind das keine "echten" Vollbilder, sondern
Platzhalter/ungenutzte Preview-Slots; die eigentlichen Charakter-Sprites
stecken hoechstwahrscheinlich in den `.BOB`-Dateien (`TONY.BOB`,
`SMACKS.BOB` etc.), nicht in den gleichnamigen `.PCC`-Dateien.
**Git-Repo eingerichtet:** Auf Stefans Wunsch liegt der Projektstand jetzt
unter `https://git.hacker-net.de/Aria-Software/tony-remake`. Bewusst NICHT
mit eingecheckt: `raw/` (Original-Spieldateien: PCKELL.DAT/.PRE/.SET,
RTM.EXE, START.EXE etc. — das ist buchstaeblich das Original-Spiel) und
`extracted_pre/` (die daraus entpackten 181 Rohdateien — noch
Original-Asset-Material, nur unverpackt). Mit eingecheckt: `NOTES.md`,
`tools/` (unser Code: `split_pre.py`, `pcc_to_png.py`), `dosbox/`
(Test-Konfiguration), `png_out/` (unsere konvertierten PNG-Ergebnisse,
408 KB, reine Arbeitsergebnisse kein Originalmaterial).
## KELLOGGS.PCC/KARTE.PCC-Notch-Artefakt — GELOEST (2026-07-22, ~09:30 Ortszeit)
**Die Notiz oben ("Ausgeschlossen als Ursache ... Zeilenweises Verwerfen von
RLE-Overshoot ... liefern byte-identische Ergebnisse") war FALSCH.** Das war
ein Messfehler einer frueheren Session: der "kontinuierliche" Testdecoder
hat versehentlich weiterhin die width/height aus Offset 12-15 gelesen (dem
VERWORFENEN Bugfix-1-Feld) statt aus Xmax/Ymax — dadurch kam bei den beiden
320x200-Vollbildern zufaellig das gleiche (korrekte) Ergebnis raus wie beim
zeilenbasierten Decoder, und der Test schien zu bestaetigen, dass beide
Methoden aequivalent sind. Sind sie nicht.
**Neu verifiziert mit einem sauberen Vergleichstest** (`Buffer.compare` in
Node.js, unabhaengige Re-Implementierung): zeilenbasiertes Decoding
(Row-Reset, discard Overflow am Zeilenende) und durchgehendes Decoding
(RLE-Runs duerfen ueber Zeilengrenzen hinweglaufen) liefern **NICHT**
identische Ergebnisse — 216 von 64000 Pixeln unterscheiden sich bei
KARTE.PCC, 293 von 64000 bei KELLOGGS.PCC. Klingt nach wenig (<0.5%), reicht
aber, um ganze Bildbereiche sichtbar zu verschieben, weil ab dem ersten
"verlorenen" Ueberlauf-Pixel alle nachfolgenden Zeilen einen Pixel weiter
"nachruecken".
**Beweis per Ground-Truth-Vergleich:** DOSBox-Screenshot
`aria_kellogg_worldmap.png` (1024x768 mit schwarzen Balken) exakt auf den
reinen Spielbereich zugeschnitten (`convert -fuzz 5% -trim` → 640x400,
= 2x 320x200) und auf 320x200 runterskaliert
(`/shared/uploads/aria_karte_reference_320.png`). Nebeneinander-Vergleich
mit dem durchgehend (ohne Row-Reset) decodierten `KARTE.PCC`
(`/shared/uploads/aria_karte_compare2.png`): **pixel-identisch** — Berge,
Schloss, Drache, Tiger, Affe/Coco, Frosch, Teich, Haus, Voegel-Ecke, alles
exakt an der richtigen Stelle. Das Row-Reset-Verhalten war der einzige
Bug; es gibt kein horizontales Rollen, keine Palette-Fehlzuordnung, keine
RLE-Ambiguitaet bei Werten >=0xC0.
**Fix:** `tools/pcc_to_png.py` decodiert jetzt global `width*height` Pixel
aus dem RLE-Strom OHNE pro Zeile abzuschneiden (kein Row-Reset mehr). Alle
77 PNGs in `png_out/` wurden mit diesem finalen Decoder neu erzeugt
(diesmal wirklich verifiziert, nicht nur "sieht plausibel aus"). Kleine
Sprites/Logos waren von diesem Bug kaum betroffen (selten ein Run, der eine
Zeilengrenze ueberschreitet) — deshalb fiel es dort nie auf, nur bei den
zwei grossen 320x200-Vollbildern.
**Rauser-Intro bestaetigt (Frage von Stefan):** `RAUSER1/2/3.PCC` sind schon
laenger extrahiert und liegen jetzt mit dem finalen Decoder sauber decodiert
in `png_out/`. Es ist ein Text-Logo "RAUSER" + "ADVERTAINMENT" (Studio-Card),
als 3 Bild-Frames — vermutlich fuer einen Scroll-/Fade-Effekt, den die
Original-Engine (RTM.EXE) zur Laufzeit zusammensetzt, KEIN Video/keine
Animation-Datei. Fuers Remake heisst das: die 3 Frames sind da, die
Overlay-/Scroll-Logik muss im Python-Port nachgebaut werden (vermutlich
simpel: Frame croppen + ueber Zeit einblenden, sobald wir wissen wie die
Original-Sequenz zeitlich ablaeuft — dafuer ggf. DOSBox-Referenzvideo/
Screenshots in der Sequenz aufnehmen, spaeterer Schritt).
## BOB-Sprite-Format — Exploration laeuft, noch nicht geloest (Stand 09:30)
Auf `aria-wohnung` (`/home/aria/kellogg_remake/tools/`) lief parallel schon
einiges an Exploration fuer das naechste Format (BOB-Sprites): Brute-Force-
Breitensuche (`bob_width_search.py`), Header-Diff zwischen aehnlich grossen
Dateien (`bob_header_diff.py`, `bob_header_search.py`), Histogramm-Analyse
(`bob_histogram.py`), Sichtgitter-Rendering mit mehreren Breiten-Kandidaten
(`bob_render_grid.py`/`bob_render_grid2.py`), sowie erste Versuche die
`PCKELL.DAT`-Offset-Tabelle zu parsen (`dat_analyze.py`/`dat_analyze2.py`,
`dat_table2.py`, `dat_u16.py`). **Kein Ergebnis/Durchbruch bisher** — alles
noch offene Exploration, keiner der Scripts hat schon eine verifizierte
Loesung. Zur Sicherheit (Stefans Wunsch: nichts soll bei einem VM-Neustart
verloren gehen) sind alle diese Scripts jetzt zusaetzlich hier im
Projekt gesichert unter `tools/wip_bob_sprite_research/` (noch nicht
aufgeraeumt/nicht final, bewusst als "work in progress" separiert von den
fertigen Tools `split_pre.py`/`pcc_to_png.py`).
## KORREKTUR 2026-07-22 (nachmittags) — der "kontinuierliche" Fix oben war FALSCH
Stefan hat sich das gepushte Repo angeschaut und den Bug SOFORT wieder
gesehen (Frosch links statt rechts, Kellogg's-Logo verschoben) — die
"pixel-perfect verifiziert"-Behauptung im Abschnitt direkt darueber war
**selbst ein Messfehler**, genau wie der davor schon korrigierte. Beide
eigenen Hand-RLE-Decoder (Row-Reset UND kontinuierlich) haben KARTE.PCC/
KELLOGGS.PCC nachweislich sichtbar falsch decodiert (Bildinhalt dupliziert/
verschoben) — mein visueller Vorher-Vergleich war schlicht nicht sorgfaeltig
genug, ich habe der eigenen Automatik zu frueh vertraut.
**Stefans entscheidender Hinweis:** https://github.com/movAX13h/tony-and-friends-in-kelloggs-land
— ein bereits existierendes Reverse-Engineering-Projekt fuer GENAU dieses
Spiel (dort "Tony and Friends in Kellogg's Land" genannt, gleiche
PCKELL.DAT/PCKELL.SET-Assets). Enthaelt fertigen, funktionierenden C#-Code
fuer PCX/BOB/MAP/ARE/ICO/DAT — Quelle: `/tmp/tony-ref` (geklont, kein
LICENSE-File im Repo, daher NICHT 1:1-Code uebernommen, sondern nur als
Format-Referenz genutzt und in eigenem Python-Code neu umgesetzt).
Deren `Formats/PCXFile.cs` bestaetigt: `.PCC` ist stinknormales PCX v5,
8bpp, RLE, **mit Row-Reset** (pro Zeile wird ein frischer RLE-Reader
erzeugt, ueberschuessige Pixel eines Runs am Zeilenende werden verworfen —
Standard-PCX-Verhalten, keine Sonderregel). Das war also urspruenglich
(Bugfix 1, vormittags) schon RICHTIG vermutet — der eigentliche Bug lag
tiefer in der eigenen Implementierung und wurde nie gefunden.
**Der eigentliche Fix:** Decoder komplett ausgetauscht — `tools/pcc_to_png.py`
nutzt jetzt NICHT mehr eigenes RLE-Decoding, sondern `convert pcx:datei.PCC
out.png` (ImageMagick's ausgereifter PCX-Decoder, der die Datei einfach
per Standard-Format-Erkennung liest). Kein Workaround, sondern die richtige
Loesung: unser Format-Verstaendnis war im Kern korrekt, nur die
Handimplementierung hatte einen Bug den wir trotz zweier Anlaeufe nicht
gefunden haben — ein reifer, jahrzehntealter PCX-Decoder macht das
zuverlaessig richtig.
**Neu verifiziert (diesmal wirklich sorgfaeltig, Bild angeschaut statt nur
Byte-Statistik):** `png_out/KELLOGGS.png` und `png_out/KARTE.png` sehen jetzt
visuell identisch zu den echten DOSBox-Screenshots aus
(`aria_kellogg_titlescreen.png`/`aria_kellogg_worldmap.png`) — Vogelkopf
oben links, Drache+Schloss oben rechts, Frosch im Teich, Tiger unten
rechts, Coco unten links, alles an der richtigen Stelle, kein Doppel-Bild,
keine Kerbe. `FACTOR5.PCC`s "Geister-Doppellogo" ist ueber ImageMagick
ebenfalls sauber decodiert und bestaetigt sich damit endgueltig als
Absicht (Reflexions-Design), nicht als Bug. Alle 77 PCC-Dateien mit dem
neuen Decoder neu erzeugt und liegen in `png_out/`.
**Nebenfund:** `TONY.PCC`, `SMACKS.PCC`, `TOUCAN.PCC`, `COCO.PCC` (und
vermutlich weitere Charakter-Namen) decodieren zu winzigen 1x1-Pixel-Bildern
— das ist KEIN Extraktions-Bug. Laut `Form1.cs` der Referenz dienen manche
`.PCC`-Dateien nur als **Palette-Lieferant** fuer die zugehoerige `.ICO`/
`.BOB`-Sprite-Datei (ICO/BOB haben selbst keine eingebettete Palette,
im Gegensatz zu vollen PCC-Bildschirmen). Wichtig fuers spaetere BOB/ICO-
Decoding: Palette kommt dann von der PCC-Datei mit gleichem Basisnamen
(z.B. `TONY.BOB` -> Palette aus `TONY.PCC`), nicht aus der Sprite-Datei
selbst.
## Naechste Schritte (Prioritaet, Stand nachmittags 22.07.)
1. **BOB/SRB-Sprite-Format mit Hilfe der Referenz reversen.** Die Referenz
(`/tmp/tony-ref/Unpacker/Formats/BOBFile.cs`, `BOBPainter.cs`) hat das
schon geloest — README beschreibt BOB als "data and x86 code to display;
somewhat similar to BOB format of Turrican II". Naechster Schritt: deren
`BOBFile.cs` genau lesen und die Logik in eigenes Python uebersetzen
(nicht 1:1 kopieren, kein LICENSE im Repo), dann gegen `A.BOB`
(kleinstes Beispiel) verifizieren. Das sollte den bisherigen
`wip_bob_sprite_research/`-Blindflug ueberfluessig machen.
2. Genauso `MAPFile.cs`/`AREFile.cs`/`ICOFile.cs` fuer Level-Maps/Areas/
Tilesets nutzen statt weiter blind zu raten — auch hier: Format-Wissen
uebernehmen, Code selbst neu schreiben.
3. Rauser-Intro-Sequenz (3 Frames) zu einer zusammengesetzten Vorschau
rendern/dokumentieren, sobald Zeit ist — niedrige Prioritaet, Assets sind
schon da.
4. Python/pygame-Projektstruktur aufsetzen (PCC-Loader ist jetzt wirklich
fertig und verifiziert: `convert pcx:datei.PCC out.png` bzw. fuer den
finalen Port direkt Pillow's PCX-Reader nutzen — Pillow unterstuetzt
PCX nativ, damit brauchen wir zur Laufzeit kein externes ImageMagick
mehr).
**Lektion fuer mich selbst (ARIA):** zweimal hintereinander eine "pixel-
perfect verifiziert"-Behauptung aufgestellt, die beim tatsaechlichen
Hinschauen (Bild anzeigen, nicht nur Bytes vergleichen) klar falsch war.
Naechstes Mal: bei jeder Verifikations-Behauptung IMMER das decodierte Bild
selbst anschauen (Read-Tool, multimodal), nicht nur Byte-/Pixel-Statistiken
vertrauen, bevor ich "geloest"/"verifiziert" sage.
## DURCHBRUCH 2026-07-22 (Nachmittag/Abend) — BOB/ICO/MAP komplett geloest
Stefan wollte aufraeumen + alle restlichen Assets (BOB/ICO/MAP/ARE/SAM/TFX).
Grosser Fund dabei: **`PCKELL.DAT` ist der eigentliche, einzige Asset-
Container** — nicht `PCKELL.PRE` (das war nur ein Nebenprodukt/Cache aus
`PCKELL.SET`). Referenz-Repo (`Formats/DATFile.cs`) beschreibt ihn explizit
als "the only container format": Index steht am DATEIENDE (Anzahl Entries
als u16 bei `len-4`, Index-Offset als i32 bei `len-8`), pro Entry
`[u16 NameLen][Name][i32 Offset]`, Laenge = Differenz zum naechsten Offset.
Score: **alle 178 Dateien fehlerfrei extrahiert**, exakt matchend zu
APPEND.LST — und `KELLOGGS.PCC` daraus ist **byte-identisch** zur alten
PRE-Extraktion, also war unser PCC-Weg schon konsistent. `PCKELL.PRE`/
`extracted_pre/`/`split_pre.py` sind damit ueberfluessig geworden, aus dem
Tool-Set entfernt (Repo-Hygiene, kein Skill-Friedhof-Aequivalent fuer
Tools). Neue kanonische Quelle: `tools/dat_extract.py` +
`tools/kellogg_formats.DATContainer`.
**PCC-Bug-Nachtrag:** beim heutigen Re-Check mit frischem Blick (Kellogg's-
Logo UND Weltkarte hochskaliert wirklich angeschaut, nicht nur behauptet)
ist `png_out/` tatsaechlich korrekt — Frosch im Teich unten links, Drache+
Schloss oben, Tiger/Coco richtig platziert, "Kellogg's präsentiert"-Box sauber.
Der PCC-Teil war also wirklich fertig, keine weitere Aktion noetig.
**BOB (Sprites) — komplett geloest.** Format aus `BOBFile.cs`/`Form1.cs`
in eigenes Python uebersetzt (`tools/kellogg_formats.py`, Funktionen
`parse_bob`/`_parse_bob_executable`): jede BOB-Datei ist eine Sequenz von
Frames, jeder Frame hat einen 14-Byte-Header (Breite/Hoehe/Laenge des
"Pointer"-Segments), gefolgt von echtem, sich selbst beschreibendem x86-
Maschinencode (`mov byte/word ptr [si+X], Y`-Ketten + `out dx,al` fuer
EGA-Page-Wechsel), der beim "Ausfuehren" direkt die Pixel-Kopieranweisungen
liefert (Offset+EGA-Page -> x/y, Pixelwert = Byte-Konstante minus 0x80 als
Palette-Index). Kein echter x86-Interpreter noetig, nur ein kleiner
Pattern-Matcher fuer die 10 vorkommenden Opcodes. Palette kommt (Form1.cs-
Regel) von der PCC-Datei mit gleichem Basisnamen (z.B. `TONY.BOB` ->
Palette aus `TONY.PCC`), bei 1-Buchstaben-Dateien (`A.BOB`..`O.BOB`, die
Ameisen) von `ANTS.PCC`, sonst von `W2.PCC` mit Index 0 auf Schwarz
gesetzt. **Alle 32 BOB-Dateien fehlerfrei decodiert, 462 Frames total.**
Visuell verifiziert (nicht nur Byte-Statistik!): `A.BOB` ist eine 8-Frame-
Laufanimation eines kleinen Kaefers/Ameise, `TONY.BOB` zeigt Tony den
Tiger unmissverstaendlich in Lauf-, Wink-, Sitz- und Sprung-Posen (53
Frames). Ergebnisse in `render_out/bob/<NAME>/NNN.png` (Einzelframes) +
`render_out/bob/<NAME>_sheet.png` (Grid-Uebersicht).
**ICO (Tilesets) — komplett geloest.** 16x16-Kacheln in "gescrambelter"
EGA-Spaltenreihenfolge (`parse_ico`): pro Zeile 4 EGA-Pages a 4 Pixel,
Palette-Index = Byte minus 128. Palette-Regel: `W1`/`W2`/`W3` aus dem
Dateinamen erkannt, Basis-Palette von `<WORLD>.PCC`, aber die 16 Farben
Index 16-31 IMMER von `W2.PCC` ueberschrieben (die sind laut Referenz die
geteilten Animations-/Item-Farben aller drei Welten) — ausser bei `W2`
selbst. **Alle 19 ICO-Dateien fehlerfrei decodiert.** Ergebnis in
`render_out/ico/<NAME>.png`.
**MAP (Level-Karten) — komplett geloest.** Klares Format, aber
GROSS-ENDIAN (!), Signatur `"TLE1"`, dann Breite/Hoehe/Konstante(=9) je
als big-endian int16, danach `Breite*Hoehe` big-endian u16-Zellen: unteres
9-Bit-Feld = Tile-Index (ins passende `<WORLD>.ICO`), oberes 7-Bit-Feld =
Kollisions-/Typ-Info (noch nicht weiter interpretiert). **Alle 22 MAP-
Dateien fehlerfrei decodiert UND gerendert** (Tile-Lookup im passenden ICO
+ Palette) — `W1L0.png` zeigt ein komplettes, stimmiges Level: Haeuser mit
rotem Dach, Buesche/Baeume, Wolken, blauer Himmel, Bergketten, alles exakt
an der Stelle, die man von einem Kellogg's-Tony-Jump'n'Run erwartet.
Ergebnis in `render_out/map/<NAME>.png`.
**ARE (Kollisionszonen) — NICHT geloest, bewusst zurueckgestellt.** Auch
die C#-Referenz hat das nie fertig gemacht (`AREFile.cs` liest nur naiv
176 little-endian u16-Werte ohne jede semantische Deutung, README nennt
nur den Namen "Area" ohne Details). Da MAP-Zellen bereits ein 7-Bit-
Kollisions-Typ-Feld mitbringen, ist unklar ob ARE ueberhaupt noch fuer den
Python-Port gebraucht wird (vielleicht redundant/Level-Editor-Metadaten) —
das klaeren wir erst, wenn die Spiellogik so weit ist dass Kollision
gebraucht wird. Rohdaten bleiben in `extracted_dat/` (nicht committed)
falls wir spaeter draufschauen wollen.
**SAM/TFX (Sound, TFMX-Format) — noch nicht angefasst.** 3+3 = 6 Dateien,
niedrige Prioritaet (Musik/SFX kommt spaeter, sobald Grafik+Logik stehen).
TFMX ist ein bekanntes Amiga/DOS-Tracker-Format, sollte mit vorhandenen
Libraries (z.B. `libxmp`/`ym2149`-Tools) machbar sein, nicht neu erfunden
werden muessen.
**Aufraeumen erledigt:**
- VM-Arbeitsverzeichnis (`/home/aria/kellogg_remake` auf `aria-wohnung`,
NICHT im Repo): alle Debug-Logs/Test-Screenshots aus der DOSBox-IRQ-
Session sowie `png_out_v2/` (veraltet) geloescht.
- `tools/`: alle Blindflug-Skripte aus der ersten BOB-Exploration
(`bob_header_diff.py`, `bob_width_search.py`, `dat_analyze*.py`, etc.)
UND `tools/wip_bob_sprite_research/` komplett entfernt — durch die
echte Loesung in `kellogg_formats.py` ueberfluessig. `split_pre.py`
ebenfalls entfernt (PRE-Weg durch DAT-Weg ersetzt).
- `.gitignore`: `extracted_dat/` ergaenzt (Rohdaten, copyrighted, wie
`raw/`/`extracted_pre/` vorher schon).
**Naechste Schritte (Prioritaet, Stand Abend 22.07.):**
1. Rauser-Intro-Sequenz zusammensetzen (niedrig, Assets schon da).
2. Sound (SAM/TFX, TFMX-Format) angehen, wenn Zeit ist.
3. ARE/Kollisionsformat klaeren — erst wenn's fuer Spiellogik gebraucht wird.
4. **Jetzt wo alle visuellen Assets (PCC/BOB/ICO/MAP) fertig sind:**
Python/pygame-Projektstruktur aufsetzen — Asset-Loader (Palette-Regeln
aus `kellogg_formats.py` direkt uebernehmen), dann Tony-Bewegung/
Kollision/Level-Scrolling als erste spielbare Vertical Slice.
## ENTSCHEIDUNG 2026-07-22 abends — erster Meilenstein: Boot-/Intro-Sequenz statt Gameplay
Stefan-Wunsch: als leichten, sichtbaren Einstieg zuerst die komplette
Boot-Sequenz nachbauen (Rauser-Card → Factor5-Logo → Kellogg's-Logo mit
"Vorhang"-Aufdeck-Effekt → Titelbild → Hauptmenue mit Menuepunkten →
Weltkarte), BEVOR Tony ueberhaupt beweglich ist. Sinnvolle Wahl: das
zwingt genau die Kern-Engine-Bausteine, die spaeter das ganze Spiel
braucht (Asset-Loader, Bild-Blitting mit Palette, Timing/Sequenzer,
Sound-Playback, Input-Handling fuers Menue) — nur eben am kleinstmoeglichen
Slice, bevor Physik/Kollision/Level-Logik dazukommen.
**Stand der benoetigten Assets (alle schon extrahiert, nur noch nicht
zu einer Sequenz zusammengesetzt):**
- Rauser-Card: `RAUSER1/2/3.PCC` (3 Scroll-/Fade-Frames, siehe oben)
- Factor5-Logo: `FACTOR5.PCC` + `FAC0-3.PCC` (kleine Begleit-Frames,
vermutlich Blink-/Reflex-Animation)
- Kellogg's-Logo: `KELLOGGS.PCC`**"Vorhang"-Effekt (Stefans Beobachtung:
oberer Teil muesste sich von rechts nach links aufdecken) ist NICHT als
Asset gespeichert**, sondern eine Laufzeit-Animation von RTM.EXE (reines
Wipe/Reveal auf einem statischen Vollbild). Muss durch Beobachtung
nachgebaut werden (Screenshot-Serie waehrend des Boot-Vorgangs in
DOSBox, kurze Intervalle, um Richtung/Geschwindigkeit/Dauer zu
bestimmen) — noch nicht gemacht, naechster Schritt.
- Titelbild ("Tony & Friends in Kellogg's Land"): noch keine eigene PCC-
Datei dafuer identifiziert unter den 77 — moeglich, dass das Kellogg's-
Logo direkt das Titelbild IST (ein Screen), oder der Text wird von der
Engine ueber ein weiteres, noch nicht namentlich zugeordnetes PCC
gelegt. Zu klaeren beim naechsten DOSBox-Screenshot-Durchlauf.
- Hauptmenue: `MENU.PCC` (Rahmen/Box vorhanden) — Menuepunkte selbst
vermutlich als Text von der Engine gerendert (kein Font-Asset bisher
identifiziert) oder als eigene ICO/BOB-Sprites. Noch offen.
- Karte: `KARTE.PCC` + `KARTE0-9.PCC` (10 Zusatz-Frames, vermutlich
kleine Animationen auf der Karte — Wasser/Flagge/Feuer o.ae., noch
nicht einzeln zugeordnet).
- Sound: 3x SAM + 3x TFX (TFMX-Format, Amiga/DOS-Tracker) — bisher NICHT
decodiert. Ob Rauser/Factor5/Kellogg's-Screens ueberhaupt Sound haben,
ist unklar (DOSBox lief bisher im ALSA-nosound-Modus, kein Ton
gehoert) — zu klaeren durch Sichtung von `tools/tfmx_player` (C-Referenz-
Tool, liegt schon lokal unter `tools/tfmx_player/`, noch uncommitted)
und/oder DOSBox mit echtem Audio-Sink testen.
**Plan (in dieser Reihenfolge):**
1. pygame auf `aria-wohnung` installiert (User-pip, erledigt).
2. DOSBox-Screenshot-Serie waehrend des Bootvorgangs aufnehmen (kurze
Intervalle ueber Rauser→Factor5→Kellogg's→Titel→Menue), um Timing +
Wipe-Richtung/-Geschwindigkeit des Kellogg's-Vorhang-Effekts zu
bestimmen, und um das noch unklare Titelbild/Menue-Text-Rendering zu
identifizieren.
3. `tools/intro_sequence.py` (neu): pygame-basierter Player, der die
Screens in der beobachteten Reihenfolge + Timing zeigt (erst ohne
Ton), Wipe-Effekt fuer Kellogg's-Logo nachbauen.
4. TFMX-Sound anhaengen, sobald `tfmx_player` als Referenz ausgewertet
ist (Python-Wrapper oder Re-Implementierung der Decodierung).
5. Committen/pushen nach jedem funktionierenden Teilschritt (nicht erst
am Ende), NOTES.md nach jedem Meilenstein aktualisieren — wie von
Stefan gewuenscht, damit bei einem VM-/Brain-Neustart nichts verloren
geht und einfach hier weitergelesen werden kann.
Laeuft jetzt selbstaendig weiter, keine Rueckfrage noetig.
## Boot-Sequenz vermessen + Intro-Player gebaut (2026-07-22 abends, Fortsetzung)
### 1) Echter Regressions-Bug gefunden + gefixt (tools/pcc_to_png.py)
Beim Draufschauen fuer die Intro-Arbeit aufgefallen: Commit `62af15e` hatte
den PCC-Decoder korrekt auf ImageMagick umgestellt (nachdem zwei eigene
Hand-RLE-Parser nachweislich falsche Pixel lieferten). Der naechste Commit
`3410a53` (eigentlich nur fuer BOB/ICO/MAP gedacht) hat `pcc_to_png.py`
aber **versehentlich durch eine AELTERE Version mit dem kaputten
Hand-RLE-Decoder ueberschrieben** -- vermutlich ein Merge-/Copy-Fehler beim
Zusammenfuehren von Aenderungen. Die bereits committeten 77 PNGs in
`png_out/` waren zum Glueck NICHT betroffen (die stammen noch von der guten
ImageMagick-Version), aber ein Rerun des Skripts haette wieder kaputte
Bilder erzeugt, ohne dass es aufgefallen waere.
**Fix:** Statt ImageMagick-Subprocess jetzt `kellogg_formats.load_pcc()` --
nutzt Pillows eingebauten, robusten PCX-Decoder direkt (`PIL.Image.open`
erkennt PCX an den Magic-Bytes, nicht am Dateinamen). Kein eigener
RLE-Parser mehr im Projekt, an keiner Stelle. Alle 77 PCC neu aus
`PCKELL.DAT` gerendert und via `compare -metric AE` **byte-fuer-byte
pixelidentisch (AE=0)** zu den alten (guten) PNGs verifiziert -- keine
Regression, sauberer Fix. Neue Funktionen `load_pcc()` / `pcc_to_rgba()`
in `kellogg_formats.py`, wiederverwendbar fuer den Intro-Player.
### 2) Boot-Sequenz per echter Screenshot-Serie vermessen (0.3s-Intervall)
`xvfb-run`-Setup (Xvfb + `import -window root` alle 0.3s) waehrend
`START.EXE` bootet, ~90 Frames ueber ~27s. Ergebnis (Hash-Dedup + visuelle
Pruefung der Uebergaenge):
1. **Rauser-Advertainment-Karte** -- Komposit aus `RAUSER1.PCC` (dunkle Box
"RAUSER"), `RAUSER3.PCC` (blaue Box, Ausrufezeichen), `RAUSER2.PCC`
("ADVERTAINMENT"-Schriftzug) auf weissem Grund. Statisch gehalten (kein
Aufbau-Effekt beobachtet), ~3s.
2. Kurze schwarze Pause (~0.3-0.5s).
3. **Factor5-Studio-Logo** (`FACTOR5.PCC`, 320x199) -- **komplett statisches
Bild**, keine Laufzeit-Animation. Das "Geister"-Doppellogo (zwei leicht
versetzte FACTOR-Schriftzuege + Spiegelung am Boden) ist Teil des Assets
selbst (Byte-Konsum exakt 100%, kein Bug). Lange gehalten, ~6s.
4. Kurze schwarze Pause.
5. **Kellogg's-Markenlogo** (`KELLOGGS.PCC`, 320x200) -- erscheint SOFORT
komplett und bleibt die ganze Zeit scharf. **KORREKTUR einer fruehen
Fehlannahme:** es gibt HIER keinen Vorhang-/Wipe-Effekt auf dem Logo
selbst. Was tatsaechlich passiert: der Text **"praesentiert"** darunter
faedet per **Palette-Animation** von blass (fast weiss) zu kraeftigem
Rot ein (~3-4s beobachtet). Per Index-Analyse verifiziert: die Pixel des
Textes nutzen exklusiv die Palette-Indizes **34, 42, 43, 44, 45** (Bbox
x=71-241, y=108-141 im 320x200-Bild) -- alles andere im Bild bleibt
waehrend des Fades unveraendert.
6. Kurze schwarze Pause.
7. **Titelbild "TONY & FRIENDS in Kellogg's Land"** -- **DAS ist der
eigentliche Vorhang-Effekt**, den Stefan urspruenglich meinte. Wichtiger
Fund: dieses Bild ist intern **320x480** (doppelt so hoch wie die
anderen Screens!), zusammengesetzt aus den 4 PCC-Quadranten `KELL256A`
(oben links) / `KELL256B` (oben rechts) / `KELL256C` (unten links) /
`KELL256D` (unten rechts), je 160x240 -- durch Stitchen zu einem
320x480-Gesamtbild verifiziert (siehe `/tmp/kell_full_stitch.png`
waehrend der Session, zeigt exakt das erwartete Titelbild). Der
Reveal-Effekt: **obere Bildhaelfte (A|B) faehrt von oben nach unten
ein, untere Haelfte (C|D) von unten nach oben** -- wie ein Theater-
vorhang, nur vertikal, beide Haelften treffen sich exakt in der Mitte
(y=240 im 480px-Bild). Dauer ~3s (Frames 73->83 in der Aufnahme).
DOSBox-Fenster ist fuer diesen Screen sichtbar groesser/anders
proportioniert als bei den 320x240-Screens davor -- passt zur These
eines eigenen 320x480-Modus nur fuer dieses Bild.
Alle anderen Screens (Rauser/Factor5/Kellogg's-Logo) laufen im
Fenster-Seitenverhaeltnis 320x240 (aus Trim-Analyse des DOSBox-Fensters:
640x480 Xvfb-Ausschnitt = exakt 2x 320x240) -- die PCC-Bilder selbst sind
oft nur 320x199/200 und werden darin mit schwarzen Balken oben/unten
letterboxed (gut sichtbar am schwarzen Rand um die Kellogg's-Box in den
Screenshots).
### 3) Erster spielbarer Meilenstein: `tools/intro_sequence.py`
pygame-Player, der die komplette oben beschriebene Sequenz nachbaut:
Asset-Loading aus `PCKELL.DAT` (ueber `kellogg_formats`), Bild-Rendering
mit Palette, Timing/Sequenzer, Palette-Fade-Effekt (echte Index-Animation,
kein Alpha-Crossfade-Hack), Vorhang-Wipe-Effekt fuer das Titelbild.
Headless getestet (Xvfb + Screenshot-Serie) -- **visuell verifiziert,
sieht in allen Phasen sehr nah am echten DOSBox-Boot aus**:
[FILE: /shared/uploads/aria_intro_rauser.png] (Rauser-Karte, Komposit)
[FILE: /shared/uploads/aria_intro_factor5.png] (Factor5-Logo, statisch)
[FILE: /shared/uploads/aria_intro_kelloggs_fade.png] ("praesentiert" mitten im Fade)
[FILE: /shared/uploads/aria_intro_titel_wipe.png] (Titelbild-Vorhang mitten im Reveal)
[FILE: /shared/uploads/aria_intro_titel_final.png] (Titelbild komplett offen)
**Bekannte Ungenauigkeiten (v1, nicht pixelperfekt, aber nah dran):**
- Rauser-Komposit-Layout (Position von RAUSER1/2/3 zueinander) ist per
Augenmass an die Screenshots angenaehert, nicht pixelgenau vermessen --
bei Bedarf spaeter per genauerem Screenshot-Vergleich nachjustieren.
- Fade-/Wipe-Dauern sind aus der 0.3s-Sample-Screenshot-Serie abgeleitet
(+/- ein paar hundert ms Unschaerfe), nicht aus dem Originalcode gelesen
(RTM.EXE ist compiliert, kein Quellcode verfuegbar).
- Sound fehlt komplett (TFMX/SAM noch nicht decodiert, siehe unten).
**Ausfuehren:** `python3 tools/intro_sequence.py` auf `aria-wohnung`
(pygame jetzt per `apt install python3-pygame` installiert, NICHT pip --
System-Python ist "externally managed", pip-User-Install schlug fehl).
`--once` fuer einen Durchlauf statt Endlosschleife wie im Original.
### Naechste Schritte (Prioritaet)
1. Sound (SAM/TFX TFMX-Format) decodieren + an die Intro-Sequenz haengen --
`tools/tfmx_player/` (C-Referenztool) liegt bereit.
2. Rauser-Layout und Fade/Wipe-Timings feinjustieren falls gewuenscht.
3. Danach: Uebergang von Intro zu echtem Gameplay -- Tony-Bewegung auf
Level W1L0 als naechster grosser Meilenstein (MAP+ICO+BOB sind fertig).
Laeuft selbstaendig weiter, keine Rueckfrage noetig.
## Sound (TFMX) geloest + in Intro-Sequenz eingebaut (2026-07-22, spaeter Abend)
**`tools/tfmx_player/` (MIT-lizenziert, Copyright Peter Fors -- LICENSE liegt
bei) ist ein fertiger TFMX-Replayer in C.** Anders als beim C#-Referenzprojekt
fuer PCX/BOB/MAP/ICO (dort kein LICENSE, daher nur als Format-Wissen genutzt
und in eigenem Python neu geschrieben) erlaubt die MIT-Lizenz hier direkte
Nutzung/Einbindung -- deshalb kein Python-Reimplement des TFMX-Formats
(waere bei ~95KB Player-Logik in `tfmx.h` ein Mehrtage-Projekt ohne Mehrwert
gegenueber der fertigen, korrekt lizenzierten Loesung).
**Build:** `tools/tfmx_player/build.sh` kompiliert `render_tfmx`
(`gcc render_tfmx.c -lm`) -- ein CLI-Tool `render_tfmx <in.tfx> <in.sam>
<out.wav> [sekunden]`, das ein TFMX-Modul zu 16-bit/44100Hz-Stereo-WAV
rendert. **Alle 3 Musikstuecke (`TITEL`, `TITEL2`, `ONGAME2`) erfolgreich
gerendert und angehoert (nicht nur Byte-Statistik!)** -- echte, hoerbare
Chiptune-Musik, kein Rauschen/Stille. Vorschau-MP3s lagen kurz unter
`/shared/uploads/aria_tfmx_*.mp3` fuer Stefan zum Reinhoeren.
**Einbau in `tools/intro_sequence.py`:** neuer Wrapper `tools/tfmx_audio.py`
extrahiert TFX+SAM per `kellogg_formats.DATContainer` aus `PCKELL.DAT`,
rendert einmalig per Subprocess zu WAV (gecacht unter
`KELLOGG_AUDIO_CACHE`/`~/kellogg_remake/audio_cache/`, NICHT committed --
ist Original-Musikmaterial wie `raw/`). `intro_sequence.py` initialisiert
jetzt `pygame.mixer`, startet beim Erreichen des Kellogg's-Logos die
`TITEL`-Musik geloopt (Annahme: durchgehende Titelmusik ueber Logo+Titelbild
+Karte -- noch nicht per Timing-Vergleich gegen DOSBox-Ton verifiziert, da
DOSBox bisher nur im Nosound-Modus lief; plausibelste v1-Annahme). Mixer-
Init ist defensiv (try/except) -- auf `aria-wohnung` (keine Soundkarte im
Container/VM) laeuft die Sequenz jetzt nachweislich sauber STUMM weiter
(getestet), mit `SDL_AUDIODRIVER=dummy` laeuft der komplette Codepfad
inklusive Musik-Rendering fehlerfrei durch (ebenfalls getestet) -- auf
Stefans Rechner mit echter Soundkarte sollte die Musik beim Ausfuehren
tatsaechlich zu hoeren sein.
**Committed:** `tools/tfmx_player/` (Quellcode + LICENSE, NICHT das
kompilierte Binary -- Build-Artefakt, per `.gitignore` draussen wie
`audio_cache/`), `tools/tfmx_audio.py`, aktualisiertes
`tools/intro_sequence.py`.
**Noch offen (niedrige Prioritaet):**
- `TITEL2`/`ONGAME2` sind noch nicht eingebunden -- vermutlich `TITEL2` fuer
die Weltkarte und `ONGAME2` fuers eigentliche Level-Gameplay, aber welche
Musik wo genau einsetzt ist nur eine Annahme (Namen sprechen dafuer,
nicht per Referenz/Timing-Vergleich verifiziert).
- Timing/Verifikation der Titelmusik-Ein-/Aussatzpunkte gegen das Original
(DOSBox bisher stumm getestet) steht noch aus.
- Rauser-Layout und Fade/Wipe-Feinjustierung (siehe vorherige Notiz) weiter
offen, niedrige Prioritaet.
### Naechste Schritte (Prioritaet, Stand spaeter Abend 22.07.)
1. `TITEL2`/`ONGAME2` an Karte/Gameplay-Musik zuordnen und einbinden, sobald
diese Screens/Modi existieren.
2. Danach: Uebergang von Intro zu echtem Gameplay -- Tony-Bewegung auf
Level W1L0 als naechster grosser Meilenstein (MAP+ICO+BOB+jetzt auch
Sound-Infrastruktur sind fertig).
3. ARE/Kollisionsformat klaeren, wenn Spiellogik es braucht.
Laeuft selbstaendig weiter, keine Rueckfrage noetig.
## Pfade relativ gemacht + Rauser/Titelbild-Korrektur (22.07.2026, Nacht)
Stefan meldete zwei Dinge: (1) alle Tools haben `/home/aria/kellogg_remake/...`
hardcodiert -- muss relativ zum Projektordner sein; (2) 6 Screenshots als
Beleg fuer Bild-Bugs (Rauser zu gross, Titelbild "gestaucht", Original hat
Scanlines die verschwinden). **Wichtig: von den 6 Screenshots ist wegen eines
Upload-Bugs (alle 6 landeten unter demselben Dateinamen und ueberschrieben
sich gegenseitig) nur der LETZTE erhalten geblieben** -- ich hab Stefan das
gemeldet und ihn gebeten, die anderen 5 bei Bedarf nochmal zu schicken. Statt
zu raten hab ich stattdessen selbst frische DOSBox-Referenzaufnahmen gemacht
(Xvfb + `import -window root`, 0.12s-Intervall) und daraus die echten Werte
gemessen -- Details unten.
**1) Pfade:** `intro_sequence.py`, `tfmx_audio.py`, `render_assets.py`,
`dat_extract.py` hatten `/home/aria/kellogg_remake/...` fest im Code. Jetzt
ueberall `PROJECT_ROOT = dirname(dirname(abspath(__file__)))` + optionales
ENV-Override (`KELLOGG_DAT`, `KELLOGG_AUDIO_CACHE`, `KELLOGG_RENDER_OUT`,
`KELLOGG_EXTRACT_OUT`). Laeuft jetzt von jedem Pfad aus (VM, Container,
woanders geklont).
**2) Rauser -- per Pixelvermessung neu gebaut:**
- Echte Groesse per Bounding-Box-Analyse gegen eine frische DOSBox-Aufnahme
gemessen: das komplette Rauser-Logo (RAUSER1+2+3 zusammen) ist im Original
nur ca. 105x40px auf einer 320x240-Leinwand -- mein v1-Code hat die
PCC-Assets 1:1 nativ gezeichnet (182x46 fuer RAUSER1 allein!), viel zu
gross. **Alle drei Rauser-Assets werden jetzt bei 0.5x Nativgroesse
gezeichnet** (`ASSET_DISPLAY_SCALE`).
- Hintergrund war in v1 WEISS die ganze Zeit -- tatsaechlich: WEISS waehrend
Slide-in+Halten, dann kurzer Fade zu GRAU (73,73,73), dann Grau halten,
dann Schwarzblende (bestehende Pause).
- **Neu: Slide-in-Animation.** Per Screenshot-Serie entdeckt: RAUSER1
("RAUSER"-Box) faehrt von LINKS ins Bild, RAUSER3 (blaue Box mit "!")
faehrt von OBEN rein, RAUSER2 ("ADVERTAINMENT") steht von Anfang an fest.
Nachgebaut mit smoothstep-Ease ueber `RAUSER_SLIDE_SECONDS=0.9s`.
- **Neu: Sound-Sting.** `TITEL2.TFX/.SAM` (das kuerzeste der 3 Musikstuecke,
724 Byte TFX) ist kein Loop-Track, sondern ein kurzer ~1s-Jingle (RMS-
Analyse zeigt: Burst 0-0.9s, dann Stille, dann Wiederholung -- typisch fuer
ein kurzes Sample, das die Tracker-Engine in einer Schleife rendert). Wird
jetzt exakt beim Erreichen der Endposition (Slide-in fertig) EINMAL
abgespielt (`pygame.mixer.Sound`, nicht geloopt).
**3) Titelbild -- KORREKTUR einer fruehen Fehlannahme (das war der Bug hinter
Stefans "gestaucht"-Meldung):**
- v1 hat die 4 Quadranten KELL256A/B/C/D (je 160x240 PCC-nativ) 1:1 zu einem
320x**480**-Bild gestapelt (A|B oben, C|D unten) und das komplett 480 hoch
angezeigt. Das war falsch.
- Per Pixel-Rekonstruktion gegen eine echte DOSBox-Aufnahme verifiziert
(mehrere Hypothesen durchprobiert, mit numpy MSE + visuellem Abgleich):
**die 4 Quadranten werden je auf HALBE Hoehe (160x120) skaliert** (Breite
bleibt nativ, NUR die Hoehe wird halbiert -- anders als beim Rauser-Fix,
wo beide Achsen gleichmaessig skaliert werden!) und im 2x2-Raster
angeordnet: A oben-links, B oben-rechts, C unten-links, D unten-rechts ->
ergibt ein korrektes 320x240-Bild. Screenshot-Vergleich (Rekonstruktion
vs. echte DOSBox-Aufnahme) war praktisch deckungsgleich.
ACHTUNG fuer kuenftige Aenderungen: bei den Rauser-Assets ist die
0.5x-Skalierung GLEICHMAESSIG (Breite+Hoehe), bei den Titelbild-Quadranten
ist es NUR die Hoehe -- zwei verschiedene Funktionen im Code
(`pcc_surface_scaled` vs. `pcc_surface_half_height`), nicht verwechseln.
- **Neu entdeckter Scanline-Effekt** (das ist genau Stefans "Screenshot 4/5/6"
-Beobachtung): waehrend die untere Bildhaelfte einwaechst, ist sie zuerst
komplett entsaettigt (graustichig) UND mit sichtbaren horizontalen
Scanlines ueberzogen -- das loest sich in einer zweiten Animationsphase zu
vollen Farben auf, bis das Bild komplett scharf ist. Per 0.12s-Screenshot-
Serie ueber die komplette Wipe-Animation hinweg entdeckt und nachgebaut
(`_scanline_desaturate()`, per numpy: Graustufen-Mix + jede zweite Zeile
abgedunkelt, Intensitaet faedet ueber die zweite Haelfte der Wipe-Dauer von
1.0 auf 0.0).
- Reveal-Mechanik selbst (oben waechst 0->120 von oben, unten waechst 0->120
von unten, treffen sich in der Mitte) war in der urspruenglichen
Beobachtung schon richtig -- nur die falsche 480px-Endgroesse und der
fehlende Scanline-Effekt waren der Bug.
**Verifiziert:** eigene DOSBox-Referenzaufnahme (Xvfb, 0.12s-Serie) gegen den
neu gebauten `intro_sequence.py`-Output verglichen -- Rauser-Slide-in, Rauser-
Groesse/Farben und Titelbild (inkl. Scanline-Phase) sehen jetzt sehr nah am
Original aus. Screenshots liegen als Beleg bei:
[FILE: /shared/uploads/aria_rauser_fixed.png] (Rauser, neu: klein, weiss, Slide-in-Mitte)
[FILE: /shared/uploads/aria_title_wipe_scanline_fixed.png] (Titelbild-Wipe MIT Scanline-Effekt)
[FILE: /shared/uploads/aria_title_final_fixed.png] (Titelbild komplett, korrekt 320x240 statt 320x480)
**Noch offen / bewusste Vereinfachungen:**
- Die feine Choreografie von RAUSER3 (slidet vermutlich als "!" und blaue Box
getrennt, siehe Screenshot-Beobachtung) wurde vereinfacht zu EINEM Element,
das von oben reinfaehrt -- sieht in der Praxis schon sehr nah dran aus,
waere aber bei Bedarf noch weiter verfeinerbar.
- Kleiner kosmetischer Rest: RAUSER2/RAUSER3 haben weissen Hintergrund IN
ihrer eigenen PCC-Bounding-Box (kein Alpha-Kanal). Auf weiss (waehrend
Slide-in+Halten) faellt das nicht auf, nach dem Fade zu Grau sieht man
einen schwachen weissen Kasten drumrum. Versuch mit `set_colorkey()`
wieder verworfen (hat auch die weissen BUCHSTABEN in RAUSER1 durchsichtig
gemacht -- schlimmer als der Kasten). Braucht eine echte Alpha-Maske pro
Asset, low priority.
- Der Scanline-Clear-Effekt ist als gleichmaessige Intensitaet ueber die
ganze untere Haelfte umgesetzt, nicht als praezise von-der-Mitte-nach-
aussen wandernde Grenze (im Original loest sich ein duenner Reststreifen
am unteren Bildrand als letztes auf) -- optische Wirkung ist aber schon
sehr aehnlich.
- VM-Testverzeichnisse (`boot_frames2/`, `intro_check_frames/`,
`title_check_frames/`) nach der Analyse wieder aufgeraeumt, nicht im Repo.
Laeuft selbstaendig weiter, keine Rueckfrage noetig.
## 22.07.2026, spaeter Abend (2): drei Feedback-Bugs von Stefan gefixt
Stefan hatte nach dem letzten Pfade-relativ/Rauser/Titelbild-Fix drei neue
Beobachtungen gemeldet, alle drei bestaetigt und behoben:
**1. "Rauser-Text fadet nicht mit aus, bleibt hart umrandet stehen."**
Ursache endlich richtig verstanden (der oben als "kosmetischer Rest, low
priority" dokumentierte Kasten): RAUSER1/2/3.PCC haben keinen Alphakanal, ihr
papierweisser Hintergrund sitzt als hartes Rechteck um die Box-Grafik. Fix:
neue Funktion `make_border_bg_transparent()` in `intro_sequence.py` -- Flood-
Fill vom Bildrand aus ueber alle "papierweissen" Pixel (Schwelle 235/255 je
Kanal), NUR die vom Rand aus zusammenhaengende Flaeche wird transparent.
Isolierte weisse Pixel MITTEN im Motiv (z.B. die weissen Buchstaben in
RAUSER1) beruehren den Rand nicht und bleiben unangetastet -- genau das
Problem, an dem der fruehere `set_colorkey()`-Versuch gescheitert war.
Verifiziert per direktem Pixel-Test (nicht nur behauptet): Eckpixel neben der
Box liegt nach dem Fix exakt bei RAUSER_GREY (73,73,73) statt weiss, und im
Bild bleiben klar sichtbare, fast voll opake Buchstaben-Pixel (Alpha bis 254)
erhalten. Screenshot der Grau-Phase zum Beleg:
[FILE: /shared/uploads/aria_rauser_fade_grey_check.png]
**2. "Musik ist zu kurz, da fehlt der Rest" + Cache-Bug gefunden.**
Zwei uebereinanderliegende Ursachen:
a) `render_wav()` cached per Dateiname OHNE den `seconds`-Parameter zu
beruecksichtigen -- ein alter Test-Render (TITEL mit 8s, TITEL2 mit 15s
aus einer frueheren Debug-Session) blieb im `audio_cache/` liegen und
wurde bei jedem Aufruf stillschweigend weiterverwendet, egal welche
`seconds` der Aufrufer diesmal wollte.
b) selbst ein frischer Render haette bei den frueher verwendeten festen
Werten (TITEL: 60s) abgeschnitten -- **TITEL ist tatsaechlich ~109s
lang, ONGAME2 ~123s**, beides deutlich laenger als angenommen.
Echter Fix in `render_tfmx.c`: statt stur `seconds` lang zu rendern, wird
jetzt in kleinen 64-Frame-Haeppchen gerendert und tfmx.h's eigenes
`real_song_end`-Flag beobachtet (wird pro Tick gesetzt, sobald die
Track-Sequenz einmal komplett durchgelaufen ist und intern neu startet) --
sobald es feuert, ist GENAU eine vollstaendige natuerliche Song-Schleife im
Kasten, egal wie lang das Stueck wirklich ist. `seconds`/`max_seconds` ist
jetzt nur noch eine Sicherheits-Obergrenze (Default 180s, grosszuegig ueber
allen 3 bekannten Stuecken). Getestet und verifiziert:
- TITEL: 109.44s (natuerliches Ende erkannt)
- TITEL2 (Rauser-Sting): 2.04s (natuerliches Ende erkannt)
- ONGAME2: 122.88s (natuerliches Ende erkannt)
Alte falschen Cache-Dateien geloescht, mit dem gefixten Tool neu gerendert
und die Laengen direkt per `pygame.mixer.Sound(...).get_length()` verifiziert
(nicht nur die WAV-Header-Bytes ausgerechnet).
**3. "Bei Factor5 gibt's auch noch ne Melodie, solange das Lied ist Factor5
anzeigen und dann zum Kellogg's-praesentiert."**
`ONGAME2` war bis jetzt der einzige der 3 TFMX-Tracks ohne zugeordneten
Screen (TITEL2 = Rauser-Sting, TITEL = Kellogg's-Logo/Titelbild-Loop) --
genau das ist offenbar die Factor5-Melodie. `run_once()` in
`intro_sequence.py` spielt ONGAME2 jetzt beim Erscheinen des Factor5-Logos
einmal komplett ab und haelt den Screen exakt so lange (`sound.get_length()`,
~123s statt der alten festen 3.0s), bevor es zur schwarzen Pause + Kellogg's-
Logo weitergeht.
**Alle 3 Fixes verifiziert, nicht nur behauptet:**
- Rauser-Alpha-Fix: direkter Pixel-Test + Screenshot (oben verlinkt).
- Musik-Laengen: `render_tfmx`-Stderr-Ausgabe UND `pygame.mixer.Sound.get_length()`
geben identische Werte.
- Kompletter Smoke-Test (`xvfb-run python3 tools/intro_sequence.py --once`)
laeuft ohne Traceback durch (headless ohne Audio-Geraet, laeuft dann
erwartungsgemaess stumm mit Fallback-Dauern).
Committet + gepusht. Naechster Schritt weiterhin offen: echtes Gameplay
(Tony auf `W1L0` bewegen).
## 22.07.2026, Nacht (3): Rauser-Aufloesung wirklich gefixt + Factor5-Sound korrigiert (Multi-Song-Fund)
Stefan hatte zwei weitere Punkte gemeldet: "Rauser-Intro-Aufloesung ist immer
noch schlecht" (mit Referenz-Screenshot: `RAUSER!`-Logo klein, mittig, viel
weisser Rand drumrum) und "beim Factor5 hast du einen Level-Sound genommen,
der echte Factor5-Sound ist ein kurzes Brausen, nur wenig laenger als der
Rauser-Sound, Bild ca. 5s eingeblendet".
**Wichtiger Fund beim Reinschauen:** Zwischen den Chat-Nachrichten war im
Hintergrund (autonomer Weiterarbeits-Timer) schon ein erster Loesungsversuch
fuer beides auf der VM entstanden, aber weder ins Git-Repo synced noch
committet/gepusht -- der Chat-Verlauf hatte den Stand nicht wiedergegeben.
Ich hab den VM-Stand zuerst gegen den Repo-Stand geprueft (`diff`), den fuer
Factor5 bereits korrekten Teil uebernommen, und den Rauser-Aufloesungs-Fix
selbst nochmal nachgerechnet, weil er in der ersten Fassung (native 1.0x
Groesse) tatsaechlich einen NEUEN Bug enthielt.
**1) Rauser-Aufloesung -- diesmal wirklich pixelvermessen statt geraten:**
Stefans Referenz-Screenshot per Bounding-Box analysiert (PIL, Python auf der
VM): das Logo nimmt dort nur **34.5% der Bildbreite / 16.6% der Bildhoehe**
ein (223x83px auf einem 646x500px-Screenshot). Das widerlegt die vorige
Annahme "native Groesse (ASSET_DISPLAY_SCALE=1.0) ist richtig" klar -- bei
nativer Groesse waeren RAUSER1+RAUSER3 zusammen 198px breit auf einer
320px-Leinwand (62%), fast doppelt so viel wie die Referenz zeigt. Die
URSPRUENGLICHE 0.5x-Skalierung war groessenmaessig also die ganze Zeit
richtig -- der eigentliche Bug war die Skalierungs-METHODE:
`pygame.transform.smoothscale()` ist bilinear und verwaescht harte
Pixel-Kanten von Pixel-Art beim Verkleinern sichtbar. Fix: `smoothscale()`
durch `pygame.transform.scale()` ersetzt (Nearest-Neighbor, kein
Weichzeichnen) in `Assets.pcc_surface_scaled()` UND `pcc_surface_half_height()`
(Titelbild-Quadranten, gleiches Prinzip), `ASSET_DISPLAY_SCALE` bleibt 0.5.
Verifiziert per eigener Bounding-Box-Nachmessung des neuen Renders: **33.0%
Breite / 13.6% Hoehe** -- nah an Stefans Referenz (34.5%/16.6%), klar besser
als der 62%-Fehlgriff der vorigen (native) Version. Frisch gerenderter
Vergleichs-Screenshot:
[FILE: /shared/uploads/aria_rauser_scale05_check.png]
**2) Factor5-Sound -- Multi-Song-Fund geklaert die Zuordnung:**
Ein einzelnes TFX-Modul kann laut `tfmx.h` (`v_songs[]`/`v_songs_count`)
MEHRERE Songs buendeln, ausgewaehlt per `start_song`-Index -- das erklaert
Stefans Vermutung "das Spiel hat noch wesentlich mehr Lieder". Debug-Rendering
aller Songs in allen 3 Modulen ergab:
- `TITEL.TFX` -> 3 Songs (song0/1 lang >15s, song2 ~8.0s)
- `TITEL2.TFX` -> 2 Songs (song0 = 2.04s, song1 = 2.40s)
- `ONGAME2.TFX` -> 14(!) Songs (mehrere >15s-Tracks, vermutlich echte
Level-/Gameplay-Musik -- song3/11/12 sehr kurz 0.16-1.36s, song2/13
~10.3s; noch nicht im Detail zugeordnet, relevant fuer spaetere
Gameplay-Musik)
Die vorige Zuordnung (ONGAME2 komplett = "Factor5-Melodie") war schlicht
falsch -- vermutlich echte Level-Musik, nicht zum Studio-Logo passend (daher
Stefans "Level-Sound"-Eindruck). TITEL2-Song1 (2.40s) passt dagegen exakt zu
"nur ein bisschen laenger als der Rauser-Sound" (Rauser-Sting = TITEL2-Song0
= 2.04s, Differenz nur 0.36s). `tools/tfmx_audio.py` bekam dafuer einen
optionalen `song_index`-Parameter (durchgereicht an `render_tfmx` als
5. CLI-Arg), `run_once()` in `intro_sequence.py` spielt jetzt TITEL2-Song1
beim Factor5-Logo ab, Anzeigedauer ist fest auf `FACTOR5_HOLD_SECONDS = 5.0`
gesetzt (statt an eine -- falsche -- Songlaenge gekoppelt).
Verifiziert: `TITEL2.wav` (Song0) 2.04s, `TITEL2_song1.wav` (Song1) 2.40s,
beide mit gesunder Amplitude (kein Stille-Bug), unterschiedliche Dateigroesse
(89984 vs. 105856 Samples -- also wirklich unterschiedlicher Inhalt, keine
zufaellige Doublette).
**Ehrlicher Vorbehalt (wichtig):** Ich kann Audio nicht selbst abhoeren --
die Zuordnung TITEL2-Song1 = "Brausen" ist eine Indizien-Schlussfolgerung aus
der Songlaenge (passt sehr gut zu Stefans Beschreibung), NICHT durch
Reinhoeren verifiziert. Falls es beim echten Testen doch nicht wie ein
Brausen klingt: melden, dann probieren wir als naechste Kandidaten TITEL-Song2
(~8.0s) oder einen der kurzen ONGAME2-Songs (3/11/12).
Beide Fixes committet + gepusht. Naechster Schritt weiterhin offen: echtes
Gameplay (Tony auf `W1L0` bewegen).
## 23.07.2026 -- Hang-Bug gefixt + alle 19 TFMX-Songs fuer Stefan gerendert
**1) Hang-Bug (Stefans Meldung "nur noch ein schwarzes Bild, verschiebe ich
das Fenster wird der Desktop reingerendert, das Programm haengt"): real,
gefunden, gefixt, verifiziert.** Ursache: `tfmx_audio.render_wav()` rief den
TFMX-Renderer als blockierenden `subprocess.run()` auf -- bei einem frischen,
ungecachten Song (v.a. TITEL, ~109s Musik) dauert das reproduzierbar ~14s,
waehrend derer die pygame-Eventloop NICHT lief. Das OS stuft ein Fenster
ohne Event-Pumping als "nicht reagierend" ein -- exakt Stefans beschriebenes
Bild (schwarz eingefroren, Desktop blitzt beim Verschieben durch).
Fix: `preload_all_audio()` rendert jetzt alle 3 benoetigten Songs (Rauser-
Sting, Factor5-Sound, Titelmusik) EINMALIG vor der eigentlichen Sequenz, per
Hintergrund-Thread + responsivem "Lade..."-Bildschirm
(`render_audio_with_loading()`). Danach greifen alle `run_once()`-Durchlaeufe
nur noch auf fertige WAV-Pfade zu, nie mehr Live-Rendering im Hauptloop.
Verifiziert per Xvfb-Testlauf (`SDL_AUDIODRIVER=dummy`, `--once`): laeuft
jetzt exit-code-0 sauber durch, kein Haengen mehr.
Committet+gepusht (`73b887e`). Nebenbei `__pycache__/` ins `.gitignore`
aufgenommen (`8509cbc`).
**2) Alle 19 TFMX-Songs gerendert, als MP3-Zip an Stefan uebergeben (NICHT
ins Git-Repo, gleiche Begruendung wie bei raw/PCKELL.DAT -- ist Original-
Spielmaterial/Musik, nur als Download fuer den Privatgebrauch, siehe
`tfmx_audio.py`-Docstring und `.gitignore`):**
- `TITEL.TFX`: song0 (=`TITEL.mp3`, 109s, aktuell die Titelmusik),
song1 (109s, bisher ungenutzt), song2 (~8s, bisher ungenutzt)
- `TITEL2.TFX`: song0 (=Rauser-Sting, 2.04s), song1 (=aktuell als
Factor5-Sound eingebaut, 2.40s -- NICHT durch Reinhoeren verifiziert,
nur aus der Songlaenge geschlossen, siehe Vorbehalt vom 22.07.)
- `ONGAME2.TFX`: 14 Songs (song0-13), vermutlich echte Level-/Gameplay-
Musik, noch keinem Screen/Level zugeordnet
19 WAVs -> MP3 (ffmpeg, libmp3lame, konvertiert um die Downloadgroesse von
~300MB WAV auf ~59MB MP3 zu druecken) -> Zip, an Stefan uebergeben.
Dateinamen im Zip = `<TFX-Name>[_song<N>].mp3` (song0/Default ohne Suffix).
**Stefans Aufgabe:** reinhoeren und mir sagen, welche Datei WIRKLICH nach
Factor5-Brausen klingt -- falls es NICHT `TITEL2_song1.mp3` ist, dann in
`intro_sequence.py`/`run_once()` den `audio_paths.get(('TITEL2', 1))`-Call
auf die richtige Datei/song_index umstellen (Kandidaten laut Laenge:
`TITEL_song2.mp3` ~8s, oder ein kurzer ONGAME2-Song).
Naechster Schritt weiterhin offen: echtes Gameplay (Tony auf `W1L0` bewegen).
Sobald Stefan den richtigen Factor5-Song bestaetigt hat, kurzer Fix, dann
weiter zu Gameplay.
## Stand 23.07.2026, vormittags -- Factor5-Sound bestaetigt + Repo-Hygiene-Fund
**1) Factor5-Sound ist bestaetigt richtig.** Stefan hat sich durch die
Hoerproben gehoert und explizit `TITEL2_song1.wav` als den richtigen
Factor5-"Brausen"-Sound zurueckgemeldet -- das ist exakt die Datei, die seit
`af2ead1` schon eingebaut ist (`audio_paths.get(('TITEL2', 1))` in
`run_once()`). Kein Code-Aenderung noetig, nur der offene Vorbehalt von
22.07. ("nicht durch Reinhoeren verifiziert") ist jetzt erledigt.
**2) Repo-Hygiene-Bug gefunden + gefixt (kein Stefan-Report, selbst
entdeckt beim Nachschauen):** `tools/tfmx_player/render_tfmx.c` war in
`origin/master` NICHT auf dem aktuellen Stand -- das Song-Index-Feature
(noetig fuer genau obigen Factor5-Fix, TITEL2 Song 1 statt Song 0) existierte
nur lokal auf der VM (`/home/aria/kellogg_remake`, gebaut, aber nie
committet), nicht im Repo selbst. Ein frischer `git clone` + `build.sh`
haette also einen Binary OHNE Song-Auswahl erzeugt -- jeder
`render_wav(..., song_index=N)`-Call waere stillschweigend auf Song 0
zurueckgefallen (kein Crash, aber hörbar falscher Sound). Genau die Art
1:1-Port-Bruch, die Stefan nicht will.
Ursache: parallel zur Session ist ein zweiter Git-Checkout unter `/tmp`
(tmpfs!) entstanden und dort unabhaengig weitergearbeitet worden, ohne vorher
zu fetchen -- Historie ist auseinandergelaufen (zwei verschiedene Commits fuer
denselben Hang-Fix). Nach Abgleich: `/home/aria/kellogg_remake` (die
tatsaechliche Arbeitskopie) war fuer ALLE Dateien ausser dieser einen C-Datei
schon 1:1 identisch mit `origin/master`. Fix: `render_tfmx.c` nachgezogen und
committet (`a077dce`), der veraltete `/tmp`-Checkout mit den ueberholten
Doppel-Commits verworfen (kein Datenverlust, deren Inhalt war ohnehin ein
aelterer Zwischenstand als das, was schon auf `origin/master` lag).
**Lehre / Infra-Aenderung:** der Arbeits-Git-Checkout fuer Commits/Pushes
liegt jetzt dauerhaft unter `/home/aria/tony-remake-git` (statt `/tmp`,
das ist RAM-tmpfs und wird bei jedem VM-Neustart geleert -- genau DAS hat
den Doppel-Checkout ueberhaupt erst ermoeglicht). Kuenftige Sessions: dort
weiterarbeiten, IMMER zuerst `git fetch && git status` gegen `origin/master`
pruefen bevor neue Commits drauf, um erneutes Auseinanderlaufen zu vermeiden.
**Naechster Schritt:** Der Factor5-Sound-Vorbehalt ist jetzt erledigt, die
Intro-Sequenz ist damit inhaltlich fertig (Rauser -> Factor5 -> Kellogg's-Logo
mit Vorhang+Scanlines -> Titelbild, alles mit korrektem Sound, freeze-frei).
Zwei Baustellen bleiben offen, beide von Stefan noch nicht explizit
beauftragt:
1. Der von Stefan gemeldete schwarze-Screen/Fenster-Haenger nach dem
Rauser-Intro sollte jetzt behoben sein (Hang-Fix oben) -- noch nicht von
Stefan nochmal getestet/bestaetigt.
2. Der naechste grosse inhaltliche Meilenstein bleibt unveraendert: echtes
Gameplay, Tony auf Level `W1L0` steuerbar machen (Map/Sprites/Kollision
sind alle schon fertig extrahiert, es fehlt nur noch die Spiellogik/der
eigentliche Python-Spiel-Loop).
## 24.07.2026 -- Aufraeumen: Duplikate entfernt, Soundfiles ins Repo
Stefan wollte einen Ueberblick + die im Projektordner angesammelten Duplikate
bereinigen. Bestandsaufnahme ergab drei redundante Brocken (~327 MB, alle nur
lokal, nicht in git):
- **`aria_kelloggs_extrahierte_assets/` (+ `.zip`) -- geloescht.** War ein
VERALTETER Snapshot-Export von `tools/`/`png_out/`/`render_out/`/
`extracted_pre/`: png_out byte-identisch, aber die Tools alt (z.B.
`intro_sequence.py` 11 KB vs. aktueller Stand 34 KB). Aktueller Stand liegt
in git + Arbeitsbaum, der Snapshot war rein historisch.
- **`aria_kelloggs_soundfiles_voll.tar.gz` -- geloescht.** Redundante
Pack-Kopie des entpackten Sound-Ordners.
- **`aria_kelloggs_soundfiles_voll/` -> `audio/` umbenannt und COMMITTET.**
25 Dateien, 167 MB: alle gerenderten Musik-WAVs (TITEL/TITEL2/ONGAME2 inkl.
aller Songs) + die `.SAM`/`.TFX`-Quellen.
**Copyright-Entscheidung (Stefan):** die WAVs kommen bewusst mit ins Repo --
freies Werbespiel, innerhalb DE frei kopierbar, WAVs nur aus `PCKELL.DAT`
extrahiert/gerendert. Format bleibt WAV (nicht FLAC/MP3), so wie extrahiert.
**`audio_cache/` bleibt gitignored** -- reiner Laufzeit-Cache, den
`intro_sequence.py` bei jedem Start selbst regeneriert; committen wuerde die
gerade beseitigte Dopplung wieder einbauen.
Installer + Handbuch (`Tony-...Installer-Alt-1.zip`, `Tony-...Manual_DOS_DE.pdf`)
im Anschluss ebenfalls ins Repo committet (gleiche Copyright-Begruendung wie
audio/).
Naechster Schritt unveraendert: echtes Gameplay (Tony auf `W1L0`).
## 24.07.2026 (Nacht) -- Factor5-Tail, Menuesystem + Weltkarte (Referenz-getrieben)
**1) Factor5-Sound "nicht ganz abgespielt" -- Renderer-Fix.** real_song_end
markiert nur das Ende der TRACK-SEQUENZ (2.4s); die noch klingenden Samples
wurden hart abgeschnitten (RMS stieg bis zum letzten Frame). render_tfmx.c
kann jetzt einen Ausklang-Tail rendern (6. CLI-Arg): loop_mode=0 stoppt den
Sequencer bei song_end, die Paula-Voices klingen weiter (erster Versuch OHNE
loop_mode-Stopp nahm die naechste Loop-Iteration auf -- per RMS-Selbst-
aehnlichkeit von 3 Anschwell-Mustern nachgewiesen). Das Factor5-"Brausen"
loopt auf Paula-Ebene endlos (kein natuerliches Ende!) -> Tail 2.6s = exakt
5.0s Gesamt (DOSBox-Screendauer ~4.8s) mit 600ms-Fade-out. Rauser-Sting endet
natuerlich bei 2.34s (Stille-Erkennung). Cache-Suffix `_tail` invalidiert
alte abgeschnittene WAVs. Commit `949a7ee`.
**2) Kompletter Menuefluss per DOSBox-Referenz erfasst** (Xvfb + python3-Xlib
XTEST-Tasten -- xdotool ist auf Stefans Rechner nicht installiert; WICHTIG:
ohne Window-Manager ist der X-Fokus PointerRoot, also erst Pointer in die
Fenstermitte bewegen + set_input_focus, sonst kommen Keys nie an):
- Titelbild wartet ENDLOS (115s passiv, kein Attract-Loop). NUR ESC oeffnet
das Hauptmenue (Space/Return tun nichts -- per Einzeltasten-Sessions
verifiziert). Menue faedet ein.
- Hauptmenue: HIGHSCORES/ANLEITUNG/OPTIONEN/START/QUIT, Default-Auswahl
START, der selektierte Eintrag BLINKT (= der Cursor). PARTY1/PARTY2 links/
rechts, unten Credits-Ticker (gelb): "FREEGAMES #3", "REALISATION RAUSER
ADVERTAINMENT GMBH REUTLINGEN", "PRODUZIERT VON FACTOR 5", "ANIMATIONEN
UND ILLUSTRATIONEN HARTWIG NIEDERGASSEL".
- OPTIONEN: MUSIK EIN/AUS, EFFEKTE EIN/AUS, EXIT. ANLEITUNG: gelbe Texttafel
(Joystick-Text, Faehigkeiten TONY=RENNEN/SMACKS=WASSERBLASEN/COCO=HANGELN
+KLETTERN/TOUCAN=FLIEGEN). HIGHSCORES: NAME/SCORE/ITEMS/LEV-Tabelle
(MATTHIAS 100000 ... BORIS 2500). START -> Weltkarte (KARTE.PCC, mit
Charakter-Animationen im Original).
- Nebenfund: Copyright-Tafel ("DIESES SPIEL IST INNERHALB DEUTSCHLANDS FREI
KOPIERBAR...") erschien in einer Session vor dem Menue -- Ausloeser noch
unklar (nicht Return, nicht Space), evtl. zyklische Attract-Tafel im Menue.
Noch nicht nachgebaut.
**3) Implementiert in game/menu.py** (Commit `c8f7d4d`): BitmapFont-Parser
fuer FON_1/FON_1B (9x7-Zellen, ABER 8px-Vorschub -- sonst passen die
32-Zeichen-Anleitungszeilen nicht in die Box) und FON_2 (16x15, 3 Reihen:
A-T / U-Z.,:!?- / 1-0). Gelbe Schrift mit schwarzem Outline-Rendering.
PARTY-Sprites per make_border_bg_transparent freistellen (papierweisser BG;
ein Schwarz-Replace loescht die KONTUREN -- sichtbar kaputt, verworfen).
intro.py: Zustandsautomat Titelbild <-> Menue <-> Karte; QUIT beendet.
**Verifiziert:** End-to-End-Lauf des REMAKES unter Xvfb mit echten XTEST-
Tasten: Intro -> ESC -> Menue -> START -> Karte -> zurueck -> Optionen ->
Anleitung -> Highscores -> QUIT (rc=0). Alle Screens visuell gegen die
DOSBox-Referenz geprueft (Menue/Highscores/Anleitung/Karte praktisch
deckungsgleich).
**Offen/naechste Schritte:**
1. Weltkarte: KARTE0-9-Animationen + Levelauswahl (Cursor auf der Karte).
2. Gameplay: Tony auf W1L0 (Map/Tiles/Sprites laengst dekodiert).
3. Copyright-Tafel-Ausloeser im Original klaeren (niedrig).
4. Highscores persistent machen + Eintrag-Eingabe (erst mit Gameplay relevant).
## 25.07.2026 -- LEVEL 1 SPIELBAR (erster Gameplay-Slice)
Stefans Auftrag: Weltkarte Level 1 auswaehlbar + "schauen was du aus Level 1
machst". Ergebnis: das erste Level laeuft.
**Referenz-Erkenntnisse (DOSBox, Xvfb+XTEST mit gehaltenen Tasten):**
- Auf der Weltkarte startet SPACE das Level (Return tut nichts, Pfeiltasten
bewegen am Spielstart keinen sichtbaren Cursor -- 0 Pixel Diff nach 2x
Right). Kurze Schwarzblende, dann steht das Level sofort.
- **Level 1 = W1L1.MAP** -- per Template-Matching des Gameplay-Screenshots
gegen die gerenderten Maps (diff W1L1=35.4 vs W1L0=56.5/W1L2=58.8; Kamera-
Match bei Map-Pixel 128,496). W1L0 ist also NICHT Level 1 (vermutlich
Bonus-/Sonderlevel).
- HUD unten 320x24 (Viewport 320x176): Kellogg's-Schriftzug, Keks-Zaehler,
Item-Box, Score, 4 Herzen, Tony-Kopf + Leben (3), Uhr zaehlt von 10:00.
- Kollision: MAP-ctype **Bit 32 = solide** (40=Boden-Oberkante=32|8,
32=Fuellung; 0=Luft, 4=Item-Marker, 16=Special). Empirisch an der
Spawn-Plattform (Zeile 40) verifiziert.
- TONY.BOB-Frames: 8-Phasen-Gehzyklus, GERADE Indizes = links, UNGERADE =
rechts (0..15); 16/17 Sprung, 24/25 Ducken (Zuordnung v1, ggf. justieren).
- GAMEBAR.BOB: 0-9 Ziffern, 10-16 Herzen/Schluessel(rot/blau/gruen)/Kopf --
Ziffern rendern mit bob-Regel-Palette dunkelrot statt weiss wie im
Original-HUD (Laufzeit-Palette unklar) -> HUD v1 nutzt FON_1B-Ziffern.
**Implementiert (game/level.py, Commit siehe git):** Level-Klasse (MAP+ICO
einmal komplett vorgerendert, Kamera blittet Ausschnitt), Tony (AABB-Physik
gegen Tile-Grid: laufen 110px/s, Sprung -300px/s, g=900 -- v1 nach Gefuehl,
Feinschliff nach Stefans Test), HUD-Nachbildung, ONGAME2-Song0 als
Levelmusik-ANNAHME (wie Factor5: nach Gehoer korrigieren). ESC -> Karte
(Titelmusik startet wieder). Weltkarte: Space/Return -> Level.
ONGAME2 wird im Audio-Preload mitgerendert (Levelstart blockiert nie).
**Verifiziert:** E2E unter Xvfb mit gehaltenen Tasten -- Tony laeuft rechts
(Kamera scrollt), faellt Plattformkante runter, laeuft links unterm
Brueckenbogen durch, ESC->Karte->Menue->QUIT rc=0. Level-Standbild praktisch
deckungsgleich mit DOSBox-Referenz (Haus/Baum/?-Block/Bruecke/Saeulen).
--once-Regression exit 0.
**Offen:** Items (ctype 4) einsammeln, Gegner, Duck-Frame-Zuordnung pruefen,
Original-HUD-Grafik, Karten-Animationen (KARTE0-9), Levelmusik-Song nach
Gehoer, Zeitablauf/Leben-Logik, weitere Level freischalten.
## 25.07.2026 (2) -- Stefans Level-Feedback eingearbeitet
Stefans annotierter Screenshot (4 Punkte), alle behoben:
1. **Figur lief rueckwaerts** -- TONY.BOB: GERADE Frames = RECHTS, ungerade
= links (Zuordnung war exakt vertauscht).
2. **Schraegen: fiel durch** -- ctype 16 ist KEINE Tuer, sondern das
SCHRAEGEN-Tile (als blaue Diagonale in der ctype-Farbvisualisierung exakt
auf dem Grashang gefunden). 45-Grad-Slope-Kollision implementiert
(Richtung per Nachbar-Heuristik, Fussmittelpunkt-basiert, Snap-Toleranz
10px). Simulation: Tony laeuft den Hang bis Plateau-Hoehe (y=559) hoch,
faellt nirgends mehr durch.
3. **HUD**: weisse Kellogg's-Leiste ergaenzt (Schriftzug aus KELLOGGS.PCC
gecroppt + klein skaliert), Herzen jetzt echte GAMEBAR-Sprites (Frame 10
voll / 11 leer; 13-15 sind die rot/blau/gruenen Schluessel -- fuer
Stefans "Tueren mit farbigem Knauf brauchen Schluessel" spaeter), Uhr-
Layout gefixt + Start bei 9:59 (4-stellig passt immer; Original zeigte
~1.5s nach Start ohnehin 9:59).
4. **RENNEN** -- Tonys Faehigkeit lt. Original-Anleitung ("MIT ALT
AKTIVIERST DU DIE FAEHIGKEITEN... TONY: RENNEN"): ALT halten (LSHIFT als
moderne Alternative), 185 statt 110 px/s, schnellere Lauf-Animation.
Physik-Feel laut Stefan gut. Levelmusik-Song prueft er nach Gehoer.
Offen bleiben: Tueren oeffnen (ALT an Tuer, Schluessel-Logik), Items,
Gegner, Karten-Animationen.
## 25.07.2026 (3) -- Stefans grosses Feedback-Paket: Charaktere, Wasser, Animationen, HUD
Referenz: 7 annotierte Screenshots von Stefan + DOSBox (machine=vgaonly --
Stefans Fund: das WASSER wird nur bei vgaonly gerendert, VGA-Palette-Effekt!).
**Asset-Funde:**
- WECH_1-4.PCC = die 4 Charakter-Koepfe FARBIG, WECH_5-8 = GRAU (= im Level
nicht verfuegbar) -- exakt das Tuer-Auswahlmenue "MIT WEM MOECHTEST DU
SPIELEN?". ITE0-3.PCC = die 4 Cerealien-HUD-Icons (Flake/Puff/Loop/Choco).
FROS/SMAC/COCO/LOOP_W1-3.ICO = SAMMEL-Cerealien-Animationsphasen pro Welt
(fuer Items spaeter). TONY-Frames: 18/19=Sprung, 24/25=Ducken, 26-33=
RENN-"Rad" (Stefans Fuesse-wie-ein-Rad), 44-49=Idle-Packung. SMACKS: 51
Frames, 18/19=Ducken, 24/25=Sprung, 31-36=Idle-Packung, 37-44=SCHWIMMEN,
45-47=Wasserblasen.
- ARE-Format angeschaut (W1L1: 6690B, 3 Regionen; Header ab Byte 8 enthaelt
Big-Endian-Pixelkoordinaten-Paare, u.a. (256,592)~Spawn-Kopfposition) --
noch NICHT geknackt, v1 nutzt LEVEL_INFO-Konstanten. TODO.
**Implementiert (game/level.py):**
- Player-Klasse mit CHAR_DEFS (Tony/Smacks/Coco/Toucan): Tony rennt (ALT/
Shift, Rad-Animation), Smacks schwimmt (kein Wasserschaden, traege
Physik, Space=Schwimmstoss), Idle-Packungs-Animation nach 5s Stillstand,
Sprung-Frames korrigiert (18/19 gestreckt statt 16/17).
- Sprunghoehe kalibriert: -360 (~72px ~ Stefans "Kopf an den Ast").
- WASSER: water_y pro Level, dunkelblaues Overlay, Nicht-Schwimmer verlieren
alle 5s ein Herz; 0 Herzen -> Leben-1 + Respawn; 0 Leben -> Karte.
- TUER (Level-1-Haus, Tiles 12-13/37-39): Kopf-Symbol drueber; ALT im STEHEN
(nicht beim Rennen) oeffnet das Charakter-Menue (WECH-Koepfe, nicht
verfuegbare grau, Auswahl blinkt), Wechsel uebernimmt Position.
- HUD nach Referenz: Keks-Box MIT Cerealien-Icon des aktiven Charakters,
Herz-Box als 2x2-GRID, Charakter-Kopf-Box (zeigt IMMER die gewaehlte
Figur) + Leben, Uhr.
**Verifiziert:** E2E (Xvfb+XTEST): Tuer -> Menue -> Smacks gewaehlt -> HUD
zeigt Smacks-Kopf+Puff-Icon, Wasser-Overlay sichtbar, laufen/springen/
ducken als Smacks, ESC/QUIT rc=0. --once Regression exit 0.
**Offen:** ARE knacken (Charaktere/Wasser/Tueren/Spawns pro Level echt),
Items einsammeln (FROS_W1-Rotationssprites + ctype 4), Smacks-Blickrichtungs-
Paritaet pruefen, Coco/Toucan-Frames zuordnen (aktuell Tony-Layout
angenommen), Kamera-Hoehe feinjustieren, Gegner, Tuer-/Schluessel-Logik.
## 25.07.2026 (4) -- zweite Feedback-Runde: Wasser-Mechanik, Slopes, Spawn-Pose, HUD v3
Stefans Live-Test (5 Screenshots), alles umgesetzt:
1. **Slope-Schweben gefixt**: in der Luft snappt der Fuss nur noch bei
echter Durchdringung der Diagonale (tol=0); nur wer schon am Boden ist,
steigt mit 8px-Toleranz bergauf. (Nebenfund: der Grashang hat ZWEI
Slope-Tiles uebereinander -- gelaufen wird auf der oberen Diagonale.)
2. **Wasser-Mechanik wie im Original**: Nicht-Schwimmer tauchen NICHT unter
-- die Oberflaeche traegt (Fuss sinkt nur 14px ein, on_ground, man kann
von ihr abspringen), Herzverlust laeuft weiter. Schwimmer (Smacks): nahe
der Oberflaeche loest die Sprungtaste einen ECHTEN Sprung aus (vorher kam
man nicht mehr aus dem Wasser raus, nur schwacher Schwimmstoss).
Smacks-Blickrichtung: passt (Stefan bestaetigt).
3. **Start-Sprite-Sequenz** beim Levelstart/Charakterwechsel: Tony streckt
den Arm hoch (Frame 22), Smacks Daumen hoch (Frame 22); ~1.1s, jede
Eingabe bricht ab.
4. **Rennsprung weiter**: RUN_SPEED 185 -> 200 ("bis hinter die Blume").
5. **HUD v3** nach Original-Screenshot: helle Creme-Rahmen statt Gold,
groessere Boxen (18px), Icon in voller Groesse, leere Herz-Plaetze
bleiben dunkel.
Verifiziert per Physik-Simulationen (Wade-Ebene 686=water_y+14 + Sprung
vy=-345 von dort; Smacks-Ausstiegssprung vy=-360 an offener Wasserstelle;
Smacks unter der Bruecke stoesst korrekt an die Decke) + E2E rc=0 + --once ok.
## 25.07.2026 (5) -- dritte Feedback-Runde: echte GAMEBAR, Pixel-Slopes, Diverses
1. **GAMEBAR.PCC ist die echte HUD-Grafik** (320x25, aus PCKELL.DAT --
Stefans Fund via preview/png_out/GAMEBAR.png): Kellogg's-Leiste, alle 6
Boxrahmen, sogar der Uhr-":". Die weissen Box-Innenflaechen fuellt die
Engine zur Laufzeit dunkelrot -> wir ersetzen sie beim Laden (Pixel-Loop
rechts der Kellogg's-Leiste). HUD zeichnet nur noch Inhalte an die
gemessenen Box-Positionen (57/105/160/216/254/286). HUD_H jetzt 25,
VIEW_H 175. (Mit W1-Palette statt eigener wird die Grafik magenta --
getestet und verworfen.)
2. **Slopes jetzt PIXELGENAU**: pro Slope-Tile (ctype 16) und Spalte wird
der oberste Nicht-Himmel-Pixel der Tile-Grafik als Laufflaeche genommen
(slope_profiles; Sky-Index = haeufigster Pixel des haeufigsten Luft-Tiles
-- NICHT der ersten Luft-Zelle, das war Tile 0/Randfueller). Die Haenge
sind tatsaechlich ~1:2 geneigt, nicht 45 Grad -- daher das restliche
"Schweben" beim Drueberlaufen. Tiles mit reiner Himmel-Spalte tragen
nicht (die oberen der gestapelten Hang-Tiles inkl. Tile 1+ctype16).
Drop-Test: Landung exakt auf Grafikkante (626.0 = Kante 626).
3. **STEP_UP=6**: die untersten 6px der Hitbox blocken nicht an Waenden --
der 4px-Rest an der Plateau-Kante wird hochgestept (hohe Waende blocken
weiter ueber die oberen Hitbox-Zeilen, verifiziert).
4. **Wasser-Grund**: unterste Map-Zeile ist jetzt immer solide (der gruene
Bodenstreifen hat keine ctypes -- Smacks hing beim Tieftauchen in der
Grafik). Smacks-Grund jetzt 736 = Streifen-Oberkante.
5. **Smacks-Steh-Frames 12/13** (0/1 sind Schrittphasen -- "beim Anhalten
lief die Animation weiter").
Verifiziert: Slope-Drop pixelgenau, Plateau-Aufstieg + Weiterlauf, Wand-
Integritaet, Smacks idle=12, --once exit 0, E2E rc=0.
## 25.07.2026 (6) -- HUD final: GAMEBAR pur (weisse Boxen) + Original-Ziffern
Stefans Korrektur zur letzten Runde: die Box-Innenflaechen sind im Original
WEISS (genau wie in der GAMEBAR.PCC-Grafik -- die dunkelroten Boxen im
frueheren DOSBox-Screenshot waren wohl ein Farb-Artefakt), die Zahlen sind
ROT in der Original-HUD-Font, und es sind 6 HERZEN (3x2).
Loesung: GAMEBAR.PCC wird jetzt PUR geblittet (keine Fuellung mehr, der
Grafik-":" ist auf weiss natuerlich sichtbar). Die Zahlen sind die
GAMEBAR.BOB-Ziffern (7x13) gerendert mit der GAMEBAR.PCC-PALETTE -- damit
sind sie sattrot mit dunkler Umrandung, exakt wie der ":" (mit der
bob-Regel-Palette waren sie braeunlich; Stefan: "koennte sogar braun sein"
-- die PCC-Palette ist die konsistente Wahl, Umschalten waere 1 Zeile).
Herzen: 6 (3x2-Grid, Frame 10 mit PCC-Palette), leere Plaetze bleiben weiss.
hearts-Start/-Reset = 6.
HUD-Render deckungsgleich mit Stefans Original-Screenshot. --once/E2E ok.
## 25.07.2026 (7) -- Charakter-Icon + echte Tuergrafik
- HUD-Charakter-Kopf: WECH-Koepfe jetzt NEAREST-skaliert (pixelig statt
weichgezeichnet) + schwarzer 1px-Umriss -- Original-Look (Stefans Hinweis
"schwarzer Rand drum"). Eigene Mini-Kopf-Assets existieren offenbar nicht
(GAMEBAR.BOB[16] ist leer, W1ANI.ICO sind Tile-Animationen).
- DOORS.BOB entschluesselt: Frame 0 = Tuer MIT Kopf-Schild (32x48), Frames
1-4 = Tueren mit ROTEN/GRUENEN/BLAUEN Knaeufen (Schluessel-Tueren!),
Frame 5 = Kopf-Schild einzeln. Das Level blittet jetzt DOORS[0] auf die
Tuerposition (statt des improvisierten Mini-Symbols) -- deckungsgleich
mit Stefans Original-Screenshot (Kopf-Schild + Punkte).
- Nebenfund: W1ANI/W2ANI/W3ANI.ICO = Tile-ANIMATIONSPHASEN (broeckelnde
Bloecke etc.) -- fuer spaeter (animierte Tiles).
## 25.07.2026 (8) -- vierte Feedback-Runde: Tuer-Sequenz, Kamera, Feinschliff
1. **Fuesse schwebten**: Sprites sinken beim Zeichnen 4px ein (SPRITE_SINK)
-- im Original stehen die Figuren IN den Blaettern, nicht drauf.
2. **Steh-Frames**: Tony idle=16/17 (0/1 sind Schrittphasen -- "letzter
Lauf-Frame blieb stehen"); Smacks-Walk auf [0..11] gekuerzt (12-15 sind
Steh-/Uebergangsframes, Idle 12/13 jetzt nicht mehr im Zyklus).
3. **Tuer-Sequenz**: DOORS[0] ist die OFFENE Tuer (Stefans Korrektur).
Ruhezustand = geschlossene Tile-Tuer + Kopf-Schild (DOORS[5]); ALT ->
Tuer zeigt Frame 0 (offen), nach 1s folgt die Tuer-Aktion (Charakter-
Menue). Gilt kuenftig fuer alle Tueren (Schluessel-Tueren DOORS 1-4).
4. **Slope-Tunneling** beim Landen gefixt: SWEPT-Check ueber alle zwischen
alter und neuer Position ueberstrichenen Zeilen (bei 8px/Frame Fall
wurde die duenne Graskante sonst uebersprungen) + Herkunftsbedingung
(alte Position muss ueber der Kante gewesen sein). Stress-Test: 50
Drops ueber die ganze Hangbreite, 0 durchgefallen.
5. **Kamera**: Standard so, dass die Figur bei ~75% Hoehe steht (mehr Blick
nach unten); DUCKEN >=1.2s gehalten faehrt die Kamera sanft ~40px runter
(Wasser unter Abgruenden sichtbar), Loslassen faehrt zurueck.
Tony-auf-Wasser+Frosties-Idle von Stefan als "sehr geil" abgenommen (kein Fix).
## 25.07.2026 (9) -- fuenfte Feedback-Runde: Wasser-Bounce, Tod-Anim, Tuer-Peek, Kamera-Grace
1. **Wasser-Bounce**: Nicht-Schwimmer stehen nicht mehr statisch auf der
Oberflaeche, sie HUEPFEN endlos (~6px, WATER_BOUNCE_V=-110); Steuerung
bleibt, voller manueller Sprung zum Rauskommen geht weiter (on_ground
bleibt beim Aufsetzen kurz true). Kein Idle im Wasser mehr (Tonys
Frosties-Fressen auf dem Wasser ist damit Geschichte ;-).
2. **Tod-Animation bei 0 Herzen**: ASSET-FUND -- Tony-Frame 38 ist Tony
OHNE HALSTUCH (erschrocken), Frame 40 das Halstuch einzeln; Smacks 26 +
29 (Kappe). Sequenz: Figur fliegt (ohne Kollision) aus dem Bild, Tuch/
Kappe flattert herab, nach 2.4s Respawn mit Leben-1 (0 Leben -> Karte).
3. **Tuer-Peek**: waehrend der 1s-Offen-Phase zeigt die Figur einen
Reinschau-Frame (Tony 34, Smacks 20 -- frontal; echte Ruecken-Frames
gibt es nicht in den BOBs).
4. **Duck-Kamera-Flackern**: 0.2s-Grace-Period -- kurze Taste/on_ground-
Blips (X11-Autorepeat sendet Release/Press-Paare) setzen den
Halte-Timer nicht mehr zurueck.
## 25.07.2026 (10) -- sechste Feedback-Runde: Wasser-Regeln + Peek-Frames
- Ducken im Wasser fuer Nicht-Schwimmer verboten (Bounce + DOWN ergab
Duck-Pose auf der Oberflaeche).
- Wasser-Schaden: alle 1.5s statt 5s (Stefan: "5 Sekunden arg lang").
- Schadens-Blinken nach Herzverlust (0.9s Sichtbarkeits-Toggle ~6Hz --
ein eigenes Aua-Sprite gibt es in den BOBs nicht). Beim Todesstart wird
das Blinken deaktiviert (sonst waere die eingefrorene Blinkphase die
ganze Anim unsichtbar gewesen).
- TUER-REINSCHAU-Frames (Stefans Fund): TONY Frame 50 (Rueckenansicht!),
SMACKS Frame 48 -- jeweils der letzte Frame vor den Magenta-
Platzhaltern. Der als "gebueckt/kriechend" einsortierte Tony-50 war in
Wahrheit das Reinschauen.
## 25.07.2026 (11) -- Disassembly-Frage + Smacks-Sprung per Referenz geklaert
Stefans Frage: Animations-Zuordnungen aus dem Binary disassemblieren?
- Byte-Muster-Suche nach Frame-Tabellen (0,2,4..14 / 26,28,30,32 / 37,39,
41,43 als u8- und u16le-Folgen) in START.EXE und RTM.EXE: KEINE Treffer
-- die Sequenzen liegen nicht als nackte Tabellen im Binary (gepackt oder
als Code). Voll-Disassembly von 148KB 16-bit-Borland-Code waere ein
Wochenprojekt -- der empirische Weg (DOSBox-Frameserie) bleibt schneller
und sicherer.
- Stattdessen DOSBox-Session mit 0.12s-Intervall: per TUER zu Smacks
gewechselt (verifiziert dabei UNSER Tuer-/Menue-Design gegen das
Original: Menue-Layout deckungsgleich, Coco/Toucan grau ✓) und
Spruenge aufgenommen. ERKENNTNISSE:
1. Der Original-Sprung hat eine ABSPRUNG-HOCKE (Kauer-Frame im
Absprungmoment, dann Streckung im Flug) -- das fehlte bei uns
("Sprunganimation nicht richtig"-Gefuehl). Eingebaut:
JUMP_SQUAT_SECONDS=0.09 zeigt kurz den Duck-Frame.
2. Die TUER-SEQUENZ DIMMT DEN LEVEL AB (Szene dunkel waehrend der
Interaktion) -- eingebaut (Overlay waehrend door_open_t).
## 25.07.2026 (12) -- BINARY-DURCHBRUCH: NE-Overlay + Original-Animationstabellen
Stefans Frage "sollen wir Disassembly versuchen?" -- JA, und es hat sich
gelohnt (kein Voll-Disassembly noetig!):
**START.EXE-Struktur:** MZ-Stub (3.6KB) + NE-Executable als Overlay ab
Offset 3646 (Borland DPMI16, UNGEPACKT, 7 Segmente: 5 CODE ~100KB, 2 DATA).
Borland-Pascal-Shortstrings ("GAMEDATA\" + Name + ".BOB" wird zur Laufzeit
konkateniert -- deshalb fand die fruehe Dateinamen-Suche keine .ARE/.MAP).
String-Fund: "Willkommen im Kellogg's Cheat-Mode" -- Level-Waehler per
Pfeiltasten existiert im Original!
**ANIMATIONSTABELLEN im DATA-Segment (@143858 ff., Format
[delay_ticks][frame...] 0xffff, 70Hz-Ticks):**
FORMEL: Engine-Frame x2 = unser BOB-Frame (gerade=rechts, +1=links)!
- [0,4] 0..7 -> Gehzyklus 0,2..14, delay 4 = 17.5 fps
- [4] 8,8,8 -> Idle 16 (bestaetigt unsere Zuordnung)
- [4] 9 -> Sprung 18 (bestaetigt)
- [4] 10,10,10 -> Frontal 20
- [4] 13,14,15,16 -> RENN-RAD 26,28,30,32 (bestaetigt!)
- [25] 23,24,23,22,...,10x5 -> ECHTE Frosties-Sequenz
46,48,46,44,...,20x5 (delay 25 = 2.8 fps) -- uebernommen.
- Weitere Listen mit globalen Sprite-Indizes (100er/200er/400er/500er/
900er) = andere BOBs/Items/Karten-Sprites (Basis-Offsets noch offen).
**WELTKARTEN-DATEN (@145114-146760):** kompletter Karten-LAUFPFAD als
Records [x,y,flag,25] ((58,40)->...ueber die ganze Karte), dazwischen
[x,y]-Paare = LEVELPUNKT-Positionen ((53,56),(108,51),(126,81),(142,53)
...), am Ende die 22 Levelnamen W1L0..W3L7 als Pascal-Strings. => Die
Weltkarten-Levelauswahl liegt komplett im Binary -- perfekte Grundlage
fuer den naechsten Meilenstein! Voll-Dump gesichert (Session-Scratchpad
anim_tables.txt; bei Bedarf neu erzeugbar, Offsets dokumentiert).
**Angewendet:** Walk/Rad-FPS auf 17.5 (Original-Timing), Frosties-Sequenz
exakt uebernommen, Idle 2.8 fps.
**Offen:** globale Sprite-Basis-Offsets pro BOB entschluesseln (dann sind
ALLE Animationszuordnungen inkl. Smacks-Sprung exakt abzulesen); Karten-
pfad in die Weltkarte einbauen; Cheat-Mode-Aktivierung finden.
## 25.07.2026 (13) -- WELTKARTE MIT LEVELAUSWAHL (Daten aus dem Binary)
game/worldmap.py: parst zur Laufzeit Pfadsegmente, Levelpunkt-Positionen
und die 22 Levelnamen direkt aus START.EXE (NE-Datensegment; Anker-Suche
statt fester Offsets, Fallback auf nur-W1L1 wenn Parse scheitert).
Struktur pro Level: Wegpunkte (flag 1) -> Standpunkt (flag 0) ->
Label-Position; Sonder-Records mit ffff-Koordinaten (Weltwechsel) werden
uebersprungen. Ergebnis: 21 Segmente + 22 Namen (letztes W3L7-Label fehlt
noch -- Randfall im Parser, spaeter).
Karten-Sprites entschluesselt: KARTE0/2 = grosser oranger/blauer
Levelpunkt (Blink-Animation laut Binary-Tabelle [100] 900,902,900!),
KARTE1/3 = kleine Punkte, KARTE4-7 = MINI-KOEPFE (Tony/Smacks/Toucan/
Coco) als Kartenfigur, KARTE8/9 = Seeschlangen-/Baum-Deko-Frames.
Bedienung: Links/Rechts wandert zum vorigen/naechsten Levelpunkt (die
Figur laeuft die Original-Wegpunkte ab, ~0.18s/Punkt), Space/Return
startet das angewaehlte Level, ESC -> Menue. Der aktive Punkt blinkt
orange/blau (0.5s) wie im Original-Datensatz. Position bleibt ueber
Level-Besuche erhalten (map_state).
Level ohne handgepflegte LEVEL_INFO bekommen einen GENERISCHEN Spawn
(erste Spalte mit Boden + 2 Luftzellen darueber); Wasser/Tuer dort noch
None. Alle 22 Level sind anwaehlbar (bewusst -- Test-freundlich, quasi
eingebauter Cheat-Mode ;-).
E2E verifiziert: Karte -> 2x Right (Figur laeuft) -> W1L3 gestartet
(generischer Spawn) -> ESC -> Karte (Position gemerkt) -> Left -> W1L2
gestartet -> ESC/ESC/QUIT rc=0. --once exit 0.
## 25.07.2026 (14) -- Karten-Koordinaten korrigiert: y-Flip + Punkt/Kopf-Blinken
Stefans Einwand (Original-Screenshots inkl. zweier Netzfunde mit
fortgeschrittenem Spielstand): nur Level-1-Punkt sichtbar, Position unten
beim Haus -- nicht in den Bergen wie bei uns.
DOSBox-Nachmessung (Frame-Diff des blinkenden Markers): Level-1-Punkt real
bei (59,153); Binary-Label ist (53,56) => die Binary-y-Achse ist GEFLIPPT:
screen_y = 209 - y. Damit liegen W1 im Wald unten, W2/W3 in Bergen/
Schloss oben -- deckungsgleich mit den Netz-Referenzen (Kopf am Teich in
Bild 1 = W1L7-Position!).
Zweiter Fund aus der Diff-Region (13x15 = Kopfgroesse): an der aktuellen
Position wechseln PUNKT und MINI-KOPF im Blinkrhythmus (die Netzbilder
zeigen je eine Phase). Eingebaut.
Noch offen (bewusst): nur-freigeschaltete-Punkte anzeigen kommt mit dem
Level-Abschluss-Feature; aktuell sind alle Punkte sichtbar/anwaehlbar
(Testphase).
## 25.07.2026 (15) -- Karten-Punkte: Blit-Anker-Offset
Stefans Abgleich: alle Punkte ein Stueck nach rechts+unten. Ursache: die
Binary-KOORDINATEN stimmen, aber der BLIT-ANKER des Originals (topleft vs
zentriert) steht nicht im Binary -- wir hatten zentriert geraten. Fix:
empirische Offsets MAP_DX=5/MAP_DY=4 in worldmap._tf() (dort feinjustieren,
falls noch nicht exakt).
## 26.07.2026 (16) -- Karte PIXELEXAKT: Template-Matching statt Schaetzung
Stefans Nachmessung: immer noch daneben ("noch ein bisschen nach rechts
und weiter runter"). Diesmal richtig gemessen: KARTE-Sprites per
Template-Matching (mit Alpha-Maske) in den DOSBox-Referenzframes gesucht.
Ergebnis eindeutig (err ~10 = VGA-DAC-Rauschen, naechster Kandidat >200):
- KARTE0 (10x7) steht DAUERHAFT, Topleft (58,162) -- in BEIDEN Blinkphasen!
- KARTE4-Kopf (14x15) blinkt DARUEBER an/aus, Topleft (53,146) (steht auf
dem Punkt). Frueherer Befund "Punkt und Kopf wechseln sich ab" war
falsch (Diff-Region war die VEREINIGUNG beider Sprites).
- KARTE3 (kleine Punkte): am Spielstart NIRGENDS auf der Karte -- nicht
freigeschaltete Level sind unsichtbar (bestaetigt die Netz-Screenshots:
dort ist das Spiel durchgespielt).
Daraus die exakten Anker (Binary-Label (x,y)): Basis = (x, 218-y);
KARTE0-Topleft = Basis+(5,0); Kopf-Topleft = Basis+(0,-16); KARTE3
zentriert auf dem grossen Punkt = Basis+(7,2) (einzige verbleibende
Annahme -- kleine Punkte gibt es in keiner DOS-Referenzaufnahme).
WICHTIGER NEBENFUND -- Segment/Namens-Zuordnung war um 1 verschoben:
Das Start-Label (53,56) gehoert zu SEGMENT 0, und 21 Segmente = exakt
die 21 anwaehlbaren Level W1L1..W3L7 (3 Welten x 7). W1L0 (22. Name,
MAP existiert) hat KEINEN Kartenpunkt (Bonus-/Geheimlevel?). Also
segments[i] = names[i+1]; der "fehlende W3L7-Randfall" aus Eintrag (13)
war keiner. Cursorstart = Segment 0 = W1L1.
Disassembly-Check (Stefans Frage "bekommst du das nicht aus dem
Disassembler?"): einziges `mov ax,218` im Binary (Offset 0xf084) ist
eine HUD-ZIFFERN-Routine (X-Positionen 186/194/202, Spriteindex =
Ziffer+500 -- notiert fuer die Punkteanzeige!). Der Karten-Anker steht
nicht als Literal im Code -> Pixelmessung war der richtige Weg.
Verifikation: eigener Karten-Render vs. DOSBox-Frame, mittlerer
Pixelfehler 5.0 (identisch mit dem Ganzbild-Grundrauschen der
VGA-DAC-Quantisierung) in beiden Blinkphasen => pixelidentisch.
--once exit 0.
## 26.07.2026 (17) -- Weltkarten-Daten fest eingebettet, START.EXE-Abhaengigkeit weg
Stefans Wunsch: Daten dauerhaft ins Spiel, parse_worldmap() raus.
Umgesetzt: SEGMENTS + LEVEL_NAMES als feste Tabellen in game/worldmap.py
(Rohkoordinaten wie im Binary, Transform/Anker unveraendert zur Laufzeit).
Der Parser lebt als Dev-Werkzeug tools/worldmap_extract.py weiter (gibt
die Tabelle als Python-Literal aus, nur fuer etwaige Regeneration).
Verifiziert: Extraktor-Output == eingebettete Tabelle, Karten-Render
weiterhin Pixelfehler 5.14 (= VGA-DAC-Grundrauschen) gegen die
DOSBox-Referenz in beiden Blinkphasen. --once exit 0. Das Spiel braucht
zur Laufzeit jetzt wieder NUR PCKELL.DAT.
## 26.07.2026 (18) -- ANIMATIONSSYSTEM KOMPLETT GEKNACKT (Disassembly + BOB-Header)
Stefans Auftrag: alle Charaktere per Disassembly sauber bauen. Ergebnis:
das komplette Sprite-/Animationssystem des Originals ist jetzt verstanden.
### Die Schluessel-Erkenntnis: BOB-Frame-Header tragen SLOT-NUMMERN
Die 14-Byte-Frame-Header (Felder u0/u1/u2 vor width/height) bedeuten:
u0 = SLOT-Nummer (Engine-Sprite-Index!), u1 = Anker-X, u2 = Anker-Y
(Anker = Fusspunkt; u2=64 bei Cocos Hangel-Frames = Anker UNTER dem
Sprite, u2=40 bei Smacks-Schwimmframes = tiefer im Wasser. Das ersetzt
unseren SPRITE_SINK-Hack durch Originaldaten.)
### Richtungs-Regel (aus set_anim, Seg3:0x4cad, Datei 62637)
sprite = slot; wenn Blickrichtung LINKS: sprite += 50 (add ax,0x32)
-> Slots 0-49 rechts, 50-99 links. BOBs sind gepaart gespeichert
(gerade=rechts slot i/2, ungerade=links +50) -- deckt sich mit allen
bisherigen Funden. Einzel-Frames (45, 48, 49, Smacks 29-31) existieren
nur einseitig.
### Universelle Slot-Semantik (fuer ALLE Charaktere gleich!)
0-7 Laufzyklus ([delay 4] = 17.5 fps) 8 Stehen/Idle
9 Springen/Fallen 10,11 Absprung-Hocke
12 Ducken 13-16 RENNEN (nur Tony hat sie!)
17,18 (Schaden?) 19,20 Tod + Sturz 22,23,24 Idle-Warteanimation
22-28 Schwimmen (nur Smacks/Toucan haben die Slots!)
[10][25,26,27,28] = Schwimmstoss-Tabelle
29-31 Toucan: Fluegelschlag (Paare 32x56) / Smacks: Blasen-Aktion (Einzel)
33,34 Toucan: Gleiten ([4][34,34,34] mit Status-Flag 0x20)
35-44 Coco: HANGELN (Paare, Anker-Y 64!) -- Code setzt 43/44/91/92 direkt
45 In-die-Tuer-gucken (Einzelframe, alle Chars; Code: Flag ds:0xd7a0)
48,49 Accessoire-Dropitems beim Tod (16x16: Muetze/Schal/...)
-> FAEHIGKEITEN ergeben sich aus vorhandenen Slots: nur Tony hat 13-16
(rennen), nur Smacks/Toucan 25-28 (schwimmen), nur Coco 35-44
(hangeln), nur Toucan 29-34 als Paare (fliegen). Deckt sich 1:1 mit
dem Handbuch!
### Toucan-Flugmechanik (aus Zustandsmaschine Seg3 ~0xfb79)
Absprung/Schlag: v = -0x18000 (16.16 fixed); jeder weitere Schlag
addiert +0x2000 (wird schwaecher); unter -0x4000 kein Schlag mehr ->
genau die "fuenfmal mit den Fluegeln" aus dem Handbuch. Anim [12][29,30,31,30].
### Weitere Code-Funde
- Anim-Tabellenformat: [delay_ticks(70Hz)][slots...][ffff=Loop|fffe=Halten]
fffe-Variante: bleibt auf letztem Frame stehen (kein Loop).
- Tuer-Erkennung im Code: ctype-Nibble == 3 (Kopfhoehe y-0x2e) -> Tuer!
ctype == 0xc -> zweiter Spezialfall (y-0x36, vermutlich Levelausgang).
- Objektstruktur es:[di]: +0 x(16.16) +4 y(16.16) +0xc v(16.16)
+0x10 Sprite-Index +0x1c/0x1e Anim-Delay +0x20/0x22 Anim-Pos
+0x26 Richtung (1/-1) +0x28 ctype-Flags +0x3e Idle-Timer +0x6c Sprungkraft
- HUD-Ziffern-Routine (mov ax,218 @0xf084): X-Positionen 186/194/202,
Spriteindex = Ziffer + 500 (GAMEBAR-Slotbasis 500).
- Charakter-Lade-Trios @52417: fros_w/tony/frosties, smac_w/smacks/
smackies, loop_w/toucan/loops, coco_w/coco/cocopops (via ds:0xb568).
- KARTE0-9-Laderoutine @22834: Handles nach ds:0x9e40 (Karten-Slots 900+).
- 100er/200er-Tabellenwerte (@144006-144152) sind NICHT die Charaktere --
vermutlich Levelobjekt-/Gegner-Slots (Ladeliste 'ani',A-O,gamebar,keys,
vitamin,platfw1,doors,blume,wolke,KROKO,BOSSNAKE,w2snake,DRAGON @4984).
Noch offen.
## 26.07.2026 (19) -- Charaktersystem auf Original-Slots umgebaut (alle 4 Figuren)
Umsetzung von Eintrag (18) in game/level.py + game/formats.py:
- formats.BobFrame traegt jetzt slot/anchor_x/anchor_y aus dem Frame-Header.
- Player haelt Sprites als {slot: (Surface, Anker)}; Blickrichtung links =
Slot+50 (Original-Regel), Einzel-Frames fallen auf die Rechts-Variante
zurueck. Gezeichnet wird am Original-ANKER (Fusspunkt) + SPRITE_SINK.
- CHAR_DEFS auf bob/item_icon/head_index geschrumpft; FAEHIGKEITEN werden
aus den vorhandenen Slots abgeleitet (can_run=13, can_swim=25,
can_fly=29+79 als Paar, can_hang=35) -- deckt sich exakt mit Handbuch.
- Animationen = Original-Tabellen: Walk [4][0-7], Run [4][13-16],
Schwimmstoss [10][25-28], Fluegelschlag [12][29,30,31,30], Idle-Warten
[25][23,24,23,22,...,10x5] (= Schachtel-essen-Posen 22-24!).
- Dabei 3 Alt-Bugs gefixt: Smacks Sprung/Ducken waren VERTAUSCHT (Sprung
ist Slot 9 = BOB 18, Ducken Slot 12 = BOB 24 -- Stefans Verdacht war
richtig); Smacks' alte Schwimm-Frames waren die LINKS-Varianten;
Smacks-Idle war ein Laufframe (Slot 6) statt Slot 8.
- NEU Toucan: Flugkette wie im Original -- Absprung armiert
flap_strength=-300, jeder Schlag (Space-Edge in der Luft) wird um 48
schwaecher, unter -70 nur noch Gleiten (Slot 34, Sinken max 55 px/s,
Pfeil-runter bricht ab). Test: exakt 5 Schlaege bis zum Gleiten ✓.
- NEU Coco: Hangeln -- greift mit Hoch-Taste in der Luft einen "Ast",
hangelt seitlich (Slots 35-44, Anker-Y 64), Runter laesst los.
EXPERIMENTELL: Ast-Trigger = ctype-Bit 8; in W1L2 liegen solche Tiles
aber nur in Zeile 1 (unsichtbar oben) -> echter Ast-ctype noch per
DOSBox/W2-Level zu verifizieren.
- Wasser-Schaden: erster Kontakt kostet SOFORT ein Herz (+Blinken), danach
alle 1.5s (Stefans Erinnerung; DOSBox-Nachmessung steht noch aus).
- Tod: Slot 19 + fallendes Accessoire Slots 48/49 im Wechsel (16x16,
Muetze/Tuch); Tuer-Gucken = Slot 45 (alle Charaktere, wie vorher).
- Smacks-Slots 29-31 = WASSERBLASEN-Posen (Handbuch: ALT-Aktion) -- fuer
spaeter (Projektil fehlt noch); Slots 20/21 (32x16) = Spritzer?, offen.
Verifiziert headless: alle 4 Chars idle/walk/duck/squat/fall = Slots
8/0/12/10/9; Smacks schwimmt (25, Stoss 25-28); Toucan 5 Schlaege ->
gleiten; Coco haengt/hangelt/loest. --once exit 0.