Initial commit: PCC-Decoder (mit Xmax/Ymax-Bugfix), Notes, DOSBox-Config
- tools/pcc_to_png.py: PCX-artiger Decoder fuer die 77 PCC-Vollbild-/Logo- Dateien, mit Fix fuer falsch gelesene Width/Height (muss aus Xmax-Xmin+1/ Ymax-Ymin+1 statt Offset 12-15 berechnet werden) - tools/split_pre.py: Extrahiert alle 181 Dateien aus PCKELL.PRE - png_out/: alle 77 konvertierten PNGs (Arbeitsergebnis, kein Original-Asset) - NOTES.md: vollstaendiger Analyse-Stand inkl. offener Punkte - dosbox/dosbox.conf: funktionierende Testkonfiguration (IRQ-Fix) Bewusst ausgeschlossen (.gitignore): raw/ und extracted_pre/, da Original- Spieldaten von Kellogg's Tony and Friends (1994), keine Weiterverbreitung.
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
"""PCKELL.PRE Splitter v7 -- FINAL (2026-07-22)
|
||||
|
||||
Format komplett verstanden:
|
||||
- .BOB-Eintraege (32x): kein Tag-Slot, Header direkt per Pfad-Praefix-Suche
|
||||
gefunden ("H:\\WIWA\\ENGINE\\GAMEDATA\\<name>"), Header = Fundstelle-4.
|
||||
- Alle Nicht-BOB-Eintraege (PCC/MAP/ARE/ICO/SAM/TFX, 146x aus APPEND.LST,
|
||||
PLUS 3 weitere System-Dateien ohne APPEND.LST-Eintrag): Tag-Slot
|
||||
[1-Byte-Namenslaenge][Dateiname][Fuellmuell bis 256 Byte], danach
|
||||
[4-Byte-LE-Laenge][Rohdaten].
|
||||
|
||||
WICHTIGER FUND: APPEND.LST listet nur die 178 Spiel-Assets. PCKELL.PRE
|
||||
enthaelt zusaetzlich, im GLEICHEN Tag-Format, direkt im Anschluss an den
|
||||
letzten APPEND.LST-Eintrag (TITEL2.SAM), noch 3 System-/Runtime-Dateien:
|
||||
DPMI16BI.OVL, RTM.EXE, START.EXE (die DOS-Extender-Runtime -- macht Sinn,
|
||||
das ist ein self-contained Redistributable). Der letzte dieser 3 Eintraege
|
||||
endet EXAKT auf EOF (0x238966) -- damit ist das Format zu 100% verifiziert,
|
||||
keine Bytes bleiben unerklaert.
|
||||
|
||||
Ergebnis: 178 (APPEND.LST) + 3 (System) = 181 Dateien total.
|
||||
"""
|
||||
import struct
|
||||
import os
|
||||
|
||||
RAW = "/home/aria/kellogg_remake/raw/PCKELL.PRE"
|
||||
APPEND_LST = "/home/aria/kellogg_remake/raw/APPEND.LST"
|
||||
OUT_DIR = "/home/aria/kellogg_remake/extracted_pre"
|
||||
|
||||
with open(RAW, "rb") as f:
|
||||
data = f.read()
|
||||
with open(APPEND_LST) as f:
|
||||
names = [l.strip() for l in f if l.strip()]
|
||||
|
||||
# die 3 unlisted System-Dateien, empirisch gefunden direkt im Anschluss
|
||||
EXTRA_NAMES = ["DPMI16BI.OVL", "RTM.EXE", "START.EXE"]
|
||||
|
||||
PATH_PREFIX = b"H:\\WIWA\\ENGINE\\GAMEDATA\\"
|
||||
N = len(data)
|
||||
|
||||
pos = 0x100
|
||||
entries = []
|
||||
all_names = names + EXTRA_NAMES
|
||||
|
||||
for i, name in enumerate(all_names):
|
||||
is_bob = name.upper().endswith(".BOB")
|
||||
if is_bob:
|
||||
marker = PATH_PREFIX + name.encode("ascii")
|
||||
p = data.find(marker, pos)
|
||||
if p == -1:
|
||||
print(f"FEHLER: Pfad fuer #{i} ({name}) nicht gefunden ab {pos:#x}")
|
||||
break
|
||||
header_start = p - 4
|
||||
length = struct.unpack_from("<I", data, header_start)[0]
|
||||
path_start = p
|
||||
nul = data.index(b"\x00", path_start)
|
||||
path = data[path_start:nul].decode("cp437", errors="replace")
|
||||
data_start = nul + 1
|
||||
else:
|
||||
tb = name.encode("ascii")
|
||||
marker = bytes([len(tb)]) + tb
|
||||
tag_pos = data.find(marker, pos)
|
||||
if tag_pos == -1:
|
||||
tag_pos = data.find(marker, max(0, pos - 300))
|
||||
if tag_pos == -1:
|
||||
print(f"FEHLER: Tag fuer #{i} ({name}) nicht gefunden ab {pos:#x}")
|
||||
break
|
||||
header_start = tag_pos + 256
|
||||
length = struct.unpack_from("<I", data, header_start)[0]
|
||||
path = None
|
||||
data_start = header_start + 4
|
||||
|
||||
data_end = data_start + length
|
||||
if data_end > N:
|
||||
print(f"FEHLER: Datenende {data_end:#x} > Dateigroesse bei #{i} ({name}) length={length}")
|
||||
break
|
||||
entries.append((name, header_start, length, data_start, data_end, path))
|
||||
pos = data_end
|
||||
|
||||
print(f"{len(entries)} von {len(all_names)} Eintraegen gefunden.")
|
||||
if entries:
|
||||
print(f"Letztes Datenende: {entries[-1][4]:#x} von {N:#x} (Match: {entries[-1][4] == N})")
|
||||
|
||||
mism = sum(1 for name, hs, length, ds, de, path in entries
|
||||
if path is not None and path.split("\\")[-1].upper() != name.upper())
|
||||
print(f"BOB Pfad-Mismatches: {mism}")
|
||||
|
||||
if len(entries) == len(all_names) and entries[-1][4] == N and mism == 0:
|
||||
os.makedirs(OUT_DIR, exist_ok=True)
|
||||
for name, hs, length, ds, de, path in entries:
|
||||
with open(os.path.join(OUT_DIR, name), "wb") as out:
|
||||
out.write(data[ds:de])
|
||||
print(f">>> ERFOLG: {len(entries)} Dateien geschrieben nach {OUT_DIR}")
|
||||
import collections
|
||||
ext_count = collections.Counter(n.split(".")[-1].upper() for n, *_ in entries)
|
||||
print("Typen:", dict(ext_count))
|
||||
else:
|
||||
print(">>> Unvollstaendig.")
|
||||
for e in entries[-5:]:
|
||||
print(" ", e[0], hex(e[1]), e[2])
|
||||
Reference in New Issue
Block a user