#!/usr/bin/env python3 # genC64Data.py -- emit the C64 game data the JoeyLib Space Taxi port # needs at runtime as C source: the custom charset (rendering AND the # exact sprite-to-background collision bits), the standard sprite # bitmaps (cab, passenger, flame, warp, death, intro star, leaving # cels), the demo-mode RNG table ($446A), the SID SFX programs (the # 9-byte blocks fed to $42E9), the engine-flame pointer table ($6DB0) # and the level-intro star velocity tables ($450C/$4513). # # Everything comes straight out of stuff/spacetaxi/raw.bin (a VICE # "bank ram" dump, 2-byte load-address header) so the port has one # source of truth: the original bytes. Output: # examples/spacetaxi/stC64Data.c / stC64Data.h (committed, do not edit) import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) RAW = os.path.join(HERE, "..", "..", "..", "stuff", "spacetaxi", "raw.bin") OUT_C = os.path.join(HERE, "..", "stC64Data.c") OUT_H = os.path.join(HERE, "..", "stC64Data.h") SPRITE_PTR_FIRST = 0xC0 SPRITE_PTR_LAST = 0xEC # $E9-$EC = passengers-leaving cels SPRITE_BYTES = 63 # (enum name, address of the 9-byte program, comment) SFX = [ ("ST_SFX_GEAR", 0x6410, "landing gear toggle ($63DD)"), ("ST_SFX_CASH", 0x6DBB, "refuel ka-ching ($6E5B)"), ("ST_SFX_FUEL_LOW", 0x6DC4, "fuel below 3 cells ($6EBD)"), ("ST_SFX_FUEL_FULL", 0x6DCD, "tank full ($6E9E)"), ("ST_SFX_LAND_HARD", 0x6DD6, "hard landing ($6520)"), ("ST_SFX_IMPACT", 0x6DDF, "wreck hits the floor ($6AE1)"), ("ST_SFX_LAND_SOFT", 0x6DE8, "soft landing ($650F)"), ("ST_SFX_CRASH", 0x6DF1, "crash start ($6A56)"), ] def load_raw(): with open(RAW, "rb") as fp: raw = fp.read() if len(raw) == 0x10002: raw = raw[2:] if len(raw) != 0x10000: sys.exit(f"{RAW}: unexpected size {len(raw)}") return raw def c_bytes(data, per_line=16, indent=" "): lines = [] for i in range(0, len(data), per_line): chunk = data[i:i + per_line] lines.append(indent + ", ".join(f"0x{b:02X}" for b in chunk) + ",") return "\n".join(lines) def main(): raw = load_raw() sprite_count = SPRITE_PTR_LAST - SPRITE_PTR_FIRST + 1 h = [] h.append("// Generated by assets/genC64Data.py from stuff/spacetaxi/raw.bin.") h.append("// Do not hand-edit; re-run the generator.") h.append("") h.append("#ifndef ST_C64_DATA_H") h.append("#define ST_C64_DATA_H") h.append("") h.append("#include ") h.append("") h.append("// The game's custom character set ($2800-$2FFF): 256 glyphs x 8 rows,") h.append("// bit 7 = leftmost pixel. Used to paint the screen AND for the") h.append("// sprite-to-background collision test (a set bit is background data).") h.append("#define ST_CHARSET_CHARS 256u") h.append("extern const uint8_t kStCharset[ST_CHARSET_CHARS][8];") h.append("") h.append("// Standard sprite bitmaps (VIC bank 0, block ptr * 64). 21 rows x 3") h.append("// bytes. Index = ptr - ST_SPRITE_PTR_FIRST.") h.append(f"#define ST_SPRITE_PTR_FIRST 0x{SPRITE_PTR_FIRST:02X}u") h.append(f"#define ST_SPRITE_PTR_LAST 0x{SPRITE_PTR_LAST:02X}u") h.append(f"#define ST_SPRITE_COUNT {sprite_count}u") h.append(f"#define ST_SPRITE_BYTES {SPRITE_BYTES}u") h.append("extern const uint8_t kStSpriteBitmaps[ST_SPRITE_COUNT][ST_SPRITE_BYTES];") h.append("") h.append("// Demo-mode RNG lookup ($446A): rnd = table[t1] + t2 (see stSim.c).") h.append("#define ST_RNG_TABLE_SIZE 64u") h.append("extern const uint8_t kStRngTable[ST_RNG_TABLE_SIZE];") h.append("") h.append("// SID SFX programs, 9 bytes each, in $42E9 order: freq lo, freq hi,") h.append("// pulse lo, pulse hi, control, AD, SR, release ticks, voice index.") h.append("typedef enum {") for name, addr, comment in SFX: h.append(f" {name}, // ${addr:04X} {comment}") h.append(" ST_SFX_COUNT") h.append("} StSfxE;") h.append("#define ST_SFX_PROGRAM_BYTES 9u") h.append("extern const uint8_t kStSfxPrograms[ST_SFX_COUNT][ST_SFX_PROGRAM_BYTES];") h.append("") h.append("// Engine-flame cel pointer by direction mask ($6DB0, 1=UP 2=DOWN 4=LEFT") h.append("// 8=RIGHT); 0 = no cel for that combination.") h.append("extern const uint8_t kStFlameCelByDirMask[16];") h.append("") h.append("// Level-intro star velocities per sprite 0..6 ($450C / $4513).") h.append("extern const int8_t kStIntroStarDx[7];") h.append("extern const int8_t kStIntroStarDy[7];") h.append("") h.append("#endif") c = [] c.append("// Generated by assets/genC64Data.py from stuff/spacetaxi/raw.bin.") c.append("// Do not hand-edit; re-run the generator.") c.append("") c.append('#include "stC64Data.h"') c.append("") c.append("const uint8_t kStCharset[ST_CHARSET_CHARS][8] = {") for ch in range(256): rows = raw[0x2800 + ch * 8:0x2800 + ch * 8 + 8] c.append(" { " + ", ".join(f"0x{b:02X}" for b in rows) + " }, // $" + f"{ch:02X}") c.append("};") c.append("") c.append("const uint8_t kStSpriteBitmaps[ST_SPRITE_COUNT][ST_SPRITE_BYTES] = {") for ptr in range(SPRITE_PTR_FIRST, SPRITE_PTR_LAST + 1): base = ptr * 64 c.append(f" {{ // ptr ${ptr:02X}") for row in range(21): b = raw[base + row * 3:base + row * 3 + 3] c.append(" " + ", ".join(f"0x{x:02X}" for x in b) + ",") c.append(" },") c.append("};") c.append("") c.append("const uint8_t kStRngTable[ST_RNG_TABLE_SIZE] = {") c.append(c_bytes(raw[0x446A:0x446A + 64])) c.append("};") c.append("") c.append("const uint8_t kStSfxPrograms[ST_SFX_COUNT][ST_SFX_PROGRAM_BYTES] = {") for name, addr, comment in SFX: b = raw[addr:addr + 9] c.append(" { " + ", ".join(f"0x{x:02X}" for x in b) + " }, // " + name) c.append("};") c.append("") flame = list(raw[0x6DB0:0x6DB0 + 11]) + [0] * 5 c.append("const uint8_t kStFlameCelByDirMask[16] = {") c.append(c_bytes(flame)) c.append("};") c.append("") dx = [b - 256 if b >= 128 else b for b in raw[0x450C:0x450C + 7]] dy = [b - 256 if b >= 128 else b for b in raw[0x4513:0x4513 + 7]] c.append("const int8_t kStIntroStarDx[7] = { " + ", ".join(str(v) for v in dx) + " };") c.append("const int8_t kStIntroStarDy[7] = { " + ", ".join(str(v) for v in dy) + " };") c.append("") with open(OUT_H, "w", encoding="ascii") as fp: fp.write("\n".join(h) + "\n") with open(OUT_C, "w", encoding="ascii") as fp: fp.write("\n".join(c) + "\n") print(f"wrote {OUT_H} and {OUT_C}") if __name__ == "__main__": main()