diff options
Diffstat (limited to 'engines/agi')
-rw-r--r-- | engines/agi/agi.cpp | 78 | ||||
-rw-r--r-- | engines/agi/agi.h | 120 | ||||
-rw-r--r-- | engines/agi/detection.cpp | 227 | ||||
-rw-r--r-- | engines/agi/graphics.cpp | 258 | ||||
-rw-r--r-- | engines/agi/graphics.h | 10 | ||||
-rw-r--r-- | engines/agi/keyboard.cpp | 8 | ||||
-rw-r--r-- | engines/agi/menu.cpp | 8 | ||||
-rw-r--r-- | engines/agi/predictive.cpp | 17 | ||||
-rw-r--r-- | engines/agi/saveload.cpp | 26 | ||||
-rw-r--r-- | engines/agi/sound.cpp | 180 | ||||
-rw-r--r-- | engines/agi/text.cpp | 4 |
11 files changed, 745 insertions, 191 deletions
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp index 0839b7de99..1c1c53dee7 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -178,7 +178,7 @@ void AgiEngine::processEvents() { case Common::KEYCODE_MINUS: key = '-'; break; - case Common::KEYCODE_9: + case Common::KEYCODE_TAB: key = 0x0009; break; case Common::KEYCODE_F1: @@ -448,6 +448,12 @@ int AgiEngine::agiInit() { loadGame(saveNameBuffer, false); // Do not check game id } +#ifdef __DS__ + // Normally, the engine loads the predictive text dictionary when the predictive dialog + // is shown. On the DS version, the word completion feature needs the dictionary too. + loadDict(); +#endif + return ec; } @@ -535,6 +541,67 @@ static const GameSettings agiSettings[] = { {NULL, NULL, 0, 0, NULL} }; +AgiTextColor AgiButtonStyle::getColor(bool hasFocus, bool pressed, bool positive) const { + if (_amigaStyle) { + if (positive) { + if (pressed) { // Positive pressed Amiga-style button + if (_olderAgi) { + return AgiTextColor(amigaBlack, amigaOrange); + } else { + return AgiTextColor(amigaBlack, amigaPurple); + } + } else { // Positive unpressed Amiga-style button + return AgiTextColor(amigaWhite, amigaGreen); + } + } else { // _amigaStyle && !positive + if (pressed) { // Negative pressed Amiga-style button + return AgiTextColor(amigaBlack, amigaCyan); + } else { // Negative unpressed Amiga-style button + return AgiTextColor(amigaWhite, amigaRed); + } + } + } else { // PC-style button + if (hasFocus || pressed) { // A pressed or in focus PC-style button + return AgiTextColor(pcWhite, pcBlack); + } else { // An unpressed PC-style button without focus + return AgiTextColor(pcBlack, pcWhite); + } + } +} + +AgiTextColor AgiButtonStyle::getColor(bool hasFocus, bool pressed, int baseFgColor, int baseBgColor) const { + return getColor(hasFocus, pressed, AgiTextColor(baseFgColor, baseBgColor)); +} + +AgiTextColor AgiButtonStyle::getColor(bool hasFocus, bool pressed, const AgiTextColor &baseColor) const { + if (hasFocus || pressed) + return baseColor.swap(); + else + return baseColor; +} + +int AgiButtonStyle::getTextOffset(bool hasFocus, bool pressed) const { + return (pressed && !_amigaStyle) ? 1 : 0; +} + +bool AgiButtonStyle::getBorder(bool hasFocus, bool pressed) const { + return _amigaStyle && !_authenticAmiga && (hasFocus || pressed); +} + +void AgiButtonStyle::setAmigaStyle(bool amigaStyle, bool olderAgi, bool authenticAmiga) { + _amigaStyle = amigaStyle; + _olderAgi = olderAgi; + _authenticAmiga = authenticAmiga; +} + +void AgiButtonStyle::setPcStyle(bool pcStyle) { + setAmigaStyle(!pcStyle); +} + +AgiButtonStyle::AgiButtonStyle(Common::RenderMode renderMode) { + setAmigaStyle(renderMode == Common::kRenderAmiga); +} + AgiEngine::AgiEngine(OSystem *syst) : Engine(syst) { // Setup mixer @@ -635,6 +702,8 @@ void AgiEngine::initialize() { } } + _buttonStyle = AgiButtonStyle(_renderMode); + _defaultButtonStyle = AgiButtonStyle(); _console = new Console(this); _gfx = new GfxMgr(this); _sound = new SoundMgr(this, _mixer); @@ -676,6 +745,13 @@ void AgiEngine::initialize() { } AgiEngine::~AgiEngine() { + // If the engine hasn't been initialized yet via AgiEngine::initialize(), don't attempt to free any resources, + // as they haven't been allocated. Fixes bug #1742432 - AGI: Engine crashes if no game is detected + if (_game.state == STATE_INIT) { + delete _rnd; // delete _rnd, as it is allocated in the constructor, not in initialize() + return; + } + agiDeinit(); _sound->deinitSound(); delete _sound; diff --git a/engines/agi/agi.h b/engines/agi/agi.h index 01db2acb23..f37b61478e 100644 --- a/engines/agi/agi.h +++ b/engines/agi/agi.h @@ -81,6 +81,7 @@ typedef signed int Err; #define MSG_BOX_COLOUR 0x0f /* White */ #define MSG_BOX_TEXT 0x00 /* Black */ #define MSG_BOX_LINE 0x04 /* Red */ +#define BUTTON_BORDER 0x00 /* Black */ #define STATUS_FG 0x00 /* Black */ #define STATUS_BG 0x0f /* White */ @@ -110,7 +111,9 @@ enum AgiGameFeatures { GF_AGI256_2 = (1 << 3), GF_AGIPAL = (1 << 4), GF_MACGOLDRUSH = (1 << 5), - GF_FANMADE = (1 << 6) + GF_FANMADE = (1 << 6), + GF_MENUS = (1 << 7), + GF_ESCPAUSE = (1 << 8) }; enum AgiGameID { @@ -153,6 +156,7 @@ enum AGIErrors { errNoLoopsInView, errViewDataError, errNoGameList, + errIOError, errUnk = 127 }; @@ -317,6 +321,118 @@ struct AgiBlock { uint8 *buffer; /* used for window background */ }; +/** AGI text color (Background and foreground color). */ +struct AgiTextColor { + /** Creates an AGI text color. Uses black text on white background by default. */ + AgiTextColor(int fgColor = 0x00, int bgColor = 0x0F) : fg(fgColor), bg(bgColor) {} + + /** Get an AGI text color with swapped foreground and background color. */ + AgiTextColor swap() const { return AgiTextColor(bg, fg); } + + int fg; ///< Foreground color (Used for text). + int bg; ///< Background color (Used for text's background). +}; + +/** + * AGI button style (Amiga or PC). + * + * Supports positive and negative button types (Used with Amiga-style only): + * Positive buttons do what the dialog was opened for. + * Negative buttons cancel what the dialog was opened for. + * Restart-dialog example: Restart-button is positive, Cancel-button negative. + * Paused-dialog example: Continue-button is positive. + */ +struct AgiButtonStyle { +// Public constants etc +public: + static const int + // Amiga colors (Indexes into the Amiga-ish palette) + amigaBlack = 0x00, ///< Accurate, is #000000 (24-bit RGB) + amigaWhite = 0x0F, ///< Practically accurate, is close to #FFFFFF (24-bit RGB) + amigaGreen = 0x02, ///< Quite accurate, should be #008A00 (24-bit RGB) + amigaOrange = 0x0C, ///< Inaccurate, too much blue, should be #FF7500 (24-bit RGB) + amigaPurple = 0x0D, ///< Inaccurate, too much green, should be #FF00FF (24-bit RGB) + amigaRed = 0x04, ///< Quite accurate, should be #BD0000 (24-bit RGB) + amigaCyan = 0x0B, ///< Inaccurate, too much red, should be #00FFDE (24-bit RGB) + // PC colors (Indexes into the EGA-palette) + pcBlack = 0x00, + pcWhite = 0x0F; + +// Public methods +public: + /** + * Get the color of the button with the given state and type using current style. + * + * @param hasFocus True if button has focus, false otherwise. + * @param pressed True if button is being pressed, false otherwise. + * @param positive True if button is positive, false if button is negative. Only matters for Amiga-style buttons. + */ + AgiTextColor getColor(bool hasFocus, bool pressed, bool positive = true) const; + + /** + * Get the color of a button with the given base color and state ignoring current style. + * Swaps foreground and background color when the button has focus or is being pressed. + * + * @param hasFocus True if button has focus, false otherwise. + * @param pressed True if button is being pressed, false otherwise. + * @param baseFgColor Foreground color of the button when it has no focus and is not being pressed. + * @param baseBgColor Background color of the button when it has no focus and is not being pressed. + */ + AgiTextColor getColor(bool hasFocus, bool pressed, int baseFgColor, int baseBgColor) const; + + /** + * Get the color of a button with the given base color and state ignoring current style. + * Swaps foreground and background color when the button has focus or is being pressed. + * + * @param hasFocus True if button has focus, false otherwise. + * @param pressed True if button is being pressed, false otherwise. + * @param baseColor Color of the button when it has no focus and is not being pressed. + */ + AgiTextColor getColor(bool hasFocus, bool pressed, const AgiTextColor &baseColor) const; + + /** + * How many pixels to offset the shown text diagonally down and to the right. + * Currently only used for pressed PC-style buttons. + */ + int getTextOffset(bool hasFocus, bool pressed) const; + + /** + * Show border around the button? + * Currently border is only used for in focus or pressed Amiga-style buttons + * when in inauthentic Amiga-style mode. + */ + bool getBorder(bool hasFocus, bool pressed) const; + + /** + * Set Amiga-button style. + * + * @param amigaStyle Set Amiga-button style if true, otherwise set PC-button style. + * @param olderAgi If true then use older AGI style in Amiga-mode, otherwise use newer. + * @param authenticAmiga If true then don't use a border around buttons in Amiga-mode, otherwise use. + */ + void setAmigaStyle(bool amigaStyle = true, bool olderAgi = false, bool authenticAmiga = false); + + /** + * Set PC-button style. + * @param pcStyle Set PC-button style if true, otherwise set default Amiga-button style. + */ + void setPcStyle(bool pcStyle = true); + +// Public constructors +public: + /** + * Create a button style based on the given rendering mode. + * @param renderMode If Common::kRenderAmiga then creates default Amiga-button style, otherwise PC-style. + */ + AgiButtonStyle(Common::RenderMode renderMode = Common::kRenderDefault); + +// Private member variables +private: + bool _amigaStyle; ///< Use Amiga-style buttons if true, otherwise use PC-style buttons. + bool _olderAgi; ///< Use older AGI style in Amiga-style mode. + bool _authenticAmiga; ///< Don't use border around buttons in Amiga-style mode. +}; + #define EGO_VIEW_TABLE 0 #define HORIZON 36 #define _WIDTH 160 @@ -594,6 +710,8 @@ public: int _oldMode; Menu* _menu; + AgiButtonStyle _buttonStyle; + AgiButtonStyle _defaultButtonStyle; char _lastSentence[40]; diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp index 1ab59c0806..79e3be3238 100644 --- a/engines/agi/detection.cpp +++ b/engines/agi/detection.cpp @@ -125,7 +125,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == AGI Demo 1 (PC) 05/87 [AGI 2.425] + // AGI Demo 1 (PC) 05/87 [AGI 2.425] { "agidemo", "Demo 1 1987-05-20", @@ -142,7 +142,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == AGI Demo 2 (IIgs) 1.0C (Censored) + // AGI Demo 2 (IIgs) 1.0C (Censored) { "agidemo", "Demo 2 1987-11-24 1.0C", @@ -159,7 +159,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == AGI Demo 2 (PC 3.5") 11/87 [AGI 2.915] + // AGI Demo 2 (PC 3.5") 11/87 [AGI 2.915] { "agidemo", "Demo 2 1987-11-24 3.5\"", @@ -176,7 +176,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == AGI Demo 2 (PC 5.25") 11/87 [v1] [AGI 2.915] + // AGI Demo 2 (PC 5.25") 11/87 [v1] [AGI 2.915] { "agidemo", "Demo 2 1987-11-24 [version 1] 5.25\"", @@ -193,7 +193,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == AGI Demo 2 (PC 5.25") 01/88 [v2] [AGI 2.917] + // AGI Demo 2 (PC 5.25") 01/88 [v2] [AGI 2.917] { "agidemo", "Demo 2 1988-01-25 [version 2] 5.25\"", @@ -210,7 +210,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == AGI Demo 3 (PC) 09/88 [AGI 3.002.102] + // AGI Demo 3 (PC) 09/88 [AGI 3.002.102] { "agidemo", "Demo 3 1988-09-13", @@ -227,7 +227,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Black Cauldron (Amiga) 2.00 6/14/87 + // Black Cauldron (Amiga) 2.00 6/14/87 { "bc", "2.00 1987-06-14", @@ -244,7 +244,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Black Cauldron (Apple IIgs) 1.0O 2/24/89 (CE) + // Black Cauldron (Apple IIgs) 1.0O 2/24/89 (CE) + // Menus not tested { "bc", "1.0O 1989-02-24 (CE)", @@ -261,7 +262,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Black Cauldron (PC) 2.00 6/14/87 [AGI 2.439] + // Black Cauldron (PC) 2.00 6/14/87 [AGI 2.439] { "bc", "2.00 1987-06-14", @@ -278,7 +279,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Black Cauldron (PC 5.25") 2.10 11/10/88 [AGI 3.002.098] + // Black Cauldron (PC 5.25") 2.10 11/10/88 [AGI 3.002.098] { "bc", "2.10 1988-11-10 5.25\"", @@ -297,7 +298,8 @@ static const AGIGameDescription gameDescriptions[] = { // These aren't supposed to work now as they require unsupported agi engine 2.01 #if 0 { - // Sarien Name == Donald Duck's Playground (Amiga) 1.0C + // Donald Duck's Playground (Amiga) 1.0C + // Menus not tested { "ddp", "1.0C 1987-04-27", @@ -314,7 +316,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Donald Duck's Playground (ST) 1.0A 8/8/86 + // Donald Duck's Playground (ST) 1.0A 8/8/86 + // Menus not tested { "ddp", "1.0A 1986-08-08", @@ -332,6 +335,7 @@ static const AGIGameDescription gameDescriptions[] = { { // reported by Filippos (thebluegr) in bugreport #1654500 + // Menus not tested { "ddp", "1.0C 1986-06-09", // verify date @@ -348,7 +352,7 @@ static const AGIGameDescription gameDescriptions[] = { #endif { - // Sarien Name == Gold Rush! (Amiga) 1.01 1/13/89 aka 2.05 3/9/89 # 2.316 + // Gold Rush! (Amiga) 1.01 1/13/89 aka 2.05 3/9/89 # 2.316 { "goldrush", "1.01 1989-01-13 aka 2.05 1989-03-09", @@ -365,7 +369,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Gold Rush! (Apple IIgs) 1.0M 2/28/89 (CE) aka 2.01 12/22/88 + // Gold Rush! (Apple IIgs) 1.0M 2/28/89 (CE) aka 2.01 12/22/88 + // Menus not tested { "goldrush", "1.0M 1989-02-28 (CE) aka 2.01 1988-12-22", @@ -382,7 +387,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Gold Rush! (ST) 1.01 1/13/89 aka 2.01 12/22/88 + // Gold Rush! (ST) 1.01 1/13/89 aka 2.01 12/22/88 { "goldrush", "1.01 1989-01-13 aka 2.01 1988-12-22", @@ -399,7 +404,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149] + // Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149] { "goldrush", "2.01 1988-12-22 5.25\"", @@ -416,7 +421,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Gold Rush! (PC 3.5") 2.01 12/22/88 [AGI 3.002.149] + // Gold Rush! (PC 3.5") 2.01 12/22/88 [AGI 3.002.149] { "goldrush", "2.01 1988-12-22 3.5\"", @@ -433,7 +438,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149] + // Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149] { "goldrush", "2.01 1988-12-22", @@ -454,7 +459,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 1 (Amiga) 1.0U # 2.082 + // King's Quest 1 (Amiga) 1.0U # 2.082 + // The original game did not have menus, they are enabled under ScummVM { "kq1", "1.0U 1986", @@ -465,13 +471,14 @@ static const AGIGameDescription gameDescriptions[] = { }, GID_KQ1, GType_V2, - 0, + GF_MENUS, 0x2440, }, { - // Sarien Name == King's Quest 1 (ST) 1.0V + // King's Quest 1 (ST) 1.0V + // The original game did not have menus, they are enabled under ScummVM { "kq1", "1.0V 1986", @@ -482,13 +489,14 @@ static const AGIGameDescription gameDescriptions[] = { }, GID_KQ1, GType_V2, - 0, + GF_MENUS, 0x2272, }, { - // Sarien Name == King's Quest 1 (IIgs) 1.0S-88223 + // King's Quest 1 (IIgs) 1.0S-88223 + // Menus not tested { "kq1", "1.0S 1988-02-23", @@ -505,7 +513,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 1 (Mac) 2.0C + // King's Quest 1 (Mac) 2.0C { "kq1", "2.0C 1987-03-26", @@ -522,7 +530,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 1 (PC 5.25"/3.5") 2.0F [AGI 2.917] + // King's Quest 1 (PC 5.25"/3.5") 2.0F [AGI 2.917] { "kq1", "2.0F 1987-05-05 5.25\"/3.5\"", @@ -539,7 +547,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 2 (IIgs) 2.0A 6/16/88 (CE) + // King's Quest 2 (IIgs) 2.0A 6/16/88 (CE) { "kq2", "2.0A 1988-06-16 (CE)", @@ -556,7 +564,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 2 (Amiga) 2.0J (Broken) + // King's Quest 2 (Amiga) 2.0J (Broken) { "kq2", "2.0J 1987-01-29 [OBJECT decrypted]", @@ -573,7 +581,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 2 (Mac) 2.0R + // King's Quest 2 (Mac) 2.0R { "kq2", "2.0R 1988-03-23", @@ -607,7 +615,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 2 (PC 5.25"/3.5") 2.2 [AGI 2.426] + // King's Quest 2 (PC 5.25"/3.5") 2.2 [AGI 2.426] { "kq2", "2.2 1987-05-07 5.25\"/3.5\"", @@ -624,7 +632,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 3 (Amiga) 1.01 11/8/86 + // King's Quest 3 (Amiga) 1.01 11/8/86 + // The original game did not have menus, they are enabled under ScummVM { "kq3", "1.01 1986-11-08", @@ -635,13 +644,14 @@ static const AGIGameDescription gameDescriptions[] = { }, GID_KQ3, GType_V2, - 0, + GF_MENUS, 0x2440, }, { - // Sarien Name == King's Quest 3 (ST) 1.02 11/18/86 + // King's Quest 3 (ST) 1.02 11/18/86 + // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game { "kq3", "1.02 1986-11-18", @@ -652,13 +662,13 @@ static const AGIGameDescription gameDescriptions[] = { }, GID_KQ3, GType_V2, - 0, + GF_ESCPAUSE, 0x2272, }, { - // Sarien Name == King's Quest 3 (Mac) 2.14 3/15/88 + // King's Quest 3 (Mac) 2.14 3/15/88 { "kq3", "2.14 1988-03-15", @@ -675,7 +685,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 3 (IIgs) 2.0A 8/28/88 (CE) + // King's Quest 3 (IIgs) 2.0A 8/28/88 (CE) { "kq3", "2.0A 1988-08-28 (CE)", @@ -692,7 +702,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 3 (Amiga) 2.15 11/15/89 # 2.333 + // King's Quest 3 (Amiga) 2.15 11/15/89 # 2.333 { "kq3", "2.15 1989-11-15", @@ -709,7 +719,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 3 (PC) 1.01 11/08/86 [AGI 2.272] + // King's Quest 3 (PC) 1.01 11/08/86 [AGI 2.272] + // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game { "kq3", "1.01 1986-11-08", @@ -720,13 +731,13 @@ static const AGIGameDescription gameDescriptions[] = { }, GID_KQ3, GType_V2, - 0, + GF_ESCPAUSE, 0x2272, }, { - // Sarien Name == King's Quest 3 (PC 5.25") 2.00 5/25/87 [AGI 2.435] + // King's Quest 3 (PC 5.25") 2.00 5/25/87 [AGI 2.435] { "kq3", "2.00 1987-05-25 5.25\"", @@ -743,7 +754,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 3 (Mac) 2.14 3/15/88 + // King's Quest 3 (Mac) 2.14 3/15/88 + // Menus not tested { "kq3", "2.14 1988-03-15 5.25\"", @@ -760,7 +772,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 3 (PC 3.5") 2.14 3/15/88 [AGI 2.936] + // King's Quest 3 (PC 3.5") 2.14 3/15/88 [AGI 2.936] { "kq3", "2.14 1988-03-15 3.5\"", @@ -777,7 +789,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 4 (PC 5.25") 2.3 9/27/88 [AGI 3.002.086] + // King's Quest 4 (PC 5.25") 2.3 9/27/88 [AGI 3.002.086] { "kq4", "2.3 1988-09-27", @@ -794,7 +806,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 4 (IIgs) 1.0K 11/22/88 (CE) + // King's Quest 4 (IIgs) 1.0K 11/22/88 (CE) + // Menus not tested { "kq4", "1.0K 1988-11-22", @@ -811,7 +824,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 4 (PC 3.5") 2.0 7/27/88 [AGI 3.002.086] + // King's Quest 4 (PC 3.5") 2.0 7/27/88 [AGI 3.002.086] { "kq4", "2.0 1988-07-27 3.5\"", @@ -828,7 +841,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 4 (PC 3.5") 2.2 9/27/88 [AGI 3.002.086] + // King's Quest 4 (PC 3.5") 2.2 9/27/88 [AGI 3.002.086] + // Menus not tested { "kq4", "2.2 1988-09-27 3.5\"", @@ -845,7 +859,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == King's Quest 4 demo (PC) [AGI 3.002.102] + // King's Quest 4 demo (PC) [AGI 3.002.102] + // Menus not tested { "kq4", "Demo 1988-12-20", @@ -862,7 +877,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Leisure Suit Larry 1 (PC 5.25"/3.5") 1.00 6/1/87 [AGI 2.440] + // Leisure Suit Larry 1 (PC 5.25"/3.5") 1.00 6/1/87 [AGI 2.440] { "lsl1", "1.00 1987-06-01 5.25\"/3.5\"", @@ -879,7 +894,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Leisure Suit Larry 1 (ST) 1.04 6/18/87 + // Leisure Suit Larry 1 (ST) 1.04 6/18/87 { "lsl1", "1.04 1987-06-18", @@ -896,7 +911,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Leisure Suit Larry 1 (Amiga) 1.05 6/26/87 # x.yyy + // Leisure Suit Larry 1 (Amiga) 1.05 6/26/87 # x.yyy { "lsl1", "1.05 1987-06-26", @@ -913,7 +928,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Leisure Suit Larry 1 (IIgs) 1.0E + // Leisure Suit Larry 1 (IIgs) 1.0E { "lsl1", "1.0E 1987", @@ -930,7 +945,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Leisure Suit Larry 1 (Mac) 1.05 6/26/87 + // Leisure Suit Larry 1 (Mac) 1.05 6/26/87 { "lsl1", "1.05 1987-06-26", @@ -947,7 +962,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Manhunter NY (ST) 1.03 10/20/88 + // Manhunter NY (ST) 1.03 10/20/88 { "mh1", "1.03 1988-10-20", @@ -964,7 +979,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Manhunter NY (IIgs) 2.0E 10/05/88 (CE) + // Manhunter NY (IIgs) 2.0E 10/05/88 (CE) { "mh1", "2.0E 1988-10-05 (CE)", @@ -981,7 +996,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Manhunter NY (Amiga) 1.06 3/18/89 + // Manhunter NY (Amiga) 1.06 3/18/89 { "mh1", "1.06 1989-03-18", @@ -999,7 +1014,7 @@ static const AGIGameDescription gameDescriptions[] = { { // reported by Filippos (thebluegr) in bugreport #1654500 - // Sarien Name == Manhunter NY (PC 5.25") 1.22 8/31/88 [AGI 3.002.107] + // Manhunter NY (PC 5.25") 1.22 8/31/88 [AGI 3.002.107] { "mh1", "1.22 1988-08-31", @@ -1016,7 +1031,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Manhunter NY (PC 3.5") 1.22 8/31/88 [AGI 3.002.102] + // Manhunter NY (PC 3.5") 1.22 8/31/88 [AGI 3.002.102] { "mh1", "1.22 1988-08-31", @@ -1033,7 +1048,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Manhunter SF (ST) 1.0 7/29/89 + // Manhunter SF (ST) 1.0 7/29/89 { "mh2", "1.0 1989-07-29", @@ -1050,7 +1065,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Manhunter SF (Amiga) 3.06 8/17/89 # 2.333 + // Manhunter SF (Amiga) 3.06 8/17/89 # 2.333 { "mh2", "3.06 1989-08-17", @@ -1067,7 +1082,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Manhunter SF (PC 5.25") 3.03 8/17/89 [AGI 3.002.149] + // Manhunter SF (PC 5.25") 3.03 8/17/89 [AGI 3.002.149] { "mh2", "3.03 1989-08-17 5.25\"", @@ -1084,7 +1099,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Manhunter SF (PC 3.5") 3.02 7/26/89 [AGI 3.002.149] + // Manhunter SF (PC 3.5") 3.02 7/26/89 [AGI 3.002.149] { "mh2", "3.02 1989-07-26 3.5\"", @@ -1099,9 +1114,11 @@ static const AGIGameDescription gameDescriptions[] = { 0x3149, }, - +#if 0 { - // Sarien Name == Mixed-Up Mother Goose (Amiga) 1.1 + // Mixed-Up Mother Goose (Amiga) 1.1 + // Problematic: crashes + // Menus not tested { "mixedup", "1.1 1986-12-10", @@ -1115,10 +1132,10 @@ static const AGIGameDescription gameDescriptions[] = { 0, 0x3086, }, - +#endif { - // Sarien Name == Mixed Up Mother Goose (IIgs) + // Mixed Up Mother Goose (IIgs) { "mixedup", "1987", @@ -1135,7 +1152,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Mixed-Up Mother Goose (PC) [AGI 2.915] + // Mixed-Up Mother Goose (PC) [AGI 2.915] { "mixedup", "1987-11-10", @@ -1153,7 +1170,8 @@ static const AGIGameDescription gameDescriptions[] = { #if 0 { - // Sarien Name == Mixed Up Mother Goose (PC) [AGI 2.915] (Broken) + // Mixed Up Mother Goose (PC) [AGI 2.915] (Broken) + // Menus not tested { "mixedup", "[corrupt/OBJECT from disk 1]", @@ -1171,7 +1189,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Police Quest 1 (PC) 2.0E 11/17/87 [AGI 2.915] + // Police Quest 1 (PC) 2.0E 11/17/87 [AGI 2.915] { "pq1", "2.0E 1987-11-17", @@ -1188,7 +1206,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Police Quest 1 (Mac) 2.0G 12/3/87 + // Police Quest 1 (Mac) 2.0G 12/3/87 { "pq1", "2.0G 1987-12-03", @@ -1205,7 +1223,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Police Quest 1 (IIgs) 2.0B-88421 + // Police Quest 1 (IIgs) 2.0B-88421 { "pq1", "2.0B 1988-04-21", @@ -1222,7 +1240,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Police Quest 1 (Amiga) 2.0B 2/22/89 # 2.310 + // Police Quest 1 (Amiga) 2.0B 2/22/89 # 2.310 { "pq1", "2.0B 1989-02-22", @@ -1239,7 +1257,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Police Quest 1 (IIgs) 2.0A-88318 + // Police Quest 1 (IIgs) 2.0A-88318 { "pq1", "2.0A 1988-03-18", @@ -1256,7 +1274,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Police Quest 1 (PC) 2.0A 10/23/87 [AGI 2.903/2.911] + // Police Quest 1 (PC) 2.0A 10/23/87 [AGI 2.903/2.911] { "pq1", "2.0A 1987-10-23", @@ -1273,7 +1291,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Police Quest 1 (Mac) 2.0G 12/3/87 + // Police Quest 1 (Mac) 2.0G 12/3/87 { "pq1", "2.0G 1987-12-03 5.25\"/ST", @@ -1307,7 +1325,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Space Quest 1 (ST) 1.1A + // Space Quest 1 (ST) 1.1A + // The original game did not have menus, they are enabled under ScummVM { "sq1", "1.1A 1986-02-06", @@ -1318,13 +1337,14 @@ static const AGIGameDescription gameDescriptions[] = { }, GID_SQ1, GType_V2, - 0, + GF_MENUS, 0x2440, }, { - // Sarien Name == Space Quest 1 (PC) 1.1A [AGI 2.272] + // Space Quest 1 (PC) 1.1A [AGI 2.272] + // The original game did not have menus, they are enabled under ScummVM { "sq1", "1.1A 1986-11-13", @@ -1335,13 +1355,14 @@ static const AGIGameDescription gameDescriptions[] = { }, GID_SQ1, GType_V2, - 0, + GF_MENUS, 0x2272, }, { - // Sarien Name == Space Quest 1 (Amiga) 1.2 # 2.082 + // Space Quest 1 (Amiga) 1.2 # 2.082 + // The original game did not have menus, they are enabled under ScummVM { "sq1", "1.2 1986", @@ -1352,13 +1373,13 @@ static const AGIGameDescription gameDescriptions[] = { }, GID_SQ1, GType_V2, - 0, + GF_MENUS, 0x2440, }, { - // Sarien Name == Space Quest 1 (Mac) 1.5D + // Space Quest 1 (Mac) 1.5D { "sq1", "1.5D 1987-04-02", @@ -1375,7 +1396,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Space Quest 1 (IIgs) 2.2 + // Space Quest 1 (IIgs) 2.2 { "sq1", "2.2 1987", @@ -1392,7 +1413,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Space Quest 1 (PC) 1.0X [AGI 2.089] + // Space Quest 1 (PC) 1.0X [AGI 2.089] + // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game { "sq1", "1.0X 1986-09-24", @@ -1403,30 +1425,14 @@ static const AGIGameDescription gameDescriptions[] = { }, GID_SQ1, GType_V2, - 0, + GF_ESCPAUSE, 0x2089, }, - { - // Sarien Name == Space Quest 1 (PC) 1.1A [AGI 2.272] - { - "sq1", - "1.1A 1986-11-13", - AD_ENTRY1("logdir", "8d8c20ab9f4b6e4817698637174a1cb6"), - Common::EN_ANY, - Common::kPlatformPC, - Common::ADGF_NO_FLAGS - }, - GID_SQ1, - GType_V2, - 0, - 0x2272, - }, - { - // Sarien Name == Space Quest 1 (PC 5.25"/3.5") 2.2 [AGI 2.426/2.917] + // Space Quest 1 (PC 5.25"/3.5") 2.2 [AGI 2.426/2.917] { "sq1", "2.2 1987-05-07 5.25\"/3.5\"", @@ -1444,7 +1450,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Space Quest 2 (PC 3.5") 2.0D [AGI 2.936] + // Space Quest 2 (PC 3.5") 2.0D [AGI 2.936] { "sq2", "2.0D 1988-03-14 3.5\"", @@ -1461,7 +1467,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Space Quest 2 (IIgs) 2.0A 7/25/88 (CE) + // Space Quest 2 (IIgs) 2.0A 7/25/88 (CE) { "sq2", "2.0A 1988-07-25 (CE)", @@ -1478,7 +1484,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Space Quest 2 (Amiga) 2.0F + // Space Quest 2 (Amiga) 2.0F { "sq2", "2.0F 1986-12-09 [VOL.2->PICTURE.16 broken]", @@ -1499,7 +1505,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Space Quest 2 (Mac) 2.0D + // Space Quest 2 (Mac) 2.0D { "sq2", "2.0D 1988-04-04", @@ -1517,7 +1523,7 @@ static const AGIGameDescription gameDescriptions[] = { { // reported by Filippos (thebluegr) in bugreport #1654500 - // Sarien Name == Space Quest 2 (PC 5.25") 2.0A [AGI 2.912] + // Space Quest 2 (PC 5.25") 2.0A [AGI 2.912] { "sq2", "2.0A 1987-11-06 5.25\"", @@ -1534,7 +1540,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Space Quest 2 (PC 3.5") 2.0A [AGI 2.912] + // Space Quest 2 (PC 3.5") 2.0A [AGI 2.912] { "sq2", "2.0A 1987-11-06 3.5\"", @@ -1551,7 +1557,8 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Space Quest 2 (PC 5.25"/ST) 2.0C/A [AGI 2.915] + // Space Quest 2 (PC 5.25"/ST) 2.0C/A [AGI 2.915] + // Menus not tested { "sq2", "2.0C/A 5.25\"/ST", @@ -1568,7 +1575,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Space Quest 2 (PC 3.5") 2.0F [AGI 2.936] + // Space Quest 2 (PC 3.5") 2.0F [AGI 2.936] { "sq2", "2.0F 1989-01-05 3.5\"", @@ -1585,7 +1592,7 @@ static const AGIGameDescription gameDescriptions[] = { { - // Sarien Name == Xmas Card 1986 (PC) [AGI 2.272] + // Xmas Card 1986 (PC) [AGI 2.272] { "xmascard", "1986-11-13 [version 1]", @@ -1675,7 +1682,7 @@ static const AGIGameDescription gameDescriptions[] = { FANMADE("Good Man (demo v3.41)", "3facd8a8f856b7b6e0f6c3200274d88c"), { - // Sarien Name == Groza + // Groza { "agi-fanmade", "Groza (russian) [AGDS sample]", diff --git a/engines/agi/graphics.cpp b/engines/agi/graphics.cpp index 54f1783d83..b6430e0f81 100644 --- a/engines/agi/graphics.cpp +++ b/engines/agi/graphics.cpp @@ -68,6 +68,158 @@ uint8 egaPalette[16 * 3] = { }; /** + * Atari ST AGI palette. + * Used by all of the tested Atari ST AGI games + * from Donald Duck's Playground (1986) to Manhunter II (1989). + * 16 RGB colors. 3 bits per color component. + */ +uint8 atariStAgiPalette[16 * 3] = { + 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, + 0x0, 0x4, 0x0, + 0x0, 0x5, 0x4, + 0x5, 0x0, 0x0, + 0x5, 0x3, 0x6, + 0x4, 0x3, 0x0, + 0x5, 0x5, 0x5, + 0x3, 0x3, 0x2, + 0x0, 0x5, 0x7, + 0x0, 0x6, 0x0, + 0x0, 0x7, 0x6, + 0x7, 0x2, 0x3, + 0x7, 0x4, 0x7, + 0x7, 0x7, 0x4, + 0x7, 0x7, 0x7 +}; + +/** + * Second generation Apple IIGS AGI palette. + * A 16-color, 12-bit RGB palette. + * + * Used by at least the following Apple IIGS AGI versions: + * 1.003 (Leisure Suit Larry I v1.0E, intro says 1987) + * 1.005 (AGI Demo 2 1987-06-30?) + * 1.006 (King's Quest I v1.0S 1988-02-23) + * 1.007 (Police Quest I v2.0B 1988-04-21 8:00am) + * 1.013 (King's Quest II v2.0A 1988-06-16 (CE)) + * 1.013 (Mixed-Up Mother Goose v2.0A 1988-05-31 10:00am) + * 1.014 (King's Quest III v2.0A 1988-08-28 (CE)) + * 1.014 (Space Quest II v2.0A, LOGIC.141 says 1988) + * 2.004 (Manhunter I v2.0E 1988-10-05 (CE)) + * 2.006 (King's Quest IV v1.0K 1988-11-22 (CE)) + * 3.001 (Black Cauldron v1.0O 1989-02-24 (CE)) + * 3.003 (Gold Rush! v1.0M 1989-02-28 (CE)) + */ +uint8 appleIIgsAgiPaletteV2[16 * 3] = { + 0x0, 0x0, 0x0, + 0x0, 0x0, 0xF, + 0x0, 0x8, 0x0, + 0x0, 0xD, 0xB, + 0xC, 0x0, 0x0, + 0xB, 0x7, 0xD, + 0x8, 0x5, 0x0, + 0xB, 0xB, 0xB, + 0x7, 0x7, 0x7, + 0x0, 0xB, 0xF, + 0x0, 0xE, 0x0, + 0x0, 0xF, 0xD, + 0xF, 0x9, 0x8, + 0xD, 0x9, 0xF, // Only this differs from the 1st generation palette + 0xE, 0xE, 0x0, + 0xF, 0xF, 0xF +}; + +/** + * First generation Amiga & Apple IIGS AGI palette. + * A 16-color, 12-bit RGB palette. + * + * Used by at least the following Amiga AGI versions: + * 2.082 (King's Quest I v1.0U 1986) + * 2.082 (Space Quest I v1.2 1986) + * 2.090 (King's Quest III v1.01 1986-11-08) + * 2.107 (King's Quest II v2.0J 1987-01-29) + * x.yyy (Black Cauldron v2.00 1987-06-14) + * x.yyy (Larry I v1.05 1987-06-26) + * + * Also used by at least the following Apple IIGS AGI versions: + * 1.002 (Space Quest I, intro says v2.2 1987) + */ +uint8 amigaAgiPaletteV1[16 * 3] = { + 0x0, 0x0, 0x0, + 0x0, 0x0, 0xF, + 0x0, 0x8, 0x0, + 0x0, 0xD, 0xB, + 0xC, 0x0, 0x0, + 0xB, 0x7, 0xD, + 0x8, 0x5, 0x0, + 0xB, 0xB, 0xB, + 0x7, 0x7, 0x7, + 0x0, 0xB, 0xF, + 0x0, 0xE, 0x0, + 0x0, 0xF, 0xD, + 0xF, 0x9, 0x8, + 0xF, 0x7, 0x0, + 0xE, 0xE, 0x0, + 0xF, 0xF, 0xF +}; + +/** + * Second generation Amiga AGI palette. + * A 16-color, 12-bit RGB palette. + * + * Used by at least the following Amiga AGI versions: + * 2.202 (Space Quest II v2.0F. Intro says 1988. ScummVM 0.10.0 detects as 1986-12-09) + */ +uint8 amigaAgiPaletteV2[16 * 3] = { + 0x0, 0x0, 0x0, + 0x0, 0x0, 0xF, + 0x0, 0x8, 0x0, + 0x0, 0xD, 0xB, + 0xC, 0x0, 0x0, + 0xB, 0x7, 0xD, + 0x8, 0x5, 0x0, + 0xB, 0xB, 0xB, + 0x7, 0x7, 0x7, + 0x0, 0xB, 0xF, + 0x0, 0xE, 0x0, + 0x0, 0xF, 0xD, + 0xF, 0x9, 0x8, + 0xD, 0x0, 0xF, + 0xE, 0xE, 0x0, + 0xF, 0xF, 0xF +}; + +/** + * Third generation Amiga AGI palette. + * A 16-color, 12-bit RGB palette. + * + * Used by at least the following Amiga AGI versions: + * 2.310 (Police Quest I v2.0B 1989-02-22) + * 2.316 (Gold Rush! v2.05 1989-03-09) + * x.yyy (Manhunter I v1.06 1989-03-18) + * 2.333 (Manhunter II v3.06 1989-08-17) + * 2.333 (King's Quest III v2.15 1989-11-15) + */ +uint8 amigaAgiPaletteV3[16 * 3] = { + 0x0, 0x0, 0x0, + 0x0, 0x0, 0xB, + 0x0, 0xB, 0x0, + 0x0, 0xB, 0xB, + 0xB, 0x0, 0x0, + 0xB, 0x0, 0xB, + 0xC, 0x7, 0x0, + 0xB, 0xB, 0xB, + 0x7, 0x7, 0x7, + 0x0, 0x0, 0xF, + 0x0, 0xF, 0x0, + 0x0, 0xF, 0xF, + 0xF, 0x0, 0x0, + 0xF, 0x0, 0xF, + 0xF, 0xF, 0x0, + 0xF, 0xF, 0xF +}; + +/** * 16 color amiga-ish palette. */ uint8 newPalette[16 * 3] = { @@ -554,13 +706,42 @@ void GfxMgr::printCharacter(int x, int y, char c, int fg, int bg) { } /** - * Draw button + * Draw a default style button. + * Swaps background and foreground color if button is in focus or being pressed. * @param x x coordinate of the button * @param y y coordinate of the button * @param a set if the button has focus * @param p set if the button is pressed + * @param fgcolor foreground color of the button when it is neither in focus nor being pressed + * @param bgcolor background color of the button when it is neither in focus nor being pressed */ -void GfxMgr::drawButton(int x, int y, const char *s, int a, int p, int fgcolor, int bgcolor) { +void GfxMgr::drawDefaultStyleButton(int x, int y, const char *s, int a, int p, int fgcolor, int bgcolor) { + int textOffset = _vm->_defaultButtonStyle.getTextOffset(a > 0, p > 0); + AgiTextColor color = _vm->_defaultButtonStyle.getColor (a > 0, p > 0, fgcolor, bgcolor); + bool border = _vm->_defaultButtonStyle.getBorder (a > 0, p > 0); + + rawDrawButton(x, y, s, color.fg, color.bg, border, textOffset); +} + +/** + * Draw a button using the currently chosen style. + * Amiga-style is used for the Amiga-rendering mode, PC-style is used otherwise. + * @param x x coordinate of the button + * @param y y coordinate of the button + * @param hasFocus set if the button has focus + * @param pressed set if the button is pressed + * @param positive set if button is positive, otherwise button is negative (Only matters with Amiga-style buttons) + * TODO: Make Amiga-style buttons a bit wider as they were in Amiga AGI games. + */ +void GfxMgr::drawCurrentStyleButton(int x, int y, const char *label, bool hasFocus, bool pressed, bool positive) { + int textOffset = _vm->_buttonStyle.getTextOffset(hasFocus, pressed); + AgiTextColor color = _vm->_buttonStyle.getColor(hasFocus, pressed, positive); + bool border = _vm->_buttonStyle.getBorder(hasFocus, pressed); + + rawDrawButton(x, y, label, color.fg, color.bg, border, textOffset); +} + +void GfxMgr::rawDrawButton(int x, int y, const char *s, int fgcolor, int bgcolor, bool border, int textOffset) { int len = strlen(s); int x1, y1, x2, y2; @@ -569,8 +750,12 @@ void GfxMgr::drawButton(int x, int y, const char *s, int a, int p, int fgcolor, x2 = x + CHAR_COLS * len + 2; y2 = y + CHAR_LINES + 2; + // Draw a filled rectangle that's larger than the button. Used for drawing + // a border around the button as the button itself is drawn after this. + drawRectangle(x1, y1, x2, y2, border ? BUTTON_BORDER : MSG_BOX_COLOUR); + while (*s) { - putTextCharacter(0, x + (!!p), y + (!!p), *s++, a ? bgcolor : fgcolor, a ? fgcolor : bgcolor); + putTextCharacter(0, x + textOffset, y + textOffset, *s++, fgcolor, bgcolor); x += CHAR_COLS; } @@ -623,39 +808,26 @@ int GfxMgr::keypress() { /** * Initialize the color palette - * This function initializes the color palette using the specified 16-color + * This function initializes the color palette using the specified * RGB palette. - * @param p A pointer to the 16-color RGB palette. + * @param p A pointer to the source RGB palette. + * @param colorCount Count of colors in the source palette. + * @param fromBits Bits per source color component. + * @param toBits Bits per destination color component. */ -void GfxMgr::initPalette(uint8 *p) { - int i; - - for (i = 0; i < 48; i++) { - _palette[i] = p[i]; +void GfxMgr::initPalette(uint8 *p, uint colorCount, uint fromBits, uint toBits) { + const uint srcMax = (1 << fromBits) - 1; + const uint destMax = (1 << toBits) - 1; + for (uint col = 0; col < colorCount; col++) { + for (uint comp = 0; comp < 3; comp++) { // Convert RGB components + _palette[col * 4 + comp] = (p[col * 3 + comp] * destMax) / srcMax; + } + _palette[col * 4 + 3] = 0; // Set alpha to zero } } void GfxMgr::gfxSetPalette() { - int i; - byte pal[256 * 4]; - - if (!(_vm->getFeatures() & (GF_AGI256 | GF_AGI256_2))) { - for (i = 0; i < 16; i++) { - pal[i * 4 + 0] = _palette[i * 3 + 0] << 2; - pal[i * 4 + 1] = _palette[i * 3 + 1] << 2; - pal[i * 4 + 2] = _palette[i * 3 + 2] << 2; - pal[i * 4 + 3] = 0; - } - g_system->setPalette(pal, 0, 16); - } else { - for (i = 0; i < 256; i++) { - pal[i * 4 + 0] = vgaPalette[i * 3 + 0]; - pal[i * 4 + 1] = vgaPalette[i * 3 + 1]; - pal[i * 4 + 2] = vgaPalette[i * 3 + 2]; - pal[i * 4 + 3] = 0; - } - g_system->setPalette(pal, 0, 256); - } + g_system->setPalette(_palette, 0, 256); } //Gets AGIPAL Data @@ -739,7 +911,27 @@ static const byte sciMouseCursor[] = { }; /** - * RGBA-palette for the black and white SCI-style arrow cursor. + * A black and white Apple IIGS style arrow cursor (9x11). + * 0 = Transparent. + * 1 = Black (#000000 in 24-bit RGB). + * 2 = White (#FFFFFF in 24-bit RGB). + */ +static const byte appleIIgsMouseCursor[] = { + 2,2,0,0,0,0,0,0,0, + 2,1,2,0,0,0,0,0,0, + 2,1,1,2,0,0,0,0,0, + 2,1,1,1,2,0,0,0,0, + 2,1,1,1,1,2,0,0,0, + 2,1,1,1,1,1,2,0,0, + 2,1,1,1,1,1,1,2,0, + 2,1,1,1,1,1,1,1,2, + 2,1,1,2,1,1,2,2,0, + 2,2,2,0,2,1,1,2,0, + 0,0,0,0,0,2,2,2,0 +}; + +/** + * RGBA-palette for the black and white SCI and Apple IIGS arrow cursors. */ static const byte sciMouseCursorPalette[] = { 0x00, 0x00, 0x00, 0x00, // Black @@ -819,7 +1011,9 @@ void GfxMgr::setCursor(bool amigaStyleCursor) { * @see deinit_video() */ int GfxMgr::initVideo() { - if (_vm->_renderMode == Common::kRenderEGA) + if (_vm->getFeatures() & (GF_AGI256 | GF_AGI256_2)) + initPalette(vgaPalette, 256, 8); + else if (_vm->_renderMode == Common::kRenderEGA) initPalette(egaPalette); else initPalette(newPalette); diff --git a/engines/agi/graphics.h b/engines/agi/graphics.h index b1f9c0e1d7..e06af90f5d 100644 --- a/engines/agi/graphics.h +++ b/engines/agi/graphics.h @@ -41,7 +41,7 @@ class GfxMgr { private: AgiEngine *_vm; - uint8 _palette[16 * 3]; + uint8 _palette[256 * 4]; uint8 *_agiScreen; unsigned char *_screen; @@ -50,6 +50,9 @@ private: uint8 _agipalPalette[16 * 3]; int _agipalFileNum; +private: + void rawDrawButton(int x, int y, const char *s, int fgcolor, int bgcolor, bool border, int textOffset); + public: GfxMgr(AgiEngine *vm); @@ -74,12 +77,13 @@ public: void clearScreen(int); void clearConsoleScreen(int); void drawBox(int, int, int, int, int, int, int); - void drawButton(int, int, const char *, int, int, int fgcolor = 0, int bgcolor = 0); + void drawDefaultStyleButton(int, int, const char *, int, int, int fgcolor = 0, int bgcolor = 0); + void drawCurrentStyleButton(int x, int y, const char *label, bool hasFocus, bool pressed = false, bool positive = true); int testButton(int, int, const char *); void drawRectangle(int, int, int, int, int); void saveBlock(int, int, int, int, uint8 *); void restoreBlock(int, int, int, int, uint8 *); - void initPalette(uint8 *); + void initPalette(uint8 *p, uint colorCount = 16, uint fromBits = 6, uint toBits = 8); void setAGIPal(int); int getAGIPalFileNum(); void drawFrame(int x1, int y1, int x2, int y2, int c1, int c2); diff --git a/engines/agi/keyboard.cpp b/engines/agi/keyboard.cpp index 3acc81ddff..2b7559f754 100644 --- a/engines/agi/keyboard.cpp +++ b/engines/agi/keyboard.cpp @@ -107,8 +107,10 @@ int AgiEngine::handleController(int key) { VtEntry *v = &_game.viewTable[0]; int i; - /* AGI 3.149 games and The Black Cauldron need KEY_ESCAPE to use menus */ - if (key == 0 || (key == KEY_ESCAPE && agiGetRelease() != 0x3149 && getGameID() != GID_BC) ) + // AGI 3.149 games and The Black Cauldron need KEY_ESCAPE to use menus + // Games with the GF_ESCPAUSE flag need KEY_ESCAPE to pause the game + if (key == 0 || + (key == KEY_ESCAPE && agiGetRelease() != 0x3149 && getGameID() != GID_BC && !(getFeatures() & GF_ESCPAUSE)) ) return false; if ((getGameID() == GID_MH1 || getGameID() == GID_MH2) && (key == KEY_ENTER) && @@ -127,7 +129,7 @@ int AgiEngine::handleController(int key) { } if (key == BUTTON_LEFT) { - if (getflag(fMenusWork) && g_mouse.y <= CHAR_LINES) { + if ((getflag(fMenusWork) || (getFeatures() & GF_MENUS)) && g_mouse.y <= CHAR_LINES) { newInputMode(INPUT_MENU); return true; } diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp index 5edaaf0ded..7aa7f5e55d 100644 --- a/engines/agi/menu.cpp +++ b/engines/agi/menu.cpp @@ -267,7 +267,7 @@ bool Menu::keyhandler(int key) { static int menuActive = false; static int buttonUsed = 0; - if (!_vm->getflag(fMenusWork)) + if (!_vm->getflag(fMenusWork) && !(_vm->getFeatures() & GF_MENUS)) return false; if (!menuActive) { @@ -351,6 +351,12 @@ bool Menu::keyhandler(int key) { debugC(6, kDebugLevelMenu | kDebugLevelInput, "event %d registered", d->event); _vm->_game.evKeyp[d->event].occured = true; _vm->_game.evKeyp[d->event].data = d->event; + // In LSL1, event 0x20 is set when changing the game speed to normal via the menu + // Do not set the event data to 0x20, as this event is then incorrectly triggered + // when the spacebar is pressed, which has a keycode equal to 0x20 as well + // Fixes bug #1751390 - "LSL: after changing game speed, space key turn unfunctional" + if (d->event == 0x20) + _vm->_game.evKeyp[d->event].data = d->event + 1; goto exit_menu; } } diff --git a/engines/agi/predictive.cpp b/engines/agi/predictive.cpp index 67ebed5f05..eef4360cbf 100644 --- a/engines/agi/predictive.cpp +++ b/engines/agi/predictive.cpp @@ -30,6 +30,10 @@ #include "common/func.h" #include "common/config-manager.h" +#ifdef __DS__ +#include "wordcompletion.h" +#endif + namespace Agi { #define kModePre 0 @@ -200,9 +204,9 @@ bool AgiEngine::predictiveDialog(void) { color2 = 7; } if (i == 14) { - _gfx->drawButton(bx[i], by[i], modes[mode], i == active, 0, color1, color2); + _gfx->drawDefaultStyleButton(bx[i], by[i], modes[mode], i == active, 0, color1, color2); } else { - _gfx->drawButton(bx[i], by[i], buttons[i], i == active, 0, color1, color2); + _gfx->drawDefaultStyleButton(bx[i], by[i], buttons[i], i == active, 0, color1, color2); } } @@ -521,6 +525,10 @@ void AgiEngine::loadDict(void) { while ((ptr = strchr(ptr, '\n'))) { *ptr = 0; ptr++; +#ifdef __DS__ + // Pass the line on to the DS word list + DS::addAutoCompleteLine(_predictiveDictLine[i - 1]); +#endif _predictiveDictLine[i++] = ptr; } if (_predictiveDictLine[lines - 1][0] == 0) @@ -529,6 +537,11 @@ void AgiEngine::loadDict(void) { _predictiveDictLineCount = lines; debug("Loaded %d lines", _predictiveDictLineCount); +#ifdef __DS__ + // Sort the DS word completion list, to allow for a binary chop later (in the ds backend) + DS::sortAutoCompleteWordList(); +#endif + uint32 time3 = _system->getMillis(); printf("Time to parse pred.dic: %d, total: %d\n", time3-time2, time3-time1); } diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index 9144dae96c..05ce80b1a3 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -55,6 +55,7 @@ int AgiEngine::saveGame(const char *fileName, const char *description) { int i; struct ImageStackElement *ptr = _imageStack; Common::OutSaveFile *out; + int result = errOK; debugC(3, kDebugLevelMain | kDebugLevelSavegame, "AgiEngine::saveGame(%s, %s)", fileName, description); if (!(out = _saveFileMan->openForSaving(fileName))) { @@ -206,14 +207,15 @@ int AgiEngine::saveGame(const char *fileName, const char *description) { out->writeSint16BE(_gfx->getAGIPalFileNum()); out->finalize(); - if (out->ioFailed()) + if (out->ioFailed()) { warning("Can't write file '%s'. (Disk full?)", fileName); - else + result = errIOError; + } else debugC(1, kDebugLevelMain | kDebugLevelSavegame, "Saved game %s in file %s", description, fileName); delete out; debugC(3, kDebugLevelMain | kDebugLevelSavegame, "Closed %s", fileName); - return errOK; + return result; } int AgiEngine::loadGame(const char *fileName, bool checkId) { @@ -516,7 +518,7 @@ int AgiEngine::selectSlot() { buttonY = (vm + 17) * CHAR_LINES; for (i = 0; i < 2; i++) - _gfx->drawButton(buttonX[i], buttonY, buttonText[i], 0, 0, MSG_BOX_TEXT, MSG_BOX_COLOUR); + _gfx->drawCurrentStyleButton(buttonX[i], buttonY, buttonText[i], false, false, i == 0); AllowSyntheticEvents on(this); int oldFirstSlot = _firstSlot + 1; @@ -751,20 +753,24 @@ int AgiEngine::saveGameDialog() { sprintf(fileName, "%s", getSavegameFilename(slot)); debugC(8, kDebugLevelMain | kDebugLevelResources, "file is [%s]", fileName); - saveGame(fileName, desc); + int result = saveGame(fileName, desc); - messageBox("Game saved."); + if (result == errOK) + messageBox("Game saved."); + else + messageBox("Error saving game."); - return errOK; + return result; } int AgiEngine::saveGameSimple() { char fileName[MAX_PATH]; sprintf(fileName, "%s", getSavegameFilename(0)); - saveGame(fileName, "Default savegame"); - - return errOK; + int result = saveGame(fileName, "Default savegame"); + if (result != errOK) + messageBox("Error saving game."); + return result; } int AgiEngine::loadGameDialog() { diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp index 954cca8f8f..b083b77440 100644 --- a/engines/agi/sound.cpp +++ b/engines/agi/sound.cpp @@ -40,26 +40,43 @@ namespace Agi { #ifdef USE_IIGS_SOUND -/** - * AGI engine sound envelope structure. - */ -struct SoundEnvelope { +struct IIgsEnvelopeSegment { uint8 bp; - uint8 incHi; - uint8 inc_lo; + uint16 inc; ///< 8b.8b fixed point, big endian? }; -struct SoundWavelist { +#define ENVELOPE_SEGMENT_COUNT 8 +struct IIgsEnvelope { + IIgsEnvelopeSegment seg[ENVELOPE_SEGMENT_COUNT]; +}; + +// 2**(1/12) i.e. the 12th root of 2 +#define SEMITONE 1.059463094359295 + +struct IIgsWaveInfo { uint8 top; uint8 addr; uint8 size; +// Oscillator channel (Bits 4-7 of mode-byte). Simplified to use only stereo here. +#define MASK_OSC_CHANNEL (1 << 4) +#define OSC_CHANNEL_LEFT (1 << 4) +#define OSC_CHANNEL_RIGHT (0 << 4) +// Oscillator halt bit (Bit 0 of mode-byte) +#define MASK_OSC_HALT (1 << 0) +#define OSC_HALT (1 << 0) +// Oscillator mode (Bits 1 and 2 of mode-byte) +#define MASK_OSC_MODE (3 << 1) +#define OSC_MODE_LOOP (0 << 1) +#define OSC_MODE_ONESHOT (1 << 1) +#define OSC_MODE_SYNC_AM (2 << 1) +#define OSC_MODE_SWAP (3 << 1) uint8 mode; - uint8 relHi; - uint8 relLo; + uint16 relPitch; ///< 8b.8b fixed point, big endian? }; -struct SoundInstrument { - struct SoundEnvelope env[8]; +#define MAX_WAVE_COUNT 8 +struct IIgsInstrumentHeader { + IIgsEnvelope env; uint8 relseg; uint8 priority; uint8 bendrange; @@ -68,19 +85,19 @@ struct SoundInstrument { uint8 spare; uint8 wac; uint8 wbc; - struct SoundWavelist wal[8]; - struct SoundWavelist wbl[8]; + IIgsWaveInfo wal[MAX_WAVE_COUNT]; + IIgsWaveInfo wbl[MAX_WAVE_COUNT]; }; -struct SoundIIgsSample { - uint8 typeLo; - uint8 typeHi; - uint8 srateLo; - uint8 srateHi; - uint16 unknown[2]; - uint8 sizeLo; - uint8 sizeHi; - uint16 unknown2[13]; +struct IIgsSampleHeader { + uint16 type; + uint8 pitch; ///< Logarithmic, base is 2**(1/12), unknown multiplier (Possibly in range 1040-1080) + uint8 unknownByte_Ofs3; // 0x7F in Gold Rush's sound resource 60, 0 in all others. + uint8 volume; ///< Current guess: Logarithmic in 6 dB steps + uint8 unknownByte_Ofs5; ///< 0 in all tested samples. + uint16 instrumentSize; ///< Little endian. 44 in all tested samples. A guess. + uint16 sampleSize; ///< Little endian. Accurate in all tested samples excluding Manhunter I's sound resource 16. + IIgsInstrumentHeader instrument; }; #if 0 @@ -89,6 +106,117 @@ static int numInstruments; static uint8 *wave; #endif +bool readIIgsEnvelope(IIgsEnvelope &envelope, Common::SeekableReadStream &stream) { + for (int segNum = 0; segNum < ENVELOPE_SEGMENT_COUNT; segNum++) { + envelope.seg[segNum].bp = stream.readByte(); + envelope.seg[segNum].inc = stream.readUint16BE(); + } + return !stream.ioFailed(); +} + +bool readIIgsWaveInfo(IIgsWaveInfo &waveInfo, Common::SeekableReadStream &stream) { + waveInfo.top = stream.readByte(); + waveInfo.addr = stream.readByte(); + waveInfo.size = stream.readByte(); + waveInfo.mode = stream.readByte(); + waveInfo.relPitch = stream.readUint16BE(); + return !stream.ioFailed(); +} + +/** + * Read an Apple IIGS instrument header from the given stream. + * @param header The header to which to write the data. + * @param stream The source stream from which to read the data. + * @return True if successful, false otherwise. + */ +bool readIIgsInstrumentHeader(IIgsInstrumentHeader &header, Common::SeekableReadStream &stream) { + readIIgsEnvelope(header.env, stream); + header.relseg = stream.readByte(); + header.priority = stream.readByte(); + header.bendrange = stream.readByte(); + header.vibdepth = stream.readByte(); + header.vibspeed = stream.readByte(); + header.spare = stream.readByte(); + header.wac = stream.readByte(); + header.wbc = stream.readByte(); + for (int waveA = 0; waveA < header.wac; waveA++) // Read A wave lists + readIIgsWaveInfo(header.wal[waveA], stream); + for (int waveB = 0; waveB < header.wbc; waveB++) // Read B wave lists + readIIgsWaveInfo(header.wbl[waveB], stream); + return !stream.ioFailed(); +} + +/** + * Read an Apple IIGS AGI sample header from the given stream. + * @param header The header to which to write the data. + * @param stream The source stream from which to read the data. + * @return True if successful, false otherwise. + */ +bool readIIgsSampleHeader(IIgsSampleHeader &header, Common::SeekableReadStream &stream) { + header.type = stream.readUint16LE(); + header.pitch = stream.readByte(); + header.unknownByte_Ofs3 = stream.readByte(); + header.volume = stream.readByte(); + header.unknownByte_Ofs5 = stream.readByte(); + header.instrumentSize = stream.readUint16LE(); + header.sampleSize = stream.readUint16LE(); + return readIIgsInstrumentHeader(header.instrument, stream); +} + +/** + * Load an Apple IIGS AGI sample resource from the given stream and + * create an AudioStream out of it. + * + * @param stream The source stream. + * @param resnum Sound resource number. Optional. Used for error messages. + * @return A non-null AudioStream pointer if successful, NULL otherwise. + * @note In case of failure (i.e. NULL is returned), stream is reset back + * to its original position and its I/O failed -status is cleared. + * TODO: Add better handling of invalid resource number when printing error messages. + * TODO: Add support for looping sounds. + * FIXME: Fix sample rate calculation, it's probably not accurate at the moment. + */ +Audio::AudioStream *makeIIgsSampleStream(Common::SeekableReadStream &stream, int resnum = -1) { + const uint32 startPos = stream.pos(); + IIgsSampleHeader header; + Audio::AudioStream *result = NULL; + bool readHeaderOk = readIIgsSampleHeader(header, stream); + + // Check that the header was read ok and that it's of the correct type + // and that there's room for the sample data in the stream. + if (readHeaderOk && header.type == AGI_SOUND_SAMPLE) { // An Apple IIGS AGI sample resource + uint32 tailLen = stream.size() - stream.pos(); + if (tailLen < header.sampleSize) { // Check if there's no room for the sample data in the stream + // Apple IIGS Manhunter I: Sound resource 16 has only 16074 bytes + // of sample data although header says it should have 16384 bytes. + warning("Apple IIGS sample (%d) too short (%d bytes. Should be %d bytes). Using the part that's left", resnum, tailLen, header.sampleSize); + header.sampleSize = (uint16) tailLen; // Use the part that's left + } + if (header.pitch > 0x7F) { // Check if the pitch is invalid + warning("Apple IIGS sample (%d) has too high pitch (0x%02x)", resnum, header.pitch); + header.pitch &= 0x7F; // Apple IIGS AGI probably did it this way too + } + // Allocate memory for the sample data and read it in + byte *sampleData = (byte *) malloc(header.sampleSize); + uint32 readBytes = stream.read(sampleData, header.sampleSize); + if (readBytes == header.sampleSize) { // Check that we got all the data we requested + // Make an audio stream from the mono, 8 bit, unsigned input data + byte flags = Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED; + int rate = (int) (1076 * pow(SEMITONE, header.pitch)); + result = Audio::makeLinearInputStream(sampleData, header.sampleSize, rate, flags, 0, 0); + } + } + + // If couldn't make a sample out of the input stream for any reason then + // rewind back to stream's starting position and clear I/O failed -status. + if (result == NULL) { + stream.seek(startPos); + stream.clearIOFailed(); + } + + return result; +} + #endif static int playing; @@ -169,7 +297,7 @@ void SoundMgr::unloadSound(int resnum) { } void SoundMgr::decodeSound(int resnum) { -#ifdef USE_IIGS_SOUND +#if 0 int type, size; int16 *buf; uint8 *src; @@ -190,12 +318,12 @@ void SoundMgr::decodeSound(int resnum) { _vm->_game.sounds[resnum].rdata = (uint8 *) buf; free(src); } -#endif /* USE_IIGS_SOUND */ +#endif } void SoundMgr::startSound(int resnum, int flag) { int i, type; -#ifdef USE_IIGS_SOUND +#if 0 struct SoundIIgsSample *smp; #endif @@ -218,7 +346,7 @@ void SoundMgr::startSound(int resnum, int flag) { song = (uint8 *)_vm->_game.sounds[resnum].rdata; switch (type) { -#ifdef USE_IIGS_SOUND +#if 0 case AGI_SOUND_SAMPLE: debugC(3, kDebugLevelSound, "IIGS sample"); smp = (struct SoundIIgsSample *)_vm->_game.sounds[resnum].rdata; diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp index 565e94fa26..1d653a9415 100644 --- a/engines/agi/text.cpp +++ b/engines/agi/text.cpp @@ -364,7 +364,7 @@ int AgiEngine::selectionBox(const char *m, const char **b) { debugC(4, kDebugLevelText, "waiting..."); for (;;) { for (i = 0; b[i]; i++) - _gfx->drawButton(bx[i], by[i], b[i], i == active, 0, MSG_BOX_TEXT, MSG_BOX_COLOUR); + _gfx->drawCurrentStyleButton(bx[i], by[i], b[i], i == active, false, i == 0); _gfx->pollTimer(); /* msdos driver -> does nothing */ key = doPollKeyboard(); @@ -555,7 +555,7 @@ char *AgiEngine::agiSprintf(const char *s) { break; case 's': i = strtoul(s, NULL, 10); - safeStrcat(p, _game.strings[i]); + safeStrcat(p, agiSprintf(_game.strings[i])); break; case 'm': i = strtoul(s, NULL, 10) - 1; |