Files
Aerofoil/GpApp/SavedGames.cpp

441 lines
13 KiB
C++
Raw Normal View History

2019-11-11 00:11:59 -05:00
//============================================================================
//----------------------------------------------------------------------------
// SavedGames.c
//----------------------------------------------------------------------------
//============================================================================
#include "PLDialogs.h"
2019-11-11 00:11:59 -05:00
#include "PLStringCompare.h"
2020-01-24 02:09:19 -05:00
#include "DialogManager.h"
2019-11-11 00:11:59 -05:00
#include "Externs.h"
2020-11-10 20:04:29 -05:00
#include "FileBrowserUI.h"
2020-01-05 02:33:03 -05:00
#include "FileManager.h"
2020-11-10 20:04:29 -05:00
#include "FontFamily.h"
2019-11-11 00:11:59 -05:00
#include "House.h"
2020-09-12 13:32:53 -04:00
#include "GpIOStream.h"
2020-01-05 02:33:03 -05:00
#include "InputManager.h"
#include "MacFileInfo.h"
#include "MemoryManager.h"
2020-11-10 20:04:29 -05:00
#include "PLStandardColors.h"
#include "ResolveCachingColor.h"
2020-01-05 02:33:03 -05:00
#include <assert.h>
2019-11-11 00:11:59 -05:00
#define kSavedGameVersion 0x0200
void SavedGameMismatchError (StringPtr);
gameType smallGame;
2019-12-29 06:38:18 -05:00
extern VFileSpec *theHousesSpecs;
2019-11-11 00:11:59 -05:00
extern short numStarsRemaining, thisHouseIndex;
extern Boolean twoPlayerGame;
//============================================================== Functions
2020-11-10 20:04:29 -05:00
static const int kStarsOffset = 180;
static const int kGlidersOffset = 260;
static const int kScoreOffset = 320;
struct FBUI_Save_Context
{
};
static void FBUI_Save_DrawLabels(void *context, DrawSurface *surface, const Point &basePoint)
2020-11-10 20:04:29 -05:00
{
PortabilityLayer::ResolveCachingColor blackColor(StdColors::Black());
PortabilityLayer::RenderedFont *rfont = GetFont(PortabilityLayer::FontPresets::kSystem12Bold);
2020-11-10 20:04:29 -05:00
surface->DrawString(basePoint + Point::Create(kStarsOffset, 0), PSTR("Stars Left"), blackColor, rfont);
surface->DrawString(basePoint + Point::Create(kGlidersOffset, 0), PSTR("Gliders"), blackColor, rfont);
surface->DrawString(basePoint + Point::Create(kScoreOffset, 0), PSTR("Score"), blackColor, rfont);
}
static void FBUI_Save_DrawFileDetails(void *context, DrawSurface *surface, const Point &basePoint, const Rect &constraintRect, void *fileDetails)
2020-11-10 20:04:29 -05:00
{
PortabilityLayer::ResolveCachingColor blackColor(StdColors::Black());
PortabilityLayer::RenderedFont *rfont = GetFont(PortabilityLayer::FontPresets::kSystem12Bold);
2020-11-10 20:04:29 -05:00
const game2Type *gameData = static_cast<const game2Type*>(fileDetails);
Str255 numStr;
NumToString(gameData->wasStarsLeft, numStr);
surface->DrawString(basePoint + Point::Create(kStarsOffset, 0), numStr, blackColor, rfont);
NumToString(gameData->numGliders, numStr);
surface->DrawString(basePoint + Point::Create(kGlidersOffset, 0), numStr, blackColor, rfont);
NumToString(gameData->score, numStr);
surface->DrawString(basePoint + Point::Create(kScoreOffset, 0), numStr, blackColor, rfont);
}
static void *FBUI_Save_LoadFileDetails(void *context, PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &filename)
2020-11-10 20:04:29 -05:00
{
GpIOStream *stream = nullptr;
2021-03-07 04:24:13 -05:00
if (PortabilityLayer::FileManager::GetInstance()->OpenNonCompositeFile(dirID, filename, ".sav", PortabilityLayer::EFilePermission_Read, GpFileCreationDispositions::kOpenExisting, stream) != PLErrors::kNone)
2020-11-10 20:04:29 -05:00
return nullptr;
const size_t kPrefixSize = sizeof(game2Type) - sizeof(savedRoom);
game2Type *gameData = static_cast<game2Type*>(PortabilityLayer::MemoryManager::GetInstance()->Alloc(kPrefixSize));
if (!gameData)
{
stream->Close();
return nullptr;
}
if (stream->Read(gameData, kPrefixSize) != kPrefixSize)
{
PortabilityLayer::MemoryManager::GetInstance()->Release(gameData);
stream->Close();
return nullptr;
}
stream->Close();
return gameData;
}
static void FBUI_Save_FreeFileDetails(void *context, void *fileDetails)
2020-11-10 20:04:29 -05:00
{
PortabilityLayer::MemoryManager::GetInstance()->Release(fileDetails);
}
static bool FBUI_Save_FilterFile(void *context, PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &filename)
2021-03-07 04:24:13 -05:00
{
return true;
}
static bool FBUI_Save_IsDeleteValid(void *context, PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &filename)
2021-05-09 22:48:23 -04:00
{
return true;
}
static void FBUI_Save_OnDeleted(void *context, PortabilityLayer::VirtualDirectory_t dirID, const PLPasStr &filename)
{
}
static PortabilityLayer::FileBrowserUI_DetailsCallbackAPI GetSavedGameDetailsAPI(FBUI_Save_Context *context)
2020-11-10 20:04:29 -05:00
{
PortabilityLayer::FileBrowserUI_DetailsCallbackAPI api;
api.m_context = context;
2021-03-29 21:41:11 -04:00
api.m_drawLabelsCallback = FBUI_Save_DrawLabels;
api.m_drawFileDetailsCallback = FBUI_Save_DrawFileDetails;
api.m_loadFileDetailsCallback = FBUI_Save_LoadFileDetails;
api.m_freeFileDetailsCallback = FBUI_Save_FreeFileDetails;
api.m_filterFileCallback = FBUI_Save_FilterFile;
2021-05-09 22:48:23 -04:00
api.m_isDeleteValidCallback = FBUI_Save_IsDeleteValid;
api.m_onDeletedCallback = FBUI_Save_OnDeleted;
2020-11-10 20:04:29 -05:00
return api;
}
2019-11-11 00:11:59 -05:00
//-------------------------------------------------------------- SaveGame2
2020-11-12 19:16:47 -05:00
Boolean SaveGame2 (void)
2019-11-11 00:11:59 -05:00
{
2020-01-05 02:33:03 -05:00
// Bringing up the save file UI can cause key/mouse events to be missed, resulting in state being stuck when this comes back.
// To avoid that, clear all state here.
PortabilityLayer::InputManager::GetInstance()->ClearState();
2019-11-11 00:11:59 -05:00
Str255 gameNameStr;
2020-06-01 00:38:24 -04:00
size_t byteCount;
2019-11-11 00:11:59 -05:00
houseType *thisHousePtr;
roomType *srcRoom;
savedRoom *destRoom;
gamePtr savedGame;
2020-01-05 02:33:03 -05:00
short r, i, numRooms;
2019-11-11 00:11:59 -05:00
char wasState;
2020-09-12 13:32:53 -04:00
GpIOStream *gameStream = nullptr;
2020-01-05 02:33:03 -05:00
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
PortabilityLayer::FileManager *fm = PortabilityLayer::FileManager::GetInstance();
2019-11-11 00:11:59 -05:00
2020-06-01 00:33:50 -04:00
FlushEvents();
2019-11-11 00:11:59 -05:00
thisHousePtr = *thisHouse;
numRooms = thisHousePtr->nRooms;
byteCount = sizeof(game2Type) + sizeof(savedRoom) * numRooms;
2020-01-05 02:33:03 -05:00
savedGame = (gamePtr)mm->Alloc(byteCount);
2019-11-11 00:11:59 -05:00
if (savedGame == nil)
{
2019-12-29 06:38:18 -05:00
YellowAlert(kYellowFailedSaveGame, PLErrors::kOutOfMemory);
2020-11-12 19:16:47 -05:00
return false;
2019-11-11 00:11:59 -05:00
}
2020-01-05 02:33:03 -05:00
memset(savedGame, 0, byteCount);
2019-11-11 00:11:59 -05:00
GetFirstWordOfString(thisHouseName, gameNameStr);
if (gameNameStr[0] > 23)
gameNameStr[0] = 23;
2020-01-05 02:33:03 -05:00
PasStringConcat(gameNameStr, PSTR(" Game"));
VFileSpec spec;
spec.m_dir = PortabilityLayer::VirtualDirectories::kUserSaves;
spec.m_name[0] = 0;
char savePath[sizeof(spec.m_name) + 1];
size_t savePathLength = 0;
FBUI_Save_Context context;
if (!fm->PromptSaveFile(spec.m_dir, ".sav", savePath, savePathLength, sizeof(spec.m_name), PLPasStr(gameNameStr), PSTR("Save Game"), false, GetSavedGameDetailsAPI(&context)))
2020-01-05 02:33:03 -05:00
{
mm->Release(savedGame);
2020-11-12 19:16:47 -05:00
return false;
2020-01-05 02:33:03 -05:00
}
assert(savePathLength < sizeof(spec.m_name) - 1);
spec.m_name[0] = static_cast<uint8_t>(savePathLength);
memcpy(spec.m_name + 1, savePath, savePathLength);
2019-11-11 00:11:59 -05:00
thisHousePtr = *thisHouse;
savedGame->house = theHousesSpecs[thisHouseIndex];
savedGame->version = kSavedGameVersion;
savedGame->wasStarsLeft = numStarsRemaining;
savedGame->timeStamp = thisHousePtr->timeStamp;
savedGame->where.h = theGlider.dest.left;
savedGame->where.v = theGlider.dest.top;
savedGame->score = theScore;
savedGame->unusedLong = 0L;
savedGame->unusedLong2 = 0L;
savedGame->energy = batteryTotal;
savedGame->bands = bandsTotal;
savedGame->roomNumber = thisRoomNumber;
savedGame->gliderState = theGlider.mode;
savedGame->numGliders = mortals;
savedGame->foil = foilTotal;
savedGame->nRooms = numRooms;
savedGame->facing = theGlider.facing;
savedGame->showFoil = showFoil;
for (r = 0; r < numRooms; r++)
{
destRoom = &(savedGame->savedData[r]);
srcRoom = &(thisHousePtr->rooms[r]);
destRoom->unusedShort = 0;
destRoom->unusedByte = 0;
destRoom->visited = srcRoom->visited;
for (i = 0; i < kMaxRoomObs; i++)
destRoom->objects[i] = srcRoom->objects[i];
}
2020-01-05 02:33:03 -05:00
2021-03-07 04:24:13 -05:00
PLError_t theErr = fm->OpenNonCompositeFile(spec.m_dir, spec.m_name, ".sav", PortabilityLayer::EFilePermission_Write, GpFileCreationDispositions::kCreateOrOverwrite, gameStream);
2020-01-05 02:33:03 -05:00
if (CheckFileError(theErr, PSTR("Saved Game")))
2019-11-11 00:11:59 -05:00
{
2021-03-07 04:24:13 -05:00
if (gameStream->Write(savedGame, byteCount) != byteCount)
2019-11-11 00:11:59 -05:00
{
2021-03-07 04:24:13 -05:00
CheckFileError(PLErrors::kIOError, PSTR("Saved Game"));
2019-11-11 00:11:59 -05:00
}
2021-03-07 04:24:13 -05:00
gameStream->Close();
2019-11-11 00:11:59 -05:00
}
2020-01-05 02:33:03 -05:00
mm->Release(savedGame);
2020-11-12 19:16:47 -05:00
return true;
2019-11-11 00:11:59 -05:00
}
//-------------------------------------------------------------- SavedGameMismatchError
void SavedGameMismatchError (StringPtr gameName)
{
#define kSavedGameErrorAlert 1044
short whoCares;
InitCursor();
// CenterAlert(kSavedGameErrorAlert);
DialogTextSubstitutions substitutions(gameName, thisHouseName, PSTR(""), PSTR(""));
2019-11-11 00:11:59 -05:00
whoCares = PortabilityLayer::DialogManager::GetInstance()->DisplayAlert(kSavedGameErrorAlert, &substitutions);
2019-11-11 00:11:59 -05:00
}
//-------------------------------------------------------------- OpenSavedGame
Boolean OpenSavedGame (void)
{
houseType *thisHousePtr;
roomType *destRoom;
savedRoom *srcRoom;
gamePtr savedGame;
short r, i, gameRefNum;
char wasState;
2020-01-05 02:33:03 -05:00
PortabilityLayer::FileManager *fm = PortabilityLayer::FileManager::GetInstance();
PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance();
VFileSpec spec;
spec.m_dir = PortabilityLayer::VirtualDirectories::kUserSaves;
spec.m_name[0] = 0;
char savePath[sizeof(spec.m_name) + 1];
size_t savePathLength = 0;
FBUI_Save_Context context;
if (!fm->PromptOpenFile(spec.m_dir, ".sav", savePath, savePathLength, sizeof(spec.m_name), PSTR("Open Saved Game"), false, GetSavedGameDetailsAPI(&context)))
2020-01-05 02:33:03 -05:00
return false;
assert(savePathLength < sizeof(spec.m_name) - 1);
spec.m_name[0] = static_cast<uint8_t>(savePathLength);
memcpy(spec.m_name + 1, savePath, savePathLength);
2020-09-12 13:32:53 -04:00
GpIOStream *gameStream = nullptr;
2021-03-07 04:24:13 -05:00
PLError_t theErr = fm->OpenNonCompositeFile(spec.m_dir, spec.m_name, ".sav", PortabilityLayer::EFilePermission_Read, GpFileCreationDispositions::kOpenExisting, gameStream);
2020-01-05 02:33:03 -05:00
if (!CheckFileError(theErr, PSTR("Saved Game")))
2019-11-11 00:11:59 -05:00
return(false);
2020-01-05 02:33:03 -05:00
2020-09-12 13:32:53 -04:00
const GpUFilePos_t fileSizeFP = gameStream->Size();
2020-01-05 02:33:03 -05:00
if (fileSizeFP > SIZE_MAX)
2019-11-11 00:11:59 -05:00
{
2020-01-05 02:33:03 -05:00
gameStream->Close();
return false;
2019-11-11 00:11:59 -05:00
}
2020-01-05 02:33:03 -05:00
const size_t byteCount = static_cast<size_t>(fileSizeFP);
savedGame = (gamePtr)mm->Alloc(byteCount);
2019-11-11 00:11:59 -05:00
if (savedGame == nil)
{
2019-12-29 06:38:18 -05:00
YellowAlert(kYellowFailedSaveGame, PLErrors::kOutOfMemory);
2020-01-05 02:33:03 -05:00
gameStream->Close();
2019-11-11 00:11:59 -05:00
return(false);
}
2020-01-05 02:33:03 -05:00
if (gameStream->Read(savedGame, byteCount) != byteCount)
2019-11-11 00:11:59 -05:00
{
2020-01-05 02:33:03 -05:00
CheckFileError(PLErrors::kIOError, PSTR("Saved Game"));
mm->Release(savedGame);
gameStream->Close();
2019-11-11 00:11:59 -05:00
return(false);
}
thisHousePtr = *thisHouse;
2020-01-05 02:33:03 -05:00
if (!StrCmp::Equal(savedGame->house.m_name, thisHouseName))
2019-11-11 00:11:59 -05:00
{
2020-01-05 02:33:03 -05:00
SavedGameMismatchError(savedGame->house.m_name);
mm->Release(savedGame);
gameStream->Close();
2019-11-11 00:11:59 -05:00
return(false);
}
else if (thisHousePtr->timeStamp != savedGame->timeStamp)
{
YellowAlert(kYellowSavedTimeWrong, 0);
2020-01-05 02:33:03 -05:00
mm->Release(savedGame);
gameStream->Close();
2019-11-11 00:11:59 -05:00
return(false);
}
else if (savedGame->version != kSavedGameVersion)
{
YellowAlert(kYellowSavedVersWrong, kSavedGameVersion);
2020-01-05 02:33:03 -05:00
mm->Release(savedGame);
gameStream->Close();
2019-11-11 00:11:59 -05:00
return(false);
}
else if (savedGame->nRooms != thisHousePtr->nRooms)
{
YellowAlert(kYellowSavedRoomsWrong, savedGame->nRooms - thisHousePtr->nRooms);
2020-01-05 02:33:03 -05:00
mm->Release(savedGame);
gameStream->Close();
2019-11-11 00:11:59 -05:00
return(false);
}
else
{
smallGame.wasStarsLeft = savedGame->wasStarsLeft;
smallGame.where.h = savedGame->where.h;
smallGame.where.v = savedGame->where.v;
smallGame.score = savedGame->score;
smallGame.unusedLong = savedGame->unusedLong;
smallGame.unusedLong2 = savedGame->unusedLong2;
smallGame.energy = savedGame->energy;
smallGame.bands = savedGame->bands;
smallGame.roomNumber = savedGame->roomNumber;
smallGame.gliderState = savedGame->gliderState;
smallGame.numGliders = savedGame->numGliders;
smallGame.foil = savedGame->foil;
smallGame.unusedShort = 0;
smallGame.facing = savedGame->facing;
smallGame.showFoil = savedGame->showFoil;
for (r = 0; r < savedGame->nRooms; r++)
{
srcRoom = &(savedGame->savedData[r]);
destRoom = &(thisHousePtr->rooms[r]);
destRoom->visited = srcRoom->visited;
for (i = 0; i < kMaxRoomObs; i++)
destRoom->objects[i] = srcRoom->objects[i];
}
}
2020-01-05 02:33:03 -05:00
mm->Release(savedGame);
gameStream->Close();
2019-11-11 00:11:59 -05:00
return (true);
}
//-------------------------------------------------------------- SaveGame
// This is probably about 3 days away from becoming the "old" function<6F>
// for saving games.
#if 0
2019-11-11 00:11:59 -05:00
void SaveGame (Boolean doSave)
{
houseType *thisHousePtr;
UInt32 stamp;
char wasState;
if (twoPlayerGame)
return;
thisHousePtr = *thisHouse;
if (doSave)
{
thisHousePtr->savedGame.version = kSavedGameVersion;
thisHousePtr->savedGame.wasStarsLeft = numStarsRemaining;
GetDateTime(&stamp);
thisHousePtr->savedGame.timeStamp = (long)stamp;
thisHousePtr->savedGame.where.h = theGlider.dest.left;
thisHousePtr->savedGame.where.v = theGlider.dest.top;
thisHousePtr->savedGame.score = theScore;
thisHousePtr->savedGame.unusedLong = 0L;
thisHousePtr->savedGame.unusedLong2 = 0L;
thisHousePtr->savedGame.energy = batteryTotal;
thisHousePtr->savedGame.bands = bandsTotal;
thisHousePtr->savedGame.roomNumber = thisRoomNumber;
thisHousePtr->savedGame.gliderState = theGlider.mode;
thisHousePtr->savedGame.numGliders = mortals;
thisHousePtr->savedGame.foil = foilTotal;
thisHousePtr->savedGame.unusedShort = 0;
thisHousePtr->savedGame.facing = theGlider.facing;
thisHousePtr->savedGame.showFoil = showFoil;
thisHousePtr->hasGame = true;
}
else
{
thisHousePtr->hasGame = false;
}
if (doSave)
{
if (!WriteHouse(theMode == kEditMode))
YellowAlert(kYellowFailedWrite, 0);
}
}
#endif