// Copyright (c) 2014 Alex Diener. All rights reserved. #include "watertowerclassic/LevelFileIO.h" #include "utilities/IOUtilities.h" #include #include static uint8_t memreadUInt8(struct memreadContext * context) { uint8_t uint8; memread(context, 1, &uint8); return uint8; } static uint16_t memreadUInt16Big(struct memreadContext * context) { uint16_t uint16; memread(context, 2, &uint16); return swapBigEndian16(uint16); } static uint32_t memreadUInt32Big(struct memreadContext * context) { uint32_t uint32; memread(context, 4, &uint32); return swapBigEndian32(uint32); } LevelSetModel * LevelFileIO_createLevelSetFromFile(const char * path, const char * name) { void * fileContents; size_t fileLength; LevelSetModel * levelSetModel; unsigned int charIndex, pathLength; fileContents = readFileSimple(path, &fileLength); if (fileContents == NULL) { return NULL; } pathLength = strlen(path); for (charIndex = pathLength - 1; charIndex > 0; charIndex--) { if (path[charIndex] == '/' || path[charIndex] == '\\') { charIndex++; break; } } levelSetModel = LevelFileIO_createLevelSetFromData(fileContents, fileLength, name == NULL ? path + charIndex : name); free(fileContents); return levelSetModel; } LevelSetModel * LevelFileIO_createLevelSetFromData(const void * data, size_t length, const char * name) { struct memreadContext context; LevelSetModel * levelSetModel; LevelModel * levelModel; unsigned int levelCount, levelIndex; context = memreadContextInit(data, length); levelCount = memreadUInt16Big(&context); if (levelCount == 0 || levelCount > LEVEL_COUNT_MAX) { return NULL; } levelSetModel = LevelSetModel_create(name); levelSetModel->levels = malloc(sizeof(LevelModel *) * levelCount); for (levelIndex = 0; levelIndex < levelCount; levelIndex++) { unsigned int blockIndex, powerupIndex; levelModel = LevelModel_create(); levelModel->playerStartPosition.x = memreadUInt16Big(&context); levelModel->playerStartPosition.y = memreadUInt16Big(&context); for (blockIndex = 0; blockIndex < LEVEL_BLOCK_COUNT_X * LEVEL_BLOCK_COUNT_Y; blockIndex++) { uint16_t param1; uint32_t param2; levelModel->blocks[blockIndex].type = memreadUInt8(&context); switch (levelModel->blocks[blockIndex].type) { case BLOCK_TYPE_SWITCHABLE: memread(&context, 8, NULL); levelModel->blocks[blockIndex].properties.switchable.on = !!memreadUInt8(&context); param1 = memreadUInt16Big(&context); param2 = memreadUInt32Big(&context); levelModel->blocks[blockIndex].properties.switchable.switchIDs[0] = param1 & 0x0F; levelModel->blocks[blockIndex].properties.switchable.switchIDs[1] = param1 >> 4 & 0x0F; levelModel->blocks[blockIndex].properties.switchable.switchIDs[2] = param1 >> 8 & 0x0F; levelModel->blocks[blockIndex].properties.switchable.switchIDs[3] = param1 >> 12; levelModel->blocks[blockIndex].properties.switchable.switchIDs[4] = param2 & 0x0F; levelModel->blocks[blockIndex].properties.switchable.switchIDs[5] = param2 >> 4 & 0x0F; levelModel->blocks[blockIndex].properties.switchable.switchIDs[6] = param2 >> 8 & 0x0F; levelModel->blocks[blockIndex].properties.switchable.switchIDs[7] = param2 >> 12 & 0x0F; levelModel->blocks[blockIndex].properties.switchable.switchIDs[8] = param2 >> 16 & 0x0F; levelModel->blocks[blockIndex].properties.switchable.switchIDs[9] = param2 >> 20 & 0x0F; levelModel->blocks[blockIndex].properties.switchable.switchIDs[10] = param2 >> 24 & 0x0F; levelModel->blocks[blockIndex].properties.switchable.switchIDs[11] = param2 >> 28; break; case BLOCK_TYPE_MOVING: memread(&context, 9, NULL); param1 = memreadUInt16Big(&context); memread(&context, 4, NULL); levelModel->blocks[blockIndex].properties.moving.speed = (param1 & 0x7C) << 11; levelModel->blocks[blockIndex].properties.moving.direction = param1 & 0x03; levelModel->blocks[blockIndex].properties.moving.position.x = blockIndex % LEVEL_BLOCK_COUNT_X * LEVEL_BLOCK_SIZE + LEVEL_BLOCK_SIZE / 2; levelModel->blocks[blockIndex].properties.moving.position.y = blockIndex / LEVEL_BLOCK_COUNT_X * LEVEL_BLOCK_SIZE + LEVEL_BLOCK_SIZE / 2; break; case BLOCK_TYPE_PHASING: memread(&context, 9, NULL); param1 = memreadUInt16Big(&context); memread(&context, 4, NULL); levelModel->blocks[blockIndex].properties.phasing.initialDelay = param1 & 0x1F; levelModel->blocks[blockIndex].properties.phasing.onPeriod = param1 >> 5 & 0x1F; levelModel->blocks[blockIndex].properties.phasing.offPeriod = param1 >> 10 & 0x1F; levelModel->blocks[blockIndex].properties.phasing.initiallyOn = param1 >> 15 & 0x01; break; case BLOCK_TYPE_LEVER: memread(&context, 9, NULL); levelModel->blocks[blockIndex].properties.lever.id = memreadUInt16Big(&context); memread(&context, 4, NULL); break; case BLOCK_TYPE_TELEPORT: memread(&context, 9, NULL); levelModel->blocks[blockIndex].properties.teleport.id = memreadUInt16Big(&context); memread(&context, 4, NULL); break; case BLOCK_TYPE_PUSHABLE: case BLOCK_TYPE_FLOATING: case BLOCK_TYPE_ICE: memread(&context, 15, NULL); levelModel->blocks[blockIndex].properties.pushable.yVelocity = 0; levelModel->blocks[blockIndex].properties.pushable.position.x = blockIndex % LEVEL_BLOCK_COUNT_X * LEVEL_BLOCK_SIZE + LEVEL_BLOCK_SIZE / 2; levelModel->blocks[blockIndex].properties.pushable.position.y = blockIndex / LEVEL_BLOCK_COUNT_X * LEVEL_BLOCK_SIZE + LEVEL_BLOCK_SIZE / 2; break; case BLOCK_TYPE_FALLING: memread(&context, 15, NULL); levelModel->blocks[blockIndex].properties.falling.isUnsteady = false; levelModel->blocks[blockIndex].properties.falling.framesUntilFalling = 0; levelModel->blocks[blockIndex].properties.falling.isFalling = false; levelModel->blocks[blockIndex].properties.falling.yVelocity = 0; levelModel->blocks[blockIndex].properties.falling.position.x = blockIndex % LEVEL_BLOCK_COUNT_X * LEVEL_BLOCK_SIZE + LEVEL_BLOCK_SIZE / 2; levelModel->blocks[blockIndex].properties.falling.position.y = blockIndex / LEVEL_BLOCK_COUNT_X * LEVEL_BLOCK_SIZE + LEVEL_BLOCK_SIZE / 2; break; default: memread(&context, 15, NULL); break; } levelModel->blocks[blockIndex].tilePosition.x = blockIndex % LEVEL_BLOCK_COUNT_X; levelModel->blocks[blockIndex].tilePosition.y = blockIndex / LEVEL_BLOCK_COUNT_X; levelModel->blocks[blockIndex].markedForRemoval = false; } levelModel->powerupCount = memreadUInt16Big(&context); for (powerupIndex = 0; powerupIndex < levelModel->powerupCount; powerupIndex++) { levelModel->powerups[powerupIndex].type = memreadUInt8(&context); memread(&context, 2, NULL); levelModel->powerups[powerupIndex].tilePosition.x = memreadUInt16Big(&context); levelModel->powerups[powerupIndex].tilePosition.y = memreadUInt16Big(&context); } levelModel->waterLevel = (int) memreadUInt32Big(&context) / (float) 0x10000; levelModel->waterSpeed = (int) memreadUInt32Big(&context) / (float) 0x10000; levelModel->pointsToUnlockNextLevel = memreadUInt16Big(&context); levelModel->lastBlockWaterLevel = levelModel->waterLevel / LEVEL_BLOCK_SIZE; levelSetModel->levels[levelSetModel->levelCount++] = levelModel; } return levelSetModel; } bool LevelFileIO_writeLevelSetToFile(LevelSetModel * levelSet, const char * path) { void * data; size_t length; bool success; data = LevelFileIO_writeLevelSetToData(levelSet, &length); if (data == NULL) { return false; } success = writeFileSimple(path, data, length); free(data); return success; } static void memwriteUInt8(struct memwriteContext * context, uint8_t uint8) { memwrite(context, 1, &uint8); } static void memwriteUInt16Big(struct memwriteContext * context, uint16_t uint16) { uint16 = swapBigEndian16(uint16); memwrite(context, 2, &uint16); } static void memwriteUInt32Big(struct memwriteContext * context, uint32_t uint32) { uint32 = swapBigEndian32(uint32); memwrite(context, 4, &uint32); } void * LevelFileIO_writeLevelSetToData(LevelSetModel * levelSet, size_t * outLength) { struct memwriteContext context; unsigned int levelIndex; LevelModel * levelModel; context = memwriteContextInit(NULL, 0, 0, true); memwriteUInt16Big(&context, levelSet->levelCount); for (levelIndex = 0; levelIndex < levelSet->levelCount; levelIndex++) { unsigned int blockIndex, powerupIndex; levelModel = levelSet->levels[levelIndex]; memwriteUInt16Big(&context, levelModel->playerStartPosition.x); memwriteUInt16Big(&context, levelModel->playerStartPosition.y); for (blockIndex = 0; blockIndex < LEVEL_BLOCK_COUNT_X * LEVEL_BLOCK_COUNT_Y; blockIndex++) { uint16_t param1; uint32_t param2; memwriteUInt8(&context, levelModel->blocks[blockIndex].type); switch (levelModel->blocks[blockIndex].type) { case BLOCK_TYPE_SWITCHABLE: memwrite(&context, 8, NULL); memwriteUInt8(&context, levelModel->blocks[blockIndex].properties.switchable.on); param1 = 0; param1 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[0]; param1 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[1] << 4; param1 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[2] << 8; param1 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[3] << 12; param2 = 0; param2 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[4]; param2 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[5] << 4; param2 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[6] << 8; param2 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[7] << 12; param2 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[8] << 16; param2 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[9] << 20; param2 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[10] << 24; param2 |= levelModel->blocks[blockIndex].properties.switchable.switchIDs[11] << 28; memwriteUInt16Big(&context, param1); memwriteUInt32Big(&context, param2); break; case BLOCK_TYPE_MOVING: memwrite(&context, 9, NULL); param1 = 0; param1 |= levelModel->blocks[blockIndex].properties.moving.speed >> 11 & 0x7C; param1 |= levelModel->blocks[blockIndex].properties.moving.direction; memwriteUInt16Big(&context, param1); memwrite(&context, 4, NULL); break; case BLOCK_TYPE_PHASING: memwrite(&context, 9, NULL); param1 = 0; param1 |= levelModel->blocks[blockIndex].properties.phasing.initialDelay; param1 |= levelModel->blocks[blockIndex].properties.phasing.onPeriod << 5; param1 |= levelModel->blocks[blockIndex].properties.phasing.offPeriod << 10; param1 |= levelModel->blocks[blockIndex].properties.phasing.initiallyOn << 15; memwriteUInt16Big(&context, param1); memwrite(&context, 4, NULL); break; case BLOCK_TYPE_LEVER: memwrite(&context, 9, NULL); memwriteUInt16Big(&context, levelModel->blocks[blockIndex].properties.lever.id); memwrite(&context, 4, NULL); break; case BLOCK_TYPE_TELEPORT: memwrite(&context, 9, NULL); memwriteUInt16Big(&context, levelModel->blocks[blockIndex].properties.teleport.id); memwrite(&context, 4, NULL); break; default: memwrite(&context, 15, NULL); break; } } memwriteUInt16Big(&context, levelModel->powerupCount); for (powerupIndex = 0; powerupIndex < levelModel->powerupCount; powerupIndex++) { memwriteUInt8(&context, levelModel->powerups[powerupIndex].type); memwrite(&context, 2, NULL); memwriteUInt16Big(&context, levelModel->powerups[powerupIndex].tilePosition.x); memwriteUInt16Big(&context, levelModel->powerups[powerupIndex].tilePosition.y); } memwriteUInt32Big(&context, levelModel->waterLevel * 0x10000); memwriteUInt32Big(&context, levelModel->waterSpeed * 0x10000); memwriteUInt16Big(&context, levelModel->pointsToUnlockNextLevel); } *outLength = context.length; return context.data; }