/* 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 3 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, see . * */ #ifndef SCUMM_SCUMM_H #define SCUMM_SCUMM_H #include "engines/engine.h" #include "common/endian.h" #include "common/events.h" #include "common/file.h" #include "common/savefile.h" #include "common/keyboard.h" #include "common/mutex.h" #include "common/random.h" #include "common/rect.h" #include "common/rendermode.h" #include "common/serializer.h" #include "common/str.h" #include "common/textconsole.h" #include "graphics/surface.h" #include "graphics/sjis.h" #include "graphics/palette.h" #include "scumm/gfx.h" #include "scumm/detection.h" #include "scumm/script.h" #ifdef __DS__ /* This disables the dual layer mode which is used in FM-Towns versions * of SCUMM games and which emulates the behavior of the original code. * The only purpose is code size reduction for certain backends. * SCUMM 3 (FM-Towns) games will run in English in normal (DOS VGA) mode, * which should work just fine in most situations. Some glitches might * occur. Japanese mode and SCUMM 5 FM-Towns games will not work without * dual layer (and 16 bit color) support. */ #define DISABLE_TOWNS_DUAL_LAYER_MODE #endif namespace GUI { class Dialog; } using GUI::Dialog; namespace Common { class SeekableReadStream; class WriteStream; } namespace Graphics { class FontSJIS; } /** * This is the namespace of the SCUMM engine. * * Status of this engine: * Complete support for all SCUMM based LucasArts adventures. * Complete support for many Humongous Entertainment games, * but for some of the newer ones, this is still work in progress. * * Games using this engine: * - Classic 2D LucasArts adventures * - numerous Humongous Entertainment games */ namespace Scumm { class Actor; class BaseCostumeLoader; class BaseCostumeRenderer; class BaseScummFile; class CharsetRenderer; class IMuse; class IMuseDigital; class MusicEngine; class Player_Towns; class ScummEngine; class ScummDebugger; class Sound; class Localizer; class GlyphRenderer_v7; struct Box; struct BoxCoords; struct FindObjectInRoom; // Use g_scumm from error() ONLY extern ScummEngine *g_scumm; /* System Wide Constants */ enum { NUM_SENTENCE = 6, NUM_SHADOW_PALETTE = 8 }; /* SCUMM Debug Channels */ void debugC(int level, MSVC_PRINTF const char *s, ...) GCC_PRINTF(2, 3); enum { DEBUG_GENERAL = 1 << 0, // General debug DEBUG_SCRIPTS = 1 << 2, // Track script execution (start/stop/pause) DEBUG_OPCODES = 1 << 3, // Track opcode invocations DEBUG_VARS = 1 << 4, // Track variable changes DEBUG_RESOURCE = 1 << 5, // Track resource loading / allocation DEBUG_IMUSE = 1 << 6, // Track iMUSE events DEBUG_SOUND = 1 << 7, // General Sound Debug DEBUG_ACTORS = 1 << 8, // General Actor Debug DEBUG_INSANE = 1 << 9, // Track INSANE DEBUG_SMUSH = 1 << 10, // Track SMUSH DEBUG_MOONBASE_AI = 1 << 11 // Moonbase AI }; struct VerbSlot; struct ObjectData; enum { /** * Lighting flag that indicates whether the normal palette, or the 'dark' * palette shall be used to draw actors. * Apparently only used in very old games (so far only NESCostumeRenderer * checks it). */ LIGHTMODE_actor_use_base_palette = 1 << 0, /** * Lighting flag that indicates whether the room is currently lit. Normally * always on. Used for rooms in which the light can be switched "off". */ LIGHTMODE_room_lights_on = 1 << 1, /** * Lighting flag that indicates whether a flashlight like device is active. * Used in Loom (flashlight follows the actor) and Indy 3 (flashlight * follows the mouse). Only has any effect if the room lights are off. */ LIGHTMODE_flashlight_on = 1 << 2, /** * Lighting flag that indicates whether actors are to be drawn with their * own custom palette, or using a fixed 'dark' palette. This is the * modern successor of LIGHTMODE_actor_use_base_palette. * Note: It is tempting to 'merge' these two flags, but since flags can * check their values, this is probably not a good idea. */ LIGHTMODE_actor_use_colors = 1 << 3 // }; enum { MBS_LEFT_CLICK = 0x8000, MBS_RIGHT_CLICK = 0x4000, MBS_MOUSE_MASK = (MBS_LEFT_CLICK | MBS_RIGHT_CLICK), MBS_MAX_KEY = 0x0200 }; struct SentenceTab { byte verb; byte preposition; uint16 objectA; uint16 objectB; uint8 freezeCount; }; struct StringSlot { int16 xpos; int16 ypos; int16 right; int16 height; byte color; byte charset; bool center; bool overhead; bool no_talk_anim; bool wrapping; }; struct StringTab : StringSlot { // The 'default' values for this string slot. This is used so that the // string slot can temporarily be set to different values, and then be // easily reset to a previously set default. StringSlot _default; void saveDefault() { StringSlot &s = *this; _default = s; } void loadDefault() { StringSlot &s = *this; s = _default; } }; struct ScummEngine_v0_Delays { bool _screenScroll; uint _objectRedrawCount; uint _objectStripRedrawCount; uint _actorRedrawCount; uint _actorLimbRedrawDrawCount; }; enum WhereIsObject { WIO_NOT_FOUND = -1, WIO_INVENTORY = 0, WIO_ROOM = 1, WIO_GLOBAL = 2, WIO_LOCAL = 3, WIO_FLOBJECT = 4 }; struct SaveStateMetaInfos { uint32 date; uint16 time; uint32 playtime; }; enum UserStates { USERSTATE_SET_FREEZE = 0x01, // freeze scripts if USERSTATE_FREEZE_ON is set, unfreeze otherwise USERSTATE_SET_CURSOR = 0x02, // shows cursor if USERSTATE_CURSOR_ON is set, hides it otherwise USERSTATE_SET_IFACE = 0x04, // change user-interface (sentence-line, inventory, verb-area) USERSTATE_FREEZE_ON = 0x08, // only interpreted if USERSTATE_SET_FREEZE is set USERSTATE_CURSOR_ON = 0x10, // only interpreted if USERSTATE_SET_CURSOR is set USERSTATE_IFACE_SENTENCE = 0x20, // only interpreted if USERSTATE_SET_IFACE is set USERSTATE_IFACE_INVENTORY = 0x40, // only interpreted if USERSTATE_SET_IFACE is set USERSTATE_IFACE_VERBS = 0x80 // only interpreted if USERSTATE_SET_IFACE is set }; #define USERSTATE_IFACE_ALL (USERSTATE_IFACE_SENTENCE | USERSTATE_IFACE_INVENTORY | USERSTATE_IFACE_VERBS) /** * A list of resource types. * WARNING: Do not change the order of these, as the savegame format relies * on it; any change made here will break savegame compatibility! */ enum ResType { rtInvalid = 0, rtFirst = 1, rtRoom = 1, rtScript = 2, rtCostume = 3, rtSound = 4, rtInventory = 5, rtCharset = 6, rtString = 7, rtVerb = 8, rtActorName = 9, rtBuffer = 10, rtScaleTable = 11, rtTemp = 12, rtFlObject = 13, rtMatrix = 14, rtBox = 15, rtObjectName = 16, rtRoomScripts = 17, rtRoomImage = 18, rtImage = 19, rtTalkie = 20, rtSpoolBuffer = 21, rtLast = 21 }; typedef uint16 ResId; class ResourceManager; /** * DOS Programmable Interval Timer constants. * * The SCUMM engine (v1-v7, DOS) timer ticks are based on the jiffy unit (roughly 60Hz). * Well, if we want to be pedantic about it, it operates on quarter jiffies (240Hz), * a rate at which several screen effects are updated; but still, this value is divided * by 4 in the main game loop in order for it to operate on whole jiffies. * In order to obtain this behavior, the PIT is programmed to operate at roughly 240Hz, * though these timings change from version to version (or game by game, for v6). * * Glossary: * - Base frequency: this is the frequency at which the Intel 8253/54 PIT * operates, namely obtained with the formula 105/88, which * yields 1.193181818... MHz. We are storing it in Hz; * * - Divisor: the base frequency in DOS is not used as-is, but it is instead * divided by a customizable divisor which can range between * 0 and (2^16-1), where 0 is a shortcut for 2^16. This operation * yields the custom frequency at which our custom interrupt * gets called (hence "Programmable"); * * - Orchestrator: starting from SCUMM v5, games started using iMUSE, and apparently * needed a more precise timing handling; this led to the introduction * of a main orchestrator timer (which then handled the execution of other sub-timers), whose divisor (4096) was set up in the IMS * drivers up until v7, in which the divisor (3977) was set up in the * executable as a part of the INSANE orchestration; * * - Sub-timer: custom made timers, operating under an orchestrator; in the macros * below, "INC" refers to the increment of an accumulator which gets * updated at each iteration of the orchestrator interrupt; "THRESH" * refers to a threshold value of the aforementioned accumulator, * beyond which the accumulator is decremented by the threshold value, * and the interrupt of the sub-timer gets executed (e.g. the values * below mainly refer to the interrupt which increments the SCUMM * quarter frame counter. * * All the values below are presented as doubles, so to safely yield fractional results. */ #define PIT_BASE_FREQUENCY 1193182.0 // In Hz #define PIT_V1_DIVISOR 65536.0 #define PIT_V2_4_DIVISOR 5041.0 #define PIT_V5_6_ORCHESTRATOR_DIVISOR 4096.0 #define PIT_V5_6_SUBTIMER_INC 3433.0 #define PIT_V5_SUBTIMER_THRESH 4167.0 #define PIT_V6_SAMNMAX_SUBTIMER_THRESH 4167.0 #define PIT_V6_DOTT_SUBTIMER_THRESH 4237.0 #define PIT_V7_ORCHESTRATOR_DIVISOR 3977.0 #define PIT_V7_SUBTIMER_INC 3977.0 #define PIT_V7_SUBTIMER_THRESH 4971.0 #define LOOM_STEAM_CDDA_RATE 240.0 /** * Amiga timing constants. * * Amiga versions of SCUMM games update the game timer at every * V-Blank interrupt, incrementing it by 4 each time (which means * a full frame/jiffie). The shake timer is instead updated every * other V-Blank interrupt, so 8 quarter frames (2 frames/jiffies) * at a time. * * The base rate is 50Hz for PAL systems and 60Hz for NTSC systems. * We're going to target the latter in here, converting it in a quarter * frame frequency. */ #define AMIGA_NTSC_VBLANK_RATE 240.0 /** * Game saving/loading outcome codes */ #define GAME_PROPER_SAVE 201 #define GAME_FAILED_SAVE 202 #define GAME_PROPER_LOAD 203 #define GAME_FAILED_LOAD 204 /** * GUI defines and enums. */ #define GUI_PAGE_MAIN 0 #define GUI_PAGE_SAVE 1 #define GUI_PAGE_LOAD 2 #define GUI_PAGE_RESTART 3 // Sega CD #define GUI_PAGE_CODE_CONFIRM 4 // Sega CD #define GUI_PAGE_INVALID_CODE 5 // Sega CD #define GUI_CTRL_FIRST_SG 1 #define GUI_CTRL_LAST_SG 9 #define GUI_CTRL_SAVE_BUTTON 10 #define GUI_CTRL_LOAD_BUTTON 11 #define GUI_CTRL_PLAY_BUTTON 12 #define GUI_CTRL_QUIT_BUTTON 13 #define GUI_CTRL_OK_BUTTON 14 #define GUI_CTRL_CANCEL_BUTTON 15 #define GUI_CTRL_ARROW_UP_BUTTON 16 #define GUI_CTRL_ARROW_DOWN_BUTTON 17 #define GUI_CTRL_PATH_BUTTON 18 #define GUI_CTRL_MUSIC_SLIDER 19 #define GUI_CTRL_SPEECH_SLIDER 20 #define GUI_CTRL_SFX_SLIDER 21 #define GUI_CTRL_TEXT_SPEED_SLIDER 22 #define GUI_CTRL_DISPLAY_TEXT_CHECKBOX 23 #define GUI_CTRL_SPOOLED_MUSIC_CHECKBOX 24 #define GUI_CTRL_OUTER_BOX 26 #define GUI_CTRL_INNER_BOX 27 // Sega CD #define GUI_CTRL_NUMPAD_1 1 #define GUI_CTRL_NUMPAD_2 2 #define GUI_CTRL_NUMPAD_3 3 #define GUI_CTRL_NUMPAD_4 4 #define GUI_CTRL_NUMPAD_5 5 #define GUI_CTRL_NUMPAD_6 6 #define GUI_CTRL_NUMPAD_7 7 #define GUI_CTRL_NUMPAD_8 8 #define GUI_CTRL_NUMPAD_9 9 #define GUI_CTRL_NUMPAD_0 10 #define GUI_CTRL_RESTART_BUTTON 13 #define GUI_CTRL_ARROW_LEFT_BUTTON 16 #define GUI_CTRL_ARROW_RIGHT_BUTTON 17 #define GUI_CTRL_NUMPAD_BACK 23 enum GUIString { gsPause = 0, gsVersion = 1, gsTextSpeedSlider = 2, gsRestart = 3, gsQuitPrompt = 4, gsSave = 5, gsLoad = 6, gsPlay = 7, gsCancel = 8, gsQuit = 9, gsOK = 10, gsMustName = 11, gsGameNotSaved = 12, gsGameNotLoaded = 13, gsSaving = 14, gsLoading = 15, gsNamePrompt = 16, gsSelectLoadPrompt = 17, gsReplacePrompt = 18, gsYes = 20, gsNo = 21, gsIMuseBuffer = 22, gsVoiceAndText = 23, gsTextDisplayOnly = 24, gsVoiceOnly = 25, gsYesKey = 26, gsMusicVolumeSlider = 27, gsVoiceVolumeSlider = 28, gsSfxVolumeSlider = 29, gsHeap = 30, gsSavePath = 31, gsTitle = 32, gsDisabled = 33, gsMusic = 34, gsVoice = 35, gsSfx = 36, gsTextSpeed = 37, gsDisplayText = 38, gsSpooledMusic = 39, gsInsertSaveDisk = 40, gsSnapOn = 41, gsSnapOff = 42, gsRecalJoystick = 43, gsMouseMode = 44, gsMouseOn = 45, gsMouseOff = 46, gsJoystickOn = 47, gsJoystickOff = 48, gsSoundsOn = 49, gsSoundsOff = 50, gsVGAMode = 51, gsEGAMode = 52, gsCGAMode = 53, gsHerculesMode = 54, gsTandyMode = 55, gsCurrentPasscode = 56, gsEnterPasscode = 57, gsConfirmPasscode = 58, gsInvalidPasscode = 59, gsSlowFast = 60, gsRestartGame = 61, }; struct InternalGUIControl { int relativeCenterX; int relativeCenterY; int xPos; int yPos; int normalFillColor; int topLineColor; int bottomLineColor; int leftLineColor; int rightLineColor; int normalTextColor; int highlightedTextColor; int highlightedFillColor; bool centerText; Common::String label; bool doubleLinesFlag; }; /** * Base class for all SCUMM engines. */ class ScummEngine : public Engine, public Common::Serializable { friend class ScummDebugger; friend class CharsetRenderer; friend class CharsetRendererTownsClassic; friend class ResourceManager; public: /* Put often used variables at the top. * That results in a shorter form of the opcode * on some architectures. */ IMuse *_imuse = nullptr; IMuseDigital *_imuseDigital = nullptr; MusicEngine *_musicEngine = nullptr; Player_Towns *_townsPlayer = nullptr; Sound *_sound = nullptr; VerbSlot *_verbs = nullptr; ObjectData *_objs = nullptr; // Core variables GameSettings _game; uint8 _gameMD5[16]; /** Random number generator */ Common::RandomSource _rnd; /** Graphics manager */ Gdi *_gdi = nullptr; /** Central resource data. */ ResourceManager *_res = nullptr; bool _enableEnhancements = false; bool _useOriginalGUI = true; bool _enableAudioOverride = false; protected: VirtualMachineState vm; bool _oldSoundsPaused = false; public: // Constructor / Destructor ScummEngine(OSystem *syst, const DetectorResult &dr); ~ScummEngine() override; // Engine APIs Common::Error init(); Common::Error go(); Common::Error run() override { Common::Error err; err = init(); if (err.getCode() != Common::kNoError) return err; return go(); } void errorString(const char *buf_input, char *buf_output, int buf_output_size) override; bool hasFeature(EngineFeature f) const override; void syncSoundSettings() override; Common::Error loadGameState(int slot) override; bool canLoadGameStateCurrently() override; Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override; bool canSaveGameStateCurrently() override; void pauseEngineIntern(bool pause) override; protected: virtual void setupScumm(const Common::String &macResourceFile); virtual void resetScumm(); virtual void setupScummVars(); virtual void resetScummVars(); void setVideoModeVarToCurrentConfig(); void setupCharsetRenderer(const Common::String &macFontFile); void setupCostumeRenderer(); virtual void loadLanguageBundle(); void loadCJKFont(); void loadKorFont(); void setupMusic(int midi, const Common::String &macInstrumentFile); void setTalkSpeed(int talkspeed); int getTalkSpeed(); // Scumm main loop & helper functions. virtual void scummLoop(int delta); virtual void scummLoop_updateScummVars(); virtual void scummLoop_handleSaveLoad(); virtual void scummLoop_handleDrawing(); virtual void scummLoop_handleActors() = 0; virtual void scummLoop_handleEffects(); virtual void scummLoop_handleSound(); virtual void runBootscript(); // Event handling public: void parseEvents(); // Used by IMuseDigital::startSound protected: virtual void parseEvent(Common::Event event); void waitForTimer(int quarterFrames); uint32 _lastWaitTime; void setTimerAndShakeFrequency(); /** * Represents fractional milliseconds by decomposing the passed * value into integral and fractional parts, then incrementing the * integer part as needed on subsequent function calls. */ uint32 getIntegralTime(double fMsecs); double _msecFractParts = 0.0; virtual void processInput(); virtual void processKeyboard(Common::KeyState lastKeyHit); virtual void clearClickedStatus(); // Cursor/palette virtual void updateCursor(); virtual void animateCursor() {} virtual void updatePalette(); virtual void setDefaultCursor() {}; virtual void setCursorTransparency(int a) {}; virtual void resetCursors() {} virtual void setCursorHotspot(int x, int y) {} virtual void setCursorFromBuffer(const byte *ptr, int width, int height, int pitch, bool preventScale = false) {} public: void pauseGame(); void restart(); bool isUsingOriginalGUI(); protected: Dialog *_pauseDialog = nullptr; Dialog *_messageDialog = nullptr; Dialog *_versionDialog = nullptr; void confirmExitDialog(); void confirmRestartDialog(); void pauseDialog(); void messageDialog(const Common::U32String &message); void versionDialog(); // Original GUI int32 _bannerColors[50]; // Colors for the original GUI byte *_bannerMem = nullptr; uint32 _bannerMemSize = 0; int _bannerSaveYStart = 0; bool _messageBannerActive = false; bool _comiQuitMenuIsOpen = false; bool _closeBannerAndQueryQuitFlag = false; // The followings are needed for MI1 FM-Towns byte *_textSurfBannerMem = nullptr; uint32 _textSurfBannerMemSize = 0; InternalGUIControl _internalGUIControls[30]; // Special GUI strings const char _emptyMsg[1] = {'\0'}; const char _uncheckedBox[2] = {' ', '\0'}; const char _checkedBox[2] = {'x', '\0'}; const char _arrowUp[2] = {'\x18', '\0'}; const char _arrowDown[2] = {'\x19', '\0'}; const char _arrowLeft[2] = {'\x3c', '\0'}; const char _arrowRight[2] = {'\x3d', '\0'}; Common::StringArray _savegameNames; int _menuPage = 0; int _mainMenuSavegameLabel = 1; int _curDisplayedSaveSlotPage = 0; int _firstSaveStateOfList = 0; // For LOOM VGA bool _mainMenuIsActive = false; bool _quitByGUIPrompt = false; char _mainMenuMusicSlider[17]; char _mainMenuSpeechSlider[17]; char _mainMenuSfxSlider[17]; char _mainMenuTextSpeedSlider[17]; char _mainMenuSegaCDPasscode[5]; int _spooledMusicIsToBeEnabled = 1; int _saveScriptParam = 0; int _guiCursorAnimCounter = 0; int _v5VoiceMode = 0; // Fake flags just for sub v5 GUIs int _internalSpeakerSoundsAreOn = 1; int _guiMouseFlag = 1; int _guiJoystickFlag = 1; bool _mixerMutedByGUI = false; Graphics::Surface _savegameThumbnail; byte *_tempTextSurface = nullptr; byte *_tempMainSurface = nullptr; byte *_tempVerbSurface = nullptr; bool _postGUICharMask = false; // Saved cursor pre and post GUI byte *_curGrabbedCursor = nullptr; int8 _oldCursorState = 0; int _curCursorState = 0; int _curCursorWidth = 0; int _curCursorHeight = 0; int _curCursorHotspotX = 0; int _curCursorHotspotY = 0; virtual void setSnailCursor() {} void initBanners(); Common::KeyState showBannerAndPause(int bannerId, int32 waitTime, const char *msg, ...); Common::KeyState showOldStyleBannerAndPause(const char *msg, int color, int32 waitTime); Common::KeyState printMessageAndPause(const char *msg, int color, int32 waitTime, bool drawOnSentenceLine); void clearBanner(); void setBannerColors(int bannerId, byte r, byte g, byte b); virtual int getBannerColor(int bannerId); void setUpInternalGUIControl(int id, int normalFillColor, int normalTextColor, int topLineColor, int bottomLineColor, int leftLineColor, int rightLineColor, int highlightedTextColor, int highlightedFillColor, int anchorPointX, int anchorPointY, int x, int y, const char *label, bool centerFlag, bool unknownFlag); void drawInternalGUIControl(int id, bool highlightColor); int getInternalGUIControlFromCoordinates(int x, int y); virtual bool isSmushActive() { return false; } virtual bool isInsaneActive() { return false; } virtual void queryQuit(bool returnToLauncher); virtual void queryRestart(); virtual const char *getGUIString(int stringId); void waitForBannerInput(int32 waitTime, Common::KeyState &ks, bool &leftBtnClicked, bool &rightBtnClicked, bool handleMouseWheel = false); virtual int getGUIStringHeight(const char *str); virtual int getGUIStringWidth(const char *str); virtual void drawGUIText(const char *buttonString, Common::Rect *clipRect, int textXPos, int textYPos, int textColor, bool centerFlag); void getSliderString(int stringId, int value, char *sliderString, int size); virtual int getMusicVolume(); virtual int getSpeechVolume(); virtual int getSFXVolume(); virtual void setMusicVolume(int volume); virtual void setSpeechVolume(int volume); virtual void setSFXVolume(int volume); virtual void toggleVoiceMode(); virtual void handleLoadDuringSmush() {} virtual void setSkipVideo(int value) {} void showMainMenu(); virtual void setUpMainMenuControls(); void setUpMainMenuControlsSegaCD(); void drawMainMenuControls(); void drawMainMenuControlsSegaCD(); void updateMainMenuControls(); void updateMainMenuControlsSegaCD(); void drawMainMenuTitle(const char *title); bool executeMainMenuOperation(int op, int mouseX, int mouseY, bool &hasLoadedState); bool executeMainMenuOperationSegaCD(int op, int mouseX, int mouseY, bool &hasLoadedState); bool shouldHighlightLabelAndWait(int clickedControl); void fillSavegameLabels(); bool canWriteGame(int slotId); bool userWriteLabelRoutine(Common::KeyState &ks, bool &leftMsClicked, bool &rightMsClicked); void saveCursorPreMenu(); void restoreCursorPostMenu(); void saveSurfacesPreGUI(); void restoreSurfacesPostGUI(); public: char displayMessage(const char *altButton, MSVC_PRINTF const char *message, ...) GCC_PRINTF(3, 4); protected: byte _fastMode = 0; byte _numActors = 0; Actor **_actors = nullptr; // Has _numActors elements Actor **_sortedActors = nullptr; byte *_arraySlot = nullptr; uint16 *_inventory = nullptr; uint16 *_newNames = nullptr; public: // VAR is a wrapper around scummVar, which attempts to include additional // useful information should an illegal var access be detected. #define VAR(x) scummVar(x, #x, __FILE__, __LINE__) int32& scummVar(byte var, const char *varName, const char *file, int line) { if (var == 0xFF) { error("Illegal access to variable %s in file %s, line %d", varName, file, line); } return _scummVars[var]; } int32 scummVar(byte var, const char *varName, const char *file, int line) const { if (var == 0xFF) { error("Illegal access to variable %s in file %s, line %d", varName, file, line); } return _scummVars[var]; } protected: int16 _varwatch = 0; int32 *_roomVars = nullptr; int32 *_scummVars = nullptr; byte *_bitVars = nullptr; /* Global resource tables */ int _numVariables = 0; int _numBitVariables = 0; int _numLocalObjects = 0; int _numGlobalObjects = 0; int _numArray = 0; int _numVerbs = 0; int _numFlObject = 0; int _numInventory = 0; int _numNewNames = 0; int _numGlobalScripts = 0; int _numRoomVariables = 0; int _numPalettes = 0; int _numSprites = 0; int _numTalkies = 0; int _numUnk = 0; int _HEHeapSize = 0; public: int _numLocalScripts = 60, _numImages = 0, _numRooms = 0, _numScripts = 0, _numSounds = 0; // Used by HE games int _numCostumes = 0; // FIXME - should be protected, used by Actor::remapActorPalette int32 _numCharsets = 0; // FIXME - should be protected, used by CharsetRenderer BaseCostumeLoader *_costumeLoader = nullptr; BaseCostumeRenderer *_costumeRenderer = nullptr; int _NESCostumeSet = 0; void NES_loadCostumeSet(int n); byte *_NEScostdesc = nullptr, *_NEScostlens = nullptr, *_NEScostoffs = nullptr, *_NEScostdata = nullptr; byte _NESPatTable[2][4096]; byte _NESPalette[2][16]; byte _NESBaseTiles = 0; int _NESStartStrip = 0; protected: int _curPalIndex = 0; public: byte _currentRoom = 0; // FIXME - should be protected but Actor::isInCurrentRoom uses it int _roomResource = 0; // FIXME - should be protected but Sound::pauseSounds uses it bool _egoPositioned = false; // Used by Actor::putActor, hence public FilenamePattern _filenamePattern; virtual Common::String generateFilename(const int room) const; protected: Common::KeyState _keyPressed; bool _keyDownMap[512]; // FIXME - 512 is a guess. it's max(kbd.ascii) Common::Point _mouse; Common::Point _virtualMouse; uint16 _mouseAndKeyboardStat = 0; byte _leftBtnPressed = 0, _rightBtnPressed = 0; int _mouseWheelFlag = 0; // For original save/load dialog only /** * Last time runInputScript was run (measured in terms of OSystem::getMillis()). * This is currently only used for Indy3 mac to detect "double clicks". */ uint32 _lastInputScriptTime = 0; /** The bootparam, to be passed to the script 1, the bootscript. */ int _bootParam = 0; // Various options useful for debugging bool _dumpScripts = false; bool _hexdumpScripts = false; bool _showStack = false; bool _debugMode = false; // Save/Load class - some of this may be GUI byte _saveLoadFlag = 0, _saveLoadSlot = 0; uint32 _lastSaveTime = 0; bool _saveTemporaryState = false; bool _loadFromLauncher = false; bool _videoModeChanged = false; Common::String _saveLoadFileName; Common::String _saveLoadDescription; bool saveState(Common::WriteStream *out, bool writeHeader = true); bool saveState(int slot, bool compat, Common::String &fileName); bool loadState(int slot, bool compat); bool loadState(int slot, bool compat, Common::String &fileName); void saveLoadWithSerializer(Common::Serializer &s) override; void saveResource(Common::Serializer &ser, ResType type, ResId idx); void loadResource(Common::Serializer &ser, ResType type, ResId idx); void loadResourceOLD(Common::Serializer &ser, ResType type, ResId idx); // "Obsolete" void copyHeapSaveGameToFile(int slot, const char *saveName); bool changeSavegameName(int slot, char *newName); virtual Common::SeekableReadStream *openSaveFileForReading(int slot, bool compat, Common::String &fileName); virtual Common::WriteStream *openSaveFileForWriting(int slot, bool compat, Common::String &fileName); Common::String makeSavegameName(int slot, bool temporary) const { return makeSavegameName(_targetName, slot, temporary); } int getKeyState(int key); public: static Common::String makeSavegameName(const Common::String &target, int slot, bool temporary); bool getSavegameName(int slot, Common::String &desc); void listSavegames(bool *marks, int num); void requestSave(int slot, const Common::String &name); void requestLoad(int slot); Common::String getTargetName() const { return _targetName; } // thumbnail + info stuff public: static bool querySaveMetaInfos(const char *target, int slot, int heversion, Common::String &desc, Graphics::Surface *&thumbnail, SaveStateMetaInfos *&timeInfos); protected: void saveInfos(Common::WriteStream *file); static bool loadInfos(Common::SeekableReadStream *file, SaveStateMetaInfos *stuff); protected: /* Script VM - should be in Script class */ uint32 _localScriptOffsets[1024]; const byte *_scriptPointer = nullptr; const byte *_scriptOrgPointer = nullptr; const byte * const *_lastCodePtr = nullptr; byte _opcode = 0; byte _currentScript = 0xFF; // Let debug() work on init stage int _scummStackPos = 0; int _vmStack[256]; char _engineVersionString[50]; char _dataFileVersionString[50]; OpcodeEntry _opcodes[256]; virtual void setupOpcodes() = 0; void executeOpcode(byte i); const char *getOpcodeDesc(byte i); void initializeLocals(int slot, int *vars); int getScriptSlot(); void startScene(int room, Actor *a, int b); bool startManiac(); public: void runScript(int script, bool freezeResistant, bool recursive, int *lvarptr, int cycle = 0); void stopScript(int script); void nukeArrays(byte scriptSlot); protected: void runObjectScript(int script, int entry, bool freezeResistant, bool recursive, int *vars, int slot = -1, int cycle = 0); void runScriptNested(int script); void executeScript(); void updateScriptPtr(); virtual void runInventoryScript(int i); void inventoryScriptIndy3Mac(); virtual void checkAndRunSentenceScript(); void runExitScript(); void runEntryScript(); void runQuitScript(); void runAllScripts(); void freezeScripts(int scr); void unfreezeScripts(); bool isScriptInUse(int script) const; bool isRoomScriptRunning(int script) const; bool isScriptRunning(int script) const; void killAllScriptsExceptCurrent(); void killScriptsAndResources(); void decreaseScriptDelay(int amount); void stopObjectCode(); void stopObjectScript(int script); void getScriptBaseAddress(); void resetScriptPointer(); int getVerbEntrypoint(int obj, int entry); void refreshScriptPointer(); byte fetchScriptByte(); virtual uint fetchScriptWord(); virtual int fetchScriptWordSigned(); uint fetchScriptDWord(); int fetchScriptDWordSigned(); void ignoreScriptWord() { fetchScriptWord(); } void ignoreScriptByte() { fetchScriptByte(); } void push(int a); int pop(); virtual int readVar(uint var); virtual void writeVar(uint var, int value); // SCUMM 1/2 virtual void resetSentence() {} protected: virtual void beginCutscene(int *args); virtual void endCutscene(); void abortCutscene(); void beginOverride(); void endOverride(); void copyScriptString(byte *dst); int resStrLen(const byte *src); void doSentence(int c, int b, int a); /* Should be in Resource class */ BaseScummFile *_fileHandle = nullptr; uint32 _fileOffset = 0; public: /** The name of the (macintosh/rescumm style) container file, if any. */ Common::String _containerFile; Common::String _macCursorFile; bool openFile(BaseScummFile &file, const Common::String &filename, bool resourceFile = false); /** Is this game a Mac m68k v5 game with iMuse? */ bool isMacM68kIMuse() const; protected: int _resourceHeaderSize = 8; byte _resourceMapper[128]; const byte *_resourceLastSearchBuf; // FIXME: need to put it to savefile? uint32 _resourceLastSearchSize; // FIXME: need to put it to savefile? virtual void allocateArrays(); void openRoom(int room); void closeRoom(); void deleteRoomOffsets(); virtual void readRoomsOffsets(); void askForDisk(const char *filename, int disknum); // TODO: Use Common::String bool openResourceFile(const Common::String &filename, byte encByte); // TODO: Use Common::String void loadPtrToResource(ResType type, ResId idx, const byte *ptr); virtual int readResTypeList(ResType type); // void allocResTypeData(ResType type, uint32 tag, int num, int mode); // byte *createResource(int type, int index, uint32 size); int loadResource(ResType type, ResId idx); // void nukeResource(ResType type, ResId idx); int getResourceRoomNr(ResType type, ResId idx); virtual uint32 getResourceRoomOffset(ResType type, ResId idx); public: int getResourceSize(ResType type, ResId idx); byte *getResourceAddress(ResType type, ResId idx); virtual byte *getStringAddress(ResId idx); byte *getStringAddressVar(int i); void ensureResourceLoaded(ResType type, ResId idx); protected: Common::Mutex _resourceAccessMutex; // Used in getResourceSize(), getResourceAddress() and findResource() // to avoid race conditions between the audio thread of Digital iMUSE // and the main SCUMM thread int readSoundResource(ResId idx); int readSoundResourceSmallHeader(ResId idx); bool isResourceInUse(ResType type, ResId idx) const; virtual void setupRoomSubBlocks(); virtual void resetRoomSubBlocks(); virtual void clearRoomObjects(); virtual void resetRoomObjects(); virtual void resetRoomObject(ObjectData *od, const byte *room, const byte *searchptr = NULL); virtual void readArrayFromIndexFile(); virtual void readMAXS(int blockSize) = 0; virtual void readGlobalObjects(); virtual void readIndexFile(); virtual void readIndexBlock(uint32 block, uint32 itemsize); virtual void loadCharset(int i); void nukeCharset(int i); int _lastLoadedRoom = 0; public: const byte *findResourceData(uint32 tag, const byte *ptr); const byte *findResource(uint32 tag, const byte *ptr); void applyWorkaroundIfNeeded(ResType type, int idx); bool verifyMI2MacBootScript(); bool verifyMI2MacBootScript(byte *buf, int size); bool tryPatchMI1CannibalScript(byte *buf, int size); int getResourceDataSize(const byte *ptr) const; void dumpResource(const char *tag, int index, const byte *ptr, int length = -1); public: /* Should be in Object class */ byte OF_OWNER_ROOM = 0; int getInventorySlot(); int findInventory(int owner, int index); int getInventoryCount(int owner); protected: byte *_objectOwnerTable = nullptr, *_objectRoomTable = nullptr, *_objectStateTable = nullptr; int _numObjectsInRoom = 0; public: uint32 *_classData = nullptr; protected: void markObjectRectAsDirty(int obj); virtual void loadFlObject(uint object, uint room); void nukeFlObjects(int min, int max); int findFlObjectSlot(); int findLocalObjectSlot(); void addObjectToInventory(uint obj, uint room); void updateObjectStates(); public: bool getClass(int obj, int cls) const; // Used in actor.cpp, hence public protected: void putClass(int obj, int cls, bool set); int getState(int obj); void putState(int obj, int state); void setObjectState(int obj, int state, int x, int y); int getOwner(int obj) const; void putOwner(int obj, int owner); void setOwnerOf(int obj, int owner); void clearOwnerOf(int obj); int getObjectRoom(int obj) const; virtual bool objIsActor(int obj); virtual int objToActor(int obj); virtual int actorToObj(int actor); int getObjX(int obj); int getObjY(int obj); void getObjectWidth(int object, int &width) { int x, y, dir; getObjectXYPos(object, x, y, dir, width); } void getObjectXYPos(int object, int &x, int &y) { int dir, width; getObjectXYPos(object, x, y, dir, width); } void getObjectXYPos(int object, int &x, int &y, int &dir) { int width; getObjectXYPos(object, x, y, dir, width); } void getObjectXYPos(int object, int &x, int &y, int &dir, int &width); int getObjOldDir(int obj); int getObjNewDir(int obj); int getObjectIndex(int object) const; int getObjectImageCount(int object); int whereIsObject(int object) const; int findObject(int x, int y); void findObjectInRoom(FindObjectInRoom *fo, byte findWhat, uint object, uint room); public: int getObjectOrActorWidth(int object, int &width); // Used in v4 and below int getObjectOrActorXY(int object, int &x, int &y); // Used in actor.cpp, hence public int getDist(int x, int y, int x2, int y2); // Also used in actor.cpp protected: int getObjActToObjActDist(int a, int b); // Not sure how to handle const byte *getObjOrActorName(int obj); // these three.. void setObjectName(int obj); void addObjectToDrawQue(int object); void removeObjectFromDrawQue(int object); void clearDrawObjectQueue(); void processDrawQue(); virtual void clearDrawQueues(); uint32 getOBCDOffs(int object) const; byte *getOBCDFromObject(int obj, bool v0CheckInventory = true); const byte *getOBIMFromObjectData(const ObjectData &od); const byte *getObjectImage(const byte *ptr, int state); virtual int getObjectIdFromOBIM(const byte *obim); protected: /* Should be in Verb class */ uint16 _verbMouseOver = 0; int8 _userPut = 0; uint16 _userState = 0; virtual void handleMouseOver(bool updateInventory); virtual void redrawVerbs(); virtual void checkExecVerbs(); void verbMouseOver(int verb); int findVerbAtPos(int x, int y) const; virtual void drawVerb(int verb, int mode); virtual void runInputScript(int clickArea, int val, int mode); void restoreVerbBG(int verb); void drawVerbBitmap(int verb, int x, int y); int getVerbSlot(int id, int mode) const; void killVerb(int slot); void setVerbObject(uint room, uint object, uint verb); public: bool isValidActor(int id) const; /* Should be in Actor class */ Actor *derefActor(int id, const char *errmsg = 0) const; Actor *derefActorSafe(int id, const char *errmsg) const; protected: void walkActors(); void playActorSounds(); void redrawAllActors(); void setActorRedrawFlags(); void putActors(); void showActors(); void resetV1ActorTalkColor(); void resetActorBgs(); virtual void processActors(); void processUpperActors(); virtual int getActorFromPos(int x, int y); public: /* Actor talking stuff */ byte _actorToPrintStrFor = 0, _V1TalkingActor = 0; int _sentenceNum = 0; SentenceTab _sentence[NUM_SENTENCE]; StringTab _string[6]; byte _haveMsg = 0; int16 _talkDelay = 0; int _NES_lastTalkingActor = 0; int _NES_talkColor = 0; virtual void actorTalk(const byte *msg); void stopTalk(); int getTalkingActor(); // Wrapper around VAR_TALK_ACTOR for V1 Maniac void setTalkingActor(int variable); // Generic costume code bool isCostumeInUse(int i) const; protected: /* Should be in Graphics class? */ uint16 _screenB = 0, _screenH = 0; public: int _roomHeight = 0, _roomWidth = 0; int _screenHeight = 0, _screenWidth = 0; VirtScreen _virtscr[4]; // Virtual screen areas CameraData camera; // 'Camera' - viewport bool _cameraIsFrozen = false; int _screenStartStrip = 0, _screenEndStrip = 0; int _screenTop = 0; Common::RenderMode _renderMode; uint8 _bytesPerPixel = 1; Graphics::PixelFormat _outputPixelFormat; protected: ColorCycle _colorCycle[16]; // Palette cycles uint8 _colorUsedByCycle[256]; Graphics::PaletteLookup _pl; // Used by the internal GUI uint32 _ENCD_offs = 0, _EXCD_offs = 0; uint32 _CLUT_offs = 0, _EPAL_offs = 0; uint32 _IM00_offs = 0, _PALS_offs = 0; //ender: fullscreen bool _fullRedraw = false, _bgNeedsRedraw = false; bool _screenEffectFlag = false, _completeScreenRedraw = false; bool _disableFadeInEffect = false; struct { int hotspotX, hotspotY, width, height; byte animate, animateIndex; int8 state; } _cursor; // HACK Double the array size to handle 16-bit images. // this should be dynamically allocated based on game depth instead. byte _grabbedCursor[16384]; byte _currentCursor = 0; byte _newEffect = 0, _switchRoomEffect2 = 0, _switchRoomEffect = 0; bool _doEffect = false; bool _snapScroll = false; virtual void setBuiltinCursor(int index) {} public: bool isLightOn() const; virtual int getCurrentLights() const; protected: void initScreens(int b, int h); void initVirtScreen(VirtScreenNumber slot, int top, int width, int height, bool twobufs, bool scrollable); void initBGBuffers(int height); void initCycl(const byte *ptr); // Color cycle void decodeNESBaseTiles(); void drawObject(int obj, int arg); void drawRoomObjects(int arg); void drawRoomObject(int i, int arg); void drawBox(int x, int y, int x2, int y2, int color); void drawLine(int x1, int y1, int x2, int y2, int color); void drawPixel(VirtScreen *vs, int x, int y, int16 color, bool useBackbuffer = false); void moveScreen(int dx, int dy, int height); void restoreBackground(Common::Rect rect, byte backcolor = 0); void redrawBGStrip(int start, int num); virtual void redrawBGAreas(); void cameraMoved(); void setCameraAtEx(int at); virtual void setCameraAt(int pos_x, int pos_y); virtual void setCameraFollows(Actor *a, bool setCamera = false); virtual void moveCamera(); virtual void panCameraTo(int x, int y); void clampCameraPos(Common::Point *pt); void actorFollowCamera(int act); const byte *getPalettePtr(int palindex, int room); void setPaletteFromTable(const byte *ptr, int numcolor, int firstIndex = 0); void resetPalette(); void setCurrentPalette(int pal); void setRoomPalette(int pal, int room); void setPCEPaletteFromPtr(const byte *ptr); void setAmigaPaletteFromPtr(const byte *ptr); virtual void setPaletteFromPtr(const byte *ptr, int numcolor = -1); void setV1ColorTable(int renderMode); virtual void setPalColor(int index, int r, int g, int b); void setDirtyColors(int min, int max); const byte *findPalInPals(const byte *pal, int index); void swapPalColors(int a, int b); virtual void copyPalColor(int dst, int src); void cyclePalette(); void stopCycle(int i); virtual void palManipulateInit(int resID, int start, int end, int time); void palManipulate(); uint32 findClosestPaletteColor(byte *palette, int paletteLength, byte r, byte g, byte b); public: uint8 *getHEPaletteSlot(uint16 palSlot); uint16 get16BitColor(uint8 r, uint8 g, uint8 b); uint32 getPaletteColorFromRGB(byte *palette, byte r, byte g, byte b); uint32 getPackedRGBColorFromPalette(byte *palette, uint32 color); void fetchBlackAndWhite(uint32 &black, uint32 &white, byte *palette, int paletteEntries); int remapPaletteColor(int r, int g, int b, int threshold); // Used by Actor::remapActorPalette void readPCEPalette(const byte **ptr, byte **dest, int numEntries); void colorPCEToRGB(uint16 color, byte *r, byte *g, byte *b); void setPCETextPalette(uint8 color); protected: void moveMemInPalRes(int start, int end, byte direction); void setShadowPalette(int slot, int redScale, int greenScale, int blueScale, int startColor, int endColor); void setShadowPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor, int start, int end); virtual void darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor); public: void markRectAsDirty(VirtScreenNumber virt, int left, int right, int top, int bottom, int dirtybit = 0); void markRectAsDirty(VirtScreenNumber virt, const Common::Rect& rect, int dirtybit = 0) { markRectAsDirty(virt, rect.left, rect.right, rect.top, rect.bottom, dirtybit); } protected: // Screen rendering byte *_compositeBuf; byte *_hercCGAScaleBuf = nullptr; bool _enableEGADithering = false; bool _supportsEGADithering = false; virtual void drawDirtyScreenParts(); void updateDirtyScreen(VirtScreenNumber slot); void drawStripToScreen(VirtScreen *vs, int x, int width, int top, int bottom); void mac_markScreenAsDirty(int x, int y, int w, int h); void mac_drawStripToScreen(VirtScreen *vs, int top, int x, int y, int width, int height); void mac_drawLoomPracticeMode(); void mac_createIndy3TextBox(Actor *a); void mac_drawIndy3TextBox(); void mac_undrawIndy3TextBox(); void mac_undrawIndy3CreditsText(); void mac_drawBorder(int x, int y, int w, int h, byte color); Common::KeyState mac_showOldStyleBannerAndPause(const char *msg, int32 waitTime); const byte *postProcessDOSGraphics(VirtScreen *vs, int &pitch, int &x, int &y, int &width, int &height) const; const byte *ditherVGAtoEGA(int &pitch, int &x, int &y, int &width, int &height) const; public: VirtScreen *findVirtScreen(int y); byte *getMaskBuffer(int x, int y, int z); protected: void fadeIn(int effect); void fadeOut(int effect); void dissolveEffectSelector(); void transitionEffect(int a); void dissolveEffect(int width, int height); void scrollEffect(int dir); void updateScreenShakeEffect(); public: double getTimerFrequency(); protected: bool _shakeEnabled = false; bool _shakeTempSavedState = false; // For saving and restoring before and after GUI calls uint _shakeFrame = 0; uint32 _shakeNextTick = 0; uint32 _shakeTickCounter = 0; double _shakeTimerRate; double _timerFrequency; void setShake(int mode); int _drawObjectQueNr = 0; byte _drawObjectQue[200]; /* For each of the 410 screen strips, gfxUsageBits contains a * bitmask. The lower 80 bits each correspond to one actor and * signify if any part of that actor is currently contained in * that strip. * * If the leftmost bit is set, the strip (background) is dirty * needs to be redrawn. * * The second leftmost bit is set by restoreBlastObjectsRect() and * restoreBackground(), but I'm not yet sure why. */ uint32 gfxUsageBits[410 * 3]; void upgradeGfxUsageBits(); void setGfxUsageBit(int strip, int bit); void clearGfxUsageBit(int strip, int bit); bool testGfxUsageBit(int strip, int bit); bool testGfxAnyUsageBits(int strip); bool testGfxOtherUsageBits(int strip, int bit); public: byte _roomPalette[256]; byte *_shadowPalette = nullptr; bool _skipDrawObject = 0; int _voiceMode = 0; // HE specific byte _HEV7ActorPalette[256]; uint8 *_hePalettes = nullptr; uint16 _hePaletteSlot = 0; uint16 *_16BitPalette = nullptr; // Indy4 Amiga specific byte *_verbPalette = nullptr; ScummEngine_v0_Delays _V0Delay; protected: int _shadowPaletteSize = 0; byte _currentPalette[3 * 256]; byte _darkenPalette[3 * 256]; int _palDirtyMin = 0, _palDirtyMax = 0; byte _palManipStart = 0, _palManipEnd = 0; uint16 _palManipCounter = 0; byte *_palManipPalette = nullptr; byte *_palManipIntermediatePal = nullptr; bool _haveActorSpeechMsg = false; bool _useTalkAnims = false; uint16 _defaultTextSpeed = 0; int _saveSound = 0; bool _native_mt32 = false; bool _copyProtection = false; // Indy4 Amiga specific uint16 _amigaFirstUsedColor = 0; byte _amigaPalette[3 * 64]; void amigaPaletteFindFirstUsedColor(); void mapRoomPalette(int idx); int remapRoomPaletteColor(int r, int g, int b); void mapVerbPalette(int idx); int remapVerbPaletteColor(int r, int g, int b); // EGA dithering mode color tables for certain VGA games like MI2, LOOM Talkie... byte *_egaColorMap[2]; public: uint16 _extraBoxFlags[65]; byte getNumBoxes(); byte *getBoxMatrixBaseAddr(); byte *getBoxConnectionBase(int box); int getNextBox(byte from, byte to); void setBoxFlags(int box, int val); void setBoxScale(int box, int b); bool checkXYInBoxBounds(int box, int x, int y); BoxCoords getBoxCoordinates(int boxnum); byte getMaskFromBox(int box); Box *getBoxBaseAddr(int box); byte getBoxFlags(int box); int getBoxScale(int box); int getScale(int box, int x, int y); int getScaleFromSlot(int slot, int x, int y); protected: // Scaling slots/items struct ScaleSlot { int x1, y1, scale1; int x2, y2, scale2; }; friend void syncWithSerializer(Common::Serializer &, ScaleSlot &); ScaleSlot _scaleSlots[20]; void setScaleSlot(int slot, int x1, int y1, int scale1, int x2, int y2, int scale2); void setBoxScaleSlot(int box, int slot); void convertScaleTableToScaleSlot(int slot); void calcItineraryMatrix(byte *itineraryMatrix, int num); void createBoxMatrix(); virtual bool areBoxesNeighbors(int i, int j); /* String class */ public: CharsetRenderer *_charset = nullptr; byte _charsetColorMap[16]; /** * All text is normally rendered into this overlay surface. Then later * drawStripToScreen() composits it over the game graphics. */ Graphics::Surface _textSurface; int _textSurfaceMultiplier = 0; Graphics::Surface *_macScreen = nullptr; Graphics::Surface *_macIndy3TextBox = nullptr; protected: byte _charsetColor = 0; byte _charsetData[23][16]; int _charsetBufPos = 0; byte _charsetBuffer[512]; bool _keepText = false; bool _actorShouldStopTalking = false; byte _msgCount = 0; int _nextLeft = 0, _nextTop = 0; Localizer *_localizer = nullptr; void restoreCharsetBg(); void clearCharsetMask(); void clearTextSurface(); virtual void initCharset(int charset); virtual void printString(int m, const byte *msg); virtual bool handleNextCharsetCode(Actor *a, int *c); virtual void drawSentence() {} virtual void CHARSET_1(); bool newLine(); void drawString(int a, const byte *msg); virtual void fakeBidiString(byte *ltext, bool ignoreVerb, int ltextSize) const; void debugMessage(const byte *msg); virtual void showMessageDialog(const byte *msg); virtual int convertMessageToString(const byte *msg, byte *dst, int dstSize); int convertIntMessage(byte *dst, int dstSize, int var); int convertVerbMessage(byte *dst, int dstSize, int var); int convertNameMessage(byte *dst, int dstSize, int var); int convertStringMessage(byte *dst, int dstSize, int var); public: Common::Language _language; // Accessed by a hack in NutRenderer::loadFont // Used by class ScummDialog: virtual void translateText(const byte *text, byte *trans_buff, int transBufferSize); // Old Hebrew games require reversing the dialog text. bool reverseIfNeeded(const byte *text, byte *reverseBuf, int reverseBufSize) const; // Returns codepage that matches the game for languages that require it. Common::CodePage getDialogCodePage() const; // Somewhat hackish stuff for 2 byte support (Chinese/Japanese/Korean) bool _useCJKMode = false; bool _useMultiFont = false; int _numLoadedFont = 0; int _2byteShadow = 0; int _2byteHeight = 0; int _2byteWidth = 0; int _krStrPost = 0; byte _newLineCharacter = 0; byte *get2byteCharPtr(int idx); bool isScummvmKorTarget(); //protected: byte *_2byteFontPtr = nullptr; byte *_2byteMultiFontPtr[20]; int _2byteMultiHeight[20]; int _2byteMultiWidth[20]; int _2byteMultiShadow[20]; private: struct TranslatedLine { uint32 originalTextOffset; uint32 translatedTextOffset; }; struct TranslationRange { uint32 left; uint32 right; TranslationRange(uint32 left_, uint32 right_) : left(left_), right(right_) {} TranslationRange() : left(0), right(0) {} }; struct TranslationRoom { Common::HashMap scriptRanges; }; bool _existLanguageFile = false; bool _isRTL = false; byte *_languageBuffer = nullptr; int _numTranslatedLines = 0; TranslatedLine *_translatedLines = nullptr; uint16 *_languageLineIndex = nullptr; Common::HashMap _roomIndex; const byte *searchTranslatedLine(const byte *text, const TranslationRange &range, bool useIndex); virtual void createTextRenderer(GlyphRenderer_v7 *gr) {} public: /* Scumm Vars */ byte VAR_KEYPRESS = 0xFF; byte VAR_SYNC = 0xFF; byte VAR_EGO = 0xFF; byte VAR_CAMERA_POS_X = 0xFF; byte VAR_HAVE_MSG = 0xFF; byte VAR_ROOM = 0xFF; byte VAR_OVERRIDE = 0xFF; byte VAR_MACHINE_SPEED = 0xFF; byte VAR_ME = 0xFF; byte VAR_NUM_ACTOR = 0xFF; byte VAR_CURRENT_LIGHTS = 0xFF; byte VAR_CURRENTDRIVE = 0xFF; // How about merging this with VAR_CURRENTDISK? byte VAR_CURRENTDISK = 0xFF; byte VAR_TMR_1 = 0xFF; byte VAR_TMR_2 = 0xFF; byte VAR_TMR_3 = 0xFF; byte VAR_MUSIC_TIMER = 0xFF; byte VAR_ACTOR_RANGE_MIN = 0xFF; byte VAR_ACTOR_RANGE_MAX = 0xFF; byte VAR_CAMERA_MIN_X = 0xFF; byte VAR_CAMERA_MAX_X = 0xFF; byte VAR_TIMER_NEXT = 0xFF; byte VAR_VIRT_MOUSE_X = 0xFF; byte VAR_VIRT_MOUSE_Y = 0xFF; byte VAR_ROOM_RESOURCE = 0xFF; byte VAR_LAST_SOUND = 0xFF; byte VAR_CUTSCENEEXIT_KEY = 0xFF; byte VAR_OPTIONS_KEY = 0xFF; byte VAR_TALK_ACTOR = 0xFF; byte VAR_CAMERA_FAST_X = 0xFF; byte VAR_SCROLL_SCRIPT = 0xFF; byte VAR_ENTRY_SCRIPT = 0xFF; byte VAR_ENTRY_SCRIPT2 = 0xFF; byte VAR_EXIT_SCRIPT = 0xFF; byte VAR_EXIT_SCRIPT2 = 0xFF; byte VAR_VERB_SCRIPT = 0xFF; byte VAR_SENTENCE_SCRIPT = 0xFF; byte VAR_INVENTORY_SCRIPT = 0xFF; byte VAR_CUTSCENE_START_SCRIPT = 0xFF; byte VAR_CUTSCENE_END_SCRIPT = 0xFF; byte VAR_CHARINC = 0xFF; byte VAR_WALKTO_OBJ = 0xFF; byte VAR_DEBUGMODE = 0xFF; byte VAR_HEAPSPACE = 0xFF; byte VAR_RESTART_KEY = 0xFF; byte VAR_PAUSE_KEY = 0xFF; byte VAR_MOUSE_X = 0xFF; byte VAR_MOUSE_Y = 0xFF; byte VAR_TIMER = 0xFF; byte VAR_TIMER_TOTAL = 0xFF; byte VAR_SOUNDCARD = 0xFF; byte VAR_VIDEOMODE = 0xFF; byte VAR_MAINMENU_KEY = 0xFF; byte VAR_FIXEDDISK = 0xFF; byte VAR_CURSORSTATE = 0xFF; byte VAR_USERPUT = 0xFF; byte VAR_SOUNDRESULT = 0xFF; byte VAR_TALKSTOP_KEY = 0xFF; byte VAR_FADE_DELAY = 0xFF; byte VAR_NOSUBTITLES = 0xFF; // V5+ byte VAR_SOUNDPARAM = 0xFF; byte VAR_SOUNDPARAM2 = 0xFF; byte VAR_SOUNDPARAM3 = 0xFF; byte VAR_INPUTMODE = 0xFF; byte VAR_MEMORY_PERFORMANCE = 0xFF; byte VAR_VIDEO_PERFORMANCE = 0xFF; byte VAR_ROOM_FLAG = 0xFF; byte VAR_GAME_LOADED = 0xFF; byte VAR_NEW_ROOM = 0xFF; // V4/V5 byte VAR_V5_TALK_STRING_Y = 0xFF; // V6+ byte VAR_ROOM_WIDTH = 0xFF; byte VAR_ROOM_HEIGHT = 0xFF; byte VAR_SUBTITLES = 0xFF; byte VAR_V6_EMSSPACE = 0xFF; // V7/V8 specific variables byte VAR_CAMERA_POS_Y = 0xFF; byte VAR_CAMERA_MIN_Y = 0xFF; byte VAR_CAMERA_MAX_Y = 0xFF; byte VAR_CAMERA_THRESHOLD_X = 0xFF; byte VAR_CAMERA_THRESHOLD_Y = 0xFF; byte VAR_CAMERA_SPEED_X = 0xFF; byte VAR_CAMERA_SPEED_Y = 0xFF; byte VAR_CAMERA_ACCEL_X = 0xFF; byte VAR_CAMERA_ACCEL_Y = 0xFF; byte VAR_CAMERA_DEST_X = 0xFF; byte VAR_CAMERA_DEST_Y = 0xFF; byte VAR_CAMERA_FOLLOWED_ACTOR = 0xFF; // V7/V8 specific variables byte VAR_VERSION_KEY = 0xFF; byte VAR_DEFAULT_TALK_DELAY = 0xFF; byte VAR_CUSTOMSCALETABLE = 0xFF; byte VAR_BLAST_ABOVE_TEXT = 0xFF; byte VAR_VOICE_MODE = 0xFF; byte VAR_MUSIC_BUNDLE_LOADED = 0xFF; byte VAR_VOICE_BUNDLE_LOADED = 0xFF; byte VAR_LEFTBTN_DOWN = 0xFF; // V7/V8 byte VAR_RIGHTBTN_DOWN = 0xFF; // V7/V8 byte VAR_LEFTBTN_HOLD = 0xFF; // V6/V72HE/V7/V8 byte VAR_RIGHTBTN_HOLD = 0xFF; // V6/V72HE/V7/V8 byte VAR_SAVELOAD_SCRIPT = 0xFF; // V6/V7 (not HE) byte VAR_SAVELOAD_SCRIPT2 = 0xFF; // V6/V7 (not HE) byte VAR_SAVELOAD_PAGE = 0xFF; // V8 byte VAR_OBJECT_LABEL_FLAG = 0xFF; // V8 // V6/V7 specific variables (FT & Sam & Max specific) byte VAR_CHARSET_MASK = 0xFF; // V6 specific variables byte VAR_V6_SOUNDMODE = 0xFF; // V1/V2 specific variables byte VAR_CHARCOUNT = 0xFF; byte VAR_VERB_ALLOWED = 0xFF; byte VAR_ACTIVE_VERB = 0xFF; byte VAR_ACTIVE_OBJECT1 = 0xFF; byte VAR_ACTIVE_OBJECT2 = 0xFF; // HE specific variables byte VAR_REDRAW_ALL_ACTORS = 0xFF; // Used in setActorRedrawFlags() byte VAR_SKIP_RESET_TALK_ACTOR = 0xFF; // Used in setActorCostume() byte VAR_SOUND_CHANNEL = 0xFF; // Used in o_startSound() byte VAR_TALK_CHANNEL = 0xFF; // Used in startHETalkSound() byte VAR_SOUNDCODE_TMR = 0xFF; // Used in processSoundCode() byte VAR_RESERVED_SOUND_CHANNELS = 0xFF; // Used in findFreeSoundChannel() byte VAR_MAIN_SCRIPT = 0xFF; // Used in scummLoop() byte VAR_SCRIPT_CYCLE = 0xFF; // Used in runScript()/runObjectScript() byte VAR_NUM_SCRIPT_CYCLES = 0xFF; // Used in runAllScripts() byte VAR_QUIT_SCRIPT = 0xFF; // Used in confirmExitDialog() // Exists both in V7 and in V72HE: byte VAR_NUM_GLOBAL_OBJS = 0xFF; #ifdef USE_RGB_COLOR // FM-Towns / PC-Engine specific Graphics::FontSJIS *_cjkFont = nullptr; #endif // FM-Towns specific #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE public: bool towns_isRectInStringBox(int x1, int y1, int x2, int y2); byte _townsPaletteFlags = 0; byte _townsCharsetColorMap[16]; protected: void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h); void towns_clearStrip(int strip); #ifdef USE_RGB_COLOR void towns_setPaletteFromPtr(const byte *ptr, int numcolor = -1); void towns_setTextPaletteFromPtr(const byte *ptr); #endif void towns_setupPalCycleField(int x1, int y1, int x2, int y2); void towns_processPalCycleField(); void towns_resetPalCycleFields(); void towns_restoreCharsetBg(); void towns_scriptScrollEffect(int dir); void requestScroll(int dir); void scrollLeft() { requestScroll(-1); } void scrollRight() { requestScroll(1); } void towns_waitForScroll(int waitForDirection, int threshold = 0); void towns_updateGfx(); Common::Rect _cyclRects[10]; int _numCyclRects = 0; int _scrollRequest = 0; int _scrollDeltaAdjust = 0; bool _scrollNeedDeltaAdjust = 0; int _refreshDuration[20]; int _refreshArrayPos = 0; bool _refreshNeedCatchUp = false; bool _enableSmoothScrolling = false; uint32 _scrollTimer = 0; uint32 _scrollDestOffset = 0; uint16 _scrollFeedStrips[3]; Common::Rect _curStringRect; byte _townsOverrideShadowColor = 0; byte _textPalette[48]; byte _townsClearLayerFlag = 1; byte _townsActiveLayerFlags = 3; static const uint8 _townsLayer2Mask[]; TownsScreen *_townsScreen = nullptr; #else void scrollLeft() { redrawBGStrip(_gdi->_numStrips - 1, 1); } void scrollRight() { redrawBGStrip(0, 1); } void towns_updateGfx() {} void towns_waitForScroll(int waitForDirection, int threshold = 0) {} #endif // DISABLE_TOWNS_DUAL_LAYER_MODE }; } // End of namespace Scumm #endif