Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

1
engines/touche/POTFILES Normal file
View File

@@ -0,0 +1 @@
engines/touche/metaengine.cpp

View File

@@ -0,0 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes "" "" "highres" "midi"

View File

@@ -0,0 +1,52 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "touche/console.h"
#include "touche/touche.h"
namespace Touche {
ToucheConsole::ToucheConsole(ToucheEngine *vm) : GUI::Debugger(), _vm(vm) {
registerCmd("startMusic", WRAP_METHOD(ToucheConsole, Cmd_StartMusic));
registerCmd("stopMusic", WRAP_METHOD(ToucheConsole, Cmd_StopMusic));
}
ToucheConsole::~ToucheConsole() {
}
bool ToucheConsole::Cmd_StartMusic(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: startMusic <num>\n");
return true;
}
int num = atoi(argv[1]);
_vm->startMusic(num);
return false;
}
bool ToucheConsole::Cmd_StopMusic(int argc, const char **argv) {
_vm->stopMusic();
return false;
}
} // End of namespace Touche

45
engines/touche/console.h Normal file
View File

@@ -0,0 +1,45 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef TOUCHE_CONSOLE_H
#define TOUCHE_CONSOLE_H
#include "gui/debugger.h"
namespace Touche {
class ToucheEngine;
class ToucheConsole : public GUI::Debugger {
public:
ToucheConsole(ToucheEngine *vm);
~ToucheConsole(void) override;
private:
ToucheEngine *_vm;
bool Cmd_StartMusic(int argc, const char **argv);
bool Cmd_StopMusic(int argc, const char **argv);
};
} // End of namespace Touche
#endif

View File

@@ -0,0 +1,3 @@
begin_section("Touch&eacute;");
add_person("Gregory Montoir", "cyx", "(retired)");
end_section();

View File

@@ -0,0 +1,170 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "engines/advancedDetector.h"
#include "base/plugins.h"
#include "touche/touche.h"
static const PlainGameDescriptor toucheGames[] = {
{ "touche", "Touch\303\251: The Adventures of the Fifth Musketeer" },
{ 0, 0 }
};
static const DebugChannelDef debugFlagList[] = {
{Touche::kDebugEngine, "Engine", "Engine debug level"},
{Touche::kDebugGraphics, "Graphics", "Graphics debug level"},
{Touche::kDebugResource, "Resource", "Resource debug level"},
{Touche::kDebugOpcodes, "Opcodes", "Opcodes debug level"},
{Touche::kDebugMenu, "Menu", "Menu debug level"},
{Touche::kDebugCharset, "Charset", "Charset debug level"},
DEBUG_CHANNEL_END
};
namespace Touche {
static const ADGameDescription gameDescriptions[] = {
{ // retail version
"touche",
"",
AD_ENTRY1s("touche.dat", "2af0177f8887e3430f345e6b4d8b1414", 26350211),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
{ // retail version - tracker item #2923
"touche",
"",
AD_ENTRY1s("touche.dat", "95967f0b51d2e813e99ca00325098340", 26350190),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
{ // retail version
"touche",
"",
AD_ENTRY1s("touche.dat", "1caa20bb4d4fc2ce8eb867b6610082b3", 26558232),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
{ // retail version - tracker item #2912
"touche",
"",
AD_ENTRY1s("touche.dat", "be2ae6454b3325e410946f2322547cd4", 26625537),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
{ // retail version - tracker item #3121
"touche",
"",
AD_ENTRY1s("touche.dat", "64e95ba1decf5a5a60f8fa1840f40c62", 26529523),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
{ // fan-made translation (http://www.iagtg.net/) - tracker item #2927
"touche",
"",
AD_ENTRY1s("touche.dat", "1f442331d4b327c3488a9f6ffe9bdd25", 26367792),
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
{ // retail version - tracker item #3409
"touche",
"",
AD_ENTRY1s("touche.dat", "42d19a0bef65465109020440a9caa228", 26487370),
Common::PL_POL,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
{ // Fanmade translation by Old-Games.ru
"touche",
"v1.0.2",
AD_ENTRY1s("touche.dat", "44c1a7a619d583d458f0c24e881a0b68", 51362582),
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
{ // demo version
"touche",
"Demo",
AD_ENTRY1s("touche.dat", "ddaed436445b2e77294ed19e8ae4aa2c", 8720683),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO0()
},
AD_TABLE_END_MARKER
};
static const ADFileBasedFallback fileBasedFallback[] = {
{ &gameDescriptions[0], { "touche.dat", 0 } }, // default to english version
{ 0, { 0 } }
};
} // End of namespace Touche
static const char *const directoryGlobs[] = {
"database",
0
};
class ToucheMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
ToucheMetaEngineDetection() : AdvancedMetaEngineDetection(Touche::gameDescriptions, toucheGames) {
_md5Bytes = 4096;
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const override {
return detectGameFilebased(allFiles, Touche::fileBasedFallback);
}
const char *getName() const override {
return "touche";
}
const char *getEngineName() const override {
return "Touch\303\251: The Adventures of the Fifth Musketeer";
}
const char *getOriginalCopyright() const override {
return "Touch\303\251: The Adventures of the Fifth Musketeer (C) Clipper Software";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
REGISTER_PLUGIN_STATIC(TOUCHE_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, ToucheMetaEngineDetection);

209
engines/touche/graphics.cpp Normal file
View File

@@ -0,0 +1,209 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "graphics/surface.h"
#include "touche/graphics.h"
namespace Touche {
void Graphics::setupFont(Common::Language language) {
switch (language) {
case Common::FR_FRA:
case Common::DE_DEU:
_fontOffs = _freGerFontOffs;
_fontSize = _freGerFontSize;
_fontData = _freGerFontData;
break;
case Common::ES_ESP:
_fontOffs = _spaFontOffs;
_fontSize = _spaFontSize;
_fontData = _spaFontData;
break;
case Common::PL_POL:
_fontOffs = _polFontOffs;
_fontSize = _polFontSize;
_fontData = _polFontData;
break;
case Common::RU_RUS:
_fontOffs = _rusFontOffs;
_fontSize = _rusFontSize;
_fontData = _rusFontData;
break;
case Common::IT_ITA:
case Common::EN_ANY:
default:
_fontOffs = _engFontOffs;
_fontSize = _engFontSize;
_fontData = _engFontData;
break;
}
}
int Graphics::getStringWidth16(const char *str) {
int w = 0;
while (*str) {
char chr = *str++;
w += getCharWidth16((uint8)chr);
if (*str == '\\') {
break;
}
}
return w;
}
int Graphics::getCharWidth16(uint8 chr) {
assert(chr >= 32 && chr < 32 + _fontSize);
const uint8 *chrData = _fontData + _fontOffs[chr - 32];
return chrData[2];
}
void Graphics::drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str, int xmax) {
while (*str) {
uint8 chr = (uint8)*str++;
x += drawChar16(dst, dstPitch, chr, x, y, color);
if (xmax != 0 && x > xmax) {
break;
}
}
}
int Graphics::drawChar16(uint8 *dst, int dstPitch, uint8 chr, int x, int y, uint16 color) {
dst += y * dstPitch + x;
uint8 color1 = color & 0xFF;
uint8 color2 = color >> 8;
assert(chr >= 32 && chr < 32 + _fontSize);
const uint8 *chrData = _fontData + _fontOffs[chr - 32];
int chrHeight = chrData[1];
int chrWidth = chrData[2];
chrData += 3;
while (chrHeight--) {
int shiftCount = 0;
int mask = 0;
for (int i = 0; i < chrWidth; ++i) {
if (shiftCount == 0) {
mask = READ_BE_UINT16(chrData); chrData += 2;
shiftCount = 8;
}
int b = (mask & 0xC000) >> 14;
mask <<= 2;
--shiftCount;
if (b) {
if (b & 2) {
dst[i] = color2;
} else {
dst[i] = color1;
}
}
}
dst += dstPitch;
}
return chrWidth;
}
void Graphics::fillRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color) {
dst += y * dstPitch + x;
while (h--) {
memset(dst, color, w);
dst += dstPitch;
}
}
void Graphics::drawRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color1, uint8 color2) {
::Graphics::Surface s;
s.init(x+w, y+h, dstPitch, dst, ::Graphics::PixelFormat::createFormatCLUT8());
const int x1 = x;
const int y1 = y;
const int x2 = x + w - 1;
const int y2 = y + h - 1;
s.hLine(x1, y1, x2, color1);
s.vLine(x1, y1, y2, color1);
s.vLine(x2, y1 + 1, y2, color2);
s.hLine(x1 + 1, y2, x2, color2);
}
void Graphics::drawLine(uint8 *dst, int dstPitch, int x1, int y1, int x2, int y2, uint8 color) {
assert(x1 >= 0 && y1 >= 0 && x2 >= 0 && y2 >= 0);
::Graphics::Surface s;
s.init(MAX(x1, x2) + 1, MAX(y1, y2) + 1, dstPitch, dst, ::Graphics::PixelFormat::createFormatCLUT8());
s.drawLine(x1, y1, x2, y2, color);
}
void Graphics::copyRect(uint8 *dst, int dstPitch, int dstX, int dstY, const uint8 *src, int srcPitch, int srcX, int srcY, int w, int h, int flags) {
if (dstX < 0) {
w += dstX;
dstX = 0;
}
if (w <= 0) {
return;
}
if (dstY < 0) {
h += dstY;
dstY = 0;
}
if (h <= 0) {
return;
}
dst += dstY * dstPitch + dstX;
src += srcY * srcPitch + srcX;
while (h--) {
for (int i = 0; i < w; ++i) {
if ((flags & kTransparent) == 0 || src[i] != 0) {
dst[i] = src[i];
}
}
dst += dstPitch;
src += srcPitch;
}
}
void Graphics::copyMask(uint8 *dst, int dstPitch, int dstX, int dstY, const uint8 *src, int srcPitch, int srcX, int srcY, int w, int h, uint8 fillColor) {
if (dstX < 0) {
w += dstX;
dstX = 0;
}
if (w <= 0) {
return;
}
if (dstY < 0) {
h += dstY;
dstY = 0;
}
if (h <= 0) {
return;
}
dst += dstY * dstPitch + dstX;
src += srcY * srcPitch + srcX;
while (h--) {
for (int i = 0; i < w; ++i) {
if (src[i] != 0) {
dst[i] = fillColor;
}
}
dst += dstPitch;
src += srcPitch;
}
}
} // namespace Touche

81
engines/touche/graphics.h Normal file
View File

@@ -0,0 +1,81 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef TOUCHE_GRAPHICS_H
#define TOUCHE_GRAPHICS_H
#include "common/language.h"
namespace Touche {
class Graphics {
public:
enum {
kTransparent = 1 << 0
};
static void setupFont(Common::Language language);
static int getStringWidth16(const char *str);
static int getCharWidth16(uint8 chr);
static void drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str, int xmax = 0);
static int drawChar16(uint8 *dst, int dstPitch, uint8 chr, int x, int y, uint16 color);
static void fillRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color);
static void drawRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color1, uint8 color2);
static void drawLine(uint8 *dst, int dstPitch, int x1, int y1, int x2, int y2, uint8 color);
static void copyRect(uint8 *dst, int dstPitch, int dstX, int dstY, const uint8 *src, int srcPitch, int srcX, int srcY, int w, int h, int flags = 0);
static void copyMask(uint8 *dst, int dstPitch, int dstX, int dstY, const uint8 *src, int srcPitch, int srcX, int srcY, int w, int h, uint8 fillColor);
private:
/* font data for english version */
static const uint16 _engFontOffs[];
static const int _engFontSize;
static const uint8 _engFontData[];
/* font data for french and german versions */
static const uint16 _freGerFontOffs[];
static const int _freGerFontSize;
static const uint8 _freGerFontData[];
/* font data for spanish version */
static const uint16 _spaFontOffs[];
static const int _spaFontSize;
static const uint8 _spaFontData[];
/* font data for polish version */
static const uint16 _polFontOffs[];
static const int _polFontSize;
static const uint8 _polFontData[];
/* font data for russian version */
static const uint16 _rusFontOffs[];
static const int _rusFontSize;
static const uint8 _rusFontData[];
static const uint16 *_fontOffs;
static int _fontSize;
static const uint8 *_fontData;
};
} // namespace Touche
#endif

517
engines/touche/menu.cpp Normal file
View File

@@ -0,0 +1,517 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/events.h"
#include "common/system.h"
#include "common/savefile.h"
#include "backends/keymapper/keymapper.h"
#include "touche/graphics.h"
#include "touche/midi.h"
#include "touche/touche.h"
namespace Touche {
static void drawArrow(uint8 *dst, int dstPitch, int x, int y, int delta, uint8 color) {
static const int8 arrowCoordsTable[7][4] = {
{ 5, 0, 9, 0 },
{ 5, 0, 5, 4 },
{ -5, 4, 5, 4 },
{ -5, 0, -5, 4 },
{ -9, 0, -5, 0 },
{ -9, 0, 0, -9 },
{ 0, -9, 9, 0 }
};
for (uint i = 0; i < 7; ++i) {
const int x1 = x + arrowCoordsTable[i][0];
const int y1 = y + arrowCoordsTable[i][1] * delta;
const int x2 = x + arrowCoordsTable[i][2];
const int y2 = y + arrowCoordsTable[i][3] * delta;
Graphics::drawLine(dst, dstPitch, x1, y1, x2, y2, color);
}
}
void ToucheEngine::drawButton(Button *button) {
if (button->flags & kButtonBorder) {
Graphics::drawRect(_offscreenBuffer, kScreenWidth, button->x, button->y, button->w, button->h, 0xF7, 0xF9);
}
if (button->flags & kButtonText) {
if (button->data != 0) {
const char *str = getString(button->data);
const int w = getStringWidth(button->data);
const int h = kTextHeight;
const int x = button->x + (button->w - w) / 2;
const int y = button->y + (button->h - h) / 2;
Graphics::drawString16(_offscreenBuffer, kScreenWidth, 0xFF, x, y, str);
}
}
if (button->flags & kButtonArrow) {
int dx = 0;
int dy = 0;
switch (button->data) {
case 2000: // up arrow
dx = 1;
dy = 2;
break;
case 2001: // down arrow
dx = -1;
dy = -2;
break;
default:
break;
}
const int x = button->x + button->w / 2;
const int y = button->y + button->h / 2;
drawArrow(_offscreenBuffer, kScreenWidth, x, y + dy + 1, dx, 0xD2);
drawArrow(_offscreenBuffer, kScreenWidth, x, y + dy, dx, 0xFF);
}
}
static void drawVolumeSlideBar(uint8 *dst, int dstPitch, int volume) {
const int w = volume * 232 / 255;
if (w > 0) {
Graphics::fillRect(dst, dstPitch, 157, 259, w, 6, 0xF0);
}
if (w < 232) {
Graphics::fillRect(dst, dstPitch, 157 + w, 259, 232 - w, 6, 0xD2);
}
}
static void drawSaveGameStateDescriptions(uint8 *dst, int dstPitch, MenuData *menuData, int currentPage, int currentSlot) {
for (int i = 0, slot = currentPage * 10; i < 10; ++i, ++slot) {
const Button *b = &menuData->buttonsTable[i];
const uint8 color = (slot == currentSlot) ? 0xCB : 0xD9;
Common::String savegameNameStr = Common::String::format("%d.", slot);
Graphics::drawString16(dst, dstPitch, color, b->x, b->y, savegameNameStr.c_str());
savegameNameStr = menuData->saveLoadDescriptionsTable[slot];
if (slot == currentSlot && menuData->mode == kMenuSaveStateMode) {
savegameNameStr += "_";
}
Graphics::drawString16(dst, dstPitch, color, b->x + 30, b->y, savegameNameStr.c_str());
}
}
static void setupMenu(MenuMode mode, MenuData *menuData) {
static Button settingsButtonsTable[] = {
{ 452, 120, 94, 24, kActionLoadMenu, -52, kButtonBorder | kButtonText },
{ 452, 152, 94, 24, kActionSaveMenu, -53, kButtonBorder | kButtonText },
{ 452, 184, 94, 24, kActionRestartGame, -90, kButtonBorder | kButtonText },
{ 452, 216, 94, 24, kActionPlayGame, -54, kButtonBorder | kButtonText },
{ 452, 248, 94, 24, kActionQuitGame, -55, kButtonBorder | kButtonText },
{ 396, 130, 24, 24, kActionTextOnly, 0, kButtonBorder | kButtonText },
{ 396, 160, 24, 24, kActionVoiceOnly, 0, kButtonBorder | kButtonText },
{ 396, 190, 24, 24, kActionTextAndVoice, 0, kButtonBorder | kButtonText },
{ 126, 130, 254, 24, kActionNone, -92, kButtonBorder | kButtonText },
{ 126, 160, 254, 24, kActionNone, -93, kButtonBorder | kButtonText },
{ 126, 190, 254, 24, kActionNone, -94, kButtonBorder | kButtonText },
{ 126, 222, 294, 20, kActionNone, -91, kButtonBorder | kButtonText },
{ 126, 250, 24, 24, kActionLowerVolume, -87, kButtonBorder | kButtonText },
{ 396, 250, 24, 24, kActionUpperVolume, -88, kButtonBorder | kButtonText },
{ 154, 256, 238, 12, kActionNone, 0, kButtonBorder }
};
static Button saveLoadButtonsTable[] = {
{ 108, 120, 336, 15, kActionGameState1, 0, 0 },
{ 108, 136, 336, 15, kActionGameState2, 0, 0 },
{ 108, 152, 336, 15, kActionGameState3, 0, 0 },
{ 108, 168, 336, 15, kActionGameState4, 0, 0 },
{ 108, 184, 336, 15, kActionGameState5, 0, 0 },
{ 108, 200, 336, 15, kActionGameState6, 0, 0 },
{ 108, 216, 336, 15, kActionGameState7, 0, 0 },
{ 108, 232, 336, 15, kActionGameState8, 0, 0 },
{ 108, 248, 336, 15, kActionGameState9, 0, 0 },
{ 108, 264, 336, 15, kActionGameState10, 0, 0 },
{ 452, 120, 94, 24, kActionScrollUpSaves, 2000, kButtonBorder | kButtonArrow },
{ 452, 152, 94, 24, kActionCancelSaveLoad, -56, kButtonBorder | kButtonText },
{ 452, 216, 94, 24, kActionPerformSaveLoad, 0, kButtonBorder | kButtonText },
{ 452, 248, 94, 24, kActionScrollDownSaves, 2001, kButtonBorder | kButtonArrow }
};
switch (mode) {
case kMenuSettingsMode:
menuData->buttonsTable = settingsButtonsTable;
menuData->buttonsCount = ARRAYSIZE(settingsButtonsTable);
break;
case kMenuLoadStateMode:
saveLoadButtonsTable[12].data = -52;
menuData->buttonsTable = saveLoadButtonsTable;
menuData->buttonsCount = ARRAYSIZE(saveLoadButtonsTable);
break;
case kMenuSaveStateMode:
saveLoadButtonsTable[12].data = -53;
menuData->buttonsTable = saveLoadButtonsTable;
menuData->buttonsCount = ARRAYSIZE(saveLoadButtonsTable);
break;
default:
break;
}
}
void ToucheEngine::redrawMenu(MenuData *menu) {
Graphics::fillRect(_offscreenBuffer, kScreenWidth, 90, 102, 460, 196, 0xF8);
Graphics::drawRect(_offscreenBuffer, kScreenWidth, 90, 102, 460, 196, 0xF7, 0xF9);
Graphics::drawRect(_offscreenBuffer, kScreenWidth, 106, 118, 340, 164, 0xF9, 0xF7);
switch (menu->mode) {
case kMenuSettingsMode:
drawVolumeSlideBar(_offscreenBuffer, kScreenWidth, getMusicVolume());
menu->buttonsTable[5].data = 0;
menu->buttonsTable[6].data = 0;
menu->buttonsTable[7].data = 0;
menu->buttonsTable[5 + _talkTextMode].data = -86;
break;
case kMenuLoadStateMode:
case kMenuSaveStateMode:
drawSaveGameStateDescriptions(_offscreenBuffer, kScreenWidth, menu, _saveLoadCurrentPage, _saveLoadCurrentSlot);
break;
default:
break;
}
for (uint i = 0; i < menu->buttonsCount; ++i) {
drawButton(&menu->buttonsTable[i]);
}
}
void ToucheEngine::handleMenuAction(MenuData *menu, int actionId) {
switch (actionId) {
case kActionLoadMenu:
menu->mode = kMenuLoadStateMode;
break;
case kActionSaveMenu:
_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
menu->mode = kMenuSaveStateMode;
break;
case kActionRestartGame:
restart();
menu->quit = true;
break;
case kActionPlayGame:
menu->quit = true;
break;
case kActionQuitGame:
quitGame();
menu->quit = true;
break;
case kActionTextOnly:
_talkTextMode = kTalkModeTextOnly;
break;
case kActionVoiceOnly:
_talkTextMode = kTalkModeVoiceOnly;
break;
case kActionTextAndVoice:
_talkTextMode = kTalkModeVoiceAndText;
break;
case kActionLowerVolume:
adjustMusicVolume(-16);
break;
case kActionUpperVolume:
adjustMusicVolume(+16);
break;
case kActionScrollUpSaves:
--_saveLoadCurrentPage;
if (_saveLoadCurrentPage < 0) {
_saveLoadCurrentPage = 9;
}
_saveLoadCurrentSlot = _saveLoadCurrentPage * 10 + (_saveLoadCurrentSlot % 10);
break;
case kActionScrollDownSaves:
++_saveLoadCurrentPage;
if (_saveLoadCurrentPage > 9) {
_saveLoadCurrentPage = 0;
}
_saveLoadCurrentSlot = _saveLoadCurrentPage * 10 + (_saveLoadCurrentSlot % 10);
break;
case kActionPerformSaveLoad:
if (menu->mode == kMenuLoadStateMode) {
if (loadGameState(_saveLoadCurrentSlot).getCode() == Common::kNoError) {
menu->quit = true;
}
} else if (menu->mode == kMenuSaveStateMode) {
_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
const char *description = menu->saveLoadDescriptionsTable[_saveLoadCurrentSlot];
if (strlen(description) > 0) {
if (saveGameState(_saveLoadCurrentSlot, description).getCode() == Common::kNoError) {
menu->quit = true;
}
}
}
break;
case kActionCancelSaveLoad:
_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
menu->mode = kMenuSettingsMode;
break;
default:
if (actionId >= kActionGameState1 && actionId <= kActionGameState10) {
_saveLoadCurrentSlot = _saveLoadCurrentPage * 10 + (actionId - kActionGameState1);
}
break;
}
}
void ToucheEngine::handleOptions(int forceDisplay) {
if (_disabledInputCounter == 0 || forceDisplay != 0) {
setDefaultCursor(_currentKeyCharNum);
_gameState = kGameStateOptionsDialog;
MenuData menuData;
memset(&menuData, 0, sizeof(MenuData));
menuData.quit = false;
menuData.exit = false;
menuData.mode = kMenuSettingsMode;
int curMode = -1;
bool doRedraw = false;
while (!menuData.quit) {
if (menuData.mode != curMode) {
doRedraw = true;
setupMenu(menuData.mode, &menuData);
curMode = menuData.mode;
if (menuData.mode == kMenuLoadStateMode || menuData.mode == kMenuSaveStateMode) {
for (int i = 0; i < kMaxSaveStates; ++i) {
menuData.saveLoadDescriptionsTable[i][0] = 0;
}
Common::String gameStateFileName = generateGameStateFileName(_targetName.c_str(), 0, true);
Common::StringArray filenames = _saveFileMan->listSavefiles(gameStateFileName);
for (Common::StringArray::const_iterator it = filenames.begin(); it != filenames.end(); ++it) {
int i = getGameStateFileSlot(it->c_str());
if (i >= 0 && i < kMaxSaveStates) {
Common::InSaveFile *f = _saveFileMan->openForLoading(*it);
if (f) {
readGameStateDescription(f, menuData.saveLoadDescriptionsTable[i], 32);
delete f;
}
}
}
}
}
if (doRedraw) {
redrawMenu(&menuData);
updateScreenArea(90, 102, 460, 196);
doRedraw = false;
}
Common::Keymapper *keymapper = _eventMan->getKeymapper();
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
Common::Event event;
while (_eventMan->pollEvent(event)) {
const Button *button = 0;
switch (event.type) {
case Common::EVENT_RETURN_TO_LAUNCHER:
case Common::EVENT_QUIT:
menuData.quit = true;
menuData.exit = true;
break;
case Common::EVENT_LBUTTONDOWN:
button = menuData.findButtonUnderCursor(event.mouse.x, event.mouse.y);
if (button) {
handleMenuAction(&menuData, button->action);
doRedraw = true;
}
break;
case Common::EVENT_KEYDOWN:
if (menuData.mode == kMenuSaveStateMode) {
if (event.kbd.keycode == Common::KEYCODE_BACKSPACE) {
menuData.removeLastCharFromDescription(_saveLoadCurrentSlot);
} else {
menuData.addCharToDescription(_saveLoadCurrentSlot, (char)event.kbd.ascii);
}
doRedraw = true;
}
break;
case Common::EVENT_WHEELUP:
handleMenuAction(&menuData, kActionScrollUpSaves);
doRedraw = true;
break;
case Common::EVENT_WHEELDOWN:
handleMenuAction(&menuData, kActionScrollDownSaves);
doRedraw = true;
break;
default:
break;
}
}
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
_system->updateScreen();
_system->delayMillis(10);
}
_fullRedrawCounter = 2;
if (!menuData.exit && shouldQuit()) {
if (displayQuitDialog())
quitGame();
}
_gameState = kGameStateGameLoop;
}
}
void ToucheEngine::drawActionsPanel(int dstX, int dstY, int deltaX, int deltaY) {
Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, dstY,
_menuKitData, 42, 0, 0,
14, 24,
Graphics::kTransparent);
Graphics::copyRect(_offscreenBuffer, kScreenWidth, deltaX - 14 + dstX, dstY,
_menuKitData, 42, 0, 40,
14, 24,
Graphics::kTransparent);
Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, deltaY - 16 + dstY,
_menuKitData, 42, 0, 24,
14, 16,
Graphics::kTransparent);
Graphics::copyRect(_offscreenBuffer, kScreenWidth, deltaX - 14 + dstX, deltaY - 16 + dstY,
_menuKitData, 42, 0, 64,
14, 16,
Graphics::kTransparent);
int x1 = deltaX - 28;
int x2 = dstX + 14;
while (x1 > 0) {
int w = (x1 > 14) ? 14 : x1;
Graphics::copyRect(_offscreenBuffer, kScreenWidth, x2, dstY,
_menuKitData, 42, 0, 80,
w, 24,
Graphics::kTransparent);
Graphics::copyRect(_offscreenBuffer, kScreenWidth, x2, deltaY - 16 + dstY,
_menuKitData, 42, 0, 104,
w, 16,
Graphics::kTransparent);
x1 -= 14;
x2 += 14;
}
x1 = deltaY - 40;
x2 = dstY + 24;
while (x1 > 0) {
int w = (x1 > 120) ? 120 : x1;
Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, x2,
_menuKitData, 42, 14, 0,
14, w,
Graphics::kTransparent);
Graphics::copyRect(_offscreenBuffer, kScreenWidth, deltaX - 14 + dstX, x2,
_menuKitData, 42, 28, 0,
14, w,
Graphics::kTransparent);
x1 -= 120;
x2 += 120;
}
}
void ToucheEngine::drawConversationPanelBorder(int dstY, int srcX, int srcY) {
int dstX = 24;
int w = 48;
for (int i = 0; i < 13; ++i) {
if (i == 12) {
w = 34;
}
Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, dstY, _convKitData, 152, srcX, srcY, w, 6);
dstX += w;
}
}
void ToucheEngine::drawConversationPanel() {
Graphics::copyRect(_offscreenBuffer, kScreenWidth, 0, 320, _convKitData, 152, 0, 0, 72, 80);
int dstX = 54;
int dstY = 326;
int w = 96;
for (int i = 0; i < 7; ++i) {
if (i == 5) {
w = 50;
}
Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, dstY, _convKitData, 152, 24, 6, w, 68);
dstX += w;
}
--dstX;
Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, 320, _convKitData, 152, 120, 0, 7, 80);
dstX -= 3;
if (_scrollConversationChoiceOffset != 0) {
drawConversationPanelBorder(320, 72, 0);
Graphics::copyRect(_offscreenBuffer, kScreenWidth, 0, 320, _convKitData, 152, 128, 0, 24, 21);
Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, 320, _convKitData, 152, 128, 34, 10, 10);
} else {
drawConversationPanelBorder(320, 24, 0);
}
if (_conversationChoicesTable[_scrollConversationChoiceOffset + 4].msg != 0) {
drawConversationPanelBorder(394, 72, 74);
Graphics::copyRect(_offscreenBuffer, kScreenWidth, 0, 379, _convKitData, 152, 128, 59, 24, 21);
Graphics::copyRect(_offscreenBuffer, kScreenWidth, dstX, 394, _convKitData, 152, 128, 46, 10, 6);
} else {
drawConversationPanelBorder(394, 24, 74);
}
}
void ToucheEngine::printStatusString(const char *str) {
Graphics::fillRect(_offscreenBuffer, kScreenWidth, 0, 0, kScreenWidth, kTextHeight, 0xD7);
Graphics::drawRect(_offscreenBuffer, kScreenWidth, 0, 0, kScreenWidth, kTextHeight, 0xD6, 0xD8);
Graphics::drawString16(_offscreenBuffer, kScreenWidth, 0xFF, 0, 0, str);
updateScreenArea(0, 0, kScreenWidth, kTextHeight);
_system->updateScreen();
}
void ToucheEngine::clearStatusString() {
Graphics::copyRect(_offscreenBuffer, kScreenWidth, 0, 0,
_backdropBuffer, _currentBitmapWidth, _flagsTable[614], _flagsTable[615],
kScreenWidth, kTextHeight);
updateScreenArea(0, 0, kScreenWidth, kTextHeight);
}
int ToucheEngine::displayQuitDialog() {
debug(kDebugMenu, "ToucheEngine::displayQuitDialog()");
_gameState = kGameStateQuitDialog;
_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
printStatusString(getString(-85));
int ret = 0;
bool quitLoop = false;
while (!quitLoop) {
Common::Event event;
while (_eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_RETURN_TO_LAUNCHER:
case Common::EVENT_QUIT:
quitLoop = true;
ret = 1;
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
quitLoop = true;
if (event.customType == kToucheActionYes) {
ret = 1;
}
break;
case Common::EVENT_JOYBUTTON_DOWN:
case Common::EVENT_KEYDOWN:
quitLoop = true;
default:
break;
}
}
_system->delayMillis(10);
_system->updateScreen();
}
clearStatusString();
_gameState = kGameStateGameLoop;
_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
return ret;
}
void ToucheEngine::displayTextMode(int str) {
debug(kDebugMenu, "ToucheEngine::displayTextMode(%d)", str);
printStatusString(getString(str));
_system->delayMillis(1000);
clearStatusString();
}
} // namespace Touche

View File

@@ -0,0 +1,217 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "engines/advancedDetector.h"
#include "common/savefile.h"
#include "common/system.h"
#include "common/translation.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
#include "base/plugins.h"
#include "touche/touche.h"
class ToucheMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
public:
const char *getName() const override {
return "touche";
}
bool hasFeature(MetaEngineFeature f) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
SaveStateList listSaves(const char *target) const override;
int getMaximumSaveSlot() const override;
bool removeSaveState(const char *target, int slot) const override;
Common::String getSavegameFile(int saveGameIdx, const char *target) const override {
if (!target)
target = getName();
return Touche::generateGameStateFileName(target, saveGameIdx, saveGameIdx == kSavegameFilePattern);
}
Common::KeymapArray initKeymaps(const char *target) const override;
};
bool ToucheMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave);
}
bool Touche::ToucheEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime) ||
(f == kSupportsSubtitleOptions);
}
Common::Error ToucheMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
*engine = new Touche::ToucheEngine(syst, desc->language);
return Common::kNoError;
}
SaveStateList ToucheMetaEngine::listSaves(const char *target) const {
Common::String pattern = Touche::generateGameStateFileName(target, 0, true);
Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles(pattern);
bool slotsTable[Touche::kMaxSaveStates];
memset(slotsTable, 0, sizeof(slotsTable));
SaveStateList saveList;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
int slot = Touche::getGameStateFileSlot(file->c_str());
if (slot >= 0 && slot < Touche::kMaxSaveStates) {
slotsTable[slot] = true;
}
}
for (int slot = 0; slot < Touche::kMaxSaveStates; ++slot) {
if (slotsTable[slot]) {
Common::String file = Touche::generateGameStateFileName(target, slot);
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(file);
if (in) {
char description[64];
Touche::readGameStateDescription(in, description, sizeof(description) - 1);
if (description[0]) {
saveList.push_back(SaveStateDescriptor(this, slot, description));
}
delete in;
}
}
}
return saveList;
}
int ToucheMetaEngine::getMaximumSaveSlot() const {
return Touche::kMaxSaveStates - 1;
}
bool ToucheMetaEngine::removeSaveState(const char *target, int slot) const {
Common::String filename = Touche::generateGameStateFileName(target, slot);
return g_system->getSavefileManager()->removeSavefile(filename);
}
Common::KeymapArray ToucheMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Touche;
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "touche-default", "Default keymappings");
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
Action *act;
act = new Action(kStandardActionLeftClick, _("Left click"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeyMap->addAction(act);
act = new Action(kStandardActionRightClick, _("Right click"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeyMap->addAction(act);
{
act = new Action("SKIPORQUIT", _("Skip sequence / open quit dialog"));
act->setCustomEngineActionEvent(kToucheActionSkipOrQuit);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
gameKeyMap->addAction(act);
act = new Action("SKIPDILOG", _("Skip dialog"));
act->setCustomEngineActionEvent(kToucheActionSkipDialogue);
act->addDefaultInputMapping("SPACE");
act->addDefaultInputMapping("JOY_X");
gameKeyMap->addAction(act);
act = new Action("OPTIONS", _("Open options menu"));
act->setCustomEngineActionEvent(kToucheActionOpenOptions);
act->addDefaultInputMapping("F5");
act->addDefaultInputMapping("JOY_Y");
gameKeyMap->addAction(act);
// I18N: The actor walking pace is increased
act = new Action("ENABLEFASTWALK", _("Enable fast walk"));
act->setCustomEngineActionEvent(kToucheActionEnableFastWalk);
act->addDefaultInputMapping("F9");
act->addDefaultInputMapping("JOY_LEFT");
gameKeyMap->addAction(act);
// I18N: The actor walking pace is decreased
act = new Action("DISABLEFASTWALK", _("Disable fast walk"));
act->setCustomEngineActionEvent(kToucheActionDisableFastWalk);
act->addDefaultInputMapping("F10");
act->addDefaultInputMapping("JOY_LEFT_STICK");
gameKeyMap->addAction(act);
act = new Action("TGGLFASTMODE", _("Toggle fast mode"));
act->setCustomEngineActionEvent(kToucheActionToggleFastMode);
act->addDefaultInputMapping("C+f");
act->addDefaultInputMapping("JOY_RIGHT_STICK");
gameKeyMap->addAction(act);
act = new Action("TGGLTALKTEXT", _("Toggle between voice / text / text and voice"));
act->setCustomEngineActionEvent(kToucheActionToggleTalkTextMode);
act->addDefaultInputMapping("t");
act->addDefaultInputMapping("JOY_RIGHT");
gameKeyMap->addAction(act);
String s = ConfMan.get("language", target);
Language l = Common::parseLanguage(s);
act = new Action("YES", _("Press \"Yes\" Key"));
act->setCustomEngineActionEvent(kToucheActionYes);
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
switch (l) {
case FR_FRA:
act->addDefaultInputMapping("o");
break;
case DE_DEU:
act->addDefaultInputMapping("j");
break;
case ES_ESP:
act->addDefaultInputMapping("s");
break;
case PL_POL:
act->addDefaultInputMapping("s");
act->addDefaultInputMapping("t");
break;
default:
act->addDefaultInputMapping("y");
break;
}
gameKeyMap->addAction(act);
}
KeymapArray keymaps(2);
keymaps[0] = engineKeyMap;
keymaps[1] = gameKeyMap;
return keymaps;
}
#if PLUGIN_ENABLED_DYNAMIC(TOUCHE)
REGISTER_PLUGIN_DYNAMIC(TOUCHE, PLUGIN_TYPE_ENGINE, ToucheMetaEngine);
#else
REGISTER_PLUGIN_STATIC(TOUCHE, PLUGIN_TYPE_ENGINE, ToucheMetaEngine);
#endif

107
engines/touche/midi.cpp Normal file
View File

@@ -0,0 +1,107 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "common/stream.h"
#include "audio/midiparser.h"
#include "touche/midi.h"
namespace Touche {
static const uint8 _gmToRol[128] = {
0x01, 0x02, 0x03, 0x08, 0x04, 0x05, 0x11, 0x14, 0x66, 0x66, 0x66, 0x62, 0x69, 0x68, 0x67, 0x26,
0x09, 0x0A, 0x0B, 0x0E, 0x0F, 0x10, 0x10, 0x10, 0x3C, 0x3D, 0x3D, 0x3D, 0x3D, 0x3E, 0x3F, 0x3F,
0x47, 0x41, 0x42, 0x48, 0x45, 0x46, 0x1D, 0x1E, 0x35, 0x36, 0x37, 0x39, 0x33, 0x34, 0x3A, 0x71,
0x31, 0x32, 0x31, 0x32, 0x23, 0x23, 0x23, 0x7B, 0x59, 0x5B, 0x5F, 0x5A, 0x5D, 0x60, 0x19, 0x1A,
0x4F, 0x50, 0x51, 0x52, 0x55, 0x56, 0x57, 0x53, 0x4B, 0x49, 0x4D, 0x4E, 0x6F, 0x6C, 0x6D, 0x6E,
0x30, 0x19, 0x4E, 0x2B, 0x28, 0x23, 0x19, 0x30, 0x21, 0x25, 0x1C, 0x21, 0x24, 0x22, 0x21, 0x22,
0x2A, 0x25, 0x24, 0x26, 0x2E, 0x22, 0x29, 0x21, 0x40, 0x40, 0x6A, 0x6A, 0x68, 0x10, 0x35, 0x10,
0x7F, 0x6B, 0x69, 0x75, 0x76, 0x72, 0x74, 0x01, 0x01, 0x70, 0x01, 0x7D, 0x7C, 0x01, 0x01, 0x01
};
MidiPlayer::MidiPlayer() {
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
_nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
_driver = MidiDriver::createMidi(dev);
int ret = _driver->open();
if (ret == 0) {
_driver->setTimerCallback(this, &timerCallback);
if (_nativeMT32)
_driver->sendMT32Reset();
else
_driver->sendGMReset();
}
}
void MidiPlayer::play(Common::ReadStream &stream, int size, bool loop) {
Common::StackLock lock(_mutex);
stop();
_midiData = (byte *)malloc(size);
if (_midiData) {
stream.read(_midiData, size);
_parser = MidiParser::createParser_SMF();
_parser->setMidiDriver(this);
_parser->setTimerRate(_driver->getBaseTempo());
_parser->loadMusic(_midiData, size);
_parser->setTrack(0);
_isLooping = loop;
_isPlaying = true;
}
}
void MidiPlayer::adjustVolume(int diff) {
setVolume(_masterVolume + diff);
}
void MidiPlayer::setVolume(int volume) {
// FIXME: This is almost identical to Audio::MidiPlayer::setVolume,
// the only difference is that this implementation will always
// transmit the volume change, even if the current _masterVolume
// equals the new master volume. This *could* make a difference in
// some situations.
// So, we should determine whether the engine requires this behavioral
// difference; and maybe also if other engines could benefit from it
// (as hypothetically, it might fix some subtle bugs?)
_masterVolume = CLIP(volume, 0, 255);
Common::StackLock lock(_mutex);
for (int i = 0; i < kNumChannels; ++i) {
if (_channelsTable[i]) {
_channelsTable[i]->volume(_channelsVolume[i] * _masterVolume / 255);
}
}
}
void MidiPlayer::send(uint32 b) {
if ((b & 0xF0) == 0xC0 && _nativeMT32) { // program change
b = (b & 0xFFFF00FF) | (_gmToRol[(b >> 8) & 0x7F] << 8);
}
Audio::MidiPlayer::send(b);
}
} // Touche namespace

52
engines/touche/midi.h Normal file
View File

@@ -0,0 +1,52 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef TOUCHE_MIDI_H
#define TOUCHE_MIDI_H
#include "common/util.h"
#include "common/mutex.h"
#include "audio/midiplayer.h"
class MidiParser;
namespace Common {
class ReadStream;
}
namespace Touche {
class MidiPlayer : public Audio::MidiPlayer {
public:
MidiPlayer();
void play(Common::ReadStream &stream, int size, bool loop = false);
void adjustVolume(int diff);
void setVolume(int volume) override;
// MidiDriver_BASE interface
void send(uint32 b) override;
};
} // namespace Touche
#endif

24
engines/touche/module.mk Normal file
View File

@@ -0,0 +1,24 @@
MODULE := engines/touche
MODULE_OBJS := \
console.o \
graphics.o \
menu.o \
metaengine.o \
midi.o \
opcodes.o \
resource.o \
saveload.o \
staticres.o \
touche.o
# This module can be built as a plugin
ifeq ($(ENABLE_TOUCHE), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

1057
engines/touche/opcodes.cpp Normal file

File diff suppressed because it is too large Load Diff

691
engines/touche/resource.cpp Normal file
View File

@@ -0,0 +1,691 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/textconsole.h"
#include "audio/decoders/flac.h"
#include "audio/mixer.h"
#include "audio/decoders/mp3.h"
#include "audio/decoders/voc.h"
#include "audio/decoders/vorbis.h"
#include "audio/decoders/raw.h"
#include "audio/audiostream.h"
#include "touche/midi.h"
#include "touche/touche.h"
#include "touche/graphics.h"
namespace Touche {
enum {
kCurrentSpeechDataVersion = 1,
kSpeechDataFileHeaderSize = 4
};
struct CompressedSpeechFile {
const char *filename;
Audio::SeekableAudioStream *(*makeStream)(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
};
static const CompressedSpeechFile compressedSpeechFilesTable[] = {
#ifdef USE_FLAC
{ "TOUCHE.SOF", Audio::makeFLACStream },
#endif
#ifdef USE_VORBIS
{ "TOUCHE.SOG", Audio::makeVorbisStream },
#endif
#ifdef USE_MAD
{ "TOUCHE.SO3", Audio::makeMP3Stream },
#endif
{ 0, 0 }
};
void ToucheEngine::res_openDataFile() {
if (!_fData.open("TOUCHE.DAT")) {
error("Unable to open 'TOUCHE.DAT' for reading");
}
for (int i = 0; compressedSpeechFilesTable[i].filename; ++i) {
if (_fSpeech[0].open(compressedSpeechFilesTable[i].filename)) {
int version = _fSpeech[0].readUint16LE();
if (version == kCurrentSpeechDataVersion) {
_compressedSpeechData = i;
return;
}
warning("Unhandled version %d for compressed sound file '%s'", version, compressedSpeechFilesTable[i].filename);
return;
}
}
// _fSpeech[0] opening/closing is driven by the scripts
_fSpeech[1].open("OBJ");
_compressedSpeechData = -1;
}
void ToucheEngine::res_closeDataFile() {
res_stopSpeech(); // stop any pending speech before closing the underlying streams
_fData.close();
_fSpeech[0].close();
_fSpeech[1].close();
}
void ToucheEngine::res_allocateTables() {
_fData.seek(64);
uint32 textDataOffs = _fData.readUint32LE();
uint32 textDataSize = _fData.readUint32LE();
_textData = (uint8 *)malloc(textDataSize);
if (!_textData) {
error("Unable to allocate memory for text data");
}
_fData.seek(textDataOffs);
_fData.read(_textData, textDataSize);
_fData.seek(2);
const int bw = _fData.readUint16LE();
const int bh = _fData.readUint16LE();
uint32 size = bw * bh;
_backdropBuffer = (uint8 *)malloc(size);
if (!_backdropBuffer) {
error("Unable to allocate memory for backdrop buffer");
}
_menuKitData = (uint8 *)malloc(42 * 120);
if (!_menuKitData) {
error("Unable to allocate memory for menu kit data");
}
_convKitData = (uint8 *)malloc(152 * 80);
if (!_convKitData) {
error("Unable to allocate memory for conv kit data");
}
for (int i = 0; i < NUM_SEQUENCES; ++i) {
_sequenceDataTable[i] = (uint8 *)malloc(16384);
if (!_sequenceDataTable[i]) {
error("Unable to allocate memory for sequence data %d", i);
}
}
_programData = (uint8 *)malloc(kMaxProgramDataSize);
if (!_programData) {
error("Unable to allocate memory for program data");
}
_mouseData = (uint8 *)malloc(kCursorWidth * kCursorHeight);
if (!_mouseData) {
error("Unable to allocate memory for mouse data");
}
_iconData = (uint8 *)malloc(kIconWidth * kIconHeight);
if (!_iconData) {
error("Unable to allocate memory for object data");
}
memset(_spritesTable, 0, sizeof(_spritesTable));
_offscreenBuffer = (uint8 *)malloc(kScreenWidth * kScreenHeight);
if (!_offscreenBuffer) {
error("Unable to allocate memory for offscreen buffer");
}
}
void ToucheEngine::res_deallocateTables() {
free(_textData);
_textData = nullptr;
free(_backdropBuffer);
_backdropBuffer = nullptr;
free(_menuKitData);
_menuKitData = nullptr;
free(_convKitData);
_convKitData = nullptr;
for (int i = 0; i < NUM_SEQUENCES; ++i) {
free(_sequenceDataTable[i]);
_sequenceDataTable[i] = nullptr;
}
free(_programData);
_programData = nullptr;
free(_mouseData);
_mouseData = nullptr;
free(_iconData);
_iconData = nullptr;
for (int i = 0; i < NUM_SPRITES; ++i) {
free(_spritesTable[i].ptr);
_spritesTable[i].ptr = nullptr;
}
free(_offscreenBuffer);
_offscreenBuffer = nullptr;
}
uint32 ToucheEngine::res_getDataOffset(ResourceType type, int num, uint32 *size) {
debugC(9, kDebugResource, "ToucheEngine::res_getDataOffset() type=%d num=%d", type, num);
static const struct ResourceData {
int offs;
int count;
int type;
} dataTypesTable[] = {
{ 0x048, 100, kResourceTypeRoomImage },
{ 0x228, 30, kResourceTypeSequence },
{ 0x2A0, 50, kResourceTypeSpriteImage },
{ 0x390, 100, kResourceTypeIconImage },
{ 0x6B0, 80, kResourceTypeRoomInfo },
{ 0x908, 150, kResourceTypeProgram },
{ 0xB60, 50, kResourceTypeMusic },
{ 0xC28, 120, kResourceTypeSound }
};
const ResourceData *rd = NULL;
for (unsigned int i = 0; i < ARRAYSIZE(dataTypesTable); ++i) {
if (dataTypesTable[i].type == type) {
rd = &dataTypesTable[i];
break;
}
}
if (rd == NULL) {
error("Invalid resource type %d", type);
}
if (num < 0 || num > rd->count) {
error("Invalid resource number %d (type %d)", num, type);
}
_fData.seek(rd->offs + num * 4);
uint32 offs = _fData.readUint32LE();
assert(offs != 0);
if (size) {
uint32 nextOffs = _fData.readUint32LE();
*size = nextOffs - offs;
}
return offs;
}
void ToucheEngine::res_loadSpriteImage(int num, uint8 *dst) {
debugC(9, kDebugResource, "ToucheEngine::res_loadSpriteImage() num=%d", num);
const uint32 offs = res_getDataOffset(kResourceTypeSpriteImage, num);
_fData.seek(offs);
_currentImageWidth = _fData.readUint16LE();
_currentImageHeight = _fData.readUint16LE();
for (int i = 0; i < _currentImageHeight; ++i) {
res_decodeScanLineImageRLE(dst + _currentImageWidth * i, _currentImageWidth);
}
}
void ToucheEngine::res_loadProgram(int num) {
debugC(9, kDebugResource, "ToucheEngine::res_loadProgram() num=%d", num);
const uint32 offs = res_getDataOffset(kResourceTypeProgram, num, &_programDataSize);
_fData.seek(offs);
assert(_programDataSize <= kMaxProgramDataSize);
_fData.read(_programData, _programDataSize);
}
void ToucheEngine::res_decodeProgramData() {
debugC(9, kDebugResource, "ToucheEngine::res_decodeProgramData()");
uint8 *p;
uint8 *programDataEnd = _programData + _programDataSize;
p = _programData + READ_LE_UINT32(_programData + 32);
_script.init(p);
p = _programData + READ_LE_UINT32(_programData + 4);
_programTextDataPtr = p;
_programRectsTable.clear();
p = _programData + READ_LE_UINT32(_programData + 20);
while (p < programDataEnd) {
int16 x = READ_LE_UINT16(p); p += 2;
int16 y = READ_LE_UINT16(p); p += 2;
int16 w = READ_LE_UINT16(p); p += 2;
int16 h = READ_LE_UINT16(p); p += 2;
_programRectsTable.push_back(Common::Rect(x, y, x + w, y + h));
if (x == -1) {
break;
}
}
_programPointsTable.clear();
p = _programData + READ_LE_UINT32(_programData + 24);
while (p < programDataEnd) {
ProgramPointData ppd;
ppd.x = READ_LE_UINT16(p); p += 2;
ppd.y = READ_LE_UINT16(p); p += 2;
ppd.z = READ_LE_UINT16(p); p += 2;
ppd.order = READ_LE_UINT16(p); p += 2;
_programPointsTable.push_back(ppd);
if (ppd.x == -1) {
break;
}
}
_programWalkTable.clear();
p = _programData + READ_LE_UINT32(_programData + 28);
while (p < programDataEnd) {
ProgramWalkData pwd;
pwd.point1 = READ_LE_UINT16(p); p += 2;
if (pwd.point1 == -1) {
break;
}
assert((uint16)pwd.point1 < _programPointsTable.size());
pwd.point2 = READ_LE_UINT16(p); p += 2;
assert((uint16)pwd.point2 < _programPointsTable.size());
pwd.clippingRect = READ_LE_UINT16(p); p += 2;
pwd.area1 = READ_LE_UINT16(p); p += 2;
pwd.area2 = READ_LE_UINT16(p); p += 2;
p += 12; // unused
_programWalkTable.push_back(pwd);
}
_programAreaTable.clear();
p = _programData + READ_LE_UINT32(_programData + 8);
while (p < programDataEnd) {
ProgramAreaData pad;
int16 x = READ_LE_UINT16(p); p += 2;
if (x == -1) {
break;
}
int16 y = READ_LE_UINT16(p); p += 2;
int16 w = READ_LE_UINT16(p); p += 2;
int16 h = READ_LE_UINT16(p); p += 2;
pad.area.r = Common::Rect(x, y, x + w, y + h);
pad.area.srcX = READ_LE_UINT16(p); p += 2;
pad.area.srcY = READ_LE_UINT16(p); p += 2;
pad.id = READ_LE_UINT16(p); p += 2;
pad.state = READ_LE_UINT16(p); p += 2;
pad.animCount = READ_LE_UINT16(p); p += 2;
pad.animNext = READ_LE_UINT16(p); p += 2;
_programAreaTable.push_back(pad);
}
_programBackgroundTable.clear();
p = _programData + READ_LE_UINT32(_programData + 12);
while (p < programDataEnd) {
ProgramBackgroundData pbd;
int16 x = READ_LE_UINT16(p); p += 2;
if (x == -1) {
break;
}
int16 y = READ_LE_UINT16(p); p += 2;
int16 w = READ_LE_UINT16(p); p += 2;
int16 h = READ_LE_UINT16(p); p += 2;
pbd.area.r = Common::Rect(x, y, x + w, y + h);
pbd.area.srcX = READ_LE_UINT16(p); p += 2;
pbd.area.srcY = READ_LE_UINT16(p); p += 2;
pbd.type = READ_LE_UINT16(p); p += 2;
pbd.offset = READ_LE_UINT16(p); p += 2;
pbd.scaleMul = READ_LE_UINT16(p); p += 2;
pbd.scaleDiv = READ_LE_UINT16(p); p += 2;
_programBackgroundTable.push_back(pbd);
}
_programHitBoxTable.clear();
p = _programData + READ_LE_UINT32(_programData + 16);
while (p < programDataEnd) {
ProgramHitBoxData phbd;
phbd.item = READ_LE_UINT16(p); p += 2;
if (phbd.item == 0) {
break;
}
phbd.talk = READ_LE_UINT16(p); p += 2;
phbd.state = READ_LE_UINT16(p); p += 2;
phbd.str = READ_LE_UINT16(p); p += 2;
phbd.defaultStr = READ_LE_UINT16(p); p += 2;
for (int i = 0; i < 8; ++i) {
phbd.actions[i] = READ_LE_UINT16(p); p += 2;
}
for (int i = 0; i < 2; ++i) {
int16 x = READ_LE_UINT16(p); p += 2;
int16 y = READ_LE_UINT16(p); p += 2;
int16 w = READ_LE_UINT16(p); p += 2;
int16 h = READ_LE_UINT16(p); p += 2;
phbd.hitBoxes[i].left = x;
phbd.hitBoxes[i].top = y;
phbd.hitBoxes[i].right = x + w;
phbd.hitBoxes[i].bottom = y + h;
}
p += 8; // unused
_programHitBoxTable.push_back(phbd);
}
_programActionScriptOffsetTable.clear();
p = _programData + READ_LE_UINT32(_programData + 36);
while (p < programDataEnd) {
ProgramActionScriptOffsetData pasod;
pasod.object1 = READ_LE_UINT16(p); p += 2;
if (pasod.object1 == 0) {
break;
}
pasod.action = READ_LE_UINT16(p); p += 2;
pasod.object2 = READ_LE_UINT16(p); p += 2;
pasod.offset = READ_LE_UINT16(p); p += 2;
_programActionScriptOffsetTable.push_back(pasod);
}
_programConversationTable.clear();
int count = (READ_LE_UINT32(_programData + 44) - READ_LE_UINT32(_programData + 40)) / 6;
assert(count >= 0);
p = _programData + READ_LE_UINT32(_programData + 40);
while (p < programDataEnd && count != 0) {
ProgramConversationData pcd;
pcd.num = READ_LE_UINT16(p); p += 2;
pcd.offset = READ_LE_UINT16(p); p += 2;
pcd.msg = READ_LE_UINT16(p); p += 2;
_programConversationTable.push_back(pcd);
--count;
}
_programKeyCharScriptOffsetTable.clear();
p = _programData + READ_LE_UINT32(_programData + 44);
while (p < programDataEnd) {
ProgramKeyCharScriptOffsetData pksod;
pksod.keyChar = READ_LE_UINT16(p); p += 2;
if (pksod.keyChar == 0) {
break;
}
pksod.offset = READ_LE_UINT16(p); p += 2;
_programKeyCharScriptOffsetTable.push_back(pksod);
}
}
void ToucheEngine::res_loadRoom(int num) {
debugC(9, kDebugResource, "ToucheEngine::res_loadRoom() num=%d flag115=%d", num, _flagsTable[115]);
debug(0, "Setting up room %d", num);
const uint32 offsInfo = res_getDataOffset(kResourceTypeRoomInfo, num);
_fData.seek(offsInfo);
_fData.skip(2);
const int roomImageNum = _fData.readUint16LE();
_fData.skip(2);
_fData.read(_paletteBuffer, 3 * 256);
const uint32 offsImage = res_getDataOffset(kResourceTypeRoomImage, roomImageNum);
_fData.seek(offsImage);
res_loadBackdrop();
bool updateScreenPalette = _flagsTable[115] == 0;
// Workaround to what appears to be a scripting bug. Scripts 27 and 100 triggers
// a palette fading just after loading a room. Catch this, so that only *one*
// palette refresh occurs.
if ((_currentEpisodeNum == 27 && _currentRoomNum == 56 && num == 34) ||
(_currentEpisodeNum == 100 && _currentRoomNum == 2 && num == 1)) {
updateScreenPalette = false;
}
if (updateScreenPalette) {
updatePalette();
} else {
setPalette(0, 255, 0, 0, 0);
}
_currentRoomNum = num;
_updatedRoomAreasTable[0] = 1;
_fullRedrawCounter = 1;
_roomNeedRedraw = true;
_sequenceEntryTable[5].sprNum = -1;
_sequenceEntryTable[5].seqNum = -1;
_sequenceEntryTable[6].sprNum = -1;
_sequenceEntryTable[6].seqNum = -1;
}
void ToucheEngine::res_loadSprite(int num, int index) {
debugC(9, kDebugResource, "ToucheEngine::res_loadSprite() num=%d index=%d", num, index);
assert(index >= 0 && index < NUM_SEQUENCES);
_sequenceEntryTable[index].sprNum = num;
SpriteData *spr = &_spritesTable[index];
const uint32 offs = res_getDataOffset(kResourceTypeSpriteImage, num);
_fData.seek(offs);
_currentImageWidth = _fData.readUint16LE();
_currentImageHeight = _fData.readUint16LE();
const uint32 size = _currentImageWidth * _currentImageHeight;
if (size > spr->size) {
debug(8, "Reallocating memory for sprite %d (index %d), %d bytes needed", num, index, size - spr->size);
spr->size = size;
uint8 *buffer = NULL;
if (spr->ptr)
buffer = (uint8 *)realloc(spr->ptr, size);
if (!buffer) {
// Free previously allocated sprite (when realloc failed)
free(spr->ptr);
buffer = (uint8 *)malloc(size);
}
if (!buffer)
error("[ToucheEngine::res_loadSprite] Unable to reallocate memory for sprite %d (%d bytes)", num, size);
spr->ptr = buffer;
}
for (int i = 0; i < _currentImageHeight; ++i) {
res_decodeScanLineImageRLE(spr->ptr + _currentImageWidth * i, _currentImageWidth);
}
spr->bitmapWidth = _currentImageWidth;
spr->bitmapHeight = _currentImageHeight;
if (_flagsTable[268] == 0) {
res_loadImageHelper(spr->ptr, _currentImageWidth, _currentImageHeight);
}
spr->w = _currentImageWidth;
spr->h = _currentImageHeight;
}
void ToucheEngine::res_loadSequence(int num, int index) {
debugC(9, kDebugResource, "ToucheEngine::res_loadSequence() num=%d index=%d", num, index);
assert(index < NUM_SEQUENCES);
_sequenceEntryTable[index].seqNum = num;
const uint32 offs = res_getDataOffset(kResourceTypeSequence, num);
_fData.seek(offs);
_fData.read(_sequenceDataTable[index], 16000);
}
void ToucheEngine::res_decodeScanLineImageRLE(uint8 *dst, int lineWidth) {
int w = 0;
while (w < lineWidth) {
uint8 code = _fData.readByte();
if ((code & 0xC0) == 0xC0) {
int len = code & 0x3F;
uint8 color = _fData.readByte();
memset(dst, color, len);
dst += len;
w += len;
} else {
*dst = code;
++dst;
++w;
}
}
}
void ToucheEngine::res_loadBackdrop() {
debugC(9, kDebugResource, "ToucheEngine::res_loadBackdrop()");
_currentBitmapWidth = _fData.readUint16LE();
_currentBitmapHeight = _fData.readUint16LE();
for (int i = 0; i < _currentBitmapHeight; ++i) {
res_decodeScanLineImageRLE(_backdropBuffer + _currentBitmapWidth * i, _currentBitmapWidth);
}
_roomWidth = _currentBitmapWidth;
for (int i = 0; i < _currentBitmapWidth; ++i) {
if (_backdropBuffer[i] == 255) {
_roomWidth = i;
_backdropBuffer[i] = 0;
break;
}
}
// Workaround for bug #3305 (original bitmap has a white pixel in its transparent area).
if (_currentRoomNum == 8 && _currentBitmapWidth == 860) {
_backdropBuffer[120 * _currentBitmapWidth + 734] = 0;
}
}
void ToucheEngine::res_loadImage(int num, uint8 *dst) {
debugC(9, kDebugResource, "ToucheEngine::res_loadImage() num=%d", num);
const uint32 offsInfo = res_getDataOffset(kResourceTypeIconImage, num);
_fData.seek(offsInfo);
_currentImageWidth = _fData.readUint16LE();
_currentImageHeight = _fData.readUint16LE();
for (int i = 0; i < _currentImageHeight; ++i) {
res_decodeScanLineImageRLE(dst + _currentImageWidth * i, _currentImageWidth);
}
res_loadImageHelper(dst, _currentImageWidth, _currentImageHeight);
}
void ToucheEngine::res_loadImageHelper(uint8 *imgData, int imgWidth, int imgHeight) {
uint8 *p = imgData;
for (_currentImageHeight = 0; _currentImageHeight < imgHeight; ++_currentImageHeight, p += imgWidth) {
if (*p == 64 || *p == 255) {
break;
}
}
p = imgData;
for (_currentImageWidth = 0; _currentImageWidth < imgWidth; ++_currentImageWidth, ++p) {
if (*p == 64 || *p == 255) {
break;
}
}
if (_flagsTable[267] == 0) {
for (int i = 0; i < imgWidth * imgHeight; ++i) {
uint8 color = imgData[i];
if (color != 0) {
if (color < 64) {
color += 192;
} else {
color = 0;
}
}
imgData[i] = color;
}
}
}
void ToucheEngine::res_loadSound(int priority, int num) {
debugC(9, kDebugResource, "ToucheEngine::res_loadSound() num=%d", num);
if (priority >= 0) {
uint32 size;
const uint32 offs = res_getDataOffset(kResourceTypeSound, num, &size);
Common::SeekableReadStream *datastream = SearchMan.createReadStreamForMember("TOUCHE.DAT");
if (!datastream) {
warning("res_loadSound: Could not open TOUCHE.DAT");
return;
}
datastream->seek(offs);
Audio::AudioStream *stream = Audio::makeVOCStream(datastream, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
if (stream) {
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, stream);
}
}
}
void ToucheEngine::res_stopSound() {
_mixer->stopHandle(_sfxHandle);
}
void ToucheEngine::res_loadMusic(int num) {
debugC(9, kDebugResource, "ToucheEngine::res_loadMusic() num=%d", num);
startMusic(num);
}
void ToucheEngine::res_loadSpeech(int num) {
debugC(9, kDebugResource, "ToucheEngine::res_loadSpeech() num=%d", num);
if (num == -1) {
res_stopSpeech();
} else {
if (_compressedSpeechData < 0) { // uncompressed speech data
if (_fSpeech[0].isOpen()) {
_fSpeech[0].close();
}
Common::String filenameStr = Common::String::format("V%d", num);
_fSpeech[0].open(filenameStr.c_str());
}
if (_fSpeech[0].isOpen()) {
_flagsTable[617] = num;
}
}
}
void ToucheEngine::res_loadSpeechSegment(int num) {
debugC(9, kDebugResource, "ToucheEngine::res_loadSpeechSegment() num=%d", num);
if (_talkTextMode != kTalkModeTextOnly && _flagsTable[617] != 0) {
Audio::AudioStream *stream = 0;
if (_compressedSpeechData < 0) { // uncompressed speech data
int i = 0;
if (num >= 750) {
num -= 750;
i = 1;
}
if (!_fSpeech[i].isOpen()) {
return;
}
_fSpeech[i].seek(num * 8);
uint32 offs = _fSpeech[i].readUint32LE();
uint32 size = _fSpeech[i].readUint32LE();
if (size == 0) {
return;
}
_fSpeech[i].seek(offs);
stream = Audio::makeVOCStream(&_fSpeech[i], Audio::FLAG_UNSIGNED);
} else {
if (num >= 750) {
num -= 750;
_fSpeech[0].seek(kSpeechDataFileHeaderSize);
} else {
assert(_flagsTable[617] > 0 && _flagsTable[617] < 140);
_fSpeech[0].seek(kSpeechDataFileHeaderSize + _flagsTable[617] * 4);
}
uint32 dataOffs = _fSpeech[0].readUint32LE();
if (dataOffs == 0) {
return;
}
_fSpeech[0].seek(dataOffs + num * 8);
uint32 offs = _fSpeech[0].readUint32LE();
uint32 size = _fSpeech[0].readUint32LE();
if (size == 0) {
return;
}
_fSpeech[0].seek(offs);
Common::SeekableReadStream *tmp = _fSpeech[0].readStream(size);
if (tmp)
stream = (compressedSpeechFilesTable[_compressedSpeechData].makeStream)(tmp, DisposeAfterUse::YES);
}
if (stream) {
_speechPlaying = true;
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream);
}
}
}
void ToucheEngine::res_stopSpeech() {
debugC(9, kDebugResource, "ToucheEngine::res_stopSpeech()");
_mixer->stopHandle(_speechHandle);
_speechPlaying = false;
}
} // namespace Touche

396
engines/touche/saveload.cpp Normal file
View File

@@ -0,0 +1,396 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/textconsole.h"
#include "common/savefile.h"
#include "touche/graphics.h"
#include "touche/touche.h"
namespace Touche {
enum {
kGameStateDescriptionLen = 32,
kCurrentGameStateVersion = 6
};
static void saveOrLoad(Common::WriteStream &stream, uint16 &i) {
stream.writeUint16LE(i);
}
static void saveOrLoad(Common::ReadStream &stream, uint16 &i) {
i = stream.readUint16LE();
}
static void saveOrLoad(Common::WriteStream &stream, int16 &i) {
stream.writeSint16LE(i);
}
static void saveOrLoad(Common::ReadStream &stream, int16 &i) {
i = stream.readSint16LE();
}
static void saveOrLoadPtr(Common::WriteStream &stream, int16 *&p, int16 *base) {
int32 offset = (int32)(p - base);
stream.writeSint32LE(offset);
}
static void saveOrLoadPtr(Common::ReadStream &stream, int16 *&p, int16 *base) {
int32 offset = stream.readSint32LE();
p = base + offset;
}
template<class S>
static void saveOrLoad(S &s, Common::Rect &r) {
saveOrLoad(s, r.left);
saveOrLoad(s, r.top);
saveOrLoad(s, r.right);
saveOrLoad(s, r.bottom);
}
template<class S>
static void saveOrLoad(S &s, SequenceEntry &seq) {
saveOrLoad(s, seq.sprNum);
saveOrLoad(s, seq.seqNum);
}
template<class S>
static void saveOrLoad(S &s, KeyChar &key) {
saveOrLoad(s, key.num);
saveOrLoad(s, key.flags);
saveOrLoad(s, key.currentAnimCounter);
saveOrLoad(s, key.strNum);
saveOrLoad(s, key.walkDataNum);
saveOrLoad(s, key.spriteNum);
saveOrLoad(s, key.prevBoundingRect);
saveOrLoad(s, key.boundingRect);
saveOrLoad(s, key.xPos);
saveOrLoad(s, key.yPos);
saveOrLoad(s, key.zPos);
saveOrLoad(s, key.xPosPrev);
saveOrLoad(s, key.yPosPrev);
saveOrLoad(s, key.zPosPrev);
saveOrLoad(s, key.prevWalkDataNum);
saveOrLoad(s, key.textColor);
for (uint i = 0; i < 4; ++i) {
saveOrLoad(s, key.inventoryItems[i]);
}
saveOrLoad(s, key.money);
saveOrLoad(s, key.pointsDataNum);
saveOrLoad(s, key.currentWalkBox);
saveOrLoad(s, key.prevPointsDataNum);
saveOrLoad(s, key.currentAnim);
saveOrLoad(s, key.facingDirection);
saveOrLoad(s, key.currentAnimSpeed);
for (uint i = 0; i < 16; ++i) {
saveOrLoad(s, key.framesList[i]);
}
saveOrLoad(s, key.framesListCount);
saveOrLoad(s, key.currentFrame);
saveOrLoad(s, key.anim1Start);
saveOrLoad(s, key.anim1Count);
saveOrLoad(s, key.anim2Start);
saveOrLoad(s, key.anim2Count);
saveOrLoad(s, key.anim3Start);
saveOrLoad(s, key.anim3Count);
saveOrLoad(s, key.followingKeyCharNum);
saveOrLoad(s, key.followingKeyCharPos);
saveOrLoad(s, key.sequenceDataIndex);
saveOrLoad(s, key.sequenceDataOffset);
saveOrLoad(s, key.walkPointsListIndex);
for (uint i = 0; i < 40; ++i) {
saveOrLoad(s, key.walkPointsList[i]);
}
saveOrLoad(s, key.scriptDataStartOffset);
saveOrLoad(s, key.scriptDataOffset);
saveOrLoadPtr(s, key.scriptStackPtr, &key.scriptStackTable[39]);
saveOrLoad(s, key.delay);
saveOrLoad(s, key.waitingKeyChar);
for (uint i = 0; i < 3; ++i) {
saveOrLoad(s, key.waitingKeyCharPosTable[i]);
}
for (uint i = 0; i < 40; ++i) {
saveOrLoad(s, key.scriptStackTable[i]);
}
}
template<class S>
static void saveOrLoad(S &s, TalkEntry &entry) {
saveOrLoad(s, entry.otherKeyChar);
saveOrLoad(s, entry.talkingKeyChar);
saveOrLoad(s, entry.num);
}
template<class S>
static void saveOrLoad(S &s, ProgramHitBoxData &data) {
saveOrLoad(s, data.item);
saveOrLoad(s, data.talk);
saveOrLoad(s, data.state);
saveOrLoad(s, data.str);
saveOrLoad(s, data.defaultStr);
for (uint i = 0; i < 8; ++i) {
saveOrLoad(s, data.actions[i]);
}
for (uint i = 0; i < 2; ++i) {
saveOrLoad(s, data.hitBoxes[i]);
}
}
template<class S>
static void saveOrLoad(S &s, Area &area) {
saveOrLoad(s, area.r);
saveOrLoad(s, area.srcX);
saveOrLoad(s, area.srcY);
}
template<class S>
static void saveOrLoad(S &s, ProgramBackgroundData &data) {
saveOrLoad(s, data.area);
saveOrLoad(s, data.type);
saveOrLoad(s, data.offset);
saveOrLoad(s, data.scaleMul);
saveOrLoad(s, data.scaleDiv);
}
template<class S>
static void saveOrLoad(S &s, ProgramAreaData &data) {
saveOrLoad(s, data.area);
saveOrLoad(s, data.id);
saveOrLoad(s, data.state);
saveOrLoad(s, data.animCount);
saveOrLoad(s, data.animNext);
}
template<class S>
static void saveOrLoad(S &s, ProgramWalkData &data) {
saveOrLoad(s, data.point1);
saveOrLoad(s, data.point2);
saveOrLoad(s, data.clippingRect);
saveOrLoad(s, data.area1);
saveOrLoad(s, data.area2);
}
template<class S>
static void saveOrLoad(S &s, ProgramPointData &data) {
saveOrLoad(s, data.x);
saveOrLoad(s, data.y);
saveOrLoad(s, data.z);
saveOrLoad(s, data.order);
}
template<class A>
static void saveOrLoadCommonArray(Common::WriteStream &stream, A &array) {
uint count = array.size();
assert(count < 0xFFFF);
stream.writeUint16LE(count);
for (uint i = 0; i < count; ++i) {
saveOrLoad(stream, array[i]);
}
}
template<class A>
static void saveOrLoadCommonArray(Common::ReadStream &stream, A &array) {
uint count = stream.readUint16LE();
if (count == array.size()) {
for (uint i = 0; i < count; ++i) {
saveOrLoad(stream, array[i]);
}
}
}
template<class S, class A>
static void saveOrLoadStaticArray(S &s, A &array, uint count) {
for (uint i = 0; i < count; ++i) {
saveOrLoad(s, array[i]);
}
}
static const uint32 saveLoadEndMarker = 0x55AA55AA;
void ToucheEngine::saveGameStateData(Common::WriteStream *stream) {
setKeyCharMoney();
stream->writeUint16LE(_currentEpisodeNum);
stream->writeUint16LE(_currentMusicNum);
stream->writeUint16LE(_currentRoomNum);
stream->writeUint16LE(_flagsTable[614]);
stream->writeUint16LE(_flagsTable[615]);
stream->writeUint16LE(_disabledInputCounter);
saveOrLoadCommonArray(*stream, _programHitBoxTable);
saveOrLoadCommonArray(*stream, _programBackgroundTable);
saveOrLoadCommonArray(*stream, _programAreaTable);
saveOrLoadCommonArray(*stream, _programWalkTable);
saveOrLoadCommonArray(*stream, _programPointsTable);
stream->write(_updatedRoomAreasTable, 200);
saveOrLoadStaticArray(*stream, _sequenceEntryTable, NUM_SEQUENCES);
saveOrLoadStaticArray(*stream, _flagsTable, 1024);
saveOrLoadStaticArray(*stream, _inventoryList1, 100);
saveOrLoadStaticArray(*stream, _inventoryList2, 100);
saveOrLoadStaticArray(*stream, _inventoryList3, 6);
saveOrLoadStaticArray(*stream, _keyCharsTable, NUM_KEYCHARS);
saveOrLoadStaticArray(*stream, _inventoryItemsInfoTable, NUM_INVENTORY_ITEMS);
saveOrLoadStaticArray(*stream, _talkTable, NUM_TALK_ENTRIES);
stream->writeUint16LE(_talkListEnd);
stream->writeUint16LE(_talkListCurrent);
stream->writeUint32LE(saveLoadEndMarker);
}
void ToucheEngine::loadGameStateData(Common::ReadStream *stream) {
setKeyCharMoney();
clearDirtyRects();
_flagsTable[115] = 0;
clearRoomArea();
_currentEpisodeNum = stream->readUint16LE();
_newMusicNum = stream->readUint16LE();
_currentRoomNum = stream->readUint16LE();
res_loadRoom(_currentRoomNum);
int16 roomOffsX = _flagsTable[614] = stream->readUint16LE();
int16 roomOffsY = _flagsTable[615] = stream->readUint16LE();
_disabledInputCounter = stream->readUint16LE();
res_loadProgram(_currentEpisodeNum);
setupEpisode(-1);
saveOrLoadCommonArray(*stream, _programHitBoxTable);
saveOrLoadCommonArray(*stream, _programBackgroundTable);
saveOrLoadCommonArray(*stream, _programAreaTable);
saveOrLoadCommonArray(*stream, _programWalkTable);
saveOrLoadCommonArray(*stream, _programPointsTable);
stream->read(_updatedRoomAreasTable, 200);
for (uint i = 1; i < _updatedRoomAreasTable[0]; ++i) {
updateRoomAreas(_updatedRoomAreasTable[i], -1);
}
saveOrLoadStaticArray(*stream, _sequenceEntryTable, NUM_SEQUENCES);
saveOrLoadStaticArray(*stream, _flagsTable, 1024);
saveOrLoadStaticArray(*stream, _inventoryList1, 100);
saveOrLoadStaticArray(*stream, _inventoryList2, 100);
saveOrLoadStaticArray(*stream, _inventoryList3, 6);
saveOrLoadStaticArray(*stream, _keyCharsTable, NUM_KEYCHARS);
saveOrLoadStaticArray(*stream, _inventoryItemsInfoTable, NUM_INVENTORY_ITEMS);
saveOrLoadStaticArray(*stream, _talkTable, NUM_TALK_ENTRIES);
_talkListEnd = stream->readUint16LE();
_talkListCurrent = stream->readUint16LE();
if (stream->readUint32LE() != saveLoadEndMarker) {
warning("Corrupted gamestate data");
// if that ever happens, exit the game
quitGame();
}
_flagsTable[614] = roomOffsX;
_flagsTable[615] = roomOffsY;
for (uint i = 0; i < NUM_SEQUENCES; ++i) {
if (_sequenceEntryTable[i].seqNum != -1) {
res_loadSequence(_sequenceEntryTable[i].seqNum, i);
}
if (_sequenceEntryTable[i].sprNum != -1) {
res_loadSprite(_sequenceEntryTable[i].sprNum, i);
}
}
_currentKeyCharNum = _flagsTable[104];
_inventoryStateTable[0].displayOffset = 0;
_inventoryStateTable[1].displayOffset = 0;
_inventoryStateTable[2].displayOffset = 0;
drawInventory(_currentKeyCharNum, 1);
Graphics::copyRect(_offscreenBuffer, kScreenWidth, 0, 0,
_backdropBuffer, _currentBitmapWidth, _flagsTable[614], _flagsTable[615],
kScreenWidth, kRoomHeight);
updateRoomRegions();
_fullRedrawCounter = 1;
_roomNeedRedraw = false;
if (_flagsTable[617] != 0) {
res_loadSpeech(_flagsTable[617]);
}
debug(0, "Loaded state, current episode %d", _currentEpisodeNum);
}
Common::Error ToucheEngine::saveGameState(int num, const Common::String &description, bool isAutosave) {
bool saveOk = false;
Common::String gameStateFileName = generateGameStateFileName(_targetName.c_str(), num);
Common::OutSaveFile *f = _saveFileMan->openForSaving(gameStateFileName);
if (f) {
f->writeUint16LE(kCurrentGameStateVersion);
f->writeUint16LE(0);
char headerDescription[kGameStateDescriptionLen];
memset(headerDescription, 0, kGameStateDescriptionLen);
strncpy(headerDescription, description.c_str(), kGameStateDescriptionLen - 1);
f->write(headerDescription, kGameStateDescriptionLen);
saveGameStateData(f);
f->finalize();
if (!f->err()) {
saveOk = true;
} else {
warning("Can't write file '%s'", gameStateFileName.c_str());
}
delete f;
}
return saveOk ? Common::kNoError : Common::kUnknownError;
}
Common::Error ToucheEngine::loadGameState(int num) {
bool loadOk = false;
Common::String gameStateFileName = generateGameStateFileName(_targetName.c_str(), num);
Common::InSaveFile *f = _saveFileMan->openForLoading(gameStateFileName);
if (f) {
uint16 version = f->readUint16LE();
if (version < kCurrentGameStateVersion) {
warning("Unsupported gamestate version %d (index %d)", version, num);
} else {
f->skip(2 + kGameStateDescriptionLen);
loadGameStateData(f);
if (f->err() || f->eos()) {
warning("Can't read file '%s'", gameStateFileName.c_str());
} else {
loadOk = true;
}
}
delete f;
}
return loadOk ? Common::kNoError : Common::kUnknownError;
}
void readGameStateDescription(Common::ReadStream *f, char *description, int len) {
uint16 version = f->readUint16LE();
if (version >= kCurrentGameStateVersion) {
f->readUint16LE();
f->read(description, MIN<int>(len, kGameStateDescriptionLen));
description[len] = 0;
} else {
description[0] = 0;
}
}
Common::String generateGameStateFileName(const char *target, int slot, bool prefixOnly) {
Common::String name(target);
if (prefixOnly) {
name += ".*";
} else {
name += Common::String::format(".%d", slot);
}
return name;
}
int getGameStateFileSlot(const char *filename) {
int i = -1;
const char *slot = strrchr(filename, '.');
if (slot) {
i = atoi(slot + 1);
}
return i;
}
} // namespace Touche

2268
engines/touche/staticres.cpp Normal file

File diff suppressed because it is too large Load Diff

3497
engines/touche/touche.cpp Normal file

File diff suppressed because it is too large Load Diff

958
engines/touche/touche.h Normal file
View File

@@ -0,0 +1,958 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef TOUCHE_TOUCHE_H
#define TOUCHE_TOUCHE_H
#include "common/array.h"
#include "common/endian.h"
#include "common/file.h"
#include "common/random.h"
#include "common/rect.h"
#include "common/util.h"
#include "audio/mixer.h"
#include "engines/engine.h"
#include "touche/console.h"
/**
* This is the namespace of the Touche engine.
*
* Status of this engine: ???
*
* Games using this engine:
* - Touché: The Adventures of the Fifth Musketeer
*/
namespace Touche {
enum TOUCHEAction {
kToucheActionNone,
kToucheActionYes,
kToucheActionSkipOrQuit,
kToucheActionOpenOptions,
kToucheActionEnableFastWalk,
kToucheActionDisableFastWalk,
kToucheActionToggleFastMode,
kToucheActionToggleTalkTextMode,
kToucheActionSkipDialogue
};
struct Area {
Common::Rect r;
int16 srcX, srcY;
Area() {
srcX = srcY = 0;
}
Area(int16 x, int16 y, int16 w, int16 h) {
r = Common::Rect(x, y, x + w, y + h);
srcX = srcY = 0;
}
bool clip(const Common::Rect &rect) {
const int dx = r.left - rect.left;
if (dx < 0) {
srcX -= dx;
}
const int dy = r.top - rect.top;
if (dy < 0) {
srcY -= dy;
}
if (rect.left > r.left) {
r.left = rect.left;
}
if (rect.top > r.top) {
r.top = rect.top;
}
if (rect.right < r.right) {
r.right = rect.right;
}
if (rect.bottom < r.bottom) {
r.bottom = rect.bottom;
}
return (r.right > r.left && r.bottom > r.top);
}
};
struct KeyChar {
uint16 num;
uint16 flags;
int16 currentAnimCounter;
int16 strNum;
int16 walkDataNum;
int16 spriteNum;
Common::Rect prevBoundingRect;
Common::Rect boundingRect;
int16 xPos;
int16 yPos;
int16 zPos;
int16 xPosPrev;
int16 yPosPrev;
int16 zPosPrev;
int16 prevWalkDataNum;
uint16 textColor;
int16 inventoryItems[4];
int16 money;
int16 pointsDataNum;
int16 currentWalkBox;
uint16 prevPointsDataNum;
int16 currentAnim;
int16 facingDirection;
int16 currentAnimSpeed;
int16 framesList[16];
int16 framesListCount;
int16 currentFrame;
int16 anim1Start;
int16 anim1Count;
int16 anim2Start;
int16 anim2Count;
int16 anim3Start;
int16 anim3Count;
int16 followingKeyCharNum;
int16 followingKeyCharPos;
uint16 sequenceDataIndex;
uint16 sequenceDataOffset;
int16 walkPointsListIndex;
int16 walkPointsList[40];
uint16 scriptDataStartOffset;
uint16 scriptDataOffset;
int16 *scriptStackPtr;
int16 delay;
int16 waitingKeyChar;
int16 waitingKeyCharPosTable[3];
int16 scriptStackTable[40];
};
struct Script {
uint8 opcodeNum;
uint32 dataOffset;
int16 keyCharNum;
uint8 *dataPtr;
int16 *stackDataPtr;
int16 *stackDataBasePtr;
int16 quitFlag;
int16 stackDataTable[500];
void init(uint8 *data) {
dataPtr = data;
stackDataPtr = stackDataBasePtr = &stackDataTable[499];
dataOffset = 0;
quitFlag = 0;
}
uint8 readByte(uint32 offs) const {
return *(dataPtr + offs);
}
int16 readWord(uint32 offs) const {
return READ_LE_UINT16(dataPtr + offs);
}
uint8 readNextByte() {
uint8 val = readByte(dataOffset);
++dataOffset;
return val;
}
int16 readNextWord() {
int16 val = readWord(dataOffset);
dataOffset += 2;
return val;
}
};
struct TalkEntry {
int16 otherKeyChar;
int16 talkingKeyChar;
int16 num;
};
struct ConversationChoice {
int16 num;
int16 msg;
};
struct AnimationEntry {
int16 num;
int16 x;
int16 y;
int16 dx;
int16 dy;
int16 posNum;
int16 delayCounter;
int16 displayCounter;
Common::Rect displayRect;
void clear() {
num = 0;
x = 0;
y = 0;
dx = 0;
dy = 0;
posNum = 0;
delayCounter = 0;
displayRect.top = 0;
displayRect.left = 0;
displayRect.bottom = 0;
displayRect.right = 0;
}
};
struct SequenceEntry {
int16 sprNum;
int16 seqNum;
};
struct SpriteData {
uint32 size;
uint8 *ptr;
uint16 bitmapWidth;
uint16 bitmapHeight;
uint16 w;
uint16 h;
};
struct InventoryState {
int16 displayOffset;
int16 lastItem;
int16 itemsPerLine;
int16 *itemsList;
};
struct ProgramPointData {
int16 x, y, z;
int16 order;
};
struct ProgramWalkData {
int16 point1;
int16 point2;
int16 clippingRect;
int16 area1;
int16 area2;
};
struct ProgramAreaData {
Area area;
int16 id;
int16 state;
int16 animCount;
int16 animNext;
};
struct ProgramBackgroundData {
Area area;
int16 type;
int16 offset;
int16 scaleMul;
int16 scaleDiv;
};
struct ProgramHitBoxData {
int16 item;
int16 talk;
uint16 state;
int16 str;
int16 defaultStr;
int16 actions[8];
Common::Rect hitBoxes[2];
};
struct ProgramActionScriptOffsetData {
int16 object1;
int16 action;
int16 object2;
uint16 offset;
};
struct ProgramKeyCharScriptOffsetData {
int16 keyChar;
uint16 offset;
};
struct ProgramConversationData {
int16 num;
uint16 offset;
int16 msg;
};
enum {
kDebugEngine = 1,
kDebugGraphics,
kDebugResource,
kDebugOpcodes,
kDebugMenu,
kDebugCharset,
};
enum ResourceType {
kResourceTypeRoomImage = 0,
kResourceTypeSequence,
kResourceTypeSpriteImage,
kResourceTypeIconImage,
kResourceTypeRoomInfo,
kResourceTypeProgram,
kResourceTypeMusic,
kResourceTypeSound
};
enum TalkMode {
kTalkModeTextOnly = 0,
kTalkModeVoiceOnly,
kTalkModeVoiceAndText,
kTalkModeCount
};
enum ScriptFlag {
kScriptStopped = 1 << 0,
kScriptPaused = 1 << 1
};
enum SaveLoadMode {
kSaveGameState = 0,
kLoadGameState
};
enum InventoryArea {
kInventoryCharacter = 0,
kInventoryMoneyDisplay,
kInventoryGoldCoins,
kInventorySilverCoins,
kInventoryMoney,
kInventoryScroller1,
kInventoryObject1,
kInventoryObject2,
kInventoryObject3,
kInventoryObject4,
kInventoryObject5,
kInventoryObject6,
kInventoryScroller2
};
enum {
kScreenWidth = 640,
kScreenHeight = 400,
kRoomHeight = 352,
kStartupEpisode = 90,
// TODO: If the following truncation is intentional (it probably is) it should be clearly marked as such
kCycleDelay = 1000 / (1193180 / 32768),
kIconWidth = 58,
kIconHeight = 42,
kCursorWidth = 58,
kCursorHeight = 42,
kTextHeight = 16,
kMaxProgramDataSize = 61440,
kMaxSaveStates = 100
};
enum StringType {
kStringTypeDefault,
kStringTypeConversation
};
enum GameState {
kGameStateGameLoop,
kGameStateOptionsDialog,
kGameStateQuitDialog,
kGameStateNone
};
enum ActionId {
kActionNone,
// settings menu
kActionLoadMenu,
kActionSaveMenu,
kActionRestartGame,
kActionPlayGame,
kActionQuitGame,
kActionTextOnly,
kActionVoiceOnly,
kActionTextAndVoice,
kActionLowerVolume,
kActionUpperVolume,
// saveLoad menu
kActionGameState1,
kActionGameState2,
kActionGameState3,
kActionGameState4,
kActionGameState5,
kActionGameState6,
kActionGameState7,
kActionGameState8,
kActionGameState9,
kActionGameState10,
kActionScrollUpSaves,
kActionScrollDownSaves,
kActionPerformSaveLoad,
kActionCancelSaveLoad
};
enum MenuMode {
kMenuSettingsMode = 0,
kMenuLoadStateMode,
kMenuSaveStateMode
};
enum ButtonFlags {
kButtonBorder = 1 << 0,
kButtonText = 1 << 1,
kButtonArrow = 1 << 2
};
struct Button {
int x, y;
int w, h;
ActionId action;
int data;
uint8 flags;
};
struct MenuData {
MenuMode mode;
Button *buttonsTable;
uint buttonsCount;
bool quit;
bool exit;
char saveLoadDescriptionsTable[kMaxSaveStates][33];
void removeLastCharFromDescription(int slot) {
char *description = saveLoadDescriptionsTable[slot];
int descriptionLen = strlen(description);
if (descriptionLen > 0) {
--descriptionLen;
description[descriptionLen] = 0;
}
}
void addCharToDescription(int slot, char chr) {
char *description = saveLoadDescriptionsTable[slot];
int descriptionLen = strlen(description);
if (descriptionLen < 32 && Common::isPrint(chr)) {
description[descriptionLen] = chr;
description[descriptionLen + 1] = 0;
}
}
const Button *findButtonUnderCursor(int cursorX, int cursorY) const {
for (uint i = 0; i < buttonsCount; ++i) {
const Button *button = &buttonsTable[i];
if (cursorX >= button->x && cursorX < button->x + button->w &&
cursorY >= button->y && cursorY < button->y + button->h) {
return button;
}
}
return 0;
}
};
void readGameStateDescription(Common::ReadStream *f, char *description, int len);
Common::String generateGameStateFileName(const char *target, int slot, bool prefixOnly = false);
int getGameStateFileSlot(const char *filename);
class MidiPlayer;
class ToucheEngine: public Engine {
public:
enum {
NUM_FLAGS = 2000,
NUM_KEYCHARS = 32,
NUM_SPRITES = 7,
NUM_SEQUENCES = 7,
NUM_CONVERSATION_CHOICES = 40,
NUM_TALK_ENTRIES = 16,
NUM_ANIMATION_ENTRIES = 4,
NUM_INVENTORY_ITEMS = 100,
NUM_DIRTY_RECTS = 30,
NUM_DIRECTIONS = 135
};
typedef void (ToucheEngine::*OpcodeProc)();
ToucheEngine(OSystem *system, Common::Language language);
~ToucheEngine() override;
// Engine APIs
Common::Error run() override;
bool hasFeature(EngineFeature f) const override;
void syncSoundSettings() override;
protected:
void restart();
void readConfigurationSettings();
void writeConfigurationSettings();
void mainLoop();
void processEvents(bool handleKeyEvents = true);
void runCycle();
int16 getRandomNumber(int max);
void changePaletteRange();
void playSoundInRange();
void resetSortedKeyCharsTable();
void setupEpisode(int num);
void setupNewEpisode();
void drawKeyChar(KeyChar *key);
void sortKeyChars();
void runKeyCharScript(KeyChar *key);
void runCurrentKeyCharScript(int mode);
void executeScriptOpcode(int16 param);
void initKeyChars(int keyChar);
void setKeyCharTextColor(int keyChar, uint16 color);
void waitForKeyCharPosition(int keyChar);
void setKeyCharBox(int keyChar, int value);
void setKeyCharFrame(int keyChar, int16 type, int16 value1, int16 value2);
void setKeyCharFacingDirection(int keyChar, int16 dir);
void initKeyCharScript(int keyChar, int16 spriteNum, int16 seqDataIndex, int16 seqDataOffs);
uint16 findProgramKeyCharScriptOffset(int keyChar) const;
bool scrollRoom(int keyChar);
void drawIcon(int x, int y, int num);
void centerScreenToKeyChar(int keyChar);
void waitForKeyCharsSet();
void redrawRoom();
void fadePalette(int firstColor, int colorCount, int scale, int scaleInc, int fadingStepsCount);
void fadePaletteFromFlags();
void moveKeyChar(uint8 *dst, int dstPitch, KeyChar *key);
void changeKeyCharFrame(KeyChar *key, int keyChar);
void setKeyCharRandomFrame(KeyChar *key);
void setKeyCharMoney();
const char *getString(int num) const;
int getStringWidth(int num) const;
void drawString(uint16 color, int x, int y, int16 num, StringType strType = kStringTypeDefault);
void drawGameString(uint16 color, int x1, int y, const char *str);
int restartKeyCharScriptOnAction(int action, int obj1, int obj2);
void buildSpriteScalingTable(int z1, int z2);
void drawSpriteOnBackdrop(int num, int x, int y);
void updateTalkFrames(int keyChar);
void setKeyCharTalkingFrame(int keyChar);
void lockUnlockHitBox(int num, int lock);
void drawHitBoxes();
void showCursor(bool show);
void setCursor(int num);
void setDefaultCursor(int num);
void handleLeftMouseButtonClickOnInventory();
void handleRightMouseButtonClickOnInventory();
void handleMouseInput(int flag);
void handleMouseClickOnRoom(int flag);
void handleMouseClickOnInventory(int flag);
void scrollScreenToPos(int num);
void clearRoomArea();
void startNewMusic();
void startNewSound();
void updateSpeech();
int handleActionMenuUnderCursor(const int16 *actions, int offs, int y, int str);
void redrawBackground();
void addRoomArea(int num, int flag);
void updateRoomAreas(int num, int flags);
void setRoomAreaState(int num, uint16 state);
void findAndRedrawRoomRegion(int num);
void updateRoomRegions();
void redrawRoomRegion(int num, bool markForRedraw);
void initInventoryObjectsTable();
void initInventoryLists();
void setupInventoryAreas();
void drawInventory(int index, int flag);
void drawAmountOfMoneyInInventory();
void packInventoryItems(int index);
void appendItemToInventoryList(int index);
void addItemToInventory(int inventory, int16 item);
void removeItemFromInventory(int inventory, int16 item);
void resetTalkingVars();
int updateKeyCharTalk(int pauseFlag);
const char *formatTalkText(int *y, int *h, const char *text);
void addToTalkTable(int talkingKeyChar, int num, int otherKeyChar);
void removeFromTalkTable(int keyChar);
void addConversationChoice(int16 num);
void removeConversationChoice(int16 num);
void runConversationScript(uint16 offset);
void findConversationByNum(int16 num);
void clearConversationChoices();
void scrollDownConversationChoice();
void scrollUpConversationChoice();
void drawCharacterConversation();
void drawConversationString(int num, uint16 color);
void clearConversationArea();
void setupConversationScript(int num);
void handleConversation();
void buildWalkPointsList(int keyChar);
int findWalkDataNum(int pointNum1, int pointNum2);
void changeWalkPath(int num1, int num2, int16 val);
void adjustKeyCharPosToWalkBox(KeyChar *key, int moveType);
void lockWalkPath(int num1, int num2);
void unlockWalkPath(int num1, int num2);
void resetPointsData(int num);
bool sortPointsData(int num1, int num2);
void updateKeyCharWalkPath(KeyChar *key, int16 dx, int16 dy, int16 dz);
void markWalkPoints(int keyChar);
void buildWalkPath(int dstPosX, int dstPosY, int keyChar);
void addToAnimationTable(int num, int posNum, int keyChar, int delayCounter);
void copyAnimationImage(int dstX, int dstY, int w, int h, const uint8 *src, int srcX, int srcY, int fillColor);
void drawAnimationImage(AnimationEntry *anim);
void processAnimationTable();
void clearAnimationTable();
void addToDirtyRect(const Common::Rect &r);
void clearDirtyRects();
void setPalette(int firstColor, int colorCount, int redScale, int greenScale, int blueScale);
void updateScreenArea(int x, int y, int w, int h);
void updateEntireScreen();
void updateDirtyScreenAreas();
void updatePalette();
void saveGameStateData(Common::WriteStream *stream);
void loadGameStateData(Common::ReadStream *stream);
Common::Error saveGameState(int num, const Common::String &description, bool isAutosave = false) override;
Common::Error loadGameState(int num) override;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
Common::String getSaveStateName(int slot) const override {
return Common::String::format("%s.%d", _targetName.c_str(), slot);
}
void setupOpcodes();
void op_nop();
void op_jnz();
void op_jz();
void op_jmp();
void op_true();
void op_false();
void op_push();
void op_not();
void op_add();
void op_sub();
void op_mul();
void op_div();
void op_mod();
void op_and();
void op_or();
void op_neg();
void op_testGreater();
void op_testEquals();
void op_testLower();
void op_fetchScriptWord();
void op_testGreaterOrEquals();
void op_testLowerOrEquals();
void op_testNotEquals();
void op_endConversation();
void op_stopScript();
void op_getFlag();
void op_setFlag();
void op_fetchScriptByte();
void op_getKeyCharWalkBox();
void op_startSound();
void op_moveKeyCharToPos();
void op_loadRoom();
void op_updateRoom();
void op_startTalk();
void op_loadSprite();
void op_loadSequence();
void op_setKeyCharBox();
void op_initKeyCharScript();
void op_setKeyCharFrame();
void op_setKeyCharDirection();
void op_clearConversationChoices();
void op_addConversationChoice();
void op_removeConversationChoice();
void op_getInventoryItem();
void op_setInventoryItem();
void op_startEpisode();
void op_setConversationNum();
void op_enableInput();
void op_disableInput();
void op_faceKeyChar();
void op_getKeyCharCurrentAnim();
void op_getCurrentKeyChar();
void op_isKeyCharActive();
void op_setPalette();
void op_changeWalkPath();
void op_lockWalkPath();
void op_initializeKeyChar();
void op_setupWaitingKeyChars();
void op_updateRoomAreas();
void op_unlockWalkPath();
void op_addItemToInventoryAndRedraw();
void op_giveItemTo();
void op_setHitBoxText();
void op_fadePalette();
void op_getInventoryItemFlags();
void op_drawInventory();
void op_stopKeyCharScript();
void op_restartKeyCharScript();
void op_getKeyCharCurrentWalkBox();
void op_getKeyCharPointsDataNum();
void op_setupFollowingKeyChar();
void op_startAnimation();
void op_setKeyCharTextColor();
void op_startMusic();
void op_sleep();
void op_setKeyCharDelay();
void op_lockHitBox();
void op_removeItemFromInventory();
void op_unlockHitBox();
void op_addRoomArea();
void op_setKeyCharFlags();
void op_unsetKeyCharFlags();
void op_loadSpeechSegment();
void op_drawSpriteOnBackdrop();
void op_startPaletteFadeIn();
void op_startPaletteFadeOut();
void op_setRoomAreaState();
void res_openDataFile();
void res_closeDataFile();
void res_allocateTables();
void res_deallocateTables();
uint32 res_getDataOffset(ResourceType type, int num, uint32 *size = NULL);
void res_loadSpriteImage(int num, uint8 *dst);
void res_loadProgram(int num);
void res_decodeProgramData();
void res_loadRoom(int num);
void res_loadSprite(int num, int index);
void res_loadSequence(int num, int index);
void res_decodeScanLineImageRLE(uint8 *dst, int lineWidth);
void res_loadBackdrop();
void res_loadImage(int num, uint8 *dst);
void res_loadImageHelper(uint8 *imgData, int imgWidth, int imgHeight);
void res_loadSound(int flag, int num);
void res_stopSound();
void res_loadMusic(int num);
void res_loadSpeech(int num);
void res_loadSpeechSegment(int num);
void res_stopSpeech();
void drawButton(Button *button);
void redrawMenu(MenuData *menu);
void handleMenuAction(MenuData *menu, int actionId);
void handleOptions(int forceDisplay);
void drawActionsPanel(int dstX, int dstY, int deltaX, int deltaY);
void drawConversationPanelBorder(int dstY, int srcX, int srcY);
void drawConversationPanel();
void printStatusString(const char *str);
void clearStatusString();
int displayQuitDialog();
void displayTextMode(int str);
Common::Point getMousePos() const;
MidiPlayer *_midiPlayer;
int _musicVolume;
Audio::SoundHandle _musicHandle;
void initMusic();
public: // To allow access from console
void startMusic(int num);
void stopMusic();
protected:
int getMusicVolume();
void setMusicVolume(int volume);
void adjustMusicVolume(int diff);
Common::Language _language;
Common::RandomSource _rnd;
bool _inp_leftMouseButtonPressed;
bool _inp_rightMouseButtonPressed;
int _disabledInputCounter;
bool _hideInventoryTexts;
GameState _gameState;
bool _displayQuitDialog;
int _saveLoadCurrentPage;
int _saveLoadCurrentSlot;
int _newMusicNum;
int _currentMusicNum;
int _newSoundNum;
int _newSoundDelay;
int _newSoundPriority;
int _playSoundCounter;
bool _speechPlaying;
Audio::SoundHandle _sfxHandle;
Audio::SoundHandle _speechHandle;
int16 _inventoryList1[101];
int16 _inventoryList2[101];
int16 _inventoryList3[7];
InventoryState _inventoryStateTable[3];
int16 _inventoryItemsInfoTable[NUM_INVENTORY_ITEMS];
int16 *_inventoryVar1;
int16 *_inventoryVar2;
int _currentCursorObject;
Common::Rect _inventoryAreasTable[13];
int _talkTextMode;
int _talkListEnd;
int _talkListCurrent;
bool _talkTextRectDefined;
bool _talkTextDisplayed;
bool _talkTextInitialized;
bool _skipTalkText;
int _talkTextSpeed;
int _keyCharTalkCounter;
int _talkTableLastTalkingKeyChar;
int _talkTableLastOtherKeyChar;
int _talkTableLastStringNum;
int _objectDescriptionNum;
TalkEntry _talkTable[NUM_TALK_ENTRIES];
bool _conversationChoicesUpdated;
int _conversationReplyNum;
bool _conversationEnded;
int _conversationNum;
int _scrollConversationChoiceOffset;
int _currentConversation;
bool _disableConversationScript;
bool _conversationAreaCleared;
ConversationChoice _conversationChoicesTable[NUM_CONVERSATION_CHOICES];
int16 _flagsTable[NUM_FLAGS];
KeyChar _keyCharsTable[NUM_KEYCHARS];
KeyChar *_sortedKeyCharsTable[NUM_KEYCHARS];
int _currentKeyCharNum;
int _newEpisodeNum;
int _currentEpisodeNum;
int _currentAmountOfMoney;
int _giveItemToKeyCharNum;
int _giveItemToObjectNum;
int _giveItemToCounter;
int _currentRoomNum;
int _waitingSetKeyCharNum1;
int _waitingSetKeyCharNum2;
int _waitingSetKeyCharNum3;
uint8 _updatedRoomAreasTable[200];
Common::Rect _moveKeyCharRect;
Common::Point _screenOffset;
int _currentObjectNum;
int _processRandomPaletteCounter;
int16 _spriteScalingIndex[1000];
int16 _spriteScalingTable[1000];
bool _fastWalkMode;
bool _fastMode;
AnimationEntry _animationTable[NUM_ANIMATION_ENTRIES];
Script _script;
const OpcodeProc *_opcodesTable;
int _numOpcodes;
Common::File _fData;
Common::File _fSpeech[2];
int _compressedSpeechData;
uint8 *_textData;
uint8 *_backdropBuffer;
uint8 *_menuKitData;
uint8 *_convKitData;
uint8 *_sequenceDataTable[NUM_SEQUENCES];
uint8 *_programData;
uint32 _programDataSize;
uint8 *_mouseData;
uint8 *_iconData;
SpriteData _spritesTable[NUM_SPRITES];
SequenceEntry _sequenceEntryTable[NUM_SEQUENCES];
int _currentBitmapWidth;
int _currentBitmapHeight;
int _currentImageWidth;
int _currentImageHeight;
int _roomWidth;
uint8 *_programTextDataPtr;
Common::Array<Common::Rect> _programRectsTable;
Common::Array<ProgramPointData> _programPointsTable;
Common::Array<ProgramWalkData> _programWalkTable;
Common::Array<ProgramAreaData> _programAreaTable;
Common::Array<ProgramBackgroundData> _programBackgroundTable;
Common::Array<ProgramHitBoxData> _programHitBoxTable;
Common::Array<ProgramActionScriptOffsetData> _programActionScriptOffsetTable;
Common::Array<ProgramKeyCharScriptOffsetData> _programKeyCharScriptOffsetTable;
Common::Array<ProgramConversationData> _programConversationTable;
Common::Rect _cursorObjectRect;
Common::Rect _talkTextRect, _talkTextRect2;
Common::Rect _screenRect;
Common::Rect _roomAreaRect;
bool _roomNeedRedraw;
int _fullRedrawCounter;
int _menuRedrawCounter;
uint8 *_offscreenBuffer;
uint8 _paletteBuffer[256 * 3];
Common::Rect _dirtyRectsTable[NUM_DIRTY_RECTS];
int _dirtyRectsTableCount;
static const uint8 _directionsTable[NUM_DIRECTIONS];
};
/*
FLAGS LIST
115 : don't set backdrop palette on room loading
118 : current amount of money
119 : current cursor object
176 : keychar max direction
266 : keychar direction override
267 : don't decode picture/sprite images (in load_image_helper)
268 : don't decode picture/sprite images
269 : disable room background animations
270 : play random sound
290 : process random palette
295 : game cycle counter (incremented)
296 : game cycle counter (incremented)
297 : game cycle counter (incremented)
298 : game cycle counter (decremented)
299 : game cycle counter (decremented)
600 : last ascii key press
603 : fade palette "scale" increment (in vbl handler)
605 : fade palette "scale"
606 : inventory redraw disabled
607 : first palette color to fade
608 : last palette color to fade
609 : max fade palette "scale"
610 : min fade palette "scale"
611 : quit game
612 : random number modulo
613 : last generated random number
614 : room scroll x offset
615 : room scroll y offset
616 : disable room scrolling
617 : current speech file number
618 : hide mouse cursor
621 : enable french version "features"
902 : debug/draw walk boxes
911 : load scripts/programs from external files
*/
} // namespace Touche
#endif