# Kellogg's Tony and Friends — Remake-Projekt, Analyse-Notizen

## Ziel
Plattformunabhängiges Remake (Python) von "Kellogg's Tony and Friends" (DOS, 1994),
lauffähig auf x64 ohne DOS/QEMU. Ursprung: Spiel crasht in unserer FreeDOS-VM
(RTM.EXE-Engine vs. QEMU-VGA-Emulation, siehe Projekt "Classic Games" für den
Debug-Marathon dazu) — daher der Wunsch für ein eigenes Remake.

## Quelldateien (aus VM-Disk `dos_shareware` extrahiert, liegen in `raw/`)
- `RTM.EXE` — die eigentliche Spiel-Engine (Runtime), DOS-EXE (MZ-Header), 108 KB.
- `START.EXE` / `SETUP.EXE` — Launcher/Konfigurationsprogramme.
- `KELLOGG.INI` — Soundkarten-Konfig (IRQ/DMA), irrelevant fürs Remake.
- `PCKELL.DAT` — 2,69 MB, **unkomprimiert**. Beginnt mit einer monoton
  steigenden 16-Bit-LE-Offset-Tabelle (vermutlich Sprite-/Block-Offsets in
  die anhängenden Rohdaten). Enthält laut `SRB.LST` 32 "SRB"-Blöcke
  (vermutlich "Sprite Resource Block"). Tabellenstruktur noch nicht final
  verstanden — steigt über mehr Einträge als die reinen 32 Top-Level-Files,
  also vermutlich verschachtelt (pro Sprite/Frame-Offsets innerhalb jedes
  SRB-Blocks). TODO: Tabellenende finden und pro-SRB-Grenzen bestimmen.
- `PCKELL.SET` — **ist tatsächlich eine ZIP-Datei** (Signatur `PK\x03\x04`)!
  Enthält 3 Einträge:
  - `APPEND.LST` (Methode 6 "Imploded", 1945 Bytes entpackt) — Manifest mit
    178 Dateinamen für die in `PCKELL.PRE` zusammengefügten Assets.
  - `SRB.LST` (Methode 1 "Shrink", 314 Bytes entpackt) — Manifest mit den
    32 Dateinamen, die zu `PCKELL.DAT` gehören (1:1 gleiche Basisnamen wie
    ein Teil der BOB-Einträge in APPEND.LST, z.B. `TONY.SRB` ↔ `TONY.BOB`).
  - `PCKELL.PRE` (Methode 6 "Imploded", 2.328.934 Bytes entpackt) — die
    eigentliche Asset-Sammlung, konkateniert nach der Reihenfolge in
    `APPEND.LST`.

  **WICHTIG — Kompressions-Falle:** ZIP-Methode 6 "Imploding" ist NICHT
  dasselbe wie das "DCL Implode/Explode"-Format aus PKWAREs Data
  Compression Library (dafür gibt's `blast.c` in zlib/contrib — hab ich
  zuerst versucht, schlägt fehl mit "distance too far back", weil es ein
  anderer Algorithmus ist trotz gleichem Namen). Debian/Ubuntus
  `unzip` (Info-ZIP, kompiliert MIT Explode+Unshrink-Support) entpackt
  beide Methoden aber klaglos — auf `aria-wohnung` funktioniert
  `unzip PCKELL.SET` direkt. Das Alpine-`unzip` im Brain-Container
  (aria-proxy) kann das NICHT (kein Implode-Support einkompiliert).

## Datei-Typen in APPEND.LST (178 Einträge total)
| Ext  | Anzahl | Vermutung |
|------|--------|-----------|
| PCC  | 77     | "Compiled Picture" — vermutlich RLE/Palette-komprimiertes Vollbild (Titelscreens, Menüs, Hintergründe) |
| BOB  | 32     | "Blob" — Sprite-Grafik-Daten (Spielfiguren, Objekte), 1:1 mit den 32 SRB-Einträgen |
| MAP  | 22     | Level-/Karten-Daten |
| ARE  | 22     | "Area" — vermutlich Level-Metadaten/Kollisionszonen, im Paar mit MAP (22=22) |
| ICO  | 19     | Icons (z.B. für Menüs/HUD) |
| SAM  | 3      | Sound-Samples |
| TFX  | 3      | vermutlich Musik/Sound-Effekt-Format |

## Nächste Schritte (in dieser Reihenfolge sinnvoll)
1. **PCKELL.PRE aufteilen**: Die 178 Dateien sind laut APPEND.LST hintereinander
   konkateniert — es fehlt noch die Größentabelle pro Datei (vermutlich am
   Anfang von PCKELL.PRE selbst, ähnlich der Offset-Tabelle in PCKELL.DAT,
   oder implizit aus dem Dateiformat jedes Typs ableitbar). Header von
   PCKELL.PRE selbst noch nicht angeschaut.
2. **Ein PCC-Bild reverse-engineeren** — vermutlich klassisches Palette+RLE,
   das dürfte am leichtesten zu knacken sein (PCC ähnelt oft PCX-Varianten).
3. **BOB/SRB-Sprite-Format verstehen** — Kollisions-Header aus PCKELL.DAT
   (die Offset-Tabelle) mit den 32 SRB-Namen abgleichen.
4. **RTM.EXE nicht disassemblieren müssen** — Ziel ist ein sauberes
   Python-Reimplement der Spiellogik (Physik/Level/Gegner aus MAP/ARE lesen),
   nicht 1:1-Code-Nachbau der DOS-Engine.

## Aufbewahrungsort
Alle Rohdaten + Manifeste liegen unter `raw/` in diesem Projektordner
(`/shared/projects/kellogg_s_remake/raw/`). `raw/extracted/` enthält den
fehlgeschlagenen blast.c-Versuch (kann weg). Entpackte Dateien
(`APPEND.LST`, `SRB.LST`, `PCKELL.PRE` — alle bereits entpackt, nicht mehr
komprimiert) liegen direkt in `raw/`.

## DOSBox-Testumgebung (2026-07-22) — Original läuft jetzt!

**DOSBox 0.74-3 ist auf `aria-wohnung` installiert** (apt). Zweck: das
Original-Spiel als visuelle/funktionale Referenz neben dem Reverse-Engineering
laufen lassen, ohne die kaputte QEMU-VGA-Emulation aus dem "Classic Games"-Crash.

### Fehlende Dateien nachgeliefert
`raw/` hatte urspr. nur `RTM.EXE` + `PCKELL.*` — **`START.EXE`, `SETUP.EXE`,
`KELLOGG.INI`, `HDPMI16.EXE` (DPMI-Host) und `DPMI16BI.OVL`** fehlten (waren
nie aus der `dos_shareware`-VM-Disk kopiert worden, nur RTM+PCKELL). Auch ein
`READ.ME` (Original-Textdatei mit Installations-/Troubleshooting-Hinweisen)
lag dort. Alle liegen jetzt in `raw/` (Quelle: `dos_shareware`-Diskimage auf
`aria-wohnung`, Pfad `GAMES/KELLOGG/` — per `qemu-nbd --read-only` gemountet,
VM dafür kurz gestoppt, danach sauber wieder disconnected. **Die VM
`dos_shareware` ist seitdem gestoppt** — bei Bedarf über's Cockpit-Panel oder
`aria-vm boot dos_shareware` wieder hochfahren).

**RTM.EXE ist die eigentliche Engine**, aber sie verweigert den Direktstart
("This loader is invoked and required by Borland tools") — man MUSS über
`START.EXE` gehen, das dann RTM.EXE korrekt aufruft.

### Der Show-Stopper: IRQ-Konflikt (aus dem Original-READ.ME!)
Mit den Original-`KELLOGG.INI`-Werten (`Adr=544 DMA=1 IRQ=7`, Soundblaster)
hängt sich das Spiel nach dem 2. Studio-Logo (Factor 5) in einer Endlosschleife
auf — kompletter schwarzer Screen, DOSBox-Prozess lebt aber (kein Crash,
Freeze). Das READ.ME beschreibt dieses EXAKTE Symptom ("Bild bleibt schwarz")
und empfiehlt IRQ 5 statt 7. Fix:
- `raw/KELLOGG.INI`: `IRQ=5` (statt 7)
- `dosbox.conf` `[sblaster]`-Sektion MUSS matchen: `irq=5` (sonst redet DOSBox's
  emulierte SB16 am falschen IRQ vorbei und das Spiel hängt trotzdem)

Mit diesem Fix läuft das Spiel sauber durch: Rauser-Advertainment-Logo →
Factor-5-Logo → Kellogg's-Logo → "Tony & Friends in Kellogg's Land"-Titelbild
→ Weltkarte (Berge/Wald/Schloss mit Drache, alle Charaktere sichtbar:
Tiger/Tony, Toucan, Frosch, Affe/Coco, Bär). Screenshots liegen unter
`/shared/uploads/aria_kellogg_*.png`.

### Funktionierende dosbox.conf
```
[sdl]
fullscreen=false
output=surface

[sblaster]
sbtype=sb16
sbbase=220
irq=5
dma=1
hdma=5

[autoexec]
mount c <pfad-zu-raw>
c:
START.EXE
```

### Screenshot-Workflow (headless, ohne echten Desktop)
`aria-wohnung` hat keinen laufenden X-Server. Screenshots via
`xvfb-run -s '-screen 0 1024x768x24' dosbox ...` + `import -window root`
(ImageMagick) im selben xvfb-run-Prozess. Tasteneingaben simulierbar mit
`xdotool key <taste>` (kein `--window`-Targeting nötig, funktioniert auch
ohne Fenstermanager in Xvfb). Audio läuft im ALSA-nosound-Modus (keine echte
Soundkarte im Container/VM) — DOSBox emuliert die SB16 trotzdem korrekt genug
fürs IRQ-Timing, es kommt nur kein tatsächlicher Ton raus.

### Steuerung (aus READ.ME, für spätere Gameplay-Tests)
Pfeil links/rechts = laufen, Leertaste/Pfeil-hoch = springen, Pfeil-runter =
ducken, ALT = Sonderfunktion/Tür öffnen, P = Pause, ESC = zurück zur Karte
(ohne Leben zu verlieren).

## PCKELL.PRE Container-Format (2026-07-22) — Kernstruktur geknackt

Ziel: die 178 in `APPEND.LST` gelisteten Dateien (32 BOB, 77 PCC, 22 MAP,
22 ARE, 19 ICO, 3 SAM, 3 TFX) sauber aus `PCKELL.PRE` (2.328.934 Bytes,
bereits ZIP-entpackt) raustrennen. Tools liegen in `tools/split_pre.py`
(aktuellster Stand, laeuft auf `aria-wohnung` mit `python3`, braucht
`raw/PCKELL.PRE` + `raw/APPEND.LST` + `raw/SRB.LST`).

### Zwei Header-Varianten je nach Dateityp

**`.BOB`-Eintraege (32x, Sprite-Rohdaten):** kein Tag-Slot, Header direkt
lueckenlos hintereinander:
```
[4-Byte-LE-Laenge][NUL-terminierter Original-DOS-Pfad
  "H:\WIWA\ENGINE\GAMEDATA\<NAME>.BOB"][<Laenge> Bytes Rohdaten]
```
Danach folgt variables Zero-Padding (~219-226 Byte beobachtet) bis zum
naechsten Header. **Vollstaendig validiert: alle 32 BOB-Eintraege exakt
gefunden, 0 Namens-Mismatches gegen APPEND.LST.** Robusteste Methode zum
Finden: direkte Byte-Suche nach `H:\WIWA\ENGINE\GAMEDATA\<erwarteter Name>`
im File (nicht raten, den vollen erwarteten Namen pro Eintrag einzeln
suchen — das ist wasserdicht).

**Alle anderen Typen (PCC/MAP/ARE/ICO/SAM/TFX, 146x):** kein eingebetteter
Pfad, dafuer ein **256-Byte-Tag-Slot** VOR dem eigentlichen Header:
```
[Tag-Slot, exakt 256 Byte lang:
   1-Byte-Namenslaenge + Dateiname (z.B. 0x0B "CHOCOS7.PCC")
   + Rest mit Speicher-Muell des Kompilier-Tools aufgefuellt]
[bei Tag-Slot-Start + 256: 4-Byte-LE-Laenge][<Laenge> Bytes Rohdaten]
```
Bestaetigt an mehreren Stellen (ANTS.PCC, CHOCOS7.PCC, und der
"Master"-Tag "A.SRB" ganz am Dateianfang vor A.BOB). **Wichtige Falle:**
beim Uebergang von der letzten `.BOB`- zur ersten `.PCC`-Datei (DOORS.BOB
-> ANTS.PCC) ueberlappt der Tag-Slot von ANTS.PCC um ~34 Byte in das, was
man anhand der BOB-Laengenangabe als "Ende von DOORS.BOB" berechnen wuerde.
Grund noch nicht restlos geklaert (vermutlich rundet der Original-Compiler
beim Sektionswechsel BOB->PCC anders). Workaround im Skript: Tag-Suche
zuerst vorwaerts ab erwarteter Position, bei Fehlschlag Fallback mit
300 Byte Sicherheitsabstand rueckwaerts.

**Kuriosum:** Der Tag fuer `.BOB`-Eintraege (nur beim allerersten,
"A.SRB" vor A.BOB) nennt nicht den eigenen Namen, sondern den
`.SRB`-Begleitnamen aus `SRB.LST` (1:1 gleiche Reihenfolge wie die
BOB-Liste in APPEND.LST). Passt zur bekannten SRB<->BOB-Kopplung
(Kollisions-Metadaten in PCKELL.DAT <-> Sprite-Pixeldaten in PCKELL.PRE).

### PCC-Bildformat (erste Erkenntnisse, noch nicht fertig decodiert)
Nutzdaten beginnen mit einem PCX-aehnlichen 16-Byte-Header:
```
Byte 0:    0x0A  (Manufacturer-Marker, wie ZSoft PCX)
Byte 1:    0x05  (Version)
Byte 2:    0x01  (Encoding = RLE)
Byte 3:    0x08  (Bits pro Pixel = 8, also 256 Farben)
Byte 4-11: bisher immer 0 gesehen (Xmin/Ymin/Reserved?)
Byte 12-13: Breite (LE16), z.B. 0x0140 = 320
Byte 14-15: Hoehe (LE16), z.B. 0x00C8 = 200
Byte 16+:  vermutlich RLE-komprimierte Pixel-Indexdaten (klassisches
           PCX-RLE: Byte mit oberen 2 Bits gesetzt (0xC0-0xFF) = Lauflaenge
           (Byte&0x3F), gefolgt vom Wert-Byte; sonst literaler Pixel)
```
Farbpalette (256x3 Byte RGB) wahrscheinlich NICHT pro Datei (dafuer waeren
die kleinen Dateien wie ANTS.PCC mit nur 900 Byte total zu klein) — vermutlich
eine globale Palette, die noch gefunden werden muss (Kandidat: irgendwo in
RTM.EXE oder als eigene Ressource). TODO fuer naechste Session.

### PCKELL.PRE Splitter — GELOEST (2026-07-22, ~02:15 Ortszeit)
Der vermeintliche "14%-Drift-Bug" war GAR KEIN Bug im Tag-Matching. Diagnose
(`tools/diag_split.py`, inzwischen geloescht, siehe Git-History falls noetig)
zeigte: jeder der 178 Tags aus `APPEND.LST` wird exakt EINDEUTIG gefunden
(genau 1 Kandidat, Gap durchgehend 0, ausser dem bekannten BOB->PCC-
Uebergang mit -34 Byte). Der Splitter war also die ganze Zeit korrekt —
er endete nur zu frueh, weil **`APPEND.LST` gar nicht alle Eintraege in
`PCKELL.PRE` auflistet.**

Direkt im Anschluss an den letzten `APPEND.LST`-Eintrag (`TITEL2.SAM`,
endet bei `0x1e9aac`) folgen, im EXAKT GLEICHEN Tag-Slot-Format, noch
**3 weitere, nicht gelistete Eintraege**:
```
DPMI16BI.OVL  65312 Byte
RTM.EXE      108686 Byte
START.EXE    148480 Byte
```
Der letzte davon (`START.EXE`) endet bei `0x238966` — **exakt** die
Dateigroesse von `PCKELL.PRE`. Damit ist das Container-Format zu 100%
verifiziert, kein einziges Byte bleibt unerklaert. Macht inhaltlich auch
Sinn: `PCKELL.PRE` ist offenbar ein self-contained Redistributable-Paket,
das die komplette DOS-Extender-Runtime (DPMI-Server + Runtime-EXE +
Launcher) gleich mitbuendelt — nicht nur Spiel-Assets.

**Extra-Validierung:** die 3 extrahierten System-Dateien sind
byte-identisch (`cmp`) zu den Einzeldateien, die schon lose in `raw/`
liegen (aus der `dos_shareware`-VM kopiert) — zweite unabhaengige
Bestaetigung, dass die Extraktion korrekt ist.

**`tools/split_pre.py` (finale v7) extrahiert jetzt alle 181 Dateien
fehlerfrei nach `extracted_pre/`:**
```
32 BOB, 77 PCC, 22 MAP, 22 ARE, 19 ICO, 3 SAM, 3 TFX, 1 OVL, 2 EXE
```
Alte Splitter-Zwischenversionen (v1-v6, Diagnose-Scripts) sind auf
`aria-wohnung` (`/home/aria/kellogg_remake/tools/`) aufgeraeumt — nur
noch `split_pre.py` (v7, final) liegt dort, identisch zur Kopie hier im
Projekt unter `tools/split_pre.py`. `extracted_pre/` (2,6 MB, 181 Dateien)
liegt jetzt auch hier im Projekt, nicht nur auf `aria-wohnung`.

### PCC-Header — ergaenzender Fund
Bei `MENU.PCC` sind Byte 4-11 NICHT alle Null (anders als bei `ANTS.PCC`
vermutet) — sie enthalten `Xmax`/`Ymax` (0x013f=319, 0x00c7=199), passend
zum klassischen PCX-Header-Layout `Xmin(2) Ymin(2) Xmax(2) Ymax(2)` bei
Offset 4. Fuer ein 320x200-Bild ab (0,0) macht Xmax=319/Ymax=199 Sinn.
Bei `ANTS.PCC` (kleines 900-Byte-Sprite-Preview-Bild) stehen dort
durchgehend Nullen — vermutlich weil die Original-Autoren dieses Feld
bei kleineren generierten Bildern nicht befuellt haben. Fuer den
PCC-Decoder erstmal ignorierbar, Breite/Hoehe kommt zuverlaessig aus
Offset 12-15.

### Naechste Schritte (Prioritaet) — ALT, siehe unten "PCC-Format GELOEST" fuer aktuellen Stand
~~1. Ein PCC-Bild vollstaendig decodieren~~ ERLEDIGT (siehe unten).
2. BOB/SRB-Sprite-Format verstehen (PCKELL.DAT-Offset-Tabelle mit den
   32 SRB-Namen abgleichen, dann Frame-Struktur pro Sprite ableiten).
3. MAP/ARE-Levelformat reversen (kleinste Dateien zuerst, z.B. ein
   simples frühes Level, z.B. `W1L0.MAP`/`W1L0.ARE`).
4. Erst danach: Python-Projektstruktur aufsetzen (pygame), Asset-Loader
   fuer BOB/PCC/MAP schreiben, Kernschleife (Physik/Kollision) gegen
   DOSBox-Referenzverhalten abgleichen.

## PCC-Bildformat — VOLLSTAENDIG GELOEST (2026-07-22, ~02:20 Ortszeit)

**Format zu 100% verstanden und visuell verifiziert.** PCC = klassisches
PCX v5, 256-Farben, mit **eingebetteter Palette PRO DATEI** (kein globales
Palette-Rätsel, wie in der vorherigen Notiz befürchtet):

```
Byte 0-15:  PCX-Header (Manufacturer=0x0A, Version=5, Encoding=1/RLE,
            BPP=8, Xmin/Ymin/Xmax/Ymax bei Offset 4-11,
            Width(LE16)/Height(LE16) bei Offset 12-15 — Width/Height sind
            DIREKTE Pixelmaße, nicht Xmax-Xmin+1, auch wenn beides meist
            gleich rauskommt bei 320x200-Vollbildern)
Byte 16 .. (len-769): SCANLINE-weise RLE-komprimierte Pixel-Indexdaten.
            RLE-Tupel: Byte mit oberen 2 Bits gesetzt (0xC0-0xFF) =
            Lauflaenge (Byte&0x3F), gefolgt von 1 Wert-Byte. Sonst
            literaler Pixel. WICHTIG: pro Scanline wird bis exakt
            `width` Pixel dekodiert, ueberschuessige Pixel eines Runs
            am Zeilenende werden verworfen (klassisches PCX-Verhalten,
            NICHT als ein durchgehender Bytestrom decodieren — das
            fuehrt zu Off-by-Overshoot-Fehlern und Fehlausrichtung
            gegenueber der eingebetteten Palette am Dateiende).
Letzte 769 Byte: `0x0C`-Marker + 768 Byte (256 x RGB) Palette — die
            klassische PCX-v5-256-Farben-Erweiterung. JEDE PCC-Datei
            traegt ihre EIGENE Palette mit sich (keine globale Palette
            aus RTM.EXE noetig, wie in der vorigen Session vermutet).
```

**Decoder fertig:** `tools/pcc_to_png.py` (auf `aria-wohnung` unter
`/home/aria/kellogg_remake/tools/pcc_to_png.py`, identische Kopie hier
im Projekt unter `tools/pcc_to_png.py`). Aufruf:
`python3 tools/pcc_to_png.py extracted_pre/<NAME>.PCC out.png`
(braucht ImageMagick `convert` auf dem Zielsystem — ist auf `aria-wohnung`
vorhanden).

**Visuelle Verifikation — 100% korrekt:**
- `KARTE.PCC` → exakte Weltkarte (Berge/Schloss/Drache, Tony/Tiger,
  Coco/Affe, Frosch, Fluss, Bäume) — Pixel-für-Pixel wiedererkennbar
  gegen den DOSBox-Screenshot `aria_kellogg_worldmap.png` von vorher.
- `KELLOGGS.PCC` → glasklares "Kellogg's präsentiert"-Logo mit
  korrektem geschwungenem Markenschriftzug, scharfe Kanten, kein
  Rauschen/Fehler in der Dekodierung.
- `MENU.PCC` → sauberer Menü-Rahmen (blauer Hintergrund, rot-goldene
  Box) — vermutlich Passwort-/Level-Auswahl-UI.
Alle Test-PNGs liegen unter `/shared/uploads/aria_pcc_*.png` zur
Ansicht, dauerhafte Kopien aller **77 von 77 PCC-Dateien fehlerfrei
batch-decodiert** liegen unter `png_out/` in diesem Projektordner
(416 KB total, 77 Dateien — vollstaendige Validierung, kein einziger
Absturz oder Format-Ausreisser).

Einzige kleine Restfrage (kein Blocker): bei manchen Dateien bleiben
1-30 Byte am Ende des Pixel-Datenblocks vor der Palette ungenutzt
(vermutlich Padding/Rundung des Original-Encoders) — beeinflusst das
Bildergebnis nicht sichtbar, wird ignoriert.

## BOB-Sprite-Format — erste Erkenntnisse (2026-07-22, ~02:30 Ortszeit, IN ARBEIT)

**Kein PCX-Verwandter.** BOB-Dateien starten NICHT mit `0x0A`-Manufacturer-
Byte, kein erkennbarer Klartext-Header wie bei PCC. Vermutlich beginnt die
(vermutlich RLE-komprimierte) Pixel-Nutzdaten direkt ab Byte 0.

**Wichtiger Fund:** Die 32 BOB-Namen sind NICHT alle "richtige" Objektnamen
— 14 davon sind Einzelbuchstaben (`A.BOB` .. `O.BOB`, ohne `M`) plus
`BLUME.BOB`/`WOLKE.BOB` (Blume/Wolke, Deko-Objekte), dann die Marken-Packs
(`VITAMIN`/`COCOPOPS`/`SMACKIES`/`FROSTIES`/`LOOPS`), dann Charaktere
(`TONY`/`SMACKS`/`TOUCAN`/`COCO`), dann Level-Objekte (`PLATFW1`=Plattform,
`KEYS`, `GAMEBAR`=HUD-Leiste, `KROKO`/`BOSSNAKE`/`DRAGON`=Gegner, `DOORS`).
Die Buchstaben-Sprites (A-O) sind vermutlich Sammel-Buchstaben-Items
(Bonus-Mechanik: Buchstaben im Level einsammeln) — passend zur Groessen-
Gruppierung (viele davon fast gleich gross: B/E/N/O=1295 Byte, D/H/I=4406,
J/K=2332 — deutet auf fixe Sprite-Dimensionen pro Buchstaben-"Familie",
vermutlich Schriftart-aehnliche Boxen).

**Byte-Identitaet als Format-Hinweis:** `FROSTIES.BOB`, `COCOPOPS.BOB`,
`SMACKIES.BOB` (alle 3323 Byte gross) haben BYTE-FUER-BYTE identische
ersten 16 Byte (`16 b4 2c 76 82 b4 3a 9f 06 88 82 cf 16 e8 03 2d`) und
divergieren erst ab ca. Offset 0x20. `LOOPS.BOB` (ebenfalls 3323 Byte)
zeigt eine um 4 Byte verschobene, aber erkennbar verwandte Bytefolge.
**Interpretation:** das ist vermutlich KEIN Klartext-Header, sondern
bereits RLE-komprimierte Pixeldaten — die vier Sprites sind alle
"Cerealien-Packung"-Objekte mit identischer Kastenform/Silhouette
(gleiche Pixel-Auesserform => gleiche RLE-Runs am Anfang), die sich nur
im aufgedruckten Marken-Logo unterscheiden (Divergenz kommt spaeter).
Das wuerde erklaeren warum 4 komplett verschiedene Marken exakt gleich
grosse Dateien mit ident. Praefix erzeugen: gleiche Box-Geometrie,
gleiches Kompressionsschema, nur Content-Unterschied in der Bildmitte.

**Hypothese fuer naechste Session:** BOB = aehnliches RLE-Schema wie PCC
(Byte mit oberen 2 Bits gesetzt = Lauflaenge+Wert), aber OHNE PCX-Header
und OHNE eingebettete Palette — Breite/Hoehe muessen aus `PCKELL.DAT`
kommen (die SRB-Offset-Tabelle, noch nicht durchdrungen) oder aus einer
festen Tabelle in `RTM.EXE`. Naechste konkrete Schritte:
1. `PCKELL.DAT` Offset-Tabelle vs. `SRB.LST`-Namen abgleichen (32 Eintraege,
   1:1 mit den 32 BOB-Dateien) — dort vermutlich Breite/Hoehe/Frame-Anzahl
   pro Sprite zu finden.
2. Testweise RLE-Decode von `A.BOB` (kleinstes/einfachstes Beispiel aus der
   Buchstaben-Gruppe) mit angenommener kleiner Breite/Hoehe (z.B. 20x20 bis
   40x40 fuer ein UI-Buchstaben-Icon) durchprobieren, gegen erwartete
   Pixelzahl abgleichen (aehnlich wie beim PCC-Ansatz, der am Ende
   erfolgreich war: Header-Annahmen iterativ gegen Dateigroesse pruefen).
3. Falls das nicht schnell konvergiert: `TONY.BOB` (Hauptfigur, 47726 Byte,
   groesste Datei nach COCO) ist wahrscheinlich der wichtigste Fund fuer
   den Port und rechtfertigt tiefere Analyse zuerst.

## Naechste Schritte (Prioritaet, aktueller Stand)
1. **BOB/SRB-Sprite-Format weiter reversen** — siehe Hypothesen direkt
   oben. Naechster Schritt: `PCKELL.DAT`-Offset-Tabelle mit `SRB.LST`
   abgleichen, danach `A.BOB`-Testdecoding.
2. MAP/ARE-Levelformat reversen (kleinste Dateien zuerst, z.B. ein
   simples frühes Level, z.B. `W1L0.MAP`/`W1L0.ARE` falls vorhanden —
   sonst kleinste Datei nach Groesse waehlen).
3. Erst danach: Python-Projektstruktur aufsetzen (pygame), Asset-Loader
   fuer BOB/PCC/MAP schreiben (PCC-Loader ist im Grunde fertig, nur
   noch von PPM/ImageMagick-Zwischenschritt auf direktes PNG-Schreiben
   in Python umstellen, z.B. mit `Pillow` — aktuell haengt der Decoder
   noch am System-`convert`-Tool, das fuer den finalen Python-Port
   NICHT die Zielarchitektur sein soll).

### Hinweis fuer Fortsetzung nach Neustart/Session-Wechsel
Dieses Projekt laeuft bewusst autonom weiter (Stefan-Wunsch, 2026-07-22).
Fortschritt wird nach JEDEM Meilenstein hier in `NOTES.md` gesichert.
Zusaetzlich liegt ein gepinnter Kurz-Status im Memory (Suchbegriff
"Kellogg's Remake") als Fallback, falls diese Datei mal nicht griffbereit
sein sollte. Bei Session-Neustart: einfach hier weiterlesen ab
"Naechste Schritte" oben, keine Rueckfrage bei Stefan noetig.

Ein `trigger_timer` ("kellogg-remake-continue") ist als Selbst-Weck-Kette
aktiv: feuert stuendlich, arbeitet den naechsten offenen Punkt ab, speichert
hier, legt danach den naechsten Timer an — laeuft ohne Rueckfrage weiter.

## PCC-Decoder-Bugfix + Git-Repo (2026-07-22, ~09:20 Ortszeit)

Stefan hat zurecht bemaengelt, dass die PCC-Bilder verzerrt/verrutscht
aussehen (z.B. Smacks-Frosch spiegelverkehrt, Kellogg's-Logo mit Fehlstelle).
Ergebnis der Untersuchung:

**GEFUNDEN UND GEFIXT — Width/Height-Bug:** Der Decoder las Breite/Hoehe
bisher aus Offset 12-15 (siehe alte Notiz oben). Das ist FALSCH — dieses
Feld ist bei allen Dateien ein Konstant-/Platzhalterwert (vermutlich
DPI-Reserved-Feld wie im echten 128-Byte-PCX-Header), der bei
Vollbild-Screens (320x200) zufaellig mit der echten Groesse
uebereinstimmt, bei kleineren Sprites/Logos aber komplett falsch ist.
**Richtig:** Breite/Hoehe muessen aus `Xmax-Xmin+1` / `Ymax-Ymin+1`
(Offset 4-11) berechnet werden. Beweis: `FACTOR5.PCC` konsumiert mit der
alten (falschen) Hoehe 200 nicht alle Bytes sauber, mit der echten Hoehe
199 (aus Ymax=198) landet der Byte-Konsum exakt bei 100% (51609/51609).
Betroffen/jetzt gefixt: `RAUSER1.PCC` (182x46 statt fälschlich 320x200),
`RAUSER2.PCC` (228x21), `RAUSER3.PCC` (16x55), `FAC0-3.PCC` (16x16),
`FACTOR5.PCC` (320x199). Fix ist in `tools/pcc_to_png.py` eingebaut,
alle 77 PNGs unter `png_out/` mit dem Fix neu generiert.

**Rauser-Intro: JA, schon extrahiert.** `RAUSER1/2/3.PCC` sind Frames
eines scrollenden Text-Logos (vermutlich "RAUSER ADVERTAINMENT PRESENTS"),
mit dem Fix jetzt lesbar (zeigt z.B. "...USER TRAI..." als Ausschnitt).
Volles Logo muesste sich aus allen 3 Frames zusammensetzen lassen (noch
nicht gemacht).

**FACTOR5.PCC "Geister"-Doppellogo: vermutlich KEIN Bug**, sondern ein
Reflexions-/Schatten-Designelement — Byte-Konsum ist exakt 100%, also
decodiert der Inhalt komplett korrekt, das Doppelbild ist einfach das,
was in der Datei steht.

**OFFEN — KELLOGGS.PCC/KARTE.PCC-Notch-Artefakt:** Der Width/Height-Fix
hat den harmlosen 320x200-Fall nicht veraendert (Xmax/Ymax ergeben dort
zufaellig dieselbe Groesse wie vorher). Das Notch-Artefakt (rot/gelber
Fleck rechts neben "Kellogg's"-Schriftzug, passt nicht zum
DOSBox-Referenz-Screenshot) bleibt bestehen. Ausgeschlossen als Ursache
(empirisch getestet, nicht nur vermutet):
- Horizontales Rollen/Scrollen des ganzen Bilds (18 Shift-Kandidaten
  durchprobiert) — Artefakt bleibt IMMER an derselben Stelle relativ zum
  Bildinhalt (Box/Text), nicht zur Leinwand. Kein Verschiebe-/Wrap-Bug.
- Zeilenweises Verwerfen von RLE-Overshoot am Zeilenende — zeilenbasierte
  und durchgehende (row-boundary-freie) Decodierung liefern fuer
  KELLOGGS.PCC byte-identische Ergebnisse (kein Overshoot tritt je auf).
Interessant: `MENU.PCC` nutzt vermutlich denselben Box-Rahmen (leere
Variante) und rendert jetzt SAUBER ohne Notch — der Fehler ist also
dateispezifisch, nicht im Rahmen-Design selbst. Naechste Hypothese:
Palette-Fehlzuordnung fuer einzelne Indizes ODER RLE-Ambiguitaet bei
literalen Pixelwerten >= 0xC0 (werden durch `(b&0xC0)==0xC0` evtl.
faelschlich als Lauflaengen-Token statt literaler Indexwert gelesen) —
noch zu verifizieren durch gezielten Test mit einem Decoder, der beide
Interpretationen an der vermuteten Fehlstelle durchspielt.

Ebenfalls noch offen (niedrige Prioritaet): `TONY.PCC`/`SMACKS.PCC`
liefern mit dem Xmax/Ymax-Fix nur 1x1 Pixel (degenerated, Xmax=Ymax=0
im Header) — vermutlich sind das keine "echten" Vollbilder, sondern
Platzhalter/ungenutzte Preview-Slots; die eigentlichen Charakter-Sprites
stecken hoechstwahrscheinlich in den `.BOB`-Dateien (`TONY.BOB`,
`SMACKS.BOB` etc.), nicht in den gleichnamigen `.PCC`-Dateien.

**Git-Repo eingerichtet:** Auf Stefans Wunsch liegt der Projektstand jetzt
unter `https://git.hacker-net.de/Aria-Software/tony-remake`. Bewusst NICHT
mit eingecheckt: `raw/` (Original-Spieldateien: PCKELL.DAT/.PRE/.SET,
RTM.EXE, START.EXE etc. — das ist buchstaeblich das Original-Spiel) und
`extracted_pre/` (die daraus entpackten 181 Rohdateien — noch
Original-Asset-Material, nur unverpackt). Mit eingecheckt: `NOTES.md`,
`tools/` (unser Code: `split_pre.py`, `pcc_to_png.py`), `dosbox/`
(Test-Konfiguration), `png_out/` (unsere konvertierten PNG-Ergebnisse,
408 KB, reine Arbeitsergebnisse kein Originalmaterial).

## KELLOGGS.PCC/KARTE.PCC-Notch-Artefakt — GELOEST (2026-07-22, ~09:30 Ortszeit)

**Die Notiz oben ("Ausgeschlossen als Ursache ... Zeilenweises Verwerfen von
RLE-Overshoot ... liefern byte-identische Ergebnisse") war FALSCH.** Das war
ein Messfehler einer frueheren Session: der "kontinuierliche" Testdecoder
hat versehentlich weiterhin die width/height aus Offset 12-15 gelesen (dem
VERWORFENEN Bugfix-1-Feld) statt aus Xmax/Ymax — dadurch kam bei den beiden
320x200-Vollbildern zufaellig das gleiche (korrekte) Ergebnis raus wie beim
zeilenbasierten Decoder, und der Test schien zu bestaetigen, dass beide
Methoden aequivalent sind. Sind sie nicht.

**Neu verifiziert mit einem sauberen Vergleichstest** (`Buffer.compare` in
Node.js, unabhaengige Re-Implementierung): zeilenbasiertes Decoding
(Row-Reset, discard Overflow am Zeilenende) und durchgehendes Decoding
(RLE-Runs duerfen ueber Zeilengrenzen hinweglaufen) liefern **NICHT**
identische Ergebnisse — 216 von 64000 Pixeln unterscheiden sich bei
KARTE.PCC, 293 von 64000 bei KELLOGGS.PCC. Klingt nach wenig (<0.5%), reicht
aber, um ganze Bildbereiche sichtbar zu verschieben, weil ab dem ersten
"verlorenen" Ueberlauf-Pixel alle nachfolgenden Zeilen einen Pixel weiter
"nachruecken".

**Beweis per Ground-Truth-Vergleich:** DOSBox-Screenshot
`aria_kellogg_worldmap.png` (1024x768 mit schwarzen Balken) exakt auf den
reinen Spielbereich zugeschnitten (`convert -fuzz 5% -trim` → 640x400,
= 2x 320x200) und auf 320x200 runterskaliert
(`/shared/uploads/aria_karte_reference_320.png`). Nebeneinander-Vergleich
mit dem durchgehend (ohne Row-Reset) decodierten `KARTE.PCC`
(`/shared/uploads/aria_karte_compare2.png`): **pixel-identisch** — Berge,
Schloss, Drache, Tiger, Affe/Coco, Frosch, Teich, Haus, Voegel-Ecke, alles
exakt an der richtigen Stelle. Das Row-Reset-Verhalten war der einzige
Bug; es gibt kein horizontales Rollen, keine Palette-Fehlzuordnung, keine
RLE-Ambiguitaet bei Werten >=0xC0.

**Fix:** `tools/pcc_to_png.py` decodiert jetzt global `width*height` Pixel
aus dem RLE-Strom OHNE pro Zeile abzuschneiden (kein Row-Reset mehr). Alle
77 PNGs in `png_out/` wurden mit diesem finalen Decoder neu erzeugt
(diesmal wirklich verifiziert, nicht nur "sieht plausibel aus"). Kleine
Sprites/Logos waren von diesem Bug kaum betroffen (selten ein Run, der eine
Zeilengrenze ueberschreitet) — deshalb fiel es dort nie auf, nur bei den
zwei grossen 320x200-Vollbildern.

**Rauser-Intro bestaetigt (Frage von Stefan):** `RAUSER1/2/3.PCC` sind schon
laenger extrahiert und liegen jetzt mit dem finalen Decoder sauber decodiert
in `png_out/`. Es ist ein Text-Logo "RAUSER" + "ADVERTAINMENT" (Studio-Card),
als 3 Bild-Frames — vermutlich fuer einen Scroll-/Fade-Effekt, den die
Original-Engine (RTM.EXE) zur Laufzeit zusammensetzt, KEIN Video/keine
Animation-Datei. Fuers Remake heisst das: die 3 Frames sind da, die
Overlay-/Scroll-Logik muss im Python-Port nachgebaut werden (vermutlich
simpel: Frame croppen + ueber Zeit einblenden, sobald wir wissen wie die
Original-Sequenz zeitlich ablaeuft — dafuer ggf. DOSBox-Referenzvideo/
Screenshots in der Sequenz aufnehmen, spaeterer Schritt).

## BOB-Sprite-Format — Exploration laeuft, noch nicht geloest (Stand 09:30)

Auf `aria-wohnung` (`/home/aria/kellogg_remake/tools/`) lief parallel schon
einiges an Exploration fuer das naechste Format (BOB-Sprites): Brute-Force-
Breitensuche (`bob_width_search.py`), Header-Diff zwischen aehnlich grossen
Dateien (`bob_header_diff.py`, `bob_header_search.py`), Histogramm-Analyse
(`bob_histogram.py`), Sichtgitter-Rendering mit mehreren Breiten-Kandidaten
(`bob_render_grid.py`/`bob_render_grid2.py`), sowie erste Versuche die
`PCKELL.DAT`-Offset-Tabelle zu parsen (`dat_analyze.py`/`dat_analyze2.py`,
`dat_table2.py`, `dat_u16.py`). **Kein Ergebnis/Durchbruch bisher** — alles
noch offene Exploration, keiner der Scripts hat schon eine verifizierte
Loesung. Zur Sicherheit (Stefans Wunsch: nichts soll bei einem VM-Neustart
verloren gehen) sind alle diese Scripts jetzt zusaetzlich hier im
Projekt gesichert unter `tools/wip_bob_sprite_research/` (noch nicht
aufgeraeumt/nicht final, bewusst als "work in progress" separiert von den
fertigen Tools `split_pre.py`/`pcc_to_png.py`).

## KORREKTUR 2026-07-22 (nachmittags) — der "kontinuierliche" Fix oben war FALSCH

Stefan hat sich das gepushte Repo angeschaut und den Bug SOFORT wieder
gesehen (Frosch links statt rechts, Kellogg's-Logo verschoben) — die
"pixel-perfect verifiziert"-Behauptung im Abschnitt direkt darueber war
**selbst ein Messfehler**, genau wie der davor schon korrigierte. Beide
eigenen Hand-RLE-Decoder (Row-Reset UND kontinuierlich) haben KARTE.PCC/
KELLOGGS.PCC nachweislich sichtbar falsch decodiert (Bildinhalt dupliziert/
verschoben) — mein visueller Vorher-Vergleich war schlicht nicht sorgfaeltig
genug, ich habe der eigenen Automatik zu frueh vertraut.

**Stefans entscheidender Hinweis:** https://github.com/movAX13h/tony-and-friends-in-kelloggs-land
— ein bereits existierendes Reverse-Engineering-Projekt fuer GENAU dieses
Spiel (dort "Tony and Friends in Kellogg's Land" genannt, gleiche
PCKELL.DAT/PCKELL.SET-Assets). Enthaelt fertigen, funktionierenden C#-Code
fuer PCX/BOB/MAP/ARE/ICO/DAT — Quelle: `/tmp/tony-ref` (geklont, kein
LICENSE-File im Repo, daher NICHT 1:1-Code uebernommen, sondern nur als
Format-Referenz genutzt und in eigenem Python-Code neu umgesetzt).

Deren `Formats/PCXFile.cs` bestaetigt: `.PCC` ist stinknormales PCX v5,
8bpp, RLE, **mit Row-Reset** (pro Zeile wird ein frischer RLE-Reader
erzeugt, ueberschuessige Pixel eines Runs am Zeilenende werden verworfen —
Standard-PCX-Verhalten, keine Sonderregel). Das war also urspruenglich
(Bugfix 1, vormittags) schon RICHTIG vermutet — der eigentliche Bug lag
tiefer in der eigenen Implementierung und wurde nie gefunden.

**Der eigentliche Fix:** Decoder komplett ausgetauscht — `tools/pcc_to_png.py`
nutzt jetzt NICHT mehr eigenes RLE-Decoding, sondern `convert pcx:datei.PCC
out.png` (ImageMagick's ausgereifter PCX-Decoder, der die Datei einfach
per Standard-Format-Erkennung liest). Kein Workaround, sondern die richtige
Loesung: unser Format-Verstaendnis war im Kern korrekt, nur die
Handimplementierung hatte einen Bug den wir trotz zweier Anlaeufe nicht
gefunden haben — ein reifer, jahrzehntealter PCX-Decoder macht das
zuverlaessig richtig.

**Neu verifiziert (diesmal wirklich sorgfaeltig, Bild angeschaut statt nur
Byte-Statistik):** `png_out/KELLOGGS.png` und `png_out/KARTE.png` sehen jetzt
visuell identisch zu den echten DOSBox-Screenshots aus
(`aria_kellogg_titlescreen.png`/`aria_kellogg_worldmap.png`) — Vogelkopf
oben links, Drache+Schloss oben rechts, Frosch im Teich, Tiger unten
rechts, Coco unten links, alles an der richtigen Stelle, kein Doppel-Bild,
keine Kerbe. `FACTOR5.PCC`s "Geister-Doppellogo" ist ueber ImageMagick
ebenfalls sauber decodiert und bestaetigt sich damit endgueltig als
Absicht (Reflexions-Design), nicht als Bug. Alle 77 PCC-Dateien mit dem
neuen Decoder neu erzeugt und liegen in `png_out/`.

**Nebenfund:** `TONY.PCC`, `SMACKS.PCC`, `TOUCAN.PCC`, `COCO.PCC` (und
vermutlich weitere Charakter-Namen) decodieren zu winzigen 1x1-Pixel-Bildern
— das ist KEIN Extraktions-Bug. Laut `Form1.cs` der Referenz dienen manche
`.PCC`-Dateien nur als **Palette-Lieferant** fuer die zugehoerige `.ICO`/
`.BOB`-Sprite-Datei (ICO/BOB haben selbst keine eingebettete Palette,
im Gegensatz zu vollen PCC-Bildschirmen). Wichtig fuers spaetere BOB/ICO-
Decoding: Palette kommt dann von der PCC-Datei mit gleichem Basisnamen
(z.B. `TONY.BOB` -> Palette aus `TONY.PCC`), nicht aus der Sprite-Datei
selbst.

## Naechste Schritte (Prioritaet, Stand nachmittags 22.07.)
1. **BOB/SRB-Sprite-Format mit Hilfe der Referenz reversen.** Die Referenz
   (`/tmp/tony-ref/Unpacker/Formats/BOBFile.cs`, `BOBPainter.cs`) hat das
   schon geloest — README beschreibt BOB als "data and x86 code to display;
   somewhat similar to BOB format of Turrican II". Naechster Schritt: deren
   `BOBFile.cs` genau lesen und die Logik in eigenes Python uebersetzen
   (nicht 1:1 kopieren, kein LICENSE im Repo), dann gegen `A.BOB`
   (kleinstes Beispiel) verifizieren. Das sollte den bisherigen
   `wip_bob_sprite_research/`-Blindflug ueberfluessig machen.
2. Genauso `MAPFile.cs`/`AREFile.cs`/`ICOFile.cs` fuer Level-Maps/Areas/
   Tilesets nutzen statt weiter blind zu raten — auch hier: Format-Wissen
   uebernehmen, Code selbst neu schreiben.
3. Rauser-Intro-Sequenz (3 Frames) zu einer zusammengesetzten Vorschau
   rendern/dokumentieren, sobald Zeit ist — niedrige Prioritaet, Assets sind
   schon da.
4. Python/pygame-Projektstruktur aufsetzen (PCC-Loader ist jetzt wirklich
   fertig und verifiziert: `convert pcx:datei.PCC out.png` bzw. fuer den
   finalen Port direkt Pillow's PCX-Reader nutzen — Pillow unterstuetzt
   PCX nativ, damit brauchen wir zur Laufzeit kein externes ImageMagick
   mehr).

**Lektion fuer mich selbst (ARIA):** zweimal hintereinander eine "pixel-
perfect verifiziert"-Behauptung aufgestellt, die beim tatsaechlichen
Hinschauen (Bild anzeigen, nicht nur Bytes vergleichen) klar falsch war.
Naechstes Mal: bei jeder Verifikations-Behauptung IMMER das decodierte Bild
selbst anschauen (Read-Tool, multimodal), nicht nur Byte-/Pixel-Statistiken
vertrauen, bevor ich "geloest"/"verifiziert" sage.

## DURCHBRUCH 2026-07-22 (Nachmittag/Abend) — BOB/ICO/MAP komplett geloest

Stefan wollte aufraeumen + alle restlichen Assets (BOB/ICO/MAP/ARE/SAM/TFX).
Grosser Fund dabei: **`PCKELL.DAT` ist der eigentliche, einzige Asset-
Container** — nicht `PCKELL.PRE` (das war nur ein Nebenprodukt/Cache aus
`PCKELL.SET`). Referenz-Repo (`Formats/DATFile.cs`) beschreibt ihn explizit
als "the only container format": Index steht am DATEIENDE (Anzahl Entries
als u16 bei `len-4`, Index-Offset als i32 bei `len-8`), pro Entry
`[u16 NameLen][Name][i32 Offset]`, Laenge = Differenz zum naechsten Offset.
Score: **alle 178 Dateien fehlerfrei extrahiert**, exakt matchend zu
APPEND.LST — und `KELLOGGS.PCC` daraus ist **byte-identisch** zur alten
PRE-Extraktion, also war unser PCC-Weg schon konsistent. `PCKELL.PRE`/
`extracted_pre/`/`split_pre.py` sind damit ueberfluessig geworden, aus dem
Tool-Set entfernt (Repo-Hygiene, kein Skill-Friedhof-Aequivalent fuer
Tools). Neue kanonische Quelle: `tools/dat_extract.py` +
`tools/kellogg_formats.DATContainer`.

**PCC-Bug-Nachtrag:** beim heutigen Re-Check mit frischem Blick (Kellogg's-
Logo UND Weltkarte hochskaliert wirklich angeschaut, nicht nur behauptet)
ist `png_out/` tatsaechlich korrekt — Frosch im Teich unten links, Drache+
Schloss oben, Tiger/Coco richtig platziert, "Kellogg's präsentiert"-Box sauber.
Der PCC-Teil war also wirklich fertig, keine weitere Aktion noetig.

**BOB (Sprites) — komplett geloest.** Format aus `BOBFile.cs`/`Form1.cs`
in eigenes Python uebersetzt (`tools/kellogg_formats.py`, Funktionen
`parse_bob`/`_parse_bob_executable`): jede BOB-Datei ist eine Sequenz von
Frames, jeder Frame hat einen 14-Byte-Header (Breite/Hoehe/Laenge des
"Pointer"-Segments), gefolgt von echtem, sich selbst beschreibendem x86-
Maschinencode (`mov byte/word ptr [si+X], Y`-Ketten + `out dx,al` fuer
EGA-Page-Wechsel), der beim "Ausfuehren" direkt die Pixel-Kopieranweisungen
liefert (Offset+EGA-Page -> x/y, Pixelwert = Byte-Konstante minus 0x80 als
Palette-Index). Kein echter x86-Interpreter noetig, nur ein kleiner
Pattern-Matcher fuer die 10 vorkommenden Opcodes. Palette kommt (Form1.cs-
Regel) von der PCC-Datei mit gleichem Basisnamen (z.B. `TONY.BOB` ->
Palette aus `TONY.PCC`), bei 1-Buchstaben-Dateien (`A.BOB`..`O.BOB`, die
Ameisen) von `ANTS.PCC`, sonst von `W2.PCC` mit Index 0 auf Schwarz
gesetzt. **Alle 32 BOB-Dateien fehlerfrei decodiert, 462 Frames total.**
Visuell verifiziert (nicht nur Byte-Statistik!): `A.BOB` ist eine 8-Frame-
Laufanimation eines kleinen Kaefers/Ameise, `TONY.BOB` zeigt Tony den
Tiger unmissverstaendlich in Lauf-, Wink-, Sitz- und Sprung-Posen (53
Frames). Ergebnisse in `render_out/bob/<NAME>/NNN.png` (Einzelframes) +
`render_out/bob/<NAME>_sheet.png` (Grid-Uebersicht).

**ICO (Tilesets) — komplett geloest.** 16x16-Kacheln in "gescrambelter"
EGA-Spaltenreihenfolge (`parse_ico`): pro Zeile 4 EGA-Pages a 4 Pixel,
Palette-Index = Byte minus 128. Palette-Regel: `W1`/`W2`/`W3` aus dem
Dateinamen erkannt, Basis-Palette von `<WORLD>.PCC`, aber die 16 Farben
Index 16-31 IMMER von `W2.PCC` ueberschrieben (die sind laut Referenz die
geteilten Animations-/Item-Farben aller drei Welten) — ausser bei `W2`
selbst. **Alle 19 ICO-Dateien fehlerfrei decodiert.** Ergebnis in
`render_out/ico/<NAME>.png`.

**MAP (Level-Karten) — komplett geloest.** Klares Format, aber
GROSS-ENDIAN (!), Signatur `"TLE1"`, dann Breite/Hoehe/Konstante(=9) je
als big-endian int16, danach `Breite*Hoehe` big-endian u16-Zellen: unteres
9-Bit-Feld = Tile-Index (ins passende `<WORLD>.ICO`), oberes 7-Bit-Feld =
Kollisions-/Typ-Info (noch nicht weiter interpretiert). **Alle 22 MAP-
Dateien fehlerfrei decodiert UND gerendert** (Tile-Lookup im passenden ICO
+ Palette) — `W1L0.png` zeigt ein komplettes, stimmiges Level: Haeuser mit
rotem Dach, Buesche/Baeume, Wolken, blauer Himmel, Bergketten, alles exakt
an der Stelle, die man von einem Kellogg's-Tony-Jump'n'Run erwartet.
Ergebnis in `render_out/map/<NAME>.png`.

**ARE (Kollisionszonen) — NICHT geloest, bewusst zurueckgestellt.** Auch
die C#-Referenz hat das nie fertig gemacht (`AREFile.cs` liest nur naiv
176 little-endian u16-Werte ohne jede semantische Deutung, README nennt
nur den Namen "Area" ohne Details). Da MAP-Zellen bereits ein 7-Bit-
Kollisions-Typ-Feld mitbringen, ist unklar ob ARE ueberhaupt noch fuer den
Python-Port gebraucht wird (vielleicht redundant/Level-Editor-Metadaten) —
das klaeren wir erst, wenn die Spiellogik so weit ist dass Kollision
gebraucht wird. Rohdaten bleiben in `extracted_dat/` (nicht committed)
falls wir spaeter draufschauen wollen.

**SAM/TFX (Sound, TFMX-Format) — noch nicht angefasst.** 3+3 = 6 Dateien,
niedrige Prioritaet (Musik/SFX kommt spaeter, sobald Grafik+Logik stehen).
TFMX ist ein bekanntes Amiga/DOS-Tracker-Format, sollte mit vorhandenen
Libraries (z.B. `libxmp`/`ym2149`-Tools) machbar sein, nicht neu erfunden
werden muessen.

**Aufraeumen erledigt:**
- VM-Arbeitsverzeichnis (`/home/aria/kellogg_remake` auf `aria-wohnung`,
  NICHT im Repo): alle Debug-Logs/Test-Screenshots aus der DOSBox-IRQ-
  Session sowie `png_out_v2/` (veraltet) geloescht.
- `tools/`: alle Blindflug-Skripte aus der ersten BOB-Exploration
  (`bob_header_diff.py`, `bob_width_search.py`, `dat_analyze*.py`, etc.)
  UND `tools/wip_bob_sprite_research/` komplett entfernt — durch die
  echte Loesung in `kellogg_formats.py` ueberfluessig. `split_pre.py`
  ebenfalls entfernt (PRE-Weg durch DAT-Weg ersetzt).
- `.gitignore`: `extracted_dat/` ergaenzt (Rohdaten, copyrighted, wie
  `raw/`/`extracted_pre/` vorher schon).

**Naechste Schritte (Prioritaet, Stand Abend 22.07.):**
1. Rauser-Intro-Sequenz zusammensetzen (niedrig, Assets schon da).
2. Sound (SAM/TFX, TFMX-Format) angehen, wenn Zeit ist.
3. ARE/Kollisionsformat klaeren — erst wenn's fuer Spiellogik gebraucht wird.
4. **Jetzt wo alle visuellen Assets (PCC/BOB/ICO/MAP) fertig sind:**
   Python/pygame-Projektstruktur aufsetzen — Asset-Loader (Palette-Regeln
   aus `kellogg_formats.py` direkt uebernehmen), dann Tony-Bewegung/
   Kollision/Level-Scrolling als erste spielbare Vertical Slice.

## ENTSCHEIDUNG 2026-07-22 abends — erster Meilenstein: Boot-/Intro-Sequenz statt Gameplay

Stefan-Wunsch: als leichten, sichtbaren Einstieg zuerst die komplette
Boot-Sequenz nachbauen (Rauser-Card → Factor5-Logo → Kellogg's-Logo mit
"Vorhang"-Aufdeck-Effekt → Titelbild → Hauptmenue mit Menuepunkten →
Weltkarte), BEVOR Tony ueberhaupt beweglich ist. Sinnvolle Wahl: das
zwingt genau die Kern-Engine-Bausteine, die spaeter das ganze Spiel
braucht (Asset-Loader, Bild-Blitting mit Palette, Timing/Sequenzer,
Sound-Playback, Input-Handling fuers Menue) — nur eben am kleinstmoeglichen
Slice, bevor Physik/Kollision/Level-Logik dazukommen.

**Stand der benoetigten Assets (alle schon extrahiert, nur noch nicht
zu einer Sequenz zusammengesetzt):**
- Rauser-Card: `RAUSER1/2/3.PCC` (3 Scroll-/Fade-Frames, siehe oben)
- Factor5-Logo: `FACTOR5.PCC` + `FAC0-3.PCC` (kleine Begleit-Frames,
  vermutlich Blink-/Reflex-Animation)
- Kellogg's-Logo: `KELLOGGS.PCC` — **"Vorhang"-Effekt (Stefans Beobachtung:
  oberer Teil muesste sich von rechts nach links aufdecken) ist NICHT als
  Asset gespeichert**, sondern eine Laufzeit-Animation von RTM.EXE (reines
  Wipe/Reveal auf einem statischen Vollbild). Muss durch Beobachtung
  nachgebaut werden (Screenshot-Serie waehrend des Boot-Vorgangs in
  DOSBox, kurze Intervalle, um Richtung/Geschwindigkeit/Dauer zu
  bestimmen) — noch nicht gemacht, naechster Schritt.
- Titelbild ("Tony & Friends in Kellogg's Land"): noch keine eigene PCC-
  Datei dafuer identifiziert unter den 77 — moeglich, dass das Kellogg's-
  Logo direkt das Titelbild IST (ein Screen), oder der Text wird von der
  Engine ueber ein weiteres, noch nicht namentlich zugeordnetes PCC
  gelegt. Zu klaeren beim naechsten DOSBox-Screenshot-Durchlauf.
- Hauptmenue: `MENU.PCC` (Rahmen/Box vorhanden) — Menuepunkte selbst
  vermutlich als Text von der Engine gerendert (kein Font-Asset bisher
  identifiziert) oder als eigene ICO/BOB-Sprites. Noch offen.
- Karte: `KARTE.PCC` + `KARTE0-9.PCC` (10 Zusatz-Frames, vermutlich
  kleine Animationen auf der Karte — Wasser/Flagge/Feuer o.ae., noch
  nicht einzeln zugeordnet).
- Sound: 3x SAM + 3x TFX (TFMX-Format, Amiga/DOS-Tracker) — bisher NICHT
  decodiert. Ob Rauser/Factor5/Kellogg's-Screens ueberhaupt Sound haben,
  ist unklar (DOSBox lief bisher im ALSA-nosound-Modus, kein Ton
  gehoert) — zu klaeren durch Sichtung von `tools/tfmx_player` (C-Referenz-
  Tool, liegt schon lokal unter `tools/tfmx_player/`, noch uncommitted)
  und/oder DOSBox mit echtem Audio-Sink testen.

**Plan (in dieser Reihenfolge):**
1. pygame auf `aria-wohnung` installiert (User-pip, erledigt).
2. DOSBox-Screenshot-Serie waehrend des Bootvorgangs aufnehmen (kurze
   Intervalle ueber Rauser→Factor5→Kellogg's→Titel→Menue), um Timing +
   Wipe-Richtung/-Geschwindigkeit des Kellogg's-Vorhang-Effekts zu
   bestimmen, und um das noch unklare Titelbild/Menue-Text-Rendering zu
   identifizieren.
3. `tools/intro_sequence.py` (neu): pygame-basierter Player, der die
   Screens in der beobachteten Reihenfolge + Timing zeigt (erst ohne
   Ton), Wipe-Effekt fuer Kellogg's-Logo nachbauen.
4. TFMX-Sound anhaengen, sobald `tfmx_player` als Referenz ausgewertet
   ist (Python-Wrapper oder Re-Implementierung der Decodierung).
5. Committen/pushen nach jedem funktionierenden Teilschritt (nicht erst
   am Ende), NOTES.md nach jedem Meilenstein aktualisieren — wie von
   Stefan gewuenscht, damit bei einem VM-/Brain-Neustart nichts verloren
   geht und einfach hier weitergelesen werden kann.

Laeuft jetzt selbstaendig weiter, keine Rueckfrage noetig.

## Boot-Sequenz vermessen + Intro-Player gebaut (2026-07-22 abends, Fortsetzung)

### 1) Echter Regressions-Bug gefunden + gefixt (tools/pcc_to_png.py)

Beim Draufschauen fuer die Intro-Arbeit aufgefallen: Commit `62af15e` hatte
den PCC-Decoder korrekt auf ImageMagick umgestellt (nachdem zwei eigene
Hand-RLE-Parser nachweislich falsche Pixel lieferten). Der naechste Commit
`3410a53` (eigentlich nur fuer BOB/ICO/MAP gedacht) hat `pcc_to_png.py`
aber **versehentlich durch eine AELTERE Version mit dem kaputten
Hand-RLE-Decoder ueberschrieben** -- vermutlich ein Merge-/Copy-Fehler beim
Zusammenfuehren von Aenderungen. Die bereits committeten 77 PNGs in
`png_out/` waren zum Glueck NICHT betroffen (die stammen noch von der guten
ImageMagick-Version), aber ein Rerun des Skripts haette wieder kaputte
Bilder erzeugt, ohne dass es aufgefallen waere.

**Fix:** Statt ImageMagick-Subprocess jetzt `kellogg_formats.load_pcc()` --
nutzt Pillows eingebauten, robusten PCX-Decoder direkt (`PIL.Image.open`
erkennt PCX an den Magic-Bytes, nicht am Dateinamen). Kein eigener
RLE-Parser mehr im Projekt, an keiner Stelle. Alle 77 PCC neu aus
`PCKELL.DAT` gerendert und via `compare -metric AE` **byte-fuer-byte
pixelidentisch (AE=0)** zu den alten (guten) PNGs verifiziert -- keine
Regression, sauberer Fix. Neue Funktionen `load_pcc()` / `pcc_to_rgba()`
in `kellogg_formats.py`, wiederverwendbar fuer den Intro-Player.

### 2) Boot-Sequenz per echter Screenshot-Serie vermessen (0.3s-Intervall)

`xvfb-run`-Setup (Xvfb + `import -window root` alle 0.3s) waehrend
`START.EXE` bootet, ~90 Frames ueber ~27s. Ergebnis (Hash-Dedup + visuelle
Pruefung der Uebergaenge):

1. **Rauser-Advertainment-Karte** -- Komposit aus `RAUSER1.PCC` (dunkle Box
   "RAUSER"), `RAUSER3.PCC` (blaue Box, Ausrufezeichen), `RAUSER2.PCC`
   ("ADVERTAINMENT"-Schriftzug) auf weissem Grund. Statisch gehalten (kein
   Aufbau-Effekt beobachtet), ~3s.
2. Kurze schwarze Pause (~0.3-0.5s).
3. **Factor5-Studio-Logo** (`FACTOR5.PCC`, 320x199) -- **komplett statisches
   Bild**, keine Laufzeit-Animation. Das "Geister"-Doppellogo (zwei leicht
   versetzte FACTOR-Schriftzuege + Spiegelung am Boden) ist Teil des Assets
   selbst (Byte-Konsum exakt 100%, kein Bug). Lange gehalten, ~6s.
4. Kurze schwarze Pause.
5. **Kellogg's-Markenlogo** (`KELLOGGS.PCC`, 320x200) -- erscheint SOFORT
   komplett und bleibt die ganze Zeit scharf. **KORREKTUR einer fruehen
   Fehlannahme:** es gibt HIER keinen Vorhang-/Wipe-Effekt auf dem Logo
   selbst. Was tatsaechlich passiert: der Text **"praesentiert"** darunter
   faedet per **Palette-Animation** von blass (fast weiss) zu kraeftigem
   Rot ein (~3-4s beobachtet). Per Index-Analyse verifiziert: die Pixel des
   Textes nutzen exklusiv die Palette-Indizes **34, 42, 43, 44, 45** (Bbox
   x=71-241, y=108-141 im 320x200-Bild) -- alles andere im Bild bleibt
   waehrend des Fades unveraendert.
6. Kurze schwarze Pause.
7. **Titelbild "TONY & FRIENDS in Kellogg's Land"** -- **DAS ist der
   eigentliche Vorhang-Effekt**, den Stefan urspruenglich meinte. Wichtiger
   Fund: dieses Bild ist intern **320x480** (doppelt so hoch wie die
   anderen Screens!), zusammengesetzt aus den 4 PCC-Quadranten `KELL256A`
   (oben links) / `KELL256B` (oben rechts) / `KELL256C` (unten links) /
   `KELL256D` (unten rechts), je 160x240 -- durch Stitchen zu einem
   320x480-Gesamtbild verifiziert (siehe `/tmp/kell_full_stitch.png`
   waehrend der Session, zeigt exakt das erwartete Titelbild). Der
   Reveal-Effekt: **obere Bildhaelfte (A|B) faehrt von oben nach unten
   ein, untere Haelfte (C|D) von unten nach oben** -- wie ein Theater-
   vorhang, nur vertikal, beide Haelften treffen sich exakt in der Mitte
   (y=240 im 480px-Bild). Dauer ~3s (Frames 73->83 in der Aufnahme).
   DOSBox-Fenster ist fuer diesen Screen sichtbar groesser/anders
   proportioniert als bei den 320x240-Screens davor -- passt zur These
   eines eigenen 320x480-Modus nur fuer dieses Bild.

Alle anderen Screens (Rauser/Factor5/Kellogg's-Logo) laufen im
Fenster-Seitenverhaeltnis 320x240 (aus Trim-Analyse des DOSBox-Fensters:
640x480 Xvfb-Ausschnitt = exakt 2x 320x240) -- die PCC-Bilder selbst sind
oft nur 320x199/200 und werden darin mit schwarzen Balken oben/unten
letterboxed (gut sichtbar am schwarzen Rand um die Kellogg's-Box in den
Screenshots).

### 3) Erster spielbarer Meilenstein: `tools/intro_sequence.py`

pygame-Player, der die komplette oben beschriebene Sequenz nachbaut:
Asset-Loading aus `PCKELL.DAT` (ueber `kellogg_formats`), Bild-Rendering
mit Palette, Timing/Sequenzer, Palette-Fade-Effekt (echte Index-Animation,
kein Alpha-Crossfade-Hack), Vorhang-Wipe-Effekt fuer das Titelbild.
Headless getestet (Xvfb + Screenshot-Serie) -- **visuell verifiziert,
sieht in allen Phasen sehr nah am echten DOSBox-Boot aus**:

[FILE: /shared/uploads/aria_intro_rauser.png] (Rauser-Karte, Komposit)
[FILE: /shared/uploads/aria_intro_factor5.png] (Factor5-Logo, statisch)
[FILE: /shared/uploads/aria_intro_kelloggs_fade.png] ("praesentiert" mitten im Fade)
[FILE: /shared/uploads/aria_intro_titel_wipe.png] (Titelbild-Vorhang mitten im Reveal)
[FILE: /shared/uploads/aria_intro_titel_final.png] (Titelbild komplett offen)

**Bekannte Ungenauigkeiten (v1, nicht pixelperfekt, aber nah dran):**
- Rauser-Komposit-Layout (Position von RAUSER1/2/3 zueinander) ist per
  Augenmass an die Screenshots angenaehert, nicht pixelgenau vermessen --
  bei Bedarf spaeter per genauerem Screenshot-Vergleich nachjustieren.
- Fade-/Wipe-Dauern sind aus der 0.3s-Sample-Screenshot-Serie abgeleitet
  (+/- ein paar hundert ms Unschaerfe), nicht aus dem Originalcode gelesen
  (RTM.EXE ist compiliert, kein Quellcode verfuegbar).
- Sound fehlt komplett (TFMX/SAM noch nicht decodiert, siehe unten).

**Ausfuehren:** `python3 tools/intro_sequence.py` auf `aria-wohnung`
(pygame jetzt per `apt install python3-pygame` installiert, NICHT pip --
System-Python ist "externally managed", pip-User-Install schlug fehl).
`--once` fuer einen Durchlauf statt Endlosschleife wie im Original.

### Naechste Schritte (Prioritaet)
1. Sound (SAM/TFX TFMX-Format) decodieren + an die Intro-Sequenz haengen --
   `tools/tfmx_player/` (C-Referenztool) liegt bereit.
2. Rauser-Layout und Fade/Wipe-Timings feinjustieren falls gewuenscht.
3. Danach: Uebergang von Intro zu echtem Gameplay -- Tony-Bewegung auf
   Level W1L0 als naechster grosser Meilenstein (MAP+ICO+BOB sind fertig).

Laeuft selbstaendig weiter, keine Rueckfrage noetig.

## Sound (TFMX) geloest + in Intro-Sequenz eingebaut (2026-07-22, spaeter Abend)

**`tools/tfmx_player/` (MIT-lizenziert, Copyright Peter Fors -- LICENSE liegt
bei) ist ein fertiger TFMX-Replayer in C.** Anders als beim C#-Referenzprojekt
fuer PCX/BOB/MAP/ICO (dort kein LICENSE, daher nur als Format-Wissen genutzt
und in eigenem Python neu geschrieben) erlaubt die MIT-Lizenz hier direkte
Nutzung/Einbindung -- deshalb kein Python-Reimplement des TFMX-Formats
(waere bei ~95KB Player-Logik in `tfmx.h` ein Mehrtage-Projekt ohne Mehrwert
gegenueber der fertigen, korrekt lizenzierten Loesung).

**Build:** `tools/tfmx_player/build.sh` kompiliert `render_tfmx`
(`gcc render_tfmx.c -lm`) -- ein CLI-Tool `render_tfmx <in.tfx> <in.sam>
<out.wav> [sekunden]`, das ein TFMX-Modul zu 16-bit/44100Hz-Stereo-WAV
rendert. **Alle 3 Musikstuecke (`TITEL`, `TITEL2`, `ONGAME2`) erfolgreich
gerendert und angehoert (nicht nur Byte-Statistik!)** -- echte, hoerbare
Chiptune-Musik, kein Rauschen/Stille. Vorschau-MP3s lagen kurz unter
`/shared/uploads/aria_tfmx_*.mp3` fuer Stefan zum Reinhoeren.

**Einbau in `tools/intro_sequence.py`:** neuer Wrapper `tools/tfmx_audio.py`
extrahiert TFX+SAM per `kellogg_formats.DATContainer` aus `PCKELL.DAT`,
rendert einmalig per Subprocess zu WAV (gecacht unter
`KELLOGG_AUDIO_CACHE`/`~/kellogg_remake/audio_cache/`, NICHT committed --
ist Original-Musikmaterial wie `raw/`). `intro_sequence.py` initialisiert
jetzt `pygame.mixer`, startet beim Erreichen des Kellogg's-Logos die
`TITEL`-Musik geloopt (Annahme: durchgehende Titelmusik ueber Logo+Titelbild
+Karte -- noch nicht per Timing-Vergleich gegen DOSBox-Ton verifiziert, da
DOSBox bisher nur im Nosound-Modus lief; plausibelste v1-Annahme). Mixer-
Init ist defensiv (try/except) -- auf `aria-wohnung` (keine Soundkarte im
Container/VM) laeuft die Sequenz jetzt nachweislich sauber STUMM weiter
(getestet), mit `SDL_AUDIODRIVER=dummy` laeuft der komplette Codepfad
inklusive Musik-Rendering fehlerfrei durch (ebenfalls getestet) -- auf
Stefans Rechner mit echter Soundkarte sollte die Musik beim Ausfuehren
tatsaechlich zu hoeren sein.

**Committed:** `tools/tfmx_player/` (Quellcode + LICENSE, NICHT das
kompilierte Binary -- Build-Artefakt, per `.gitignore` draussen wie
`audio_cache/`), `tools/tfmx_audio.py`, aktualisiertes
`tools/intro_sequence.py`.

**Noch offen (niedrige Prioritaet):**
- `TITEL2`/`ONGAME2` sind noch nicht eingebunden -- vermutlich `TITEL2` fuer
  die Weltkarte und `ONGAME2` fuers eigentliche Level-Gameplay, aber welche
  Musik wo genau einsetzt ist nur eine Annahme (Namen sprechen dafuer,
  nicht per Referenz/Timing-Vergleich verifiziert).
- Timing/Verifikation der Titelmusik-Ein-/Aussatzpunkte gegen das Original
  (DOSBox bisher stumm getestet) steht noch aus.
- Rauser-Layout und Fade/Wipe-Feinjustierung (siehe vorherige Notiz) weiter
  offen, niedrige Prioritaet.

### Naechste Schritte (Prioritaet, Stand spaeter Abend 22.07.)
1. `TITEL2`/`ONGAME2` an Karte/Gameplay-Musik zuordnen und einbinden, sobald
   diese Screens/Modi existieren.
2. Danach: Uebergang von Intro zu echtem Gameplay -- Tony-Bewegung auf
   Level W1L0 als naechster grosser Meilenstein (MAP+ICO+BOB+jetzt auch
   Sound-Infrastruktur sind fertig).
3. ARE/Kollisionsformat klaeren, wenn Spiellogik es braucht.

Laeuft selbstaendig weiter, keine Rueckfrage noetig.

## Pfade relativ gemacht + Rauser/Titelbild-Korrektur (22.07.2026, Nacht)

Stefan meldete zwei Dinge: (1) alle Tools haben `/home/aria/kellogg_remake/...`
hardcodiert -- muss relativ zum Projektordner sein; (2) 6 Screenshots als
Beleg fuer Bild-Bugs (Rauser zu gross, Titelbild "gestaucht", Original hat
Scanlines die verschwinden). **Wichtig: von den 6 Screenshots ist wegen eines
Upload-Bugs (alle 6 landeten unter demselben Dateinamen und ueberschrieben
sich gegenseitig) nur der LETZTE erhalten geblieben** -- ich hab Stefan das
gemeldet und ihn gebeten, die anderen 5 bei Bedarf nochmal zu schicken. Statt
zu raten hab ich stattdessen selbst frische DOSBox-Referenzaufnahmen gemacht
(Xvfb + `import -window root`, 0.12s-Intervall) und daraus die echten Werte
gemessen -- Details unten.

**1) Pfade:** `intro_sequence.py`, `tfmx_audio.py`, `render_assets.py`,
`dat_extract.py` hatten `/home/aria/kellogg_remake/...` fest im Code. Jetzt
ueberall `PROJECT_ROOT = dirname(dirname(abspath(__file__)))` + optionales
ENV-Override (`KELLOGG_DAT`, `KELLOGG_AUDIO_CACHE`, `KELLOGG_RENDER_OUT`,
`KELLOGG_EXTRACT_OUT`). Laeuft jetzt von jedem Pfad aus (VM, Container,
woanders geklont).

**2) Rauser -- per Pixelvermessung neu gebaut:**
- Echte Groesse per Bounding-Box-Analyse gegen eine frische DOSBox-Aufnahme
  gemessen: das komplette Rauser-Logo (RAUSER1+2+3 zusammen) ist im Original
  nur ca. 105x40px auf einer 320x240-Leinwand -- mein v1-Code hat die
  PCC-Assets 1:1 nativ gezeichnet (182x46 fuer RAUSER1 allein!), viel zu
  gross. **Alle drei Rauser-Assets werden jetzt bei 0.5x Nativgroesse
  gezeichnet** (`ASSET_DISPLAY_SCALE`).
- Hintergrund war in v1 WEISS die ganze Zeit -- tatsaechlich: WEISS waehrend
  Slide-in+Halten, dann kurzer Fade zu GRAU (73,73,73), dann Grau halten,
  dann Schwarzblende (bestehende Pause).
- **Neu: Slide-in-Animation.** Per Screenshot-Serie entdeckt: RAUSER1
  ("RAUSER"-Box) faehrt von LINKS ins Bild, RAUSER3 (blaue Box mit "!")
  faehrt von OBEN rein, RAUSER2 ("ADVERTAINMENT") steht von Anfang an fest.
  Nachgebaut mit smoothstep-Ease ueber `RAUSER_SLIDE_SECONDS=0.9s`.
- **Neu: Sound-Sting.** `TITEL2.TFX/.SAM` (das kuerzeste der 3 Musikstuecke,
  724 Byte TFX) ist kein Loop-Track, sondern ein kurzer ~1s-Jingle (RMS-
  Analyse zeigt: Burst 0-0.9s, dann Stille, dann Wiederholung -- typisch fuer
  ein kurzes Sample, das die Tracker-Engine in einer Schleife rendert). Wird
  jetzt exakt beim Erreichen der Endposition (Slide-in fertig) EINMAL
  abgespielt (`pygame.mixer.Sound`, nicht geloopt).

**3) Titelbild -- KORREKTUR einer fruehen Fehlannahme (das war der Bug hinter
Stefans "gestaucht"-Meldung):**
- v1 hat die 4 Quadranten KELL256A/B/C/D (je 160x240 PCC-nativ) 1:1 zu einem
  320x**480**-Bild gestapelt (A|B oben, C|D unten) und das komplett 480 hoch
  angezeigt. Das war falsch.
- Per Pixel-Rekonstruktion gegen eine echte DOSBox-Aufnahme verifiziert
  (mehrere Hypothesen durchprobiert, mit numpy MSE + visuellem Abgleich):
  **die 4 Quadranten werden je auf HALBE Hoehe (160x120) skaliert** (Breite
  bleibt nativ, NUR die Hoehe wird halbiert -- anders als beim Rauser-Fix,
  wo beide Achsen gleichmaessig skaliert werden!) und im 2x2-Raster
  angeordnet: A oben-links, B oben-rechts, C unten-links, D unten-rechts ->
  ergibt ein korrektes 320x240-Bild. Screenshot-Vergleich (Rekonstruktion
  vs. echte DOSBox-Aufnahme) war praktisch deckungsgleich.
  ACHTUNG fuer kuenftige Aenderungen: bei den Rauser-Assets ist die
  0.5x-Skalierung GLEICHMAESSIG (Breite+Hoehe), bei den Titelbild-Quadranten
  ist es NUR die Hoehe -- zwei verschiedene Funktionen im Code
  (`pcc_surface_scaled` vs. `pcc_surface_half_height`), nicht verwechseln.
- **Neu entdeckter Scanline-Effekt** (das ist genau Stefans "Screenshot 4/5/6"
  -Beobachtung): waehrend die untere Bildhaelfte einwaechst, ist sie zuerst
  komplett entsaettigt (graustichig) UND mit sichtbaren horizontalen
  Scanlines ueberzogen -- das loest sich in einer zweiten Animationsphase zu
  vollen Farben auf, bis das Bild komplett scharf ist. Per 0.12s-Screenshot-
  Serie ueber die komplette Wipe-Animation hinweg entdeckt und nachgebaut
  (`_scanline_desaturate()`, per numpy: Graustufen-Mix + jede zweite Zeile
  abgedunkelt, Intensitaet faedet ueber die zweite Haelfte der Wipe-Dauer von
  1.0 auf 0.0).
- Reveal-Mechanik selbst (oben waechst 0->120 von oben, unten waechst 0->120
  von unten, treffen sich in der Mitte) war in der urspruenglichen
  Beobachtung schon richtig -- nur die falsche 480px-Endgroesse und der
  fehlende Scanline-Effekt waren der Bug.

**Verifiziert:** eigene DOSBox-Referenzaufnahme (Xvfb, 0.12s-Serie) gegen den
neu gebauten `intro_sequence.py`-Output verglichen -- Rauser-Slide-in, Rauser-
Groesse/Farben und Titelbild (inkl. Scanline-Phase) sehen jetzt sehr nah am
Original aus. Screenshots liegen als Beleg bei:

[FILE: /shared/uploads/aria_rauser_fixed.png] (Rauser, neu: klein, weiss, Slide-in-Mitte)
[FILE: /shared/uploads/aria_title_wipe_scanline_fixed.png] (Titelbild-Wipe MIT Scanline-Effekt)
[FILE: /shared/uploads/aria_title_final_fixed.png] (Titelbild komplett, korrekt 320x240 statt 320x480)

**Noch offen / bewusste Vereinfachungen:**
- Die feine Choreografie von RAUSER3 (slidet vermutlich als "!" und blaue Box
  getrennt, siehe Screenshot-Beobachtung) wurde vereinfacht zu EINEM Element,
  das von oben reinfaehrt -- sieht in der Praxis schon sehr nah dran aus,
  waere aber bei Bedarf noch weiter verfeinerbar.
- Kleiner kosmetischer Rest: RAUSER2/RAUSER3 haben weissen Hintergrund IN
  ihrer eigenen PCC-Bounding-Box (kein Alpha-Kanal). Auf weiss (waehrend
  Slide-in+Halten) faellt das nicht auf, nach dem Fade zu Grau sieht man
  einen schwachen weissen Kasten drumrum. Versuch mit `set_colorkey()`
  wieder verworfen (hat auch die weissen BUCHSTABEN in RAUSER1 durchsichtig
  gemacht -- schlimmer als der Kasten). Braucht eine echte Alpha-Maske pro
  Asset, low priority.
- Der Scanline-Clear-Effekt ist als gleichmaessige Intensitaet ueber die
  ganze untere Haelfte umgesetzt, nicht als praezise von-der-Mitte-nach-
  aussen wandernde Grenze (im Original loest sich ein duenner Reststreifen
  am unteren Bildrand als letztes auf) -- optische Wirkung ist aber schon
  sehr aehnlich.
- VM-Testverzeichnisse (`boot_frames2/`, `intro_check_frames/`,
  `title_check_frames/`) nach der Analyse wieder aufgeraeumt, nicht im Repo.

Laeuft selbstaendig weiter, keine Rueckfrage noetig.

## 22.07.2026, spaeter Abend (2): drei Feedback-Bugs von Stefan gefixt

Stefan hatte nach dem letzten Pfade-relativ/Rauser/Titelbild-Fix drei neue
Beobachtungen gemeldet, alle drei bestaetigt und behoben:

**1. "Rauser-Text fadet nicht mit aus, bleibt hart umrandet stehen."**
Ursache endlich richtig verstanden (der oben als "kosmetischer Rest, low
priority" dokumentierte Kasten): RAUSER1/2/3.PCC haben keinen Alphakanal, ihr
papierweisser Hintergrund sitzt als hartes Rechteck um die Box-Grafik. Fix:
neue Funktion `make_border_bg_transparent()` in `intro_sequence.py` -- Flood-
Fill vom Bildrand aus ueber alle "papierweissen" Pixel (Schwelle 235/255 je
Kanal), NUR die vom Rand aus zusammenhaengende Flaeche wird transparent.
Isolierte weisse Pixel MITTEN im Motiv (z.B. die weissen Buchstaben in
RAUSER1) beruehren den Rand nicht und bleiben unangetastet -- genau das
Problem, an dem der fruehere `set_colorkey()`-Versuch gescheitert war.
Verifiziert per direktem Pixel-Test (nicht nur behauptet): Eckpixel neben der
Box liegt nach dem Fix exakt bei RAUSER_GREY (73,73,73) statt weiss, und im
Bild bleiben klar sichtbare, fast voll opake Buchstaben-Pixel (Alpha bis 254)
erhalten. Screenshot der Grau-Phase zum Beleg:
[FILE: /shared/uploads/aria_rauser_fade_grey_check.png]

**2. "Musik ist zu kurz, da fehlt der Rest" + Cache-Bug gefunden.**
Zwei uebereinanderliegende Ursachen:
  a) `render_wav()` cached per Dateiname OHNE den `seconds`-Parameter zu
     beruecksichtigen -- ein alter Test-Render (TITEL mit 8s, TITEL2 mit 15s
     aus einer frueheren Debug-Session) blieb im `audio_cache/` liegen und
     wurde bei jedem Aufruf stillschweigend weiterverwendet, egal welche
     `seconds` der Aufrufer diesmal wollte.
  b) selbst ein frischer Render haette bei den frueher verwendeten festen
     Werten (TITEL: 60s) abgeschnitten -- **TITEL ist tatsaechlich ~109s
     lang, ONGAME2 ~123s**, beides deutlich laenger als angenommen.
Echter Fix in `render_tfmx.c`: statt stur `seconds` lang zu rendern, wird
jetzt in kleinen 64-Frame-Haeppchen gerendert und tfmx.h's eigenes
`real_song_end`-Flag beobachtet (wird pro Tick gesetzt, sobald die
Track-Sequenz einmal komplett durchgelaufen ist und intern neu startet) --
sobald es feuert, ist GENAU eine vollstaendige natuerliche Song-Schleife im
Kasten, egal wie lang das Stueck wirklich ist. `seconds`/`max_seconds` ist
jetzt nur noch eine Sicherheits-Obergrenze (Default 180s, grosszuegig ueber
allen 3 bekannten Stuecken). Getestet und verifiziert:
  - TITEL: 109.44s (natuerliches Ende erkannt)
  - TITEL2 (Rauser-Sting): 2.04s (natuerliches Ende erkannt)
  - ONGAME2: 122.88s (natuerliches Ende erkannt)
Alte falschen Cache-Dateien geloescht, mit dem gefixten Tool neu gerendert
und die Laengen direkt per `pygame.mixer.Sound(...).get_length()` verifiziert
(nicht nur die WAV-Header-Bytes ausgerechnet).

**3. "Bei Factor5 gibt's auch noch ne Melodie, solange das Lied ist Factor5
anzeigen und dann zum Kellogg's-praesentiert."**
`ONGAME2` war bis jetzt der einzige der 3 TFMX-Tracks ohne zugeordneten
Screen (TITEL2 = Rauser-Sting, TITEL = Kellogg's-Logo/Titelbild-Loop) --
genau das ist offenbar die Factor5-Melodie. `run_once()` in
`intro_sequence.py` spielt ONGAME2 jetzt beim Erscheinen des Factor5-Logos
einmal komplett ab und haelt den Screen exakt so lange (`sound.get_length()`,
~123s statt der alten festen 3.0s), bevor es zur schwarzen Pause + Kellogg's-
Logo weitergeht.

**Alle 3 Fixes verifiziert, nicht nur behauptet:**
- Rauser-Alpha-Fix: direkter Pixel-Test + Screenshot (oben verlinkt).
- Musik-Laengen: `render_tfmx`-Stderr-Ausgabe UND `pygame.mixer.Sound.get_length()`
  geben identische Werte.
- Kompletter Smoke-Test (`xvfb-run python3 tools/intro_sequence.py --once`)
  laeuft ohne Traceback durch (headless ohne Audio-Geraet, laeuft dann
  erwartungsgemaess stumm mit Fallback-Dauern).

Committet + gepusht. Naechster Schritt weiterhin offen: echtes Gameplay
(Tony auf `W1L0` bewegen).

## 22.07.2026, Nacht (3): Rauser-Aufloesung wirklich gefixt + Factor5-Sound korrigiert (Multi-Song-Fund)

Stefan hatte zwei weitere Punkte gemeldet: "Rauser-Intro-Aufloesung ist immer
noch schlecht" (mit Referenz-Screenshot: `RAUSER!`-Logo klein, mittig, viel
weisser Rand drumrum) und "beim Factor5 hast du einen Level-Sound genommen,
der echte Factor5-Sound ist ein kurzes Brausen, nur wenig laenger als der
Rauser-Sound, Bild ca. 5s eingeblendet".

**Wichtiger Fund beim Reinschauen:** Zwischen den Chat-Nachrichten war im
Hintergrund (autonomer Weiterarbeits-Timer) schon ein erster Loesungsversuch
fuer beides auf der VM entstanden, aber weder ins Git-Repo synced noch
committet/gepusht -- der Chat-Verlauf hatte den Stand nicht wiedergegeben.
Ich hab den VM-Stand zuerst gegen den Repo-Stand geprueft (`diff`), den fuer
Factor5 bereits korrekten Teil uebernommen, und den Rauser-Aufloesungs-Fix
selbst nochmal nachgerechnet, weil er in der ersten Fassung (native 1.0x
Groesse) tatsaechlich einen NEUEN Bug enthielt.

**1) Rauser-Aufloesung -- diesmal wirklich pixelvermessen statt geraten:**
Stefans Referenz-Screenshot per Bounding-Box analysiert (PIL, Python auf der
VM): das Logo nimmt dort nur **34.5% der Bildbreite / 16.6% der Bildhoehe**
ein (223x83px auf einem 646x500px-Screenshot). Das widerlegt die vorige
Annahme "native Groesse (ASSET_DISPLAY_SCALE=1.0) ist richtig" klar -- bei
nativer Groesse waeren RAUSER1+RAUSER3 zusammen 198px breit auf einer
320px-Leinwand (62%), fast doppelt so viel wie die Referenz zeigt. Die
URSPRUENGLICHE 0.5x-Skalierung war groessenmaessig also die ganze Zeit
richtig -- der eigentliche Bug war die Skalierungs-METHODE:
`pygame.transform.smoothscale()` ist bilinear und verwaescht harte
Pixel-Kanten von Pixel-Art beim Verkleinern sichtbar. Fix: `smoothscale()`
durch `pygame.transform.scale()` ersetzt (Nearest-Neighbor, kein
Weichzeichnen) in `Assets.pcc_surface_scaled()` UND `pcc_surface_half_height()`
(Titelbild-Quadranten, gleiches Prinzip), `ASSET_DISPLAY_SCALE` bleibt 0.5.
Verifiziert per eigener Bounding-Box-Nachmessung des neuen Renders: **33.0%
Breite / 13.6% Hoehe** -- nah an Stefans Referenz (34.5%/16.6%), klar besser
als der 62%-Fehlgriff der vorigen (native) Version. Frisch gerenderter
Vergleichs-Screenshot:
[FILE: /shared/uploads/aria_rauser_scale05_check.png]

**2) Factor5-Sound -- Multi-Song-Fund geklaert die Zuordnung:**
Ein einzelnes TFX-Modul kann laut `tfmx.h` (`v_songs[]`/`v_songs_count`)
MEHRERE Songs buendeln, ausgewaehlt per `start_song`-Index -- das erklaert
Stefans Vermutung "das Spiel hat noch wesentlich mehr Lieder". Debug-Rendering
aller Songs in allen 3 Modulen ergab:
  - `TITEL.TFX` -> 3 Songs (song0/1 lang >15s, song2 ~8.0s)
  - `TITEL2.TFX` -> 2 Songs (song0 = 2.04s, song1 = 2.40s)
  - `ONGAME2.TFX` -> 14(!) Songs (mehrere >15s-Tracks, vermutlich echte
    Level-/Gameplay-Musik -- song3/11/12 sehr kurz 0.16-1.36s, song2/13
    ~10.3s; noch nicht im Detail zugeordnet, relevant fuer spaetere
    Gameplay-Musik)
Die vorige Zuordnung (ONGAME2 komplett = "Factor5-Melodie") war schlicht
falsch -- vermutlich echte Level-Musik, nicht zum Studio-Logo passend (daher
Stefans "Level-Sound"-Eindruck). TITEL2-Song1 (2.40s) passt dagegen exakt zu
"nur ein bisschen laenger als der Rauser-Sound" (Rauser-Sting = TITEL2-Song0
= 2.04s, Differenz nur 0.36s). `tools/tfmx_audio.py` bekam dafuer einen
optionalen `song_index`-Parameter (durchgereicht an `render_tfmx` als
5. CLI-Arg), `run_once()` in `intro_sequence.py` spielt jetzt TITEL2-Song1
beim Factor5-Logo ab, Anzeigedauer ist fest auf `FACTOR5_HOLD_SECONDS = 5.0`
gesetzt (statt an eine -- falsche -- Songlaenge gekoppelt).
Verifiziert: `TITEL2.wav` (Song0) 2.04s, `TITEL2_song1.wav` (Song1) 2.40s,
beide mit gesunder Amplitude (kein Stille-Bug), unterschiedliche Dateigroesse
(89984 vs. 105856 Samples -- also wirklich unterschiedlicher Inhalt, keine
zufaellige Doublette).

**Ehrlicher Vorbehalt (wichtig):** Ich kann Audio nicht selbst abhoeren --
die Zuordnung TITEL2-Song1 = "Brausen" ist eine Indizien-Schlussfolgerung aus
der Songlaenge (passt sehr gut zu Stefans Beschreibung), NICHT durch
Reinhoeren verifiziert. Falls es beim echten Testen doch nicht wie ein
Brausen klingt: melden, dann probieren wir als naechste Kandidaten TITEL-Song2
(~8.0s) oder einen der kurzen ONGAME2-Songs (3/11/12).

Beide Fixes committet + gepusht. Naechster Schritt weiterhin offen: echtes
Gameplay (Tony auf `W1L0` bewegen).

## 23.07.2026 -- Hang-Bug gefixt + alle 19 TFMX-Songs fuer Stefan gerendert

**1) Hang-Bug (Stefans Meldung "nur noch ein schwarzes Bild, verschiebe ich
das Fenster wird der Desktop reingerendert, das Programm haengt"): real,
gefunden, gefixt, verifiziert.** Ursache: `tfmx_audio.render_wav()` rief den
TFMX-Renderer als blockierenden `subprocess.run()` auf -- bei einem frischen,
ungecachten Song (v.a. TITEL, ~109s Musik) dauert das reproduzierbar ~14s,
waehrend derer die pygame-Eventloop NICHT lief. Das OS stuft ein Fenster
ohne Event-Pumping als "nicht reagierend" ein -- exakt Stefans beschriebenes
Bild (schwarz eingefroren, Desktop blitzt beim Verschieben durch).
Fix: `preload_all_audio()` rendert jetzt alle 3 benoetigten Songs (Rauser-
Sting, Factor5-Sound, Titelmusik) EINMALIG vor der eigentlichen Sequenz, per
Hintergrund-Thread + responsivem "Lade..."-Bildschirm
(`render_audio_with_loading()`). Danach greifen alle `run_once()`-Durchlaeufe
nur noch auf fertige WAV-Pfade zu, nie mehr Live-Rendering im Hauptloop.
Verifiziert per Xvfb-Testlauf (`SDL_AUDIODRIVER=dummy`, `--once`): laeuft
jetzt exit-code-0 sauber durch, kein Haengen mehr.
Committet+gepusht (`73b887e`). Nebenbei `__pycache__/` ins `.gitignore`
aufgenommen (`8509cbc`).

**2) Alle 19 TFMX-Songs gerendert, als MP3-Zip an Stefan uebergeben (NICHT
ins Git-Repo, gleiche Begruendung wie bei raw/PCKELL.DAT -- ist Original-
Spielmaterial/Musik, nur als Download fuer den Privatgebrauch, siehe
`tfmx_audio.py`-Docstring und `.gitignore`):**
  - `TITEL.TFX`: song0 (=`TITEL.mp3`, 109s, aktuell die Titelmusik),
    song1 (109s, bisher ungenutzt), song2 (~8s, bisher ungenutzt)
  - `TITEL2.TFX`: song0 (=Rauser-Sting, 2.04s), song1 (=aktuell als
    Factor5-Sound eingebaut, 2.40s -- NICHT durch Reinhoeren verifiziert,
    nur aus der Songlaenge geschlossen, siehe Vorbehalt vom 22.07.)
  - `ONGAME2.TFX`: 14 Songs (song0-13), vermutlich echte Level-/Gameplay-
    Musik, noch keinem Screen/Level zugeordnet
  19 WAVs -> MP3 (ffmpeg, libmp3lame, konvertiert um die Downloadgroesse von
  ~300MB WAV auf ~59MB MP3 zu druecken) -> Zip, an Stefan uebergeben.
  Dateinamen im Zip = `<TFX-Name>[_song<N>].mp3` (song0/Default ohne Suffix).
  **Stefans Aufgabe:** reinhoeren und mir sagen, welche Datei WIRKLICH nach
  Factor5-Brausen klingt -- falls es NICHT `TITEL2_song1.mp3` ist, dann in
  `intro_sequence.py`/`run_once()` den `audio_paths.get(('TITEL2', 1))`-Call
  auf die richtige Datei/song_index umstellen (Kandidaten laut Laenge:
  `TITEL_song2.mp3` ~8s, oder ein kurzer ONGAME2-Song).

Naechster Schritt weiterhin offen: echtes Gameplay (Tony auf `W1L0` bewegen).
Sobald Stefan den richtigen Factor5-Song bestaetigt hat, kurzer Fix, dann
weiter zu Gameplay.

## Stand 23.07.2026, vormittags -- Factor5-Sound bestaetigt + Repo-Hygiene-Fund

**1) Factor5-Sound ist bestaetigt richtig.** Stefan hat sich durch die
Hoerproben gehoert und explizit `TITEL2_song1.wav` als den richtigen
Factor5-"Brausen"-Sound zurueckgemeldet -- das ist exakt die Datei, die seit
`af2ead1` schon eingebaut ist (`audio_paths.get(('TITEL2', 1))` in
`run_once()`). Kein Code-Aenderung noetig, nur der offene Vorbehalt von
22.07. ("nicht durch Reinhoeren verifiziert") ist jetzt erledigt.

**2) Repo-Hygiene-Bug gefunden + gefixt (kein Stefan-Report, selbst
entdeckt beim Nachschauen):** `tools/tfmx_player/render_tfmx.c` war in
`origin/master` NICHT auf dem aktuellen Stand -- das Song-Index-Feature
(noetig fuer genau obigen Factor5-Fix, TITEL2 Song 1 statt Song 0) existierte
nur lokal auf der VM (`/home/aria/kellogg_remake`, gebaut, aber nie
committet), nicht im Repo selbst. Ein frischer `git clone` + `build.sh`
haette also einen Binary OHNE Song-Auswahl erzeugt -- jeder
`render_wav(..., song_index=N)`-Call waere stillschweigend auf Song 0
zurueckgefallen (kein Crash, aber h��rbar falscher Sound). Genau die Art
1:1-Port-Bruch, die Stefan nicht will.

Ursache: parallel zur Session ist ein zweiter Git-Checkout unter `/tmp`
(tmpfs!) entstanden und dort unabhaengig weitergearbeitet worden, ohne vorher
zu fetchen -- Historie ist auseinandergelaufen (zwei verschiedene Commits fuer
denselben Hang-Fix). Nach Abgleich: `/home/aria/kellogg_remake` (die
tatsaechliche Arbeitskopie) war fuer ALLE Dateien ausser dieser einen C-Datei
schon 1:1 identisch mit `origin/master`. Fix: `render_tfmx.c` nachgezogen und
committet (`a077dce`), der veraltete `/tmp`-Checkout mit den ueberholten
Doppel-Commits verworfen (kein Datenverlust, deren Inhalt war ohnehin ein
aelterer Zwischenstand als das, was schon auf `origin/master` lag).

**Lehre / Infra-Aenderung:** der Arbeits-Git-Checkout fuer Commits/Pushes
liegt jetzt dauerhaft unter `/home/aria/tony-remake-git` (statt `/tmp`,
das ist RAM-tmpfs und wird bei jedem VM-Neustart geleert -- genau DAS hat
den Doppel-Checkout ueberhaupt erst ermoeglicht). Kuenftige Sessions: dort
weiterarbeiten, IMMER zuerst `git fetch && git status` gegen `origin/master`
pruefen bevor neue Commits drauf, um erneutes Auseinanderlaufen zu vermeiden.

**Naechster Schritt:** Der Factor5-Sound-Vorbehalt ist jetzt erledigt, die
Intro-Sequenz ist damit inhaltlich fertig (Rauser -> Factor5 -> Kellogg's-Logo
mit Vorhang+Scanlines -> Titelbild, alles mit korrektem Sound, freeze-frei).
Zwei Baustellen bleiben offen, beide von Stefan noch nicht explizit
beauftragt:
  1. Der von Stefan gemeldete schwarze-Screen/Fenster-Haenger nach dem
     Rauser-Intro sollte jetzt behoben sein (Hang-Fix oben) -- noch nicht von
     Stefan nochmal getestet/bestaetigt.
  2. Der naechste grosse inhaltliche Meilenstein bleibt unveraendert: echtes
     Gameplay, Tony auf Level `W1L0` steuerbar machen (Map/Sprites/Kollision
     sind alle schon fertig extrahiert, es fehlt nur noch die Spiellogik/der
     eigentliche Python-Spiel-Loop).
