#include #include #include #include #include "mapload.h" #define MAX_PLAYERS 8 #define MAP_MAXAREA (256 * 256) #define debug(z, ...) do { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } while (0) void mapFree(GAMEMAP *map) { free(map->mGateways); free(map->mMapTiles); free(map); } /* Initialise the map structure */ GAMEMAP *mapLoad(char *filename) { char path[PATH_MAX]; GAMEMAP *map = malloc(sizeof(*map)); uint32_t gwVersion, i, j, gameVersion, gameTime, gameType, featVersion; char aFileType[4]; bool littleEndian = true; PHYSFS_file *fp = NULL; // this cries out for a class based design #define readU8(v) ( littleEndian ? PHYSFS_readULE8(fp, v) : PHYSFS_readUBE8(fp, v) ) #define readU16(v) ( littleEndian ? PHYSFS_readULE16(fp, v) : PHYSFS_readUBE16(fp, v) ) #define readU32(v) ( littleEndian ? PHYSFS_readULE32(fp, v) : PHYSFS_readUBE32(fp, v) ) #define readS8(v) ( littleEndian ? PHYSFS_readSLE8(fp, v) : PHYSFS_readSBE8(fp, v) ) #define readS16(v) ( littleEndian ? PHYSFS_readSLE16(fp, v) : PHYSFS_readSBE16(fp, v) ) #define readS32(v) ( littleEndian ? PHYSFS_readSLE32(fp, v) : PHYSFS_readSBE32(fp, v) ) /* === Load map data === */ strcpy(path, filename); strcat(path, "/game.map"); fp = PHYSFS_openRead(path); if (!fp) { debug(LOG_ERROR, "Map file %s not found", path); return NULL; } else if (PHYSFS_read(fp, aFileType, 4, 1) != 1 || !readU32(&map->version) || !readU32(&map->width) || !readU32(&map->height) || aFileType[0] != 'm' || aFileType[1] != 'a' || aFileType[2] != 'p') { debug(LOG_ERROR, "Bad header in %s", path); return NULL; } else if (map->version <= 9) { debug(LOG_ERROR, "%s: Unsupported save format version %u", path, map->version); return NULL; } else if (map->version > 36) { debug(LOG_ERROR, "%s: Undefined save format version %u", path, map->version); return NULL; } else if (map->width * map->height > MAP_MAXAREA) { debug(LOG_ERROR, "Map %s too large : %d %d", path, map->width, map->height); return NULL; } /* Allocate the memory for the map */ map->mMapTiles = calloc(map->width * map->height, sizeof(*map->mMapTiles)); if (!map->mMapTiles) { debug(LOG_ERROR, "Out of memory"); return NULL; } /* Load in the map data */ for (i = 0; i < map->width * map->height; i++) { uint16_t texture; uint8_t height; if (!readU16(&texture) || !readU8(&height)) { debug(LOG_ERROR, "%s: Error during savegame load", path); return NULL; } map->mMapTiles[i].texture = texture; map->mMapTiles[i].height = height; for (j = 0; j < MAX_PLAYERS; j++) { map->mMapTiles[i].tileVisBits = (uint8_t)(map->mMapTiles[i].tileVisBits &~ (uint8_t)(1 << j)); } } if (!readU32(&gwVersion) || !readU32(&map->numGateways) || gwVersion != 1) { debug(LOG_ERROR, "Bad gateway in %s", path); return NULL; } map->mGateways = calloc(map->numGateways, sizeof(*map->mGateways)); for (i = 0; i < map->numGateways; i++) { if (!readU8(&map->mGateways[i].x1) || !readU8(&map->mGateways[i].y1) || !readU8(&map->mGateways[i].x2) || !readU8(&map->mGateways[i].y2)) { debug(LOG_ERROR, "%s: Failed to read gateway info", path); return NULL; } } PHYSFS_close(fp); /* === Load game data === */ strcpy(path, filename); strcat(path, ".gam"); fp = PHYSFS_openRead(path); if (!fp) { debug(LOG_ERROR, "Game file %s not found", path); return NULL; } else if (PHYSFS_read(fp, aFileType, 4, 1) != 1 || aFileType[0] != 'g' || aFileType[1] != 'a' || aFileType[2] != 'm' || aFileType[3] != 'e' || !readU32(&gameVersion)) { debug(LOG_ERROR, "Bad header in %s", path); return NULL; } if (gameVersion > 35) // big-endian { littleEndian = false; } if (!readU32(&gameTime) || !readU32(&gameType) || !readS32(&map->scrollMinX) || !readS32(&map->scrollMinY) || !readU32(&map->scrollMaxX) || !readU32(&map->scrollMaxY) || PHYSFS_read(fp, map->levelName, 20, 1) != 1) { debug(LOG_ERROR, "Bad data in %s", filename); return NULL; } PHYSFS_close(fp); /* === Load game data === */ littleEndian = true; strcpy(path, filename); strcat(path, "/feat.bjo"); fp = PHYSFS_openRead(path); if (!fp) { debug(LOG_ERROR, "Feature file %s not found", path); return NULL; } else if (PHYSFS_read(fp, aFileType, 4, 1) != 1 || aFileType[0] != 'f' || aFileType[1] != 'e' || aFileType[2] != 'a' || aFileType[3] != 't' || !readU32(&featVersion) || !readU32(&map->numFeatures)) { debug(LOG_ERROR, "Bad features header in %s", path); return NULL; } PHYSFS_close(fp); return map; }