367 lines
10 KiB
C++
367 lines
10 KiB
C++
/* ResidualVM - A 3D game interpreter
|
|
*
|
|
* ResidualVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the AUTHORS
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "engines/stark/ui/menu/saveloadmenu.h"
|
|
|
|
#include "engines/stark/services/services.h"
|
|
#include "engines/stark/services/userinterface.h"
|
|
#include "engines/stark/services/stateprovider.h"
|
|
#include "engines/stark/services/global.h"
|
|
#include "engines/stark/services/settings.h"
|
|
#include "engines/stark/services/gamechapter.h"
|
|
#include "engines/stark/services/gamemessage.h"
|
|
|
|
#include "engines/stark/gfx/driver.h"
|
|
#include "engines/stark/gfx/texture.h"
|
|
#include "engines/stark/gfx/surfacerenderer.h"
|
|
|
|
#include "engines/stark/stark.h"
|
|
#include "engines/stark/savemetadata.h"
|
|
|
|
#include "engines/engine.h"
|
|
|
|
#include "common/config-manager.h"
|
|
#include "common/savefile.h"
|
|
|
|
#include "gui/message.h"
|
|
|
|
namespace Stark {
|
|
|
|
const Color SaveDataWidget::_outlineColor = Color(0x1E, 0x1E, 0x96);
|
|
const Color SaveDataWidget::_textColor = Color(0x5C, 0x48, 0x3D);
|
|
|
|
SaveLoadMenuScreen::SaveLoadMenuScreen(Gfx::Driver *gfx, Cursor *cursor, Screen::Name screenName) :
|
|
StaticLocationScreen(gfx, cursor, "LoadSaveLocation", screenName),
|
|
_page(0),
|
|
_maxPage(10) {
|
|
}
|
|
|
|
SaveLoadMenuScreen::~SaveLoadMenuScreen() {
|
|
}
|
|
|
|
void SaveLoadMenuScreen::open() {
|
|
StaticLocationScreen::open();
|
|
|
|
_maxPage = computeMaxPage();
|
|
|
|
_page = StarkSettings->getIntSetting(Settings::kSaveLoadPage);
|
|
if (_page > _maxPage) {
|
|
_page = _maxPage;
|
|
}
|
|
|
|
_widgets.push_back(new StaticLocationWidget(
|
|
"loadsavebg",
|
|
nullptr,
|
|
nullptr));
|
|
|
|
_widgets.push_back(new StaticLocationWidget(
|
|
"back to index",
|
|
CLICK_HANDLER(SaveLoadMenuScreen, backHandler),
|
|
nullptr));
|
|
_widgets.back()->setupSounds(0, 1);
|
|
|
|
_widgets.push_back(new StaticLocationWidget(
|
|
"Cancel",
|
|
CLICK_HANDLER(SaveLoadMenuScreen, backHandler),
|
|
nullptr));
|
|
_widgets.back()->setupSounds(0, 1);
|
|
|
|
_widgets.push_back(new StaticLocationWidget(
|
|
"SaveText",
|
|
nullptr,
|
|
nullptr));
|
|
|
|
_widgets.push_back(new StaticLocationWidget(
|
|
"LoadText",
|
|
nullptr,
|
|
nullptr));
|
|
|
|
_widgets.push_back(new StaticLocationWidget(
|
|
"Back",
|
|
CLICK_HANDLER(SaveLoadMenuScreen, prevPageHandler),
|
|
nullptr));
|
|
_widgets.back()->setupSounds(0, 1);
|
|
_widgets.back()->setTextColor(Color(0, 0, 0));
|
|
_widgets.back()->setVisible(_page > 0);
|
|
|
|
_widgets.push_back(new StaticLocationWidget(
|
|
"Next",
|
|
CLICK_HANDLER(SaveLoadMenuScreen, nextPageHandler),
|
|
nullptr));
|
|
_widgets.back()->setupSounds(0, 1);
|
|
_widgets.back()->setTextColor(Color(0, 0, 0));
|
|
_widgets.back()->setVisible(_page < _maxPage);
|
|
|
|
loadSaveData(_page);
|
|
}
|
|
|
|
void SaveLoadMenuScreen::close() {
|
|
ConfMan.flushToDisk();
|
|
StaticLocationScreen::close();
|
|
}
|
|
|
|
int SaveLoadMenuScreen::computeMaxPage() {
|
|
const char *target = ConfMan.getActiveDomainName().c_str();
|
|
|
|
int maxSlot = 0;
|
|
Common::StringArray saves = StarkEngine::listSaveNames(target);
|
|
for (Common::StringArray::const_iterator filename = saves.begin(); filename != saves.end(); filename++) {
|
|
int slot = StarkEngine::getSaveNameSlot(target, *filename);
|
|
|
|
if (slot > maxSlot) {
|
|
maxSlot = slot;
|
|
}
|
|
}
|
|
|
|
// Allow using one more page than the last page with saves
|
|
int maxPage = CLIP((maxSlot / _slotPerPage) + 1, 0, 110);
|
|
if (maxPage < 10) {
|
|
maxPage = 10;
|
|
}
|
|
|
|
return maxPage;
|
|
}
|
|
|
|
void SaveLoadMenuScreen::backHandler() {
|
|
StarkUserInterface->backPrevScreen();
|
|
}
|
|
|
|
void SaveLoadMenuScreen::checkError(Common::Error error) {
|
|
if (error.getCode() != Common::kNoError) {
|
|
GUI::MessageDialog dialog(error.getDesc());
|
|
dialog.runModal();
|
|
}
|
|
}
|
|
|
|
void SaveLoadMenuScreen::removeSaveDataWidgets() {
|
|
assert(_widgets.size() == 7 + _slotPerPage);
|
|
|
|
for (int i = 0; i < _slotPerPage; ++i) {
|
|
delete _widgets.back();
|
|
_widgets.pop_back();
|
|
}
|
|
}
|
|
|
|
void SaveLoadMenuScreen::loadSaveData(int page) {
|
|
for (int i = 0; i < _slotPerPage; ++i) {
|
|
_widgets.push_back(new SaveDataWidget(i + page * _slotPerPage, _gfx, this));
|
|
}
|
|
}
|
|
|
|
void SaveLoadMenuScreen::changePage(int page) {
|
|
assert(page >= 0 && page <= _maxPage);
|
|
|
|
removeSaveDataWidgets();
|
|
loadSaveData(page);
|
|
|
|
_widgets[kWidgetBack]->setVisible(page > 0);
|
|
_widgets[kWidgetNext]->setVisible(page < _maxPage);
|
|
|
|
StarkSettings->setIntSetting(Settings::kSaveLoadPage, page);
|
|
_page = page;
|
|
}
|
|
|
|
void SaveMenuScreen::open() {
|
|
SaveLoadMenuScreen::open();
|
|
_widgets[kWidgetLoadText]->setVisible(false);
|
|
}
|
|
|
|
void SaveMenuScreen::onWidgetSelected(SaveDataWidget *widget) {
|
|
if (widget->hasSave()) {
|
|
_slotToSaveAfterConfirm = widget;
|
|
|
|
Common::String format = StarkGameMessage->getTextByKey(GameMessage::kOverwriteSave);
|
|
Common::String prompt = Common::String::format(format.c_str(), widget->getName().c_str());
|
|
|
|
StarkUserInterface->confirm(prompt, this, &SaveMenuScreen::saveConfirmSlot);
|
|
} else {
|
|
saveGameToSlot(widget);
|
|
}
|
|
}
|
|
|
|
void SaveMenuScreen::saveConfirmSlot() {
|
|
assert(_slotToSaveAfterConfirm);
|
|
|
|
saveGameToSlot(_slotToSaveAfterConfirm);
|
|
_slotToSaveAfterConfirm = nullptr;
|
|
}
|
|
|
|
void SaveMenuScreen::saveGameToSlot(SaveDataWidget *widget) {
|
|
checkError(g_engine->saveGameState(widget->getSlot(), StarkGameChapter->getCurrentChapterTitle()));
|
|
|
|
// Freeze the screen for a while to let the user notice the change
|
|
widget->loadSaveDataElements();
|
|
render();
|
|
StarkGfx->flipBuffer();
|
|
g_system->delayMillis(100);
|
|
render();
|
|
StarkGfx->flipBuffer();
|
|
|
|
StarkUserInterface->backPrevScreen();
|
|
}
|
|
|
|
void LoadMenuScreen::open() {
|
|
SaveLoadMenuScreen::open();
|
|
_widgets[kWidgetSaveText]->setVisible(false);
|
|
}
|
|
|
|
void LoadMenuScreen::onWidgetSelected(SaveDataWidget *widget) {
|
|
if (!StarkGlobal->getCurrent()) {
|
|
checkError(g_engine->loadGameState(widget->getSlot()));
|
|
} else {
|
|
_slotToLoadAfterConfirm = widget->getSlot();
|
|
StarkUserInterface->confirm(GameMessage::kEndAndLoad, this, &LoadMenuScreen::loadConfirmSlot);
|
|
}
|
|
}
|
|
|
|
void LoadMenuScreen::loadConfirmSlot() {
|
|
assert(_slotToLoadAfterConfirm >= 0);
|
|
|
|
checkError(g_engine->loadGameState(_slotToLoadAfterConfirm));
|
|
_slotToLoadAfterConfirm = -1;
|
|
}
|
|
|
|
SaveDataWidget::SaveDataWidget(int slot, Gfx::Driver *gfx, SaveLoadMenuScreen *screen) :
|
|
StaticLocationWidget(nullptr, nullptr, nullptr),
|
|
_slot(slot),
|
|
_screen(screen),
|
|
_thumbWidth(kThumbnailWidth),
|
|
_thumbHeight(kThumbnailHeight),
|
|
_texture(gfx->createBitmap()),
|
|
_outline(gfx->createBitmap()),
|
|
_surfaceRenderer(gfx->createSurfaceRenderer()),
|
|
_textDesc(gfx),
|
|
_textTime(gfx),
|
|
_isMouseHovered(false),
|
|
_hasSave(false),
|
|
_name() {
|
|
// Load from the save data
|
|
loadSaveDataElements();
|
|
|
|
_textDesc.setColor(_textColor);
|
|
_textDesc.setFont(FontProvider::kCustomFont, 3);
|
|
|
|
_textTime.setColor(_textColor);
|
|
_textTime.setFont(FontProvider::kCustomFont, 3);
|
|
|
|
Graphics::PixelFormat pixelFormat = Gfx::Driver::getRGBAPixelFormat();
|
|
uint32 outlineColor = pixelFormat.ARGBToColor(
|
|
_outlineColor.a, _outlineColor.r, _outlineColor.g, _outlineColor.b
|
|
);
|
|
|
|
// Create the outline texture
|
|
Graphics::Surface lineSurface;
|
|
lineSurface.create(_thumbWidth, _thumbHeight, pixelFormat);
|
|
lineSurface.drawThickLine(0, 0, _thumbWidth - 1, 0, 2, 2, outlineColor);
|
|
lineSurface.drawThickLine(0, 0, 0, _thumbHeight - 1, 2, 2, outlineColor);
|
|
lineSurface.drawThickLine(_thumbWidth - 2, 0, _thumbWidth - 2, _thumbHeight - 2, 2, 2, outlineColor);
|
|
lineSurface.drawThickLine(0, _thumbHeight - 2, _thumbWidth - 2, _thumbHeight - 2, 2, 2, outlineColor);
|
|
|
|
_outline->update(&lineSurface);
|
|
lineSurface.free();
|
|
|
|
// Set the position
|
|
_thumbPos.x = 41 + (_slot % SaveLoadMenuScreen::_slotPerRow) * (_thumbWidth + 39);
|
|
_thumbPos.y = 61 + (_slot % SaveLoadMenuScreen::_slotPerPage / SaveLoadMenuScreen::_slotPerColumn) * (_thumbHeight + 38);
|
|
|
|
_textDescPos.x = _thumbPos.x;
|
|
_textDescPos.y = _thumbPos.y + _thumbHeight + 2;
|
|
|
|
_textTimePos.x = _thumbPos.x;
|
|
_textTimePos.y = _textDescPos.y + 12;
|
|
}
|
|
|
|
SaveDataWidget::~SaveDataWidget() {
|
|
delete _texture;
|
|
delete _outline;
|
|
delete _surfaceRenderer;
|
|
}
|
|
|
|
void SaveDataWidget::render() {
|
|
_surfaceRenderer->render(_texture, _thumbPos);
|
|
_textDesc.render(_textDescPos);
|
|
_textTime.render(_textTimePos);
|
|
if (_isMouseHovered) {
|
|
_surfaceRenderer->render(_outline, _thumbPos);
|
|
}
|
|
}
|
|
|
|
bool SaveDataWidget::isMouseInside(const Common::Point &mousePos) const {
|
|
return mousePos.x >= _thumbPos.x && mousePos.x <= _thumbPos.x + _thumbWidth &&
|
|
mousePos.y >= _thumbPos.y && mousePos.y <= _thumbPos.y + _thumbHeight;
|
|
}
|
|
|
|
void SaveDataWidget::onClick() {
|
|
StaticLocationWidget::onClick();
|
|
_screen->onWidgetSelected(this);
|
|
}
|
|
|
|
void SaveDataWidget::onMouseMove(const Common::Point &mousePos) {
|
|
StaticLocationWidget::onMouseMove(mousePos);
|
|
_isMouseHovered = isMouseInside(mousePos);
|
|
}
|
|
|
|
void SaveDataWidget::onScreenChanged() {
|
|
StaticLocationWidget::onScreenChanged();
|
|
_textDesc.resetTexture();
|
|
_textTime.resetTexture();
|
|
}
|
|
|
|
void SaveDataWidget::loadSaveDataElements() {
|
|
Common::String filename = StarkEngine::formatSaveName(ConfMan.getActiveDomainName().c_str(), _slot);
|
|
Common::InSaveFile *save = g_system->getSavefileManager()->openForLoading(filename);
|
|
if (save) {
|
|
_hasSave = true;
|
|
|
|
SaveMetadata metadata;
|
|
StateReadStream stream(save);
|
|
Common::ErrorCode metadataErrorCode = metadata.read(&stream, filename);
|
|
if (metadataErrorCode != Common::kNoError) {
|
|
error("Unable to read save metadata with error code %d.", metadataErrorCode);
|
|
}
|
|
|
|
// Obtain the thumbnail
|
|
if (metadata.version >= 9) {
|
|
Graphics::Surface *thumb = metadata.readGameScreenThumbnail(&stream);
|
|
_texture->update(thumb);
|
|
_texture->setSamplingFilter(StarkSettings->getImageSamplingFilter());
|
|
|
|
thumb->free();
|
|
delete thumb;
|
|
}
|
|
|
|
// Obtain the text
|
|
Common::String desc = metadata.description;
|
|
Common::String time = Common::String::format("%02d:%02d:%02d %02d/%02d/%02d",
|
|
metadata.saveHour, metadata.saveMinute, metadata.saveSecond,
|
|
metadata.saveMonth, metadata.saveDay, metadata.saveYear % 100);
|
|
|
|
_textDesc.setText(desc);
|
|
_textTime.setText(time);
|
|
_name = desc + " " + time;
|
|
} else {
|
|
_hasSave = false;
|
|
setVisible(_screen->isSaveMenu());
|
|
}
|
|
}
|
|
|
|
} // End of namespace Stark
|