124 lines
4.5 KiB
C
124 lines
4.5 KiB
C
// staxibake -- generate the Space Taxi cel bank (.spr) for offline
|
|
// sprite pre-compilation. Runs on the host: it links the game's shared
|
|
// cel table (stCels.c) and the C64 sprite bitmaps (stC64Data.c) and
|
|
// writes the cross-platform JSP1 sprite bank in kStCels order. The
|
|
// per-target spritebake then compiles it to a .spc the game loads at
|
|
// boot with jlSpriteBankLoadPrecompiled, so no cel is JIT-compiled on
|
|
// the 65816 (which costs ~0.4 s each).
|
|
//
|
|
// Usage: staxibake OUTPUT.spr the cab/passenger/exhaust bank
|
|
// staxibake --level LEVEL.dat OUTPUT.spr one level's hook-sprite cels
|
|
//
|
|
// The per-level bank holds the cels stLevelCelList names for that level
|
|
// (its own VIC bitmaps in the colours its hook program uses), in list
|
|
// order -- the game rebuilds the same list at level entry to key them.
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
#include "stCels.h"
|
|
#include "stC64Data.h"
|
|
|
|
// JSP1 header (mirrors tools/spritebake + jlSpriteBankLoad): magic,
|
|
// target byte (0 = cross-platform chunky), has-palette, w/h tiles,
|
|
// cell count LE16, then a 32-byte palette block, all in 44 bytes.
|
|
#define SPR_HEADER_BYTES 44u
|
|
#define SPR_OFF_TARGET 4u
|
|
#define SPR_OFF_HAS_PAL 5u
|
|
#define SPR_OFF_WIDTH 6u
|
|
#define SPR_OFF_HEIGHT 7u
|
|
#define SPR_OFF_CELLS 8u
|
|
|
|
static const uint8_t *levelBitmap(const StLevelT *level, uint8_t ptr);
|
|
static bool writeHeader(FILE *out, uint16_t count);
|
|
|
|
|
|
// A level's own sprite block, or NULL when the level does not ship it.
|
|
static const uint8_t *levelBitmap(const StLevelT *level, uint8_t ptr) {
|
|
uint8_t k;
|
|
|
|
for (k = 0u; k < level->spriteCount; k++) {
|
|
if (level->sprites[k].ptr == ptr) {
|
|
return level->sprites[k].bitmap;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static bool writeHeader(FILE *out, uint16_t count) {
|
|
uint8_t header[SPR_HEADER_BYTES];
|
|
|
|
memset(header, 0, sizeof(header));
|
|
memcpy(header, "JSP1", 4);
|
|
header[SPR_OFF_TARGET] = 0u; // cross-platform chunky
|
|
header[SPR_OFF_HAS_PAL] = 0u; // the game owns its palette
|
|
header[SPR_OFF_WIDTH] = (uint8_t)ST_CEL_TILES;
|
|
header[SPR_OFF_HEIGHT] = (uint8_t)ST_CEL_TILES;
|
|
header[SPR_OFF_CELLS] = (uint8_t)(count & 0xFFu);
|
|
header[SPR_OFF_CELLS + 1u] = (uint8_t)(count >> 8);
|
|
return fwrite(header, 1, SPR_HEADER_BYTES, out) == SPR_HEADER_BYTES;
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv) {
|
|
static StLevelT level;
|
|
static StCelDefT levelCels[ST_LEVEL_CELS_MAX];
|
|
const StCelDefT *cels = kStCels;
|
|
uint16_t count = kStCelCount;
|
|
const char *outPath;
|
|
FILE *out;
|
|
uint16_t i;
|
|
|
|
if (argc == 4 && strcmp(argv[1], "--level") == 0) {
|
|
FILE *fp = fopen(argv[2], "rb");
|
|
if (fp == NULL || !stLevelParse(&level, fp)) {
|
|
fprintf(stderr, "staxibake: cannot load level %s\n", argv[2]);
|
|
return 1;
|
|
}
|
|
fclose(fp);
|
|
cels = levelCels;
|
|
count = stLevelCelList(&level, levelCels, ST_LEVEL_CELS_MAX);
|
|
outPath = argv[3];
|
|
} else if (argc == 2) {
|
|
outPath = argv[1];
|
|
} else {
|
|
fprintf(stderr, "usage: staxibake OUTPUT.spr\n staxibake --level LEVEL.dat OUTPUT.spr\n");
|
|
return 2;
|
|
}
|
|
out = fopen(outPath, "wb");
|
|
if (out == NULL) {
|
|
fprintf(stderr, "staxibake: cannot write %s\n", outPath);
|
|
return 1;
|
|
}
|
|
if (!writeHeader(out, count)) {
|
|
fprintf(stderr, "staxibake: header write failed\n");
|
|
fclose(out);
|
|
return 1;
|
|
}
|
|
for (i = 0u; i < count; i++) {
|
|
uint8_t blob[ST_CEL_BYTES];
|
|
const uint8_t *bm;
|
|
if (cels == levelCels) {
|
|
bm = levelBitmap(&level, cels[i].ptr);
|
|
if (bm == NULL) {
|
|
fprintf(stderr, "staxibake: level %s has no sprite block $%02X\n", argv[2], (unsigned)cels[i].ptr);
|
|
fclose(out);
|
|
return 1;
|
|
}
|
|
} else {
|
|
bm = stC64SpriteBitmap((uint8_t)(cels[i].ptr - ST_SPRITE_PTR_FIRST));
|
|
}
|
|
stCelBlob(bm, cels[i].multi, cels[i].color, cels[i].mc0, cels[i].mc1, blob);
|
|
if (fwrite(blob, 1, ST_CEL_BYTES, out) != ST_CEL_BYTES) {
|
|
fprintf(stderr, "staxibake: cel %u write failed\n", (unsigned)i);
|
|
fclose(out);
|
|
return 1;
|
|
}
|
|
}
|
|
fclose(out);
|
|
fprintf(stderr, "staxibake: wrote %u cels (%ux%u tiles) -> %s\n", (unsigned)count, (unsigned)ST_CEL_TILES, (unsigned)ST_CEL_TILES, outPath);
|
|
return 0;
|
|
}
|