Files
tony-remake/NOTES.md
T

111 KiB

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.SETist 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.SRBTONY.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 0x238966exakt 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.PCCs "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 320x480-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
  1. 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.

26.07.2026 (20) -- Smacks: echtes Tauchen/Schwimmen

Stefans Feedback: Unterwasser Links/Rechts ohne Schwimmanimation; Hoch fuehlte sich wie "kleines Springen unter Wasser" an. Umbau:

  • Hoch/Runter im Wasser = sanftes Auf-/Abtauchen (Zielgeschwindigkeit -85/+110 px/s, weiche Annaeherung statt Sprung-Impulse); an der Oberflaeche haelt Hoch nur die Position.
  • Leertaste = Schwimmstoss (Impuls -140 + Stossanimation 25-28); nahe der Oberflaeche wie gehabt echter Sprung raus. HOCH loest keinen Sprung/ Stoss mehr aus (eigene Space-Flanke).
  • Bei jeder aktiven Bewegung (links/rechts/hoch/runter) laeuft die Schwimmanimation 25-28 durch; ohne Eingabe Treiben auf Slot 25. Headless verifiziert (Tauchfahrt runter/hoch, Anim-Slots, Oberflaechen- Exit nur per Space). --once exit 0. Vorher: Wasserschaden-Fix eb798a8 (can_swim statt swimming -- Smacks verlor beim Rausspringen Herzen).

26.07.2026 (21) -- Sterbe-Drop-Fix + Tuer-Menue-Leertasten-Leak

  • Stefans Fund "Gegenstaende beim Sterben lila": SLOT_DROP war falsch auf 48/49 (16x16, lila Kloetzchen -- Zweck weiter unklar) gesetzt. Richtig sind die flachen 32x16-Slots 20/21: Tonys rotes Halstuch (2 Flatter- Frames) und Smacks' oranges Cappy. Visuell verifiziert.
  • Leertasten-Leak: Bestaetigung im Charakterwechsel-Menue loeste beim Rueckwechsel ins Level sofort einen Sprung aus (Taste noch gehalten). Fix: player.jump_lock -- Sprung/Stoss gesperrt, bis die Taste einmal losgelassen wurde; nach dem Tuer-Menue gesetzt + Eventqueue geleert.

26.07.2026 (22) -- Sprunghoehe nachjustiert (Ast/Blatt-Referenz)

Stefans Referenz am Haus (W1L1): Tony springt "bis zum Ast", Smacks "bis zum Blatt" (das unterm Ast haengt). Aufloesung: im Original haben BEIDE dieselbe Sprungkraft (-0x18000 aus dem Binary, ~72px Scheitel) -- der Unterschied ist die Koerpergroesse (Tony 56px, Smacks 44px), dadurch beruehren die Koepfe verschiedene Ziele. Bei uns fehlten ein paar Pixel (SPRITE_SINK=4 + Frame-Diskretisierung): JUMP_VELOCITY -360 -> -380 (Scheitel gemessen 77px). Verifiziert: Tony-Kopf am Scheitel y=510 (Ast-Band 499-516), Smacks-Kopf y=522 (Blatt-Band 511-526). Nebenfund: smacks_jump-DOSBox-Serie zeigt auch KURZE Huepfer (~30px) -- das Original hat variable Sprunghoehe (Taste frueh loslassen), steht noch auf der Offen-Liste.

26.07.2026 (23) -- Sprunghoehe 2. Feinjustage

Stefan: beide beruehrten fast nur das Blatt; Ziel: Smacks-Kopf ans Blatt, Tony-Kopf an den Ast, jeweils aus dem Stand. JUMP_VELOCITY -380 -> -400 (Scheitel gemessen 85.6px). Kopf am Scheitel: Tony y=502 (Ast-Holz), Smacks y=514 (Blatt) -- per Level-Render am Baum verifiziert (apex_TONY/SMACKS.png). Sprungkraft bleibt fuer beide gleich; die Zielhoehen ergeben sich rein aus den Koerpergroessen (56 vs 44 px).

26.07.2026 (24) -- ARE-FORMAT GEKNACKT + Smacks' Wasserblasen (v1)

Anlass: Stefan will Blasen (Gegner fangen) + Gegner mit Sterbeanimation -> dafuer braucht es die echten Gegner-/Item-Positionen = ARE-Format.

ARE-Struktur (Decoder: tools/are_dump.py)

@8..48 Header: 10 BE-u16-Paare (Bedeutung teils offen) @352 SEKTOR-TABELLE: ceil(map_px/64) x ceil(map_px_h/64) BE-u16- Offsets (W1L1: 55x12=660), ffff = leer, sonst Offset+352 -> Objektliste des 64x64-Sektors Liste: ffff-Marker, dann 3xBE-u16-Records [Typ][dx][dy] (Pixeloffset im Sektor) bis zum naechsten ffff W1L1: 193 Objekte. Verifizierte/plausible Typen: 151 = Cerealie (x100 -- der HUD-Zaehler!), 154 = Cerealie im Wasser (x37, liegt in den Unterwasser-Clustern) 199 = TUER (exakt auf unserer Haus-Tuer Tile 12,37!) 71/72/73/76 = ?-Box-Inhalte (sitzen auf ctype-4-Tiles => ctype 4 = ?-BOX, nicht "Item" wie bisher angenommen!) 194..208 = Level-Moebel/Deko (Lampen etc.), 101/102 = Levelende? 1/2/3/11/12/17/18/20/31 = vermutlich GEGNER-Spawns (Typ->BOB-Zuordnung per DOSBox-Abgleich offen; Kandidaten: A-O.BOB, 'ani'-Ladeliste)

Wasserblasen v1 (game/level.py)

ALT beim Schwimmen/Tauchen (nur mit Slots 29-31 = Smacks): Blase waechst 29->30->31, treibt in Blickrichtung (vx 55, Daempfung), steigt nach 0.5s, platzt an der Oberflaeche oder nach 4s. Gegner-Fang (Blase + rauswerfen) kommt zusammen mit den Gegnern, sobald die Typ-Zuordnung steht. Gegner- Sterbeanimationen (wie bei den Spielfiguren) ebenfalls dann.