Stefan hat zurecht bemaengelt dass KARTE.PCC/KELLOGGS.PCC verzerrt/verrutscht
aussehen (Smacks-Frosch an falscher Position, Notch-Fehlstelle im Logo-Rahmen).
Root Cause: Der Decoder hat RLE-Runs am Ende jeder Bildzeile hart abgeschnitten
und den Rest verworfen (klassisches PCX-Verhalten angenommen). Dieses Format
haelt sich aber NICHT an die PCX-Konvention 'ein Run ueberschreitet nie eine
Zeile' -- Runs laufen frei ueber Zeilengrenzen. Verifiziert per Pixel-fuer-Pixel-
Vergleich gegen echten DOSBox-Screenshot (world map): mit durchgehender
Dekodierung (kein Row-Reset) ist das Ergebnis jetzt pixel-identisch zur Referenz.
- tools/pcc_to_png.py: Row-Reset entfernt, decodiert jetzt width*height Pixel
am Stueck aus dem RLE-Strom
- png_out/: alle 77 PNGs mit dem Fix neu generiert
- tools/wip_bob_sprite_research/: Backup der laufenden (noch ungeloesten)
BOB-Sprite-Format-Exploration von aria-wohnung, damit nichts bei einem
VM-Neustart verloren geht
- NOTES.md: Root-Cause-Analyse dokumentiert, alte Fehldiagnose ('byte-identisch
zwischen Row-Reset und kontinuierlich') korrigiert, Rauser-Intro-Frage
beantwortet (3 Frames vorhanden, Text-Logo, keine Video-Datei)
62 lines
1.9 KiB
Python
62 lines
1.9 KiB
Python
import struct
|
|
|
|
def decode_scanline(data, width, max_rows=100000):
|
|
i = 0
|
|
n = len(data)
|
|
rows = 0
|
|
row_len = 0
|
|
while i < n:
|
|
if row_len >= width:
|
|
rows += 1
|
|
row_len = 0
|
|
if rows > max_rows:
|
|
return rows, i, False
|
|
continue
|
|
b = data[i]
|
|
if (b & 0xC0) == 0xC0:
|
|
count = b & 0x3F
|
|
i += 1
|
|
if i >= n:
|
|
return rows, i, False
|
|
i += 1
|
|
need = width - row_len
|
|
take = min(count, need)
|
|
row_len += take
|
|
else:
|
|
row_len += 1
|
|
i += 1
|
|
if row_len == 0:
|
|
return rows, i, True
|
|
else:
|
|
rows += 1 # partial
|
|
return rows, i, False
|
|
|
|
base = "/home/aria/kellogg_remake/extracted_pre/"
|
|
files = ["B.BOB","E.BOB","N.BOB","O.BOB","A.BOB","J.BOB","K.BOB","D.BOB"]
|
|
|
|
for fn in files:
|
|
data = open(base+fn, "rb").read()
|
|
n = len(data)
|
|
print(f"=== {fn} size={n} ===")
|
|
found_any = False
|
|
for headerlen in range(0, 17, 1):
|
|
if headerlen % 2 != 0:
|
|
continue
|
|
header = data[:headerlen]
|
|
body = data[headerlen:]
|
|
num_u16 = headerlen // 2
|
|
u16s = struct.unpack(f"<{num_u16}H", header) if headerlen else ()
|
|
# try every pair (w_idx, h_idx) among header u16 fields as (width,height) candidates
|
|
for wi in range(num_u16):
|
|
w = u16s[wi]
|
|
if w < 2 or w > 400:
|
|
continue
|
|
rows, consumed, ok = decode_scanline(body, w)
|
|
if ok:
|
|
# check if any header u16 equals rows (=height)
|
|
match_h = [hi for hi,val in enumerate(u16s) if val == rows]
|
|
print(f" headerlen={headerlen} width_idx={wi} width={w} -> rows={rows} clean=True header_u16={u16s} height_field_match_idx={match_h}")
|
|
found_any = True
|
|
if not found_any:
|
|
print(" (no clean header+width combo found in range)")
|