mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-02 23:49:40 +00:00
MYST3: Save game update
* Save floats in a portable way * Save the creation date and description * Clean up thumbnail handling * Increment savegame version Saves from the original engine are still compatible
This commit is contained in:
parent
23dfe79d88
commit
f64ec08108
@ -167,8 +167,9 @@ public:
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSupportsDeleteSave) ||
|
||||
(f == kSupportsLoadingDuringStartup) ||
|
||||
(f == kSavesSupportThumbnail) ||
|
||||
(f == kSavesSupportMetaInfo) ||
|
||||
(f == kSavesSupportThumbnail) ||
|
||||
(f == kSavesSupportCreationDate) ||
|
||||
(f == kSavesSupportPlayTime);
|
||||
}
|
||||
|
||||
@ -224,17 +225,21 @@ public:
|
||||
GameState::syncWithSaveGame(s, data);
|
||||
|
||||
// Read and resize the thumbnail
|
||||
Graphics::Surface *thumbnail = GameState::loadThumbnail(saveFile);
|
||||
Graphics::Surface *guiThumb = new Graphics::Surface();
|
||||
guiThumb->create(160, 100, Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
|
||||
resizeThumbnail(thumbnail, guiThumb);
|
||||
resizeThumbnail(data.thumbnail.get(), guiThumb);
|
||||
|
||||
// Set metadata
|
||||
saveInfos.setThumbnail(guiThumb);
|
||||
saveInfos.setPlayTime(data.secondsPlayed * 1000);
|
||||
|
||||
thumbnail->free();
|
||||
delete thumbnail;
|
||||
if (data.saveYear != 0) {
|
||||
saveInfos.setSaveDate(data.saveYear, data.saveMonth, data.saveDay);
|
||||
saveInfos.setSaveTime(data.saveHour, data.saveMinute);
|
||||
}
|
||||
|
||||
if (data.saveDescription != "")
|
||||
saveInfos.setDescription(data.saveDescription);
|
||||
|
||||
delete saveFile;
|
||||
|
||||
|
@ -39,15 +39,9 @@ Menu::Menu(Myst3Engine *vm) :
|
||||
_saveLoadSpotItem(0),
|
||||
_saveDrawCaret(false),
|
||||
_saveCaretCounter(0) {
|
||||
_saveThumb = new Graphics::Surface();
|
||||
_saveThumb->create(240, 135, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
|
||||
}
|
||||
|
||||
Menu::~Menu() {
|
||||
if (_saveThumb) {
|
||||
_saveThumb->free();
|
||||
delete _saveThumb;
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::updateMainMenu(uint16 action) {
|
||||
@ -139,7 +133,8 @@ void Menu::goToNode(uint16 node) {
|
||||
|
||||
// ... and capture the screen
|
||||
Graphics::Surface *big = _vm->_gfx->getScreenshot();
|
||||
createThumbnail(big, _saveThumb);
|
||||
Graphics::Surface *thumb = createThumbnail(big);
|
||||
_vm->_state->setSaveThumbnail(thumb);
|
||||
big->free();
|
||||
delete big;
|
||||
}
|
||||
@ -302,13 +297,15 @@ void Menu::loadMenuSelect(uint16 item) {
|
||||
// Extract the age to load from the savegame
|
||||
GameState *gameState = new GameState(_vm);
|
||||
gameState->load(filename);
|
||||
_saveLoadAgeName = getAgeLabel(gameState);
|
||||
delete gameState;
|
||||
|
||||
// Extract the thumbnail from the save
|
||||
Common::InSaveFile *save = _vm->getSaveFileManager()->openForLoading(filename);
|
||||
saveGameReadThumbnail(save);
|
||||
delete save;
|
||||
// Update the age name
|
||||
_saveLoadAgeName = getAgeLabel(gameState);
|
||||
|
||||
// Update the save thumbnail
|
||||
if (_saveLoadSpotItem)
|
||||
_saveLoadSpotItem->updateData(gameState->getSaveThumbnail());
|
||||
|
||||
delete gameState;
|
||||
}
|
||||
|
||||
void Menu::loadMenuLoad() {
|
||||
@ -344,8 +341,9 @@ void Menu::saveMenuOpen() {
|
||||
saveLoadUpdateVars();
|
||||
|
||||
// Update the thumbnail to display
|
||||
if (_saveLoadSpotItem && _saveThumb)
|
||||
_saveLoadSpotItem->updateData(_saveThumb);
|
||||
Graphics::Surface *saveThumb = _vm->_state->getSaveThumbnail();
|
||||
if (_saveLoadSpotItem && saveThumb)
|
||||
_saveLoadSpotItem->updateData(saveThumb);
|
||||
}
|
||||
|
||||
void Menu::saveMenuSelect(uint16 item) {
|
||||
@ -389,8 +387,8 @@ void Menu::saveMenuSave() {
|
||||
|
||||
// Save the state and the thumbnail
|
||||
Common::OutSaveFile *save = _vm->getSaveFileManager()->openForSaving(fileName);
|
||||
_vm->_state->setSaveDescription(_saveName);
|
||||
_vm->_state->save(save);
|
||||
saveGameWriteThumbnail(save);
|
||||
delete save;
|
||||
|
||||
// Do next action
|
||||
@ -535,35 +533,6 @@ Common::String Menu::getAgeLabel(GameState *gameState) {
|
||||
return label;
|
||||
}
|
||||
|
||||
void Menu::saveGameReadThumbnail(Common::InSaveFile *save) {
|
||||
Graphics::Surface *thumbnail = GameState::loadThumbnail(save);
|
||||
|
||||
// Convert to RGBA
|
||||
thumbnail->convertToInPlace(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
|
||||
|
||||
if (_saveLoadSpotItem)
|
||||
_saveLoadSpotItem->updateData(thumbnail);
|
||||
|
||||
thumbnail->free();
|
||||
delete thumbnail;
|
||||
}
|
||||
|
||||
void Menu::saveGameWriteThumbnail(Common::OutSaveFile *save) {
|
||||
// The file expects BGRA data instead of RGBA
|
||||
uint8 *src = (uint8 *)_saveThumb->pixels;
|
||||
for (uint i = 0; i < kMiniatureSize; i++) {
|
||||
uint8 r, g, b, a;
|
||||
r = *src++;
|
||||
g = *src++;
|
||||
b = *src++;
|
||||
a = *src++;
|
||||
save->writeByte(b);
|
||||
save->writeByte(g);
|
||||
save->writeByte(r);
|
||||
save->writeByte(a);
|
||||
}
|
||||
}
|
||||
|
||||
Common::String Menu::prepareSaveNameForDisplay(const Common::String &name) {
|
||||
Common::String display = name;
|
||||
display.toUppercase();
|
||||
@ -580,9 +549,11 @@ Common::String Menu::prepareSaveNameForDisplay(const Common::String &name) {
|
||||
return display;
|
||||
}
|
||||
|
||||
void Menu::createThumbnail(Graphics::Surface *big, Graphics::Surface *small) {
|
||||
assert(big->format.bytesPerPixel == 4
|
||||
&& small->format.bytesPerPixel == 4);
|
||||
Graphics::Surface *Menu::createThumbnail(Graphics::Surface *big) {
|
||||
assert(big->format.bytesPerPixel == 4);
|
||||
|
||||
Graphics::Surface *small = new Graphics::Surface();
|
||||
small->create(240, 135, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
|
||||
|
||||
uint bigHeight = big->h - Renderer::kTopBorderHeight - Renderer::kBottomBorderHeight;
|
||||
uint bigYOffset = Renderer::kBottomBorderHeight;
|
||||
@ -595,9 +566,14 @@ void Menu::createThumbnail(Graphics::Surface *big, Graphics::Surface *small) {
|
||||
uint32 *src = (uint32 *)big->getBasePtr(srcX, srcY - 1);
|
||||
|
||||
// Copy RGBA pixel
|
||||
*dst++ = *src++;
|
||||
*dst++ = *src;
|
||||
}
|
||||
}
|
||||
|
||||
// The thumbnail is stored on disk in BGRA
|
||||
small->convertToInPlace(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
|
||||
|
||||
return small;
|
||||
}
|
||||
|
||||
} /* namespace Myst3 */
|
||||
|
@ -70,14 +70,13 @@ private:
|
||||
Common::String _saveName;
|
||||
bool _saveDrawCaret;
|
||||
int32 _saveCaretCounter;
|
||||
Graphics::Surface *_saveThumb;
|
||||
|
||||
static const uint kCaretSpeed = 25;
|
||||
static const uint kMiniatureSize = 240 * 135;
|
||||
|
||||
void saveLoadUpdateVars();
|
||||
|
||||
void createThumbnail(Graphics::Surface *big, Graphics::Surface *small);
|
||||
Graphics::Surface *createThumbnail(Graphics::Surface *big);
|
||||
void saveGameReadThumbnail(Common::InSaveFile *save);
|
||||
void saveGameWriteThumbnail(Common::OutSaveFile *save);
|
||||
|
||||
|
@ -316,6 +316,9 @@ void SpotItemFace::updateData(const Graphics::Surface *surface) {
|
||||
_bitmap->free();
|
||||
_bitmap->copyFrom(*surface);
|
||||
|
||||
// Ensure the pixel format is correct
|
||||
_bitmap->convertToInPlace(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
|
||||
|
||||
_drawn = false;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,54 @@
|
||||
|
||||
namespace Myst3 {
|
||||
|
||||
GameState::StateData::StateData() {
|
||||
version = GameState::kSaveVersion;
|
||||
gameRunning = true;
|
||||
currentFrame = 0;
|
||||
nextSecondsUpdate = 0;
|
||||
secondsPlayed = 0;
|
||||
dword_4C2C44 = 0;
|
||||
dword_4C2C48 = 0;
|
||||
dword_4C2C4C = 0;
|
||||
dword_4C2C50 = 0;
|
||||
dword_4C2C54 = 0;
|
||||
dword_4C2C58 = 0;
|
||||
dword_4C2C5C = 0;
|
||||
dword_4C2C60 = 0;
|
||||
currentNodeType = 0;
|
||||
lookatPitch = 0;
|
||||
lookatHeading = 0;
|
||||
lookatFOV = 0;
|
||||
pitchOffset = 0;
|
||||
headingOffset = 0;
|
||||
limitCubeCamera = 0;
|
||||
minPitch = 0;
|
||||
maxPitch = 0;
|
||||
minHeading = 0;
|
||||
maxHeading = 0;
|
||||
dword_4C2C90 = 0;
|
||||
|
||||
for (uint i = 0; i < 2048; i++)
|
||||
vars[i] = 0;
|
||||
|
||||
vars[0] = 0;
|
||||
vars[1] = 1;
|
||||
|
||||
inventoryCount = 0;
|
||||
|
||||
for (uint i = 0; i < 7; i++)
|
||||
inventoryList[i] = 0;
|
||||
|
||||
for (uint i = 0; i < 256; i++)
|
||||
zipDestinations[i] = 0;
|
||||
|
||||
saveDay = 0;
|
||||
saveMonth = 0;
|
||||
saveYear = 0;
|
||||
saveHour = 0;
|
||||
saveMinute = 0;
|
||||
}
|
||||
|
||||
GameState::GameState(Myst3Engine *vm):
|
||||
_vm(vm) {
|
||||
|
||||
@ -205,9 +253,23 @@ GameState::GameState(Myst3Engine *vm):
|
||||
GameState::~GameState() {
|
||||
}
|
||||
|
||||
void GameState::syncFloat(Common::Serializer &s, float &val,
|
||||
Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
|
||||
static const float precision = 10000.0;
|
||||
|
||||
if (s.isLoading()) {
|
||||
int32 saved;
|
||||
s.syncAsSint32LE(saved, minVersion, maxVersion);
|
||||
val = saved / precision;
|
||||
} else {
|
||||
int32 toSave = val * precision;
|
||||
s.syncAsSint32LE(toSave, minVersion, maxVersion);
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::syncWithSaveGame(Common::Serializer &s, StateData &data) {
|
||||
if (!s.syncVersion(kSaveVersion))
|
||||
error("This savegame (v%d) is too recent (max %d) please get a newer version of Residual", s.getVersion(), kSaveVersion);
|
||||
error("This savegame (v%d) is too recent (max %d) please get a newer version of ResidualVM", s.getVersion(), kSaveVersion);
|
||||
|
||||
s.syncAsUint32LE(data.gameRunning);
|
||||
s.syncAsUint32LE(data.currentFrame);
|
||||
@ -223,19 +285,36 @@ void GameState::syncWithSaveGame(Common::Serializer &s, StateData &data) {
|
||||
s.syncAsUint32LE(data.dword_4C2C60);
|
||||
s.syncAsUint32LE(data.currentNodeType);
|
||||
|
||||
// FIXME Syncing IEE754 data is not cross platform
|
||||
// Increase the savegame version and save those as integers
|
||||
s.syncBytes((byte*) &data.lookatPitch, sizeof(float));
|
||||
s.syncBytes((byte*) &data.lookatHeading, sizeof(float));
|
||||
s.syncBytes((byte*) &data.lookatFOV, sizeof(float));
|
||||
s.syncBytes((byte*) &data.pitchOffset, sizeof(float));
|
||||
s.syncBytes((byte*) &data.headingOffset, sizeof(float));
|
||||
// The original engine (v148) saved the raw IEE754 data,
|
||||
// we save decimal data as fixed point instead to be achieve portability
|
||||
if (s.getVersion() < 149) {
|
||||
s.syncBytes((byte*) &data.lookatPitch, sizeof(float));
|
||||
s.syncBytes((byte*) &data.lookatHeading, sizeof(float));
|
||||
s.syncBytes((byte*) &data.lookatFOV, sizeof(float));
|
||||
s.syncBytes((byte*) &data.pitchOffset, sizeof(float));
|
||||
s.syncBytes((byte*) &data.headingOffset, sizeof(float));
|
||||
} else {
|
||||
syncFloat(s, data.lookatPitch);
|
||||
syncFloat(s, data.lookatHeading);
|
||||
syncFloat(s, data.lookatFOV);
|
||||
syncFloat(s, data.pitchOffset);
|
||||
syncFloat(s, data.headingOffset);
|
||||
}
|
||||
|
||||
s.syncAsUint32LE(data.limitCubeCamera);
|
||||
s.syncBytes((byte*) &data.minPitch, sizeof(float));
|
||||
s.syncBytes((byte*) &data.maxPitch, sizeof(float));
|
||||
s.syncBytes((byte*) &data.minHeading, sizeof(float));
|
||||
s.syncBytes((byte*) &data.maxHeading, sizeof(float));
|
||||
|
||||
if (s.getVersion() < 149) {
|
||||
s.syncBytes((byte*) &data.minPitch, sizeof(float));
|
||||
s.syncBytes((byte*) &data.maxPitch, sizeof(float));
|
||||
s.syncBytes((byte*) &data.minHeading, sizeof(float));
|
||||
s.syncBytes((byte*) &data.maxHeading, sizeof(float));
|
||||
} else {
|
||||
syncFloat(s, data.minPitch);
|
||||
syncFloat(s, data.maxPitch);
|
||||
syncFloat(s, data.minHeading);
|
||||
syncFloat(s, data.maxHeading);
|
||||
}
|
||||
|
||||
s.syncAsUint32LE(data.dword_4C2C90);
|
||||
|
||||
for (uint i = 0; i < 2048; i++)
|
||||
@ -248,15 +327,24 @@ void GameState::syncWithSaveGame(Common::Serializer &s, StateData &data) {
|
||||
|
||||
for (uint i = 0; i < 256; i++)
|
||||
s.syncAsByte(data.zipDestinations[i]);
|
||||
|
||||
s.syncAsByte(data.saveDay, 149);
|
||||
s.syncAsByte(data.saveMonth, 149);
|
||||
s.syncAsUint16LE(data.saveYear, 149);
|
||||
s.syncAsByte(data.saveHour, 149);
|
||||
s.syncAsByte(data.saveMinute, 149);
|
||||
s.syncString(data.saveDescription, 149);
|
||||
|
||||
if (s.isLoading()) {
|
||||
Graphics::Surface *thumbnail = new Graphics::Surface();
|
||||
thumbnail->create(240, 135, Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
|
||||
data.thumbnail = Common::SharedPtr<Graphics::Surface>(thumbnail, Graphics::SharedPtrSurfaceDeleter());
|
||||
}
|
||||
s.syncBytes((byte *)data.thumbnail->pixels, 240 * 135 * 4);
|
||||
}
|
||||
|
||||
void GameState::newGame() {
|
||||
memset(&_data, 0, sizeof(_data));
|
||||
|
||||
_data.version = kSaveVersion;
|
||||
_data.gameRunning = true;
|
||||
_data.vars[0] = 0;
|
||||
_data.vars[1] = 1;
|
||||
_data = StateData();
|
||||
}
|
||||
|
||||
bool GameState::load(const Common::String &file) {
|
||||
@ -270,22 +358,18 @@ bool GameState::load(const Common::String &file) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Graphics::Surface *GameState::loadThumbnail(Common::InSaveFile *save) {
|
||||
// Start of thumbnail data
|
||||
save->seek(8580);
|
||||
|
||||
Graphics::Surface *thumbnail = new Graphics::Surface();
|
||||
thumbnail->create(240, 135, Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
|
||||
|
||||
// Read BGRA
|
||||
save->read(thumbnail->pixels, 240 * 135 * 4);
|
||||
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
bool GameState::save(Common::OutSaveFile *saveFile) {
|
||||
Common::Serializer s = Common::Serializer(0, saveFile);
|
||||
|
||||
// Update save creation info
|
||||
TimeDate t;
|
||||
g_system->getTimeAndDate(t);
|
||||
_data.saveYear = t.tm_year + 1900;
|
||||
_data.saveMonth = t.tm_mon + 1;
|
||||
_data.saveDay = t.tm_mday;
|
||||
_data.saveHour = t.tm_hour;
|
||||
_data.saveMinute = t.tm_min;
|
||||
|
||||
_data.gameRunning = false;
|
||||
syncWithSaveGame(s, _data);
|
||||
_data.gameRunning = true;
|
||||
@ -293,6 +377,17 @@ bool GameState::save(Common::OutSaveFile *saveFile) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Graphics::Surface *GameState::getSaveThumbnail() const {
|
||||
return _data.thumbnail.get();
|
||||
}
|
||||
|
||||
void GameState::setSaveThumbnail(Graphics::Surface *thumb) {
|
||||
if (_data.thumbnail.get() == thumb)
|
||||
return;
|
||||
|
||||
_data.thumbnail = Common::SharedPtr<Graphics::Surface>(thumb, Graphics::SharedPtrSurfaceDeleter());
|
||||
}
|
||||
|
||||
Common::Array<uint16> GameState::getInventory() {
|
||||
Common::Array<uint16> items;
|
||||
|
||||
|
@ -51,8 +51,6 @@ public:
|
||||
bool load(const Common::String &file);
|
||||
bool save(Common::OutSaveFile *save);
|
||||
|
||||
static Graphics::Surface *loadThumbnail(Common::InSaveFile *save);
|
||||
|
||||
int32 getVar(uint16 var);
|
||||
void setVar(uint16 var, int32 value);
|
||||
bool evaluate(int16 condition);
|
||||
@ -234,6 +232,11 @@ public:
|
||||
float getMinHeading() { return _data.minHeading; }
|
||||
float getMaxHeading() { return _data.maxHeading; }
|
||||
|
||||
|
||||
Graphics::Surface *getSaveThumbnail() const;
|
||||
void setSaveThumbnail(Graphics::Surface *thumb);
|
||||
void setSaveDescription(const Common::String &description) { _data.saveDescription = description; }
|
||||
|
||||
Common::Array<uint16> getInventory();
|
||||
void updateInventory(const Common::Array<uint16> &items);
|
||||
|
||||
@ -267,6 +270,19 @@ public:
|
||||
uint32 inventoryCount;
|
||||
uint32 inventoryList[7];
|
||||
int8 zipDestinations[256];
|
||||
|
||||
uint8 saveDay;
|
||||
uint8 saveMonth;
|
||||
uint16 saveYear;
|
||||
|
||||
uint8 saveHour;
|
||||
uint8 saveMinute;
|
||||
|
||||
Common::String saveDescription;
|
||||
|
||||
Common::SharedPtr<Graphics::Surface> thumbnail;
|
||||
|
||||
StateData();
|
||||
};
|
||||
|
||||
static void syncWithSaveGame(Common::Serializer &s, StateData &data);
|
||||
@ -274,7 +290,7 @@ public:
|
||||
private:
|
||||
Myst3Engine *_vm;
|
||||
|
||||
static const uint32 kSaveVersion = 148;
|
||||
static const uint32 kSaveVersion = 149;
|
||||
|
||||
StateData _data;
|
||||
|
||||
@ -293,6 +309,10 @@ private:
|
||||
|
||||
int32 engineGet(uint16 var);
|
||||
void engineSet(uint16 var, int32 value);
|
||||
|
||||
static void syncFloat(Common::Serializer &s, float &val,
|
||||
Common::Serializer::Version minVersion = 0,
|
||||
Common::Serializer::Version maxVersion = Common::Serializer::kLastVersion);
|
||||
};
|
||||
|
||||
} /* namespace Myst3 */
|
||||
|
Loading…
Reference in New Issue
Block a user