diff options
32 files changed, 447 insertions, 147 deletions
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp index 0c6eb29665..d41ecb5e9b 100644 --- a/engines/kyra/detection.cpp +++ b/engines/kyra/detection.cpp @@ -1117,7 +1117,7 @@ SaveStateList KyraMetaEngine::listSaves(const char *target) const { if (slotNum >= 0 && slotNum <= 999) { Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); if (in) { - if (Kyra::KyraEngine_v1::readSaveHeader(in, header) == Kyra::KyraEngine_v1::kRSHENoError) + if (Kyra::KyraEngine_v1::readSaveHeader(in, false, header) == Kyra::KyraEngine_v1::kRSHENoError) saveList.push_back(SaveStateDescriptor(slotNum, header.description, *file)); delete in; } diff --git a/engines/kyra/gui.h b/engines/kyra/gui.h index 1361bdb399..7db8f52f16 100644 --- a/engines/kyra/gui.h +++ b/engines/kyra/gui.h @@ -32,6 +32,8 @@ #include "common/array.h" #include "common/func.h" +#include "graphics/surface.h" + namespace Kyra { #define BUTTON_FUNCTOR(type, x, y) Button::Callback(new Common::Functor1Mem<Button*, int, type>(x, y)) @@ -153,6 +155,8 @@ public: void processHighlights(Menu &menu, int mouseX, int mouseY); + // utilities for thumbnail creation + virtual void createScreenThumbnail(Graphics::Surface &dst) = 0; protected: KyraEngine_v1 *_vm; Screen *_screen; diff --git a/engines/kyra/gui_hof.cpp b/engines/kyra/gui_hof.cpp index 7d56743af5..fb2aa24e1e 100644 --- a/engines/kyra/gui_hof.cpp +++ b/engines/kyra/gui_hof.cpp @@ -33,6 +33,8 @@ #include "common/savefile.h" +#include "graphics/scaler.h" + namespace Kyra { void KyraEngine_HoF::loadButtonShapes() { @@ -793,6 +795,10 @@ int GUI_HoF::optionsButton(Button *button) { #pragma mark - +void GUI_HoF::createScreenThumbnail(Graphics::Surface &dst) { + ::createThumbnail(&dst, _vm->_screenBuffer, Screen::SCREEN_W, Screen::SCREEN_H, _screen->getPalette(1)); +} + void GUI_HoF::setupPalette() { memcpy(_screen->getPalette(1), _screen->getPalette(0), 768); @@ -996,7 +1002,7 @@ int GUI_HoF::gameOptionsTalkie(Button *caller) { if (_vm->_lang != lang) { _reloadTemporarySave = true; - _vm->saveGame(_vm->getSavegameFilename(999), "Temporary Kyrandia 2 Savegame"); + _vm->saveGame(_vm->getSavegameFilename(999), "Temporary Kyrandia 2 Savegame", 0); _vm->loadCCodeBuffer("C_CODE.XXX"); if (_vm->_flags.isTalkie) _vm->loadOptionsBuffer("OPTIONS.XXX"); diff --git a/engines/kyra/gui_hof.h b/engines/kyra/gui_hof.h index f64336a8f6..a9c0426a2b 100644 --- a/engines/kyra/gui_hof.h +++ b/engines/kyra/gui_hof.h @@ -41,6 +41,8 @@ public: void initStaticData(); int optionsButton(Button *button); + + void createScreenThumbnail(Graphics::Surface &dst); private: const char *getMenuTitle(const Menu &menu); const char *getMenuItemTitle(const MenuItem &menuItem); diff --git a/engines/kyra/gui_lok.cpp b/engines/kyra/gui_lok.cpp index 47ce698e50..ca1c94e215 100644 --- a/engines/kyra/gui_lok.cpp +++ b/engines/kyra/gui_lok.cpp @@ -37,6 +37,8 @@ #include "common/events.h" #include "common/system.h" +#include "graphics/scaler.h" + namespace Kyra { void KyraEngine_LoK::initMainButtonList() { @@ -199,6 +201,15 @@ GUI_LoK::~GUI_LoK() { delete[] _menu; } +void GUI_LoK::createScreenThumbnail(Graphics::Surface &dst) { + uint8 *screen = new uint8[Screen::SCREEN_W*Screen::SCREEN_H]; + if (screen) { + _screen->queryPageFromDisk("SEENPAGE.TMP", 0, screen); + ::createThumbnail(&dst, screen, Screen::SCREEN_W, Screen::SCREEN_H, _screen->getPalette(2)); + } + delete[] screen; +} + int GUI_LoK::processButtonList(Button *list, uint16 inputFlag, int8 mouseWheel) { while (list) { if (list->flags & 8) { @@ -736,8 +747,12 @@ int GUI_LoK::saveGame(Button *button) { } else { if (_savegameOffset == 0 && _vm->_gameToLoad == 0) _vm->_gameToLoad = getNextSavegameSlot(); - if (_vm->_gameToLoad > 0) - _vm->saveGame(_vm->getSavegameFilename(_vm->_gameToLoad), _savegameName); + if (_vm->_gameToLoad > 0) { + Graphics::Surface thumb; + createScreenThumbnail(thumb); + _vm->saveGame(_vm->getSavegameFilename(_vm->_gameToLoad), _savegameName, &thumb); + thumb.free(); + } } return 0; diff --git a/engines/kyra/gui_lok.h b/engines/kyra/gui_lok.h index 16b7ef9183..0ce718d7a7 100644 --- a/engines/kyra/gui_lok.h +++ b/engines/kyra/gui_lok.h @@ -103,6 +103,8 @@ public: int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel); int buttonMenuCallback(Button *caller); + + void createScreenThumbnail(Graphics::Surface &dst); private: void initStaticResource(); diff --git a/engines/kyra/gui_mr.cpp b/engines/kyra/gui_mr.cpp index 6822b303c3..7a84d7093a 100644 --- a/engines/kyra/gui_mr.cpp +++ b/engines/kyra/gui_mr.cpp @@ -33,6 +33,8 @@ #include "common/savefile.h" +#include "graphics/scaler.h" + namespace Kyra { void KyraEngine_MR::loadButtonShapes() { @@ -1138,6 +1140,10 @@ int KyraEngine_MR::albumClose(Button *caller) { GUI_MR::GUI_MR(KyraEngine_MR *vm) : GUI_v2(vm), _vm(vm), _screen(vm->_screen) { } +void GUI_MR::createScreenThumbnail(Graphics::Surface &dst) { + ::createThumbnail(&dst, _vm->_screenBuffer, Screen::SCREEN_W, Screen::SCREEN_H, _screen->getPalette(0)); +} + void GUI_MR::flagButtonEnable(Button *button) { if (!button) return; @@ -1450,7 +1456,7 @@ int GUI_MR::gameOptions(Button *caller) { if (_vm->_lang != lang) { _reloadTemporarySave = true; - _vm->saveGame(_vm->getSavegameFilename(999), "Temporary Kyrandia 3 Savegame"); + _vm->saveGame(_vm->getSavegameFilename(999), "Temporary Kyrandia 3 Savegame", 0); if (!_vm->loadLanguageFile("ITEMS.", _vm->_itemFile)) error("Couldn't load ITEMS"); if (!_vm->loadLanguageFile("SCORE.", _vm->_scoreFile)) diff --git a/engines/kyra/gui_mr.h b/engines/kyra/gui_mr.h index 5bd3569031..a78d0559a6 100644 --- a/engines/kyra/gui_mr.h +++ b/engines/kyra/gui_mr.h @@ -47,6 +47,8 @@ public: int redrawButtonCallback(Button *button); int optionsButton(Button *button); + + void createScreenThumbnail(Graphics::Surface &dst); private: void getInput(); diff --git a/engines/kyra/gui_v2.cpp b/engines/kyra/gui_v2.cpp index e4cec760fa..e9d81f3b8a 100644 --- a/engines/kyra/gui_v2.cpp +++ b/engines/kyra/gui_v2.cpp @@ -618,7 +618,12 @@ int GUI_v2::saveMenu(Button *caller) { restorePage1(_vm->_screenBuffer); restorePalette(); - _vm->saveGame(_vm->getSavegameFilename(_saveSlot), _saveDescription); + + Graphics::Surface thumb; + createScreenThumbnail(thumb); + _vm->saveGame(_vm->getSavegameFilename(_saveSlot), _saveDescription, &thumb); + thumb.free(); + _displayMenu = false; _madeSave = true; diff --git a/engines/kyra/kyra_hof.cpp b/engines/kyra/kyra_hof.cpp index 0271f5ea0d..2f8f2674e4 100644 --- a/engines/kyra/kyra_hof.cpp +++ b/engines/kyra/kyra_hof.cpp @@ -436,7 +436,7 @@ void KyraEngine_HoF::startup() { if (_gameToLoad == -1) { snd_playWanderScoreViaMap(52, 1); enterNewScene(_mainCharacter.sceneId, _mainCharacter.facing, 0, 0, 1); - saveGame(getSavegameFilename(0), "New Game"); + saveGame(getSavegameFilename(0), "New Game", 0); } else { loadGame(getSavegameFilename(_gameToLoad)); } diff --git a/engines/kyra/kyra_hof.h b/engines/kyra/kyra_hof.h index f6e887c648..dc4161f0c1 100644 --- a/engines/kyra/kyra_hof.h +++ b/engines/kyra/kyra_hof.h @@ -907,7 +907,7 @@ protected: int _dbgPass; // save/load specific - void saveGame(const char *fileName, const char *saveName); + void saveGame(const char *fileName, const char *saveName, const Graphics::Surface *thumbnail); void loadGame(const char *fileName); }; diff --git a/engines/kyra/kyra_lok.cpp b/engines/kyra/kyra_lok.cpp index c852f6e3ee..3d228210b9 100644 --- a/engines/kyra/kyra_lok.cpp +++ b/engines/kyra/kyra_lok.cpp @@ -388,7 +388,7 @@ void KyraEngine_LoK::startup() { _gui->buttonMenuCallback(0); _menuDirectlyToLoad = false; } else - saveGame(getSavegameFilename(0), "New game"); + saveGame(getSavegameFilename(0), "New game", 0); } else { _screen->setFont(Screen::FID_8_FNT); loadGame(getSavegameFilename(_gameToLoad)); @@ -470,7 +470,7 @@ void KyraEngine_LoK::delay(uint32 amount, bool update, bool isMainLoop) { else { char savegameName[14]; sprintf(savegameName, "Quicksave %d", event.kbd.keycode - '0'); - saveGame(saveLoadSlot, savegameName); + saveGame(saveLoadSlot, savegameName, 0); } } else if (event.kbd.flags == Common::KBD_CTRL) { if (event.kbd.keycode == 'd') diff --git a/engines/kyra/kyra_lok.h b/engines/kyra/kyra_lok.h index 1def95ddbf..e6fc0dc774 100644 --- a/engines/kyra/kyra_lok.h +++ b/engines/kyra/kyra_lok.h @@ -214,7 +214,7 @@ public: protected: int32 _speechPlayTime; - void saveGame(const char *fileName, const char *saveName); + void saveGame(const char *fileName, const char *saveName, const Graphics::Surface *thumbnail); void loadGame(const char *fileName); protected: diff --git a/engines/kyra/kyra_mr.cpp b/engines/kyra/kyra_mr.cpp index a61253199b..9b9549b778 100644 --- a/engines/kyra/kyra_mr.cpp +++ b/engines/kyra/kyra_mr.cpp @@ -684,7 +684,7 @@ void KyraEngine_MR::startup() { assert(_invWsa); _invWsa->open("MOODOMTR.WSA", 1, 0); _invWsaFrame = 6; - saveGame(getSavegameFilename(0), (const char*)getTableEntry(_optionsFile, 33)); + saveGame(getSavegameFilename(0), (const char*)getTableEntry(_optionsFile, 33), 0); _soundDigital->beginFadeOut(_musicSoundChannel, 60); delayWithTicks(60); if (_gameToLoad == -1) diff --git a/engines/kyra/kyra_mr.h b/engines/kyra/kyra_mr.h index 5f9f6f91a3..a6fb9af20c 100644 --- a/engines/kyra/kyra_mr.h +++ b/engines/kyra/kyra_mr.h @@ -583,7 +583,7 @@ private: int albumClose(Button *caller); // save/load - void saveGame(const char *fileName, const char *saveName); + void saveGame(const char *fileName, const char *saveName, const Graphics::Surface *thumbnail); void loadGame(const char *fileName); // opcodes diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h index 738a3fc8ec..438d625503 100644 --- a/engines/kyra/kyra_v1.h +++ b/engines/kyra/kyra_v1.h @@ -290,6 +290,8 @@ protected: bool originalSave; // savegame from original interpreter bool oldHeader; // old scummvm save header + + Graphics::Surface *thumbnail; }; enum kReadSaveHeaderError { @@ -299,10 +301,10 @@ protected: kRSHEIoError = 3 }; - static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *file, SaveHeader &header); + static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *file, bool loadThumbnail, SaveHeader &header); Common::SeekableReadStream *openSaveForReading(const char *filename, SaveHeader &header); - Common::WriteStream *openSaveForWriting(const char *filename, const char *saveName) const; + Common::WriteStream *openSaveForWriting(const char *filename, const char *saveName, const Graphics::Surface *thumbnail) const; }; } // End of namespace Kyra diff --git a/engines/kyra/kyra_v2.cpp b/engines/kyra/kyra_v2.cpp index 2e704f2aa2..4e1bf38991 100644 --- a/engines/kyra/kyra_v2.cpp +++ b/engines/kyra/kyra_v2.cpp @@ -186,7 +186,7 @@ int KyraEngine_v2::checkInput(Button *buttonList, bool mainLoop) { } else { char savegameName[14]; sprintf(savegameName, "Quicksave %d", event.kbd.keycode - '0'); - saveGame(saveLoadSlot, savegameName); + saveGame(saveLoadSlot, savegameName, 0); } } else if (event.kbd.flags == Common::KBD_CTRL) { if (event.kbd.keycode == 'd') diff --git a/engines/kyra/kyra_v2.h b/engines/kyra/kyra_v2.h index 6fdf30fff8..e7f9634fc6 100644 --- a/engines/kyra/kyra_v2.h +++ b/engines/kyra/kyra_v2.h @@ -419,7 +419,7 @@ protected: int o2_getVocHigh(EMCState *script); // save/load specific - virtual void saveGame(const char *fileName, const char *saveName) = 0; + virtual void saveGame(const char *fileName, const char *saveName, const Graphics::Surface *thumbnail) = 0; virtual void loadGame(const char *fileName) = 0; }; diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp index 31c5e15fa6..c08967c85c 100644 --- a/engines/kyra/saveload.cpp +++ b/engines/kyra/saveload.cpp @@ -26,10 +26,11 @@ #include "common/endian.h" #include "common/savefile.h" #include "common/system.h" +#include "graphics/thumbnail.h" #include "kyra/kyra_v1.h" -#define CURRENT_SAVE_VERSION 13 +#define CURRENT_SAVE_VERSION 14 #define GF_FLOPPY (1 << 0) #define GF_TALKIE (1 << 1) @@ -37,7 +38,7 @@ namespace Kyra { -KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header) { +KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) { uint32 type = in->readUint32BE(); header.originalSave = false; header.oldHeader = false; @@ -108,6 +109,16 @@ KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::Seekab if (header.version >= 2) header.flags = in->readUint32BE(); + if (header.version >= 14) { + if (loadThumbnail) { + header.thumbnail = new Graphics::Surface(); + assert(header.thumbnail); + Graphics::loadThumbnail(*in, *header.thumbnail); + } else { + Graphics::skipThumbnailHeader(*in); + } + } + return (in->ioFailed() ? kRSHEIoError : kRSHENoError); } @@ -118,7 +129,7 @@ Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filena if (!(in = _saveFileMan->openForLoading(filename))) return 0; - kReadSaveHeaderError errorCode = KyraEngine_v1::readSaveHeader(in, header); + kReadSaveHeaderError errorCode = KyraEngine_v1::readSaveHeader(in, false, header); if (errorCode != kRSHENoError) { if (errorCode == kRSHEInvalidType) warning("No ScummVM Kyra engine savefile header."); @@ -162,8 +173,8 @@ Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filena return in; } -Common::WriteStream *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName) const { - debugC(9, kDebugLevelMain, "KyraEngine_v1::openSaveForWriting('%s', '%s')", filename, saveName); +Common::WriteStream *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName, const Graphics::Surface *thumbnail) const { + debugC(9, kDebugLevelMain, "KyraEngine_v1::openSaveForWriting('%s', '%s', %p)", filename, saveName, (const void *)thumbnail); if (_quitFlag) return 0; @@ -191,6 +202,11 @@ Common::WriteStream *KyraEngine_v1::openSaveForWriting(const char *filename, con return 0; } + if (thumbnail) + Graphics::saveThumbnail(*out, *thumbnail); + else + Graphics::saveThumbnail(*out); + return out; } diff --git a/engines/kyra/saveload_hof.cpp b/engines/kyra/saveload_hof.cpp index 954cbccfa9..2b245f6167 100644 --- a/engines/kyra/saveload_hof.cpp +++ b/engines/kyra/saveload_hof.cpp @@ -35,10 +35,10 @@ namespace Kyra { -void KyraEngine_HoF::saveGame(const char *fileName, const char *saveName) { - debugC(9, kDebugLevelMain, "KyraEngine_HoF::saveGame('%s', '%s')", fileName, saveName); +void KyraEngine_HoF::saveGame(const char *fileName, const char *saveName, const Graphics::Surface *thumb) { + debugC(9, kDebugLevelMain, "KyraEngine_LoK::saveGame('%s', '%s', %p)", fileName, saveName, (const void *)thumb); - Common::OutSaveFile *out = openSaveForWriting(fileName, saveName); + Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb); if (!out) { warning("Can't open file '%s', game not loadable", fileName); return; diff --git a/engines/kyra/saveload_lok.cpp b/engines/kyra/saveload_lok.cpp index 8af73acc61..f734ffd5d9 100644 --- a/engines/kyra/saveload_lok.cpp +++ b/engines/kyra/saveload_lok.cpp @@ -218,13 +218,13 @@ void KyraEngine_LoK::loadGame(const char *fileName) { delete in; } -void KyraEngine_LoK::saveGame(const char *fileName, const char *saveName) { - debugC(9, kDebugLevelMain, "KyraEngine_LoK::saveGame('%s', '%s')", fileName, saveName); +void KyraEngine_LoK::saveGame(const char *fileName, const char *saveName, const Graphics::Surface *thumb) { + debugC(9, kDebugLevelMain, "KyraEngine_LoK::saveGame('%s', '%s', %p)", fileName, saveName, (const void *)thumb); if (_quitFlag) return; - Common::OutSaveFile *out = openSaveForWriting(fileName, saveName); + Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb); if (!out) return; diff --git a/engines/kyra/saveload_mr.cpp b/engines/kyra/saveload_mr.cpp index 51efc33723..8849f99523 100644 --- a/engines/kyra/saveload_mr.cpp +++ b/engines/kyra/saveload_mr.cpp @@ -32,10 +32,10 @@ namespace Kyra { -void KyraEngine_MR::saveGame(const char *fileName, const char *saveName) { - debugC(9, kDebugLevelMain, "KyraEngine_MR::saveGame('%s', '%s')", fileName, saveName); +void KyraEngine_MR::saveGame(const char *fileName, const char *saveName, const Graphics::Surface *thumb) { + debugC(9, kDebugLevelMain, "KyraEngine_LoK::saveGame('%s', '%s', %p)", fileName, saveName, (const void *)thumb); - Common::OutSaveFile *out = openSaveForWriting(fileName, saveName); + Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb); if (!out) { warning("Can't open file '%s', game not loadable", fileName); return; diff --git a/engines/kyra/screen_lok.cpp b/engines/kyra/screen_lok.cpp index 011c90dde9..da88abc61f 100644 --- a/engines/kyra/screen_lok.cpp +++ b/engines/kyra/screen_lok.cpp @@ -147,8 +147,14 @@ void Screen_LoK::savePageToDisk(const char *file, int page) { void Screen_LoK::loadPageFromDisk(const char *file, int page) { debugC(9, kDebugLevelScreen, "Screen_LoK::loadPageFromDisk('%s', %d)", file, page); + if (!_saveLoadPage[page/2]) { + warning("trying to restore page %d, but no backup found", page); + return; + } + copyBlockToPage(page, 0, 0, SCREEN_W, SCREEN_H, _saveLoadPage[page/2]); delete[] _saveLoadPage[page/2]; + _saveLoadPage[page/2] = 0; if (_saveLoadPageOvl[page/2]) { uint8 *dstPage = getOverlayPtr(page); @@ -160,7 +166,17 @@ void Screen_LoK::loadPageFromDisk(const char *file, int page) { memcpy(dstPage, _saveLoadPageOvl[page/2], SCREEN_OVL_SJIS_SIZE); delete[] _saveLoadPageOvl[page/2]; _saveLoadPageOvl[page/2] = 0; - } _saveLoadPage[page/2] = 0; + } +} + +void Screen_LoK::queryPageFromDisk(const char *file, int page, uint8 *buffer) { + debugC(9, kDebugLevelScreen, "Screen_LoK::queryPageFromDisk('%s', %d, %p)", file, page, (const void *)buffer); + if (!_saveLoadPage[page/2]) { + warning("trying to query page %d, but no backup found", page); + return; + } + + memcpy(buffer, _saveLoadPage[page/2], SCREEN_W*SCREEN_H); } void Screen_LoK::deletePageFromDisk(int page) { diff --git a/engines/kyra/screen_lok.h b/engines/kyra/screen_lok.h index 74df23a543..5b4b8a9266 100644 --- a/engines/kyra/screen_lok.h +++ b/engines/kyra/screen_lok.h @@ -50,6 +50,7 @@ public: void savePageToDisk(const char *file, int page); void loadPageFromDisk(const char *file, int page); + void queryPageFromDisk(const char *file, int page, uint8 *buffer); void deletePageFromDisk(int page); void copyBackgroundBlock(int x, int page, int flag); diff --git a/engines/kyra/script_mr.cpp b/engines/kyra/script_mr.cpp index 9a059ead2a..cf2d2aceb1 100644 --- a/engines/kyra/script_mr.cpp +++ b/engines/kyra/script_mr.cpp @@ -293,7 +293,7 @@ int KyraEngine_MR::o3_updateScore(EMCState *script) { int KyraEngine_MR::o3_makeSecondChanceSave(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_makeSecondChanceSave(%p) ()", (const void *)script); - saveGame(getSavegameFilename(999), "SECOND CHANCE SAVE GAME"); + saveGame(getSavegameFilename(999), "SECOND CHANCE SAVE GAME", 0); return 0; } diff --git a/engines/kyra/timer_mr.cpp b/engines/kyra/timer_mr.cpp index 37a910ccf2..ea7f64fed1 100644 --- a/engines/kyra/timer_mr.cpp +++ b/engines/kyra/timer_mr.cpp @@ -65,7 +65,7 @@ void KyraEngine_MR::timerRunSceneScript7(int arg) { void KyraEngine_MR::timerFleaDeath(int arg) { debugC(9, kDebugLevelMain | kDebugLevelTimer, "KyraEngine_MR::timerFleaDeath(%d)", arg); _timer->setCountdown(4, 5400); - saveGame(getSavegameFilename(999), "SECOND CHANCE SAVE GAME"); + saveGame(getSavegameFilename(999), "SECOND CHANCE SAVE GAME", 0); _screen->hideMouse(); _timer->disable(4); runAnimationScript("FLEADTH1.EMC", 0, 0, 1, 1); diff --git a/engines/scumm/thumbnail.cpp b/engines/scumm/thumbnail.cpp index 40f1ee48e5..da5cad945e 100644 --- a/engines/scumm/thumbnail.cpp +++ b/engines/scumm/thumbnail.cpp @@ -27,76 +27,39 @@ #include "common/system.h" #include "common/savefile.h" #include "graphics/scaler.h" +#include "graphics/thumbnail.h" #include "scumm/scumm.h" namespace Scumm { -#define THMB_VERSION 1 - -struct ThumbnailHeader { - uint32 type; - uint32 size; - byte version; - uint16 width, height; - byte bpp; -}; - -#define ThumbnailHeaderSize (4+4+1+2+2+1) - -inline void colorToRGB(uint16 color, uint8 &r, uint8 &g, uint8 &b) { - r = (((color >> 11) & 0x1F) << 3); - g = (((color >> 5) & 0x3F) << 2); - b = ((color&0x1F) << 3); -} - Graphics::Surface *ScummEngine::loadThumbnail(Common::SeekableReadStream *file) { - ThumbnailHeader header; - - header.type = file->readUint32BE(); - // We also accept the bad 'BMHT' header here, for the sake of compatibility - // with some older savegames which were written incorrectly due to a bug in - // ScummVM which wrote the thumb header type incorrectly on LE systems. - if (header.type != MKID_BE('THMB') && header.type != MKID_BE('BMHT')) - return 0; - - header.size = file->readUint32BE(); - header.version = file->readByte(); - - if (header.version > THMB_VERSION) { - file->skip(header.size - 9); - warning("Loading a newer thumbnail version"); - return 0; - } + // TODO: Until backwards seeking in compressed save files is not supported + // We can not use this. - header.width = file->readUint16BE(); - header.height = file->readUint16BE(); - header.bpp = file->readByte(); - - // TODO: support other bpp values than 2 - if (header.bpp != 2) { - file->skip(header.size - 14); - return 0; - } + //if (!Graphics::checkThumbnailHeader(*file)) + // return 0; Graphics::Surface *thumb = new Graphics::Surface(); - thumb->create(header.width, header.height, sizeof(OverlayColor)); - - OverlayColor* pixels = (OverlayColor *)thumb->pixels; - - for (int y = 0; y < thumb->h; ++y) { - for (int x = 0; x < thumb->w; ++x) { - uint8 r, g, b; - colorToRGB(file->readUint16BE(), r, g, b); - - // converting to current OSystem Color - *pixels++ = _system->RGBToColor(r, g, b); - } + assert(thumb); + if (!Graphics::loadThumbnail(*file, *thumb)) { + delete thumb; + return 0; } return thumb; } void ScummEngine::saveThumbnail(Common::OutSaveFile *file) { + // Until we support no thumbnails in the SCUMM save formats for NDS + // we save a dummy thumbnail. + // + // TODO: Actually all what has to be done about it, is to update + // the code in engines/scumm/saveload.o which skips the saveheader. + // Currently impossible because of lacking backward seek support for + // compressed save files. + // When we change that code to use the new API from graphics/thumbnail.h + // it should be all fine to save no header at all for NDS. + Graphics::Surface thumb; #if !defined(__DS__) @@ -104,26 +67,7 @@ void ScummEngine::saveThumbnail(Common::OutSaveFile *file) { #endif thumb.create(kThumbnailWidth, kThumbnailHeight2, sizeof(uint16)); - ThumbnailHeader header; - header.type = MKID_BE('THMB'); - header.size = ThumbnailHeaderSize + thumb.w*thumb.h*thumb.bytesPerPixel; - header.version = THMB_VERSION; - header.width = thumb.w; - header.height = thumb.h; - header.bpp = thumb.bytesPerPixel; - - file->writeUint32BE(header.type); - file->writeUint32BE(header.size); - file->writeByte(header.version); - file->writeUint16BE(header.width); - file->writeUint16BE(header.height); - file->writeByte(header.bpp); - - // TODO: for later this shouldn't be casted to uint16... - uint16* pixels = (uint16 *)thumb.pixels; - for (uint16 p = 0; p < thumb.w*thumb.h; ++p, ++pixels) - file->writeUint16BE(*pixels); - + Graphics::saveThumbnail(*file, thumb); thumb.free(); } diff --git a/graphics/module.mk b/graphics/module.mk index 93e2db26c5..6fb2e89a82 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -16,7 +16,8 @@ MODULE_OBJS := \ primitives.o \ scaler.o \ scaler/thumbnail.o \ - surface.o + surface.o \ + thumbnail.o ifndef DISABLE_SCALERS MODULE_OBJS += \ diff --git a/graphics/scaler.h b/graphics/scaler.h index 2cf3f66239..95900de412 100644 --- a/graphics/scaler.h +++ b/graphics/scaler.h @@ -78,10 +78,22 @@ enum { extern void createThumbnail(const uint8* src, uint32 srcPitch, uint8* dstPtr, uint32 dstPitch, int width, int height); /** - * creates a thumbnail from the current screen (without overlay) + * Creates a thumbnail from the current screen (without overlay). + * * @param surf a surface (will always have 16 bpp after this for now) * @return false if a error occured */ -extern bool createThumbnailFromScreen(Graphics::Surface* surf); +extern bool createThumbnailFromScreen(Graphics::Surface *surf); + +/** + * Creates a thumbnail from a buffer. + * + * @param surf destination surface (will always have 16 bpp after this for now) + * @param pixels raw pixel data + * @param w width + * @param h height + * @param palette palette in RGB format + */ +extern bool createThumbnail(Graphics::Surface *surf, const uint8 *pixels, int w, int h, const uint8 *palette); #endif diff --git a/graphics/scaler/thumbnail.cpp b/graphics/scaler/thumbnail.cpp index f1caa5d2e5..bdfa0ff5f6 100644 --- a/graphics/scaler/thumbnail.cpp +++ b/graphics/scaler/thumbnail.cpp @@ -126,70 +126,93 @@ static bool grabScreen565(Graphics::Surface *surf) { return true; } -bool createThumbnailFromScreen(Graphics::Surface* surf) { - assert(surf); - - int screenWidth = g_system->getWidth(); - int screenHeight = g_system->getHeight(); - - Graphics::Surface screen; - - if (!grabScreen565(&screen)) - return false; +static bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in) { + uint16 width = in.w; + uint16 inHeight = in.h; - uint16 width = screenWidth; - - if (screenWidth < 320) { + if (width < 320) { // Special case to handle MM NES (uses a screen width of 256) width = 320; // center MM NES screen Graphics::Surface newscreen; - newscreen.create(width, screen.h, screen.bytesPerPixel); + newscreen.create(width, in.h, in.bytesPerPixel); - uint8 *dst = (uint8*)newscreen.getBasePtr((320 - screenWidth) / 2, 0); - uint8 *src = (uint8*)screen.getBasePtr(0, 0); - uint16 height = screen.h; + uint8 *dst = (uint8*)newscreen.getBasePtr((320 - in.w) / 2, 0); + const uint8 *src = (uint8*)in.getBasePtr(0, 0); + uint16 height = in.h; while (height--) { - memcpy(dst, src, screen.pitch); + memcpy(dst, src, in.pitch); dst += newscreen.pitch; - src += screen.pitch; + src += in.pitch; } - screen.free(); - screen = newscreen; - } else if (screenWidth == 720) { + in.free(); + in = newscreen; + } else if (width == 720) { // Special case to handle Hercules mode width = 640; - screenHeight = 400; + inHeight = 400; // cut off menu and so on.. Graphics::Surface newscreen; - newscreen.create(width, 400, screen.bytesPerPixel); + newscreen.create(width, 400, in.bytesPerPixel); - uint8 *dst = (uint8*)newscreen.getBasePtr(0, (400 - 240) / 2); - uint8 *src = (uint8*)screen.getBasePtr(41, 28); + uint8 *dst = (uint8*)in.getBasePtr(0, (400 - 240) / 2); + const uint8 *src = (uint8*)in.getBasePtr(41, 28); for (int y = 0; y < 240; ++y) { - memcpy(dst, src, 640 * screen.bytesPerPixel); + memcpy(dst, src, 640 * in.bytesPerPixel); dst += newscreen.pitch; - src += screen.pitch; + src += in.pitch; } - screen.free(); - screen = newscreen; + in.free(); + in = newscreen; } - uint16 newHeight = !(screenHeight % 240) ? kThumbnailHeight2 : kThumbnailHeight1; + uint16 newHeight = !(inHeight % 240) ? kThumbnailHeight2 : kThumbnailHeight1; int gBitFormatBackUp = gBitFormat; gBitFormat = 565; - surf->create(kThumbnailWidth, newHeight, sizeof(uint16)); - createThumbnail((const uint8*)screen.pixels, width * sizeof(uint16), (uint8*)surf->pixels, surf->pitch, width, screenHeight); + out.create(kThumbnailWidth, newHeight, sizeof(uint16)); + createThumbnail((const uint8 *)in.pixels, width * sizeof(uint16), (uint8 *)out.pixels, out.pitch, width, inHeight); gBitFormat = gBitFormatBackUp; - screen.free(); + in.free(); return true; } + +bool createThumbnailFromScreen(Graphics::Surface* surf) { + assert(surf); + + Graphics::Surface screen; + + if (!grabScreen565(&screen)) + return false; + + return createThumbnail(*surf, screen); +} + +bool createThumbnail(Graphics::Surface *surf, const uint8 *pixels, int w, int h, const uint8 *palette) { + assert(surf); + + Graphics::Surface screen; + screen.create(w, h, 2); + + for (uint y = 0; y < screen.h; ++y) { + for (uint x = 0; x < screen.w; ++x) { + byte r, g, b; + r = palette[pixels[y * w + x] * 3]; + g = palette[pixels[y * w + x] * 3 + 1]; + b = palette[pixels[y * w + x] * 3 + 2]; + + ((uint16 *)screen.pixels)[y * screen.w + x] = RGBToColor<ColorMasks<565> >(r, g, b); + } + } + + return createThumbnail(*surf, screen); +} + diff --git a/graphics/thumbnail.cpp b/graphics/thumbnail.cpp new file mode 100644 index 0000000000..eb7dd3c6c1 --- /dev/null +++ b/graphics/thumbnail.cpp @@ -0,0 +1,174 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * 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. + * + * $URL$ + * $Id$ + */ + +#include "graphics/thumbnail.h" +#include "graphics/scaler.h" +#include "common/endian.h" +#include "common/system.h" + +namespace Graphics { + +namespace { +#define THMB_VERSION 1 + +struct ThumbnailHeader { + uint32 type; + uint32 size; + byte version; + uint16 width, height; + byte bpp; +}; + +#define ThumbnailHeaderSize (4+4+1+2+2+1) + +inline void colorToRGB(uint16 color, uint8 &r, uint8 &g, uint8 &b) { + r = (((color >> 11) & 0x1F) << 3); + g = (((color >> 5) & 0x3F) << 2); + b = ((color&0x1F) << 3); +} + +bool loadHeader(Common::SeekableReadStream &in, ThumbnailHeader &header, bool outputWarnings) { + header.type = in.readUint32BE(); + // We also accept the bad 'BMHT' header here, for the sake of compatibility + // with some older savegames which were written incorrectly due to a bug in + // ScummVM which wrote the thumb header type incorrectly on LE systems. + if (header.type != MKID_BE('THMB') && header.type != MKID_BE('BMHT')) { + if (outputWarnings) + warning("couldn't find thumbnail header type"); + return false; + } + + header.size = in.readUint32BE(); + header.version = in.readByte(); + + if (header.version > THMB_VERSION) { + if (outputWarnings) + warning("trying to load a newer thumbnail version: %d instead of %d", header.version, THMB_VERSION); + return false; + } + + header.width = in.readUint16BE(); + header.height = in.readUint16BE(); + header.bpp = in.readByte(); + + return true; +} +} // end of anonymous namespace + +bool checkThumbnailHeader(Common::SeekableReadStream &in) { + uint32 position = in.pos(); + ThumbnailHeader header; + + bool hasHeader = loadHeader(in, header, false); + + in.seek(position, SEEK_SET); + + return hasHeader; +} + +bool skipThumbnailHeader(Common::SeekableReadStream &in) { + uint32 position = in.pos(); + ThumbnailHeader header; + + if (!loadHeader(in, header, true)) { + in.seek(position, SEEK_SET); + return false; + } + + in.seek(header.size - (in.pos() - position), SEEK_CUR); + return true; +} + +bool loadThumbnail(Common::SeekableReadStream &in, Graphics::Surface &to) { + ThumbnailHeader header; + + if (!loadHeader(in, header, true)) + return false; + + if (header.bpp != 2) { + warning("trying to load thumbnail with unsupported bit depth %d", header.bpp); + return false; + } + + to.create(header.width, header.height, sizeof(OverlayColor)); + + OverlayColor *pixels = (OverlayColor *)to.pixels; + for (int y = 0; y < to.h; ++y) { + for (int x = 0; x < to.w; ++x) { + uint8 r, g, b; + colorToRGB(in.readUint16BE(), r, g, b); + + // converting to current OSystem Color + *pixels++ = g_system->RGBToColor(r, g, b); + } + } + + return true; +} + +bool saveThumbnail(Common::WriteStream &out) { + Graphics::Surface thumb; + + if (!createThumbnailFromScreen(&thumb)) { + warning("Couldn't create thumbnail from screen, aborting thumbnail save"); + return false; + } + + bool success = saveThumbnail(out, thumb); + thumb.free(); + + return success; +} + +bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb) { + if (thumb.bytesPerPixel != 2) { + warning("trying to save thumbnail with bpp different than 2"); + return false; + } + + ThumbnailHeader header; + header.type = MKID_BE('THMB'); + header.size = ThumbnailHeaderSize + thumb.w*thumb.h*thumb.bytesPerPixel; + header.version = THMB_VERSION; + header.width = thumb.w; + header.height = thumb.h; + header.bpp = thumb.bytesPerPixel; + + out.writeUint32BE(header.type); + out.writeUint32BE(header.size); + out.writeByte(header.version); + out.writeUint16BE(header.width); + out.writeUint16BE(header.height); + out.writeByte(header.bpp); + + // TODO: for later this shouldn't be casted to uint16... + uint16 *pixels = (uint16 *)thumb.pixels; + for (uint16 p = 0; p < thumb.w*thumb.h; ++p, ++pixels) + out.writeUint16BE(*pixels); + + return true; +} + +} // end of namespace Graphics + diff --git a/graphics/thumbnail.h b/graphics/thumbnail.h new file mode 100644 index 0000000000..0553306519 --- /dev/null +++ b/graphics/thumbnail.h @@ -0,0 +1,69 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * 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. + * + * $URL$ + * $Id$ + */ + +#ifndef GRAPHICS_THUMBNAIL_H +#define GRAPHICS_THUMBNAIL_H + +#include "common/stream.h" +#include "graphics/surface.h" + +namespace Graphics { + +/** + * Checks for presence of the thumbnail save header. + * Seeks automatically back to start position after check. + * + * @param in stream to check for header + */ +bool checkThumbnailHeader(Common::SeekableReadStream &in); + +/** + * Skips a thumbnail header, if present. + * + * @param in stream to process + */ +bool skipThumbnailHeader(Common::SeekableReadStream &in); + +/** + * Lodas a thumbnail from the given input stream. + * The loaded thumbnail will be automatically converted to the + * current overlay pixelformat. + */ +bool loadThumbnail(Common::SeekableReadStream &in, Graphics::Surface &to); + +/** + * Saves a thumbnail to the given write stream. + * Automatically creates a thumbnail from screen contents. + */ +bool saveThumbnail(Common::WriteStream &out); + +/** + * Saves a (given) thumbnail to the given write stream. + */ +bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb); + +} // end of namespace Graphics + +#endif + |