// Space Taxi -- STL4 level file parser (plain stdio, no JoeyLib), shared // by the game and the host-side simulation harness. // // Layout (all bytes, written by stuff/spacetaxi/romToLevel.py): // "STL4" // name[22] $7D78 level name in screen codes // header[9] $7D00 VIC colour block // padCount, specialPad, fuelRate // spawn[5] $7D5B // templates[8] $7D8F Y accel, X accel, Y gravity, X gravity (LE words) // pads[10][8] $7D0A raw slots // screen[1000], color[1000] // levelIndex // spriteCount, then spriteCount x (ptr, 63 bytes) // hookData[616] $7D98..$7FFF #include #include #include "stSim.h" static bool readBlock(FILE *fp, void *dst, size_t n); static bool readBlock(FILE *fp, void *dst, size_t n) { return fread(dst, 1, n, fp) == n; } bool stLevelParse(StLevelT *out, FILE *fp) { uint8_t hdr[4]; uint8_t counts[3]; uint8_t templates[8]; uint8_t slots[ST_MAX_PADS][8]; uint8_t k; memset(out, 0, sizeof(*out)); if (!readBlock(fp, hdr, 4u) || hdr[0] != 'S' || hdr[1] != 'T' || hdr[2] != 'L' || hdr[3] != '4') { return false; } if (!readBlock(fp, out->name, ST_LEVEL_NAME_CHARS)) { return false; } out->name[ST_LEVEL_NAME_CHARS] = 0u; if (!readBlock(fp, out->header, 9u) || !readBlock(fp, counts, 3u) || !readBlock(fp, out->spawn, 5u)) { return false; } out->padCount = counts[0]; out->specialPad = counts[1]; out->fuelRate = counts[2]; if (!readBlock(fp, templates, 8u)) { return false; } out->accelY = (uint16_t)(templates[0] | ((uint16_t)templates[1] << 8)); out->accelX = (uint16_t)(templates[2] | ((uint16_t)templates[3] << 8)); out->gravY = (uint16_t)(templates[4] | ((uint16_t)templates[5] << 8)); out->gravX = (uint16_t)(templates[6] | ((uint16_t)templates[7] << 8)); if (!readBlock(fp, slots, sizeof(slots))) { return false; } for (k = 0u; k < ST_MAX_PADS; k++) { out->pads[k].x1Hi = slots[k][0]; out->pads[k].x1Lo = slots[k][1]; out->pads[k].x2Hi = slots[k][2]; out->pads[k].x2Lo = slots[k][3]; out->pads[k].row = slots[k][4]; out->pads[k].passMsb = slots[k][5]; out->pads[k].passCol = slots[k][6]; out->pads[k].unused = slots[k][7]; } if (!readBlock(fp, out->screen, ST_SCREEN_CELLS) || !readBlock(fp, out->color, ST_SCREEN_CELLS)) { return false; } if (!readBlock(fp, &out->levelIndex, 1u) || !readBlock(fp, &out->spriteCount, 1u)) { return false; } if (out->spriteCount > ST_MAX_LEVEL_SPRITES) { return false; } for (k = 0u; k < out->spriteCount; k++) { if (!readBlock(fp, &out->sprites[k].ptr, 1u) || !readBlock(fp, out->sprites[k].bitmap, ST_SPRITE_BYTES)) { return false; } } if (!readBlock(fp, out->hookData, ST_HOOK_BYTES)) { return false; } return true; }