From 85404c91f415d66434703c4caaf2b446a5a31dc1 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Fri, 24 Jul 2026 00:29:03 +0200 Subject: [PATCH] Projektstruktur: game/-Package angelegt, README ergaenzt Laufzeit-Code vom Dev-Werkzeug getrennt: - game/: das eigentliche Spiel, startbar per `python -m game [--once]`. - formats.py (war tools/kellogg_formats.py) -- Asset-Parser, Kern-Bibliothek - audio.py (war tools/tfmx_audio.py) -- TFMX-Musik-Wrapper - intro.py (war tools/intro_sequence.py) -- Boot-/Intro-Sequenz - tfmx_player/(war tools/tfmx_player/) -- MIT-C-Renderer - __init__.py / __main__.py fuer `python -m game` - tools/: nur noch Dev-Werkzeuge (dat_extract, pcc_to_png, render_assets), importieren jetzt aus game.formats. - README.md: Ueberblick, Start, Struktur, Voraussetzungen, Rechtliches. - .gitignore: tfmx_player-Binary-Pfad auf game/ nachgezogen. Intro laeuft unveraendert (headless-Smoke-Test `python -m game --once` exit 0). Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 4 +- README.md | 97 +++++++++++++++++++++ game/__init__.py | 12 +++ game/__main__.py | 10 +++ tools/tfmx_audio.py => game/audio.py | 8 +- tools/kellogg_formats.py => game/formats.py | 0 tools/intro_sequence.py => game/intro.py | 7 +- {tools => game}/tfmx_player/LICENSE | 0 {tools => game}/tfmx_player/build.sh | 0 {tools => game}/tfmx_player/paula.h | 0 {tools => game}/tfmx_player/player_api.h | 0 {tools => game}/tfmx_player/render_tfmx.c | 0 {tools => game}/tfmx_player/tfmx.h | 0 tools/pcc_to_png.py | 6 +- tools/render_assets.py | 4 +- 15 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 README.md create mode 100644 game/__init__.py create mode 100644 game/__main__.py rename tools/tfmx_audio.py => game/audio.py (96%) rename tools/kellogg_formats.py => game/formats.py (100%) rename tools/intro_sequence.py => game/intro.py (99%) rename {tools => game}/tfmx_player/LICENSE (100%) rename {tools => game}/tfmx_player/build.sh (100%) rename {tools => game}/tfmx_player/paula.h (100%) rename {tools => game}/tfmx_player/player_api.h (100%) rename {tools => game}/tfmx_player/render_tfmx.c (100%) rename {tools => game}/tfmx_player/tfmx.h (100%) diff --git a/.gitignore b/.gitignore index 3e65484..465e355 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,8 @@ extracted_dat/ # Gerenderte Original-Musik (TFMX->WAV) -- ist Original-Spielmaterial, # wird lokal gecacht, nicht committed (wie raw/extracted_dat) audio_cache/ -tools/tfmx_player/render_tfmx -tools/tfmx_player/*.o +game/tfmx_player/render_tfmx +game/tfmx_player/*.o # Python-Bytecode-Cache __pycache__/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..534a74a --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# Tony & Friends in Kellogg's Land — Remake + +Ein plattformunabhängiges Remake (Python / pygame) des DOS-Jump'n'Runs +**„Kellogg's Tony and Friends in Kellogg's Land"** (1994). Ziel ist eine +lauffähige Neuimplementierung auf modernem x64, ohne DOS/DOSBox/QEMU — das +Original crasht in unserer FreeDOS-VM (RTM.EXE-Engine vs. QEMU-VGA-Emulation). + +Das Remake liest die Grafiken, Sprites, Level und Musik **direkt aus den +Original-Spieldateien** (`raw/PCKELL.DAT`) und dekodiert sie zur Laufzeit — es +werden keine vorab entpackten Assets benötigt. + +## Status + +- ✅ **Alle Asset-Formate geknackt & verifiziert:** PCC (Bilder), BOB (Sprites), + ICO (Tilesets), MAP (Level), TFMX (Musik). +- ✅ **Boot-/Intro-Sequenz** spielbar (Rauser → Factor5 → Kellogg's-Logo → + Titelbild, mit Fades, Vorhang-Wipe und Musik). +- ⏳ **Nächster Meilenstein:** echter Gameplay-Loop — Tony auf Level `W1L0` + bewegen (Physik, Kollision, Level-Scrolling). Existiert noch nicht. + +Die vollständige, chronologische Reverse-Engineering-Historie mit allen +Format-Details steht in [NOTES.md](NOTES.md). + +## Rechtliches + +„Kellogg's Tony and Friends in Kellogg's Land" war ein kostenloses Werbespiel +und ist innerhalb Deutschlands frei kopierbar. Die im Repo enthaltenen +gerenderten Musik-WAVs (`audio/`) wurden lediglich aus den Original-Daten +extrahiert. Die eigentlichen Original-Spieldateien (`raw/`) sind **nicht** im +Repo (siehe `.gitignore`) — sie werden lokal benötigt, um das Remake zu starten. + +Der TFMX-Musik-Renderer unter [game/tfmx_player/](game/tfmx_player/) ist +Fremdcode (MIT-Lizenz, © Peter Fors) und liegt mit seiner LICENSE bei. Die +Format-Kenntnisse zu PCC/BOB/MAP/ICO stammen als Referenz aus +[movAX13h/tony-and-friends-in-kelloggs-land](https://github.com/movAX13h/tony-and-friends-in-kelloggs-land); +der Python-Code hier ist eine eigenständige Neuimplementierung. + +## Voraussetzungen + +- Python 3, `pygame` +- `Pillow` (PCX-Dekodierung), `numpy` (optional, für den Scanline-Effekt im Intro) +- `gcc` (einmalig, um den TFMX-Renderer zu bauen — passiert automatisch beim + ersten Start) +- Die Original-Datei `PCKELL.DAT` unter `raw/` (nicht im Repo) + +``` +sudo apt install python3-pygame python3-pil python3-numpy gcc +``` + +## Starten + +``` +python3 -m game # Intro in Endlosschleife (wie Original-Titelbildschirm) +python3 -m game --once # ein Durchlauf, dann beenden +``` + +Abbruch jederzeit per `ESC`. Ohne Soundkarte (z.B. headless) läuft das Spiel +stumm weiter statt abzustürzen. + +Liegt `PCKELL.DAT` woanders, kann der Pfad per Umgebungsvariable gesetzt werden: + +``` +KELLOGG_DAT=/pfad/zu/PCKELL.DAT python3 -m game +``` + +## Projektstruktur + +``` +game/ Das eigentliche Spiel (startbar per `python -m game`) + formats.py Parser/Decoder für alle Asset-Formate aus PCKELL.DAT + audio.py Wrapper um den TFMX-Musik-Renderer (rendert & cached WAVs) + intro.py Boot-/Intro-Sequenz + tfmx_player/ MIT-lizenzierter C-Renderer für TFMX-Musik +tools/ Reine Dev-Werkzeuge (Assets auspacken / als PNG ansehen) + dat_extract.py PCKELL.DAT → alle 178 Einzeldateien (nach extracted_dat/) + pcc_to_png.py Eine PCC-Bilddatei → PNG + render_assets.py Batch-Render aller BOB/ICO/MAP → render_out/ +raw/ Original-Spieldateien (PCKELL.DAT etc.) — NICHT im Repo +audio/ Aus PCKELL.DAT gerenderte Musik-WAVs (Archiv) +png_out/ Vorab gerenderte PCC-Bilder (Dev-Ausgabe) +render_out/ Vorab gerenderte BOB/ICO/MAP (Dev-Ausgabe) +dosbox/ DOSBox-Konfiguration zum Laufenlassen des Originals (Referenz) +NOTES.md Vollständiges Reverse-Engineering-Log +``` + +`game/formats.py` ist das gemeinsame Fundament: sowohl das Spiel als auch die +Dev-Werkzeuge in `tools/` importieren daraus. Zur Laufzeit braucht das Spiel +**nur** `raw/PCKELL.DAT` — die Ausgaben unter `png_out/`, `render_out/` und +`audio/` sind Verifikations-/Archiv-Artefakte, keine Laufzeit-Abhängigkeit. + +## Original als Referenz laufen lassen (DOSBox) + +Unter [dosbox/](dosbox/) liegt eine funktionierende Konfiguration, um das +Original zum visuellen Abgleich zu starten. Wichtig: In `KELLOGG.INI` **IRQ=5** +statt 7 setzen (sonst hängt sich das Spiel nach dem Factor-5-Logo auf) und die +`[sblaster]`-IRQ in der `dosbox.conf` passend dazu — Details in +[NOTES.md](NOTES.md). diff --git a/game/__init__.py b/game/__init__.py new file mode 100644 index 0000000..e5e862f --- /dev/null +++ b/game/__init__.py @@ -0,0 +1,12 @@ +"""Tony & Friends in Kellogg's Land -- plattformunabhaengiges Remake. + +Laufzeit-Package des Spiels. Enthaelt: + formats.py -- Parser/Decoder fuer die Original-Assets aus raw/PCKELL.DAT + (PCC-Bilder, BOB-Sprites, ICO-Tilesets, MAP-Level, Paletten). + audio.py -- Wrapper um den TFMX-Musik-Renderer (tfmx_player/). + intro.py -- die Boot-/Intro-Sequenz (Rauser -> Factor5 -> Kellogg's -> + Titelbild), erster spielbarer Meilenstein. + tfmx_player/-- MIT-lizenzierter C-Renderer fuer die TFMX-Musik. + +Start: python -m game [--once] +""" diff --git a/game/__main__.py b/game/__main__.py new file mode 100644 index 0000000..51bdf6b --- /dev/null +++ b/game/__main__.py @@ -0,0 +1,10 @@ +"""Startpunkt des Spiels: `python -m game [--once]`. + +Spielt aktuell die Intro-Sequenz. Hier kommt spaeter die Ablaufsteuerung +(Intro -> Hauptmenue -> Weltkarte -> Gameplay) rein; bis dahin delegiert es +direkt an die Intro-Szene. +""" +from .intro import main + +if __name__ == '__main__': + main() diff --git a/tools/tfmx_audio.py b/game/audio.py similarity index 96% rename from tools/tfmx_audio.py rename to game/audio.py index d8da61c..8d05364 100644 --- a/tools/tfmx_audio.py +++ b/game/audio.py @@ -1,6 +1,6 @@ """ Duenner Python-Wrapper um das MIT-lizenzierte `tfmx_player/render_tfmx` -(siehe tools/tfmx_player/LICENSE, Copyright Peter Fors) -- rendert ein +(siehe game/tfmx_player/LICENSE, Copyright Peter Fors) -- rendert ein TFMX-Musikstueck (.TFX + .SAM aus PCKELL.DAT) einmalig zu einer WAV-Datei und cached das Ergebnis, damit die Intro-Sequenz die Musik per pygame.mixer abspielen kann. @@ -99,9 +99,9 @@ def render_wav(container, name, seconds=180, force=False, song_index=None): if __name__ == '__main__': - # Kleiner CLI-Test: python3 tfmx_audio.py [seconds] [song_index] - sys.path.insert(0, _HERE) - from kellogg_formats import DATContainer + # Kleiner CLI-Test: python3 -m game.audio [seconds] [song_index] + sys.path.insert(0, _PROJECT_ROOT) + from game.formats import DATContainer dat_path = sys.argv[1] name = sys.argv[2] seconds = int(sys.argv[3]) if len(sys.argv) > 3 else 20 diff --git a/tools/kellogg_formats.py b/game/formats.py similarity index 100% rename from tools/kellogg_formats.py rename to game/formats.py diff --git a/tools/intro_sequence.py b/game/intro.py similarity index 99% rename from tools/intro_sequence.py rename to game/intro.py index a5ccd91..3d52839 100644 --- a/tools/intro_sequence.py +++ b/game/intro.py @@ -105,7 +105,7 @@ Ladebildschirm (render_audio_with_loading() -- Rendering laeuft in einem Thread, Hauptthread pumpt weiter Events). run_once() greift danach nur noch auf fertige WAV-Pfade zu, nie mehr Live-Rendering im Hauptloop. -Aufruf: python3 intro_sequence.py [--once] (--once: nach einem Durchlauf beenden, +Aufruf: python3 -m game [--once] (--once: nach einem Durchlauf beenden, sonst loopt die Sequenz weiter wie im Original-Titelbildschirm) """ import sys @@ -114,9 +114,8 @@ import time import threading from collections import deque -sys.path.insert(0, os.path.dirname(__file__)) -from kellogg_formats import DATContainer, load_pcc -import tfmx_audio +from .formats import DATContainer, load_pcc +from . import audio as tfmx_audio import pygame diff --git a/tools/tfmx_player/LICENSE b/game/tfmx_player/LICENSE similarity index 100% rename from tools/tfmx_player/LICENSE rename to game/tfmx_player/LICENSE diff --git a/tools/tfmx_player/build.sh b/game/tfmx_player/build.sh similarity index 100% rename from tools/tfmx_player/build.sh rename to game/tfmx_player/build.sh diff --git a/tools/tfmx_player/paula.h b/game/tfmx_player/paula.h similarity index 100% rename from tools/tfmx_player/paula.h rename to game/tfmx_player/paula.h diff --git a/tools/tfmx_player/player_api.h b/game/tfmx_player/player_api.h similarity index 100% rename from tools/tfmx_player/player_api.h rename to game/tfmx_player/player_api.h diff --git a/tools/tfmx_player/render_tfmx.c b/game/tfmx_player/render_tfmx.c similarity index 100% rename from tools/tfmx_player/render_tfmx.c rename to game/tfmx_player/render_tfmx.c diff --git a/tools/tfmx_player/tfmx.h b/game/tfmx_player/tfmx.h similarity index 100% rename from tools/tfmx_player/tfmx.h rename to game/tfmx_player/tfmx.h diff --git a/tools/pcc_to_png.py b/tools/pcc_to_png.py index 0d05575..2745316 100644 --- a/tools/pcc_to_png.py +++ b/tools/pcc_to_png.py @@ -14,13 +14,13 @@ Hand-Decoder ersetzt -- die bereits committeten PNGs in png_out/ waren davon zum Glueck nicht betroffen (die sind noch von der guten ImageMagick-Version), aber ein Rerun dieses Skripts haette wieder kaputte Bilder erzeugt. Fix jetzt: Pillow statt Hand-RLE oder externem `convert`-Subprocess -- kein Format- -Parsing mehr in eigenem Code, nutzt tools/kellogg_formats.load_pcc(). +Parsing mehr in eigenem Code, nutzt game.formats.load_pcc(). Nutzung: python3 pcc_to_png.py """ import sys, os -sys.path.insert(0, os.path.dirname(__file__)) -from kellogg_formats import load_pcc +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from game.formats import load_pcc def main(): diff --git a/tools/render_assets.py b/tools/render_assets.py index 0b5718e..fd56513 100644 --- a/tools/render_assets.py +++ b/tools/render_assets.py @@ -1,6 +1,6 @@ import sys, os -sys.path.insert(0, os.path.dirname(__file__)) -from kellogg_formats import DATContainer, get_bob_palette, get_world_palette, parse_bob, parse_ico, parse_map +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from game.formats import DATContainer, get_bob_palette, get_world_palette, parse_bob, parse_ico, parse_map from PIL import Image PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))