144 lines
3.8 KiB
C
144 lines
3.8 KiB
C
// Space Taxi -- level loader.
|
|
//
|
|
// Reads a level .dat file produced by `tools/spacetaxi/mkLevel.py`
|
|
// (see assets/levels/format.md for the byte layout).
|
|
//
|
|
// A level file is small (~2-3 KB raw, plus a name + per-pad config),
|
|
// loaded once per scene change. Read fully into RAM; tilemap + colormap
|
|
// stay inside the StLevelT struct for the life of the game state.
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "spacetaxi.h"
|
|
|
|
JOEYLIB_SEGMENT("STAXI")
|
|
|
|
// STL2 adds the per-level physics templates (xAccel/yAccel/xGrav/yGrav)
|
|
// and the full VIC color block ($7D00-$7D08). Drops the patience byte
|
|
// from each fare entry (Space Taxi proper has no patience timeout).
|
|
#define ST_LEVEL_MAGIC0 'S'
|
|
#define ST_LEVEL_MAGIC1 'T'
|
|
#define ST_LEVEL_MAGIC2 'L'
|
|
#define ST_LEVEL_MAGIC3 '2'
|
|
|
|
|
|
static bool readByte(FILE *fp, uint8_t *out) {
|
|
int c = fgetc(fp);
|
|
if (c == EOF) {
|
|
return false;
|
|
}
|
|
*out = (uint8_t)c;
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool readBytes(FILE *fp, void *dst, size_t n) {
|
|
return fread(dst, 1, n, fp) == n;
|
|
}
|
|
|
|
|
|
bool stLevelLoad(StLevelT *out, const char *path) {
|
|
FILE *fp;
|
|
uint8_t hdr[4];
|
|
uint8_t nameLen;
|
|
uint8_t i;
|
|
size_t cells;
|
|
|
|
memset(out, 0, sizeof(*out));
|
|
|
|
fp = fopen(path, "rb");
|
|
if (fp == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (!readBytes(fp, hdr, 4) ||
|
|
hdr[0] != ST_LEVEL_MAGIC0 || hdr[1] != ST_LEVEL_MAGIC1 ||
|
|
hdr[2] != ST_LEVEL_MAGIC2 || hdr[3] != ST_LEVEL_MAGIC3) {
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
|
|
if (!readByte(fp, &nameLen) || nameLen >= sizeof(out->name)) {
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
if (!readBytes(fp, out->name, nameLen)) {
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
out->name[nameLen] = '\0';
|
|
|
|
if (!readByte(fp, &out->tileBankId) ||
|
|
!readByte(fp, &out->musicId) ||
|
|
!readByte(fp, &out->bgColor) ||
|
|
!readByte(fp, &out->borderColor) ||
|
|
!readByte(fp, &out->taxiSpawnTileX) ||
|
|
!readByte(fp, &out->taxiSpawnTileY)) {
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
|
|
{
|
|
uint8_t xGravByte;
|
|
uint8_t yGravByte;
|
|
if (!readByte(fp, &out->xAccel) ||
|
|
!readByte(fp, &out->yAccel) ||
|
|
!readByte(fp, &xGravByte) ||
|
|
!readByte(fp, &yGravByte)) {
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
out->xGrav = (int8_t)xGravByte;
|
|
out->yGrav = (int8_t)yGravByte;
|
|
}
|
|
if (!readByte(fp, &out->bgColor1) ||
|
|
!readByte(fp, &out->bgColor2) ||
|
|
!readByte(fp, &out->bgColor3) ||
|
|
!readByte(fp, &out->spriteMc0) ||
|
|
!readByte(fp, &out->spriteMc1) ||
|
|
!readByte(fp, &out->sprite0Color) ||
|
|
!readByte(fp, &out->sprite1Color)) {
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
|
|
if (!readByte(fp, &out->padCount) || out->padCount > ST_MAX_PADS) {
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
for (i = 0u; i < out->padCount; i++) {
|
|
if (!readByte(fp, &out->pads[i].letter) ||
|
|
!readByte(fp, &out->pads[i].tileX) ||
|
|
!readByte(fp, &out->pads[i].tileY) ||
|
|
!readByte(fp, &out->pads[i].tileW)) {
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!readByte(fp, &out->fareCount) || out->fareCount > ST_MAX_FARES) {
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
for (i = 0u; i < out->fareCount; i++) {
|
|
if (!readByte(fp, &out->fares[i].spawnPad) ||
|
|
!readByte(fp, &out->fares[i].destPad)) {
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
cells = (size_t)ST_TILEMAP_W * (size_t)ST_PLAYFIELD_ROWS;
|
|
if (!readBytes(fp, out->tilemap, cells) ||
|
|
!readBytes(fp, out->colormap, cells)) {
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
|
|
fclose(fp);
|
|
return true;
|
|
}
|
|
|
|
|