scummvm/engines/dm/dm.h
Adrian Frühwirth 00e59a3122 ALL: Load savegame thumbnail only when necessary
This commit introduces the following changes:

1. Graphics::loadThumbnail()

   Now returns a boolean and takes a new argument skipThumbnail which
   defaults to false. In case of true, loadThumbnail() reads past the
   thumbnail data in the input stream instead of actually loading the
   thumbnail. This simplifies savegame handling where, up until now,
   many engines always read the whole savegame metadata (including
   the thumbnail) and then threw away the thumbnail when not needed
   (which is in almost all cases, the most common exception being
   MetaEngine::querySaveMetaInfos() which is responsible for loading
   savegame metadata for displaying it in the GUI launcher.

2. readSavegameHeader()

   Engines which already implement such a method (name varies) now take
   a new argument skipThumbnail (default: true) which is passed
   through to loadThumbnail(). This means that the default case for
   readSavegameHeader() is now _not_ loading the thumbnail from a
   savegame and just reading past it. In those cases, e.g.
   querySaveMetaInfos(), where we actually are interested in loading
   the thumbnail readSavegameHeader() needs to explicitely be called
   with skipThumbnail == false.

   Engines whose readSavegameHeader() (name varies) already takes an
   argument loadThumbnail have been adapted to have a similar
   prototype and semantics.
   I.e. readSaveHeader(in, loadThumbnail, header) now is
   readSaveHeader(in, header, skipThumbnail).

3. Error handling

   Engines which previously did not check the return value of
   readSavegameHeader() (name varies) now do so ensuring that possibly
   broken savegames (be it a broken thumbnail or something else) don't
   make it into the GUI launcher list in the first place.
2018-04-07 09:26:20 +02:00

340 lines
12 KiB
C++

/* 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.
*
*/
/*
* Based on the Reverse Engineering work of Christophe Fontanel,
* maintainer of the Dungeon Master Encyclopaedia (http://dmweb.free.fr/)
*/
#ifndef DM_DM_H
#define DM_DM_H
#include "engines/engine.h"
#include "engines/savestate.h"
#include "common/random.h"
#include "common/savefile.h"
#include "common/str.h"
#include "common/memstream.h"
#include "advancedDetector.h"
#include "dm/console.h"
struct ADGameDescription;
namespace DM {
class DisplayMan;
class DungeonMan;
class EventManager;
class MenuMan;
class ChampionMan;
class ObjectMan;
class InventoryMan;
class TextMan;
class MovesensMan;
class GroupMan;
class Timeline;
class ProjExpl;
class DialogMan;
class SoundMan;
enum OriginalSaveFormat {
kDMSaveFormatAcceptAny = -1,
kDMSaveFormatEndOfList = 0,
kDMSaveFormatNone = 0,
kDMSaveFormatAtari = 1,
kDMSaveFormatAmigaPC98FmTowns = 2,
kCSBSaveFormatAtari = 2,
kDMSaveFormatAppleIIgs = 3,
kDMSaveFormatAmiga36PC = 5,
kCSBSaveFormatAmigaPC98FmTowns = 5,
kDMSaveFormatTotal
};
enum OriginalSavePlatform {
kDMSavePlatformAcceptAny = -1,
kDMSavePlatformEndOfList = 0,
kDMSavePlatformNone = 0,
kDMSavePlatformAtariSt = 1, // @ C1_PLATFORM_ATARI_ST
kDMSavePlatformAppleIIgs = 2, // @ C2_PLATFORM_APPLE_IIGS
kDMSavePlatformAmiga = 3, // @ C3_PLATFORM_AMIGA
kDMSavePlatformPC98 = 5, // @ C5_PLATFORM_PC98
kDMSavePlatformX68000 = 6, // @ C6_PLATFORM_X68000
kDMSavePlatformFmTownsEN = 7, // @ C7_PLATFORM_FM_TOWNS_EN
kDMSavePlatformFmTownsJP = 8, // @ C8_PLATFORM_FM_TOWNS_JP
kDMSavePlatformPC = 9, // @ C9_PLATFORM_PC
kDMSavePlatformTotal
};
enum SaveTarget {
kDMSaveTargetAcceptAny = -1,
kDMSaveTargetEndOfList = 0,
kDMSaveTargetNone = 0,
kDMSaveTargetDM21 = 1,
kDMSaveTargetTotal
};
enum Direction {
kDMDirNorth = 0,
kDMDirEast = 1,
kDMDirSouth = 2,
kDMDirWest = 3
};
enum ThingType {
kDMThingTypeParty = -1, // @ CM1_THING_TYPE_PARTY
kDMThingTypeDoor = 0, // @ C00_THING_TYPE_DOOR
kDMThingTypeTeleporter = 1, // @ C01_THING_TYPE_TELEPORTER
kDMstringTypeText = 2, // @ C02_THING_TYPE_TEXTSTRING
kDMThingTypeSensor = 3, // @ C03_THING_TYPE_SENSOR
kDMThingTypeGroup = 4, // @ C04_THING_TYPE_GROUP
kDMThingTypeWeapon = 5, // @ C05_THING_TYPE_WEAPON
kDMThingTypeArmour = 6, // @ C06_THING_TYPE_ARMOUR
kDMThingTypeScroll = 7, // @ C07_THING_TYPE_SCROLL
kDMThingTypePotion = 8, // @ C08_THING_TYPE_POTION
kDMThingTypeContainer = 9, // @ C09_THING_TYPE_CONTAINER
kDMThingTypeJunk = 10, // @ C10_THING_TYPE_JUNK
kDMThingTypeProjectile = 14, // @ C14_THING_TYPE_PROJECTILE
kDMThingTypeExplosion = 15, // @ C15_THING_TYPE_EXPLOSION
kDMThingTypeTotal = 16 // +1 than the last (explosionThingType)
}; // @ C[00..15]_THING_TYPE_...
enum Cell {
kDMCellAny = -1, // @ CM1_CELL_ANY
kDMCellNorthWest = 0, // @ C00_CELL_NORTHWEST
kDMCellNorthEast = 1, // @ C01_CELL_NORTHEAST
kDMCellSouthEast = 2, // @ C02_CELL_SOUTHEAST
kDMCellSouthWest = 3 // @ C03_CELL_SOUTHWEST
};
enum GameMode {
kDMModeLoadSavedGame = 0, // @ C000_MODE_LOAD_SAVED_GAME
kDMModeLoadDungeon = 1, // @ C001_MODE_LOAD_DUNGEON
kDMModeWaitingOnEntrance = 99, // @ C099_MODE_WAITING_ON_ENTRANCE
kDMModeEntranceDrawCredits = 202 // @ C202_MODE_ENTRANCE_DRAW_CREDITS
};
enum LoadgameResult {
kDMLoadgameFailure = -1, // @ CM1_LOAD_GAME_FAILURE
kDMLoadgameSuccess = 1// @ C01_LOAD_GAME_SUCCESS
};
enum MapIndice {
kDMMapIndexNone = -1, // @ CM1_MAP_INDEX_NONE
kDMMapIndexEntrance = 255 // @ C255_MAP_INDEX_ENTRANCE
};
#define kDMMaskDecodeEvenIfInvisible 0x8000 // @ MASK0x8000_DECODE_EVEN_IF_INVISIBLE
#define kDMMaskMergeCycles 0x8000 // @ MASK0x8000_MERGE_CYCLES
#define kDMSlotBoxInventoryFirstSlot 8 // @ C08_SLOT_BOX_INVENTORY_FIRST_SLOT
#define kDMSlotBoxInventoryActionHand 9 // @ C09_SLOT_BOX_INVENTORY_ACTION_HAND
#define kDMSlotBoxChestFirstSlot 38 // @ C38_SLOT_BOX_CHEST_FIRST_SLOT
struct DMADGameDescription {
ADGameDescription _desc;
SaveTarget _saveTargetToWrite;
OriginalSaveFormat _origSaveFormatToWrite;
OriginalSavePlatform _origPlatformToWrite;
SaveTarget _saveTargetToAccept[kDMSaveTargetTotal + 1];
OriginalSaveFormat _saveFormatToAccept[kDMSaveFormatTotal + 1];
OriginalSavePlatform _origPlatformToAccept[kDMSavePlatformTotal + 1];
};
class Thing {
public:
uint16 _data;
Thing() : _data(0) {}
Thing(const Thing &other) { set(other._data); }
explicit Thing(uint16 d) { set(d); }
void set(uint16 d) {
_data = d;
}
byte getCell() const { return _data >> 14; }
ThingType getType() const { return (ThingType)((_data >> 10) & 0xF); }
uint16 getIndex() const { return _data & 0x3FF; }
void setCell(uint16 cell) { _data = (_data & ~(0x3 << 14)) | ((cell & 0x3) << 14); }
void setType(uint16 type) { _data = (_data & ~(0xF << 10)) | ((type & 0xF) << 10); }
void setIndex(uint16 index) { _data = (_data & ~0x3FF) | (index & 0x3FF); }
uint16 getTypeAndIndex() { return _data & 0x3FFF; }
uint16 toUint16() const { return _data; } // I don't like 'em cast operators
bool operator==(const Thing &rhs) const { return _data == rhs._data; }
bool operator!=(const Thing &rhs) const { return _data != rhs._data; }
}; // @ THING
#define setFlag(val, mask) ((val) |= (mask))
#define getFlag(val, mask) ((val) & (mask))
#define clearFlag(val, mask) ((val) &= (~(mask))) // @ M09_CLEAR
// Note: F0026_MAIN_GetBoundedValue<T> has been replaced by CLIP<T>
#define CALL_MEMBER_FN(object, ptrToMember) ((object).*(ptrToMember))
struct SaveGameHeader {
byte _version;
SaveStateDescriptor _descr;
};
class DMEngine : public Engine {
private:
void startGame(); // @ F0462_START_StartGame_CPSF
void processNewPartyMap(uint16 mapIndex); // @ F0003_MAIN_ProcessNewPartyMap_CPSE
void initializeGame(); // @ F0463_START_InitializeGame_CPSADEF
void initMemoryManager(); // @ F0448_STARTUP1_InitializeMemoryManager_CPSADEF
void gameloop(); // @ F0002_MAIN_GameLoop_CPSDF
void initConstants();
Common::String getSavefileName(uint16 slot);
void writeSaveGameHeader(Common::OutSaveFile *out, const Common::String &saveName);
bool writeCompleteSaveFile(int16 slot, Common::String &desc, int16 saveAndPlayChoice);
void drawEntrance(); // @ F0439_STARTEND_DrawEntrance
void fuseSequenceUpdate(); // @ F0445_STARTEND_FuseSequenceUpdate
void processEntrance(); // @ F0441_STARTEND_ProcessEntrance
void openEntranceDoors(); // @ F0438_STARTEND_OpenEntranceDoors
void drawTittle(); // @ F0437_STARTEND_DrawTitle
public:
explicit DMEngine(OSystem *syst, const DMADGameDescription *gameDesc);
~DMEngine();
virtual bool hasFeature(EngineFeature f) const;
virtual Common::Error loadGameState(int slot);
virtual bool canLoadGameStateCurrently();
bool isDemo() const;
GUI::Debugger *getDebugger() { return _console; }
void delay(uint16 verticalBlank); // @ F0022_MAIN_Delay
uint16 getScaledProduct(uint16 val, uint16 scale, uint16 vale2); // @ F0030_MAIN_GetScaledProduct
uint16 getRandomNumber(uint32 max) { return _rnd->getRandomNumber(max - 1); }
int16 ordinalToIndex(int16 val); // @ M01_ORDINAL_TO_INDEX
int16 indexToOrdinal(int16 val); // @ M00_INDEX_TO_ORDINAL
virtual Common::Error run(); // @ main
void saveGame(); // @ F0433_STARTEND_ProcessCommand140_SaveGame_CPSCDF
LoadgameResult loadgame(int16 slot); // @ F0435_STARTEND_LoadGame_CPSF
void endGame(bool doNotDrawCreditsOnly); // @ F0444_STARTEND_Endgame
void entranceDrawCredits();
void fuseSequence(); // @ F0446_STARTEND_FuseSequence
Common::Language getGameLanguage();
Direction turnDirRight(int16 dir); // @ M17_NEXT
Direction turnDirLeft(int16 dir); // @ M19_PREVIOUS
Direction returnOppositeDir(int16 dir); // @ M18_OPPOSITE
bool isOrientedWestEast(int16 dir); // @ M16_IS_ORIENTED_WEST_EAST
uint16 normalizeModulo4(int16 dir); // @ M21_NORMALIZE
int32 filterTime(int32 mapTime); // @ M30_TIME
int32 setMapAndTime(uint32 map, uint32 time); // @ M33_SET_MAP_AND_TIME
uint16 getMap(int32 mapTime); // @ M29_MAP
Thing thingWithNewCell(Thing thing, int16 cell); // @ M15_THING_WITH_NEW_CELL
int16 getDistance(int16 mapx1, int16 mapy1, int16 mapx2, int16 mapy2); // @ M38_DISTANCE
int32 setMap(int32 mapTime, uint32 map); // @ M31_setMap
private:
uint16 _dungeonId; // @ G0526_ui_DungeonID
byte *_entranceDoorAnimSteps[10]; // @ G0562_apuc_Bitmap_EntranceDoorAnimationSteps
byte *_interfaceCredits; // @ G0564_puc_Graphic5_InterfaceCredits
Common::RandomSource *_rnd;
byte *_savedScreenForOpenEntranceDoors; // ad-hoc HACK
const DMADGameDescription *_gameVersion;
bool _canLoadFromGMM;
public:
Console *_console;
DisplayMan *_displayMan;
DungeonMan *_dungeonMan;
EventManager *_eventMan;
MenuMan *_menuMan;
ChampionMan *_championMan;
ObjectMan *_objectMan;
InventoryMan *_inventoryMan;
TextMan *_textMan;
MovesensMan *_moveSens;
GroupMan *_groupMan;
Timeline *_timeline;
ProjExpl *_projexpl;
DialogMan *_dialog;
SoundMan *_sound;
Common::MemoryWriteStreamDynamic *_saveThumbnail;
bool _engineShouldQuit;
int _loadSaveSlotAtRuntime;
GameMode _gameMode; // @ G0298_B_NewGame
bool _restartGameRequest; // @ G0523_B_RestartGameRequested
bool _stopWaitingForPlayerInput; // @ G0321_B_StopWaitingForPlayerInput
bool _gameTimeTicking; // @ G0301_B_GameTimeTicking
bool _restartGameAllowed; // @ G0524_B_RestartGameAllowed
bool _pressingEye; // @ G0331_B_PressingEye
bool _stopPressingEye; // @ G0332_B_StopPressingEye
bool _pressingMouth; // @ G0333_B_PressingMouth
bool _stopPressingMouth; // @ G0334_B_StopPressingMouth
bool _highlightBoxInversionRequested; // @ G0340_B_HighlightBoxInversionRequested
int16 _projectileDisableMovementTicks; // @ G0311_i_ProjectileDisabledMovementTicks
int16 _lastProjectileDisabledMovementDirection; // @ G0312_i_LastProjectileDisabledMovementDirection
bool _gameWon; // @ G0302_B_GameWon
int16 _newPartyMapIndex; // @ G0327_i_NewPartyMapIndex
bool _setMousePointerToObjectInMainLoop; // @ G0325_B_SetMousePointerToObjectInMainLoop
int16 _disabledMovementTicks; // @ G0310_i_DisabledMovementTicks
int8 _dirIntoStepCountEast[4]; // @ G0233_ai_Graphic559_DirectionToStepEastCount
int8 _dirIntoStepCountNorth[4]; // @ G0234_ai_Graphic559_DirectionToStepNorthCount
int32 _gameTime; // @ G0313_ul_GameTime
char _stringBuildBuffer[128]; // @ G0353_ac_StringBuildBuffer
int16 _waitForInputMaxVerticalBlankCount; // @ G0318_i_WaitForInputMaximumVerticalBlankCount
Thing _thingNone; // @ C0xFFFF_THING_NONE
Thing _thingEndOfList; // @ C0xFFFE_THING_ENDOFLIST
Thing _thingFirstExplosion; // @ C0xFF80_THING_FIRST_EXPLOSION
Thing _thingExplFireBall; // @ C0xFF80_THING_EXPLOSION_FIREBALL
Thing _thingExplSlime; // @ C0xFF81_THING_EXPLOSION_SLIME
Thing _thingExplLightningBolt; // @ C0xFF82_THING_EXPLOSION_LIGHTNING_BOLT
Thing _thingExplHarmNonMaterial; // @ C0xFF83_THING_EXPLOSION_HARM_NON_MATERIAL
Thing _thingExplOpenDoor; // @ C0xFF84_THING_EXPLOSION_OPEN_DOOR
Thing _thingExplPoisonBolt; // @ C0xFF86_THING_EXPLOSION_POISON_BOLT
Thing _thingExplPoisonCloud; // @ C0xFF87_THING_EXPLOSION_POISON_CLOUD
Thing _thingExplSmoke; // @ C0xFFA8_THING_EXPLOSION_SMOKE
Thing _thingExplFluxcage; // @ C0xFFB2_THING_EXPLOSION_FLUXCAGE
Thing _thingExplRebirthStep1; // @ C0xFFE4_THING_EXPLOSION_REBIRTH_STEP1
Thing _thingExplRebirthStep2; // @ C0xFFE5_THING_EXPLOSION_REBIRTH_STEP2
Thing _thingParty; // @ C0xFFFF_THING_PARTY
};
WARN_UNUSED_RESULT bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader *header, bool skipThumbnail = true);
} // End of namespace DM
#endif