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/tony/POTFILES Normal file
View File

@@ -0,0 +1 @@
engines/tony/tony.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 tony "Tony Tough and the Night of Roasted Moths" yes "" "" "16bit highres"

5
engines/tony/credits.pl Normal file
View File

@@ -0,0 +1,5 @@
begin_section("Tony");
add_person("Arnaud Boutonné", "Strangerke", "");
add_person("Paul Gilbert", "dreammaster", "");
add_person("Alyssa Milburn", "fuzzie", "");
end_section();

2505
engines/tony/custom.cpp Normal file

File diff suppressed because it is too large Load Diff

84
engines/tony/custom.h Normal file
View File

@@ -0,0 +1,84 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_CUSTOM_H
#define TONY_CUSTOM_H
#include "common/str.h"
#include "tony/mpal/mpal.h"
namespace Tony {
using namespace MPAL;
struct MusicFileEntry {
const char *_name;
int _sync;
};
#define INIT_CUSTOM_FUNCTION MapCustomFunctions
#define BEGIN_CUSTOM_FUNCTION_MAP() \
static void AssignError(int num) { \
error("Custom function %u has been already assigned!", num); \
} \
void INIT_CUSTOM_FUNCTION(LPCUSTOMFUNCTION *lpMap, Common::String *lpStrMap) \
{
#define END_CUSTOM_FUNCTION_MAP() \
}
#define ASSIGN(num, func) \
if (lpMap[num] != NULL) \
AssignError(num); \
lpMap[num] = func; \
lpStrMap[num] = #func;
class RMTony;
class RMPointer;
class RMGameBoxes;
class RMLocation;
class RMInventory;
class RMInput;
void charsSaveAll(Common::OutSaveFile *f);
void charsLoadAll(Common::InSaveFile *f);
void mCharResetCodes();
void saveChangedHotspot(Common::OutSaveFile *f);
void loadChangedHotspot(Common::InSaveFile *f);
void reapplyChangedHotspot();
void restoreMusic(CORO_PARAM);
void saveMusic(Common::OutSaveFile *f);
void loadMusic(Common::InSaveFile *f);
void INIT_CUSTOM_FUNCTION(LPCUSTOMFUNCTION *lpMap, Common::String *lpStrMap);
void setupGlobalVars(RMTony *tony, RMPointer *ptr, RMGameBoxes *box, RMLocation *loc, RMInventory *inv, RMInput *input);
#endif
} // end of namespace Tony

128
engines/tony/debugger.cpp Normal file
View File

@@ -0,0 +1,128 @@
/* 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/coroutines.h"
#include "tony/debugger.h"
#include "tony/globals.h"
#include "tony/tony.h"
namespace Tony {
Debugger::Debugger() : GUI::Debugger() {
registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
registerCmd("scene", WRAP_METHOD(Debugger, Cmd_Scene));
registerCmd("dirty_rects", WRAP_METHOD(Debugger, Cmd_DirtyRects));
}
static int strToInt(const char *s) {
if (!*s)
// No string at all
return 0;
else if (toupper(s[strlen(s) - 1]) != 'H')
// Standard decimal string
return atoi(s);
// Hexadecimal string
uint tmp = 0;
int read = sscanf(s, "%xh", &tmp);
if (read < 1)
error("strToInt failed on string \"%s\"", s);
return (int)tmp;
}
/**
* Support process for changing the scene
*/
struct ChangeSceneDetails {
int sceneNumber;
int x;
int y;
};
void DebugChangeScene(CORO_PARAM, const void *param) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
uint32 result;
const ChangeSceneDetails *details = (const ChangeSceneDetails *)param;
RMPoint scenePos(details->x, details->y);
CORO_BEGIN_CODE(_ctx);
CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, false, &result);
g_vm->getEngine()->loadLocation(details->sceneNumber, scenePos, RMPoint(-1, -1));
mainEnableGUI();
CORO_END_CODE;
}
/**
* This command loads up the specified new scene number
*/
bool Debugger::Cmd_Scene(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Usage: %s <scene number> [<x> <y>]\n", argv[0]);
return true;
}
int sceneNumber = strToInt(argv[1]);
if (sceneNumber >= g_vm->_theBoxes.getLocBoxesCount()) {
debugPrintf("Invalid scene\n");
return true;
}
RMPoint scenePos;
if (argc >= 4) {
scenePos._x = strToInt(argv[2]);
scenePos._y = strToInt(argv[3]);
} else {
// Get the box areas for the scene, and choose one so as to have a default
// position for Tony that will be in the walkable areas
RMBoxLoc *box = g_vm->_theBoxes.getBoxes(sceneNumber);
scenePos.set(box->_boxes[0]._hotspot[0]._hotx, box->_boxes[0]._hotspot[0]._hoty);
}
// Set up a process to change the scene
ChangeSceneDetails details;
details.sceneNumber = sceneNumber;
details.x = scenePos._x;
details.y = scenePos._y;
CoroScheduler.createProcess(DebugChangeScene, &details, sizeof(ChangeSceneDetails));
return false;
}
/**
* Turns showing dirty rects on or off
*/
bool Debugger::Cmd_DirtyRects(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage; %s [on | off]\n", argv[0]);
return true;
} else {
g_vm->_window.showDirtyRects(strcmp(argv[1], "on") == 0);
return false;
}
}
} // End of namespace Tony

42
engines/tony/debugger.h Normal file
View File

@@ -0,0 +1,42 @@
/* 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 TONY_DEBUGGER_H
#define TONY_DEBUGGER_H
#include "common/scummsys.h"
#include "gui/debugger.h"
namespace Tony {
class Debugger : public GUI::Debugger {
public:
Debugger();
~Debugger() override {}
protected:
bool Cmd_Scene(int argc, const char **argv);
bool Cmd_DirtyRects(int argc, const char **argv);
};
} // End of namespace Tony
#endif

View File

@@ -0,0 +1,74 @@
/* 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 "base/plugins.h"
#include "engines/advancedDetector.h"
#include "tony/detection.h"
#include "tony/tony.h"
static const PlainGameDescriptor tonyGames[] = {
{"tony", "Tony Tough and the Night of Roasted Moths"},
{0, 0}
};
static const DebugChannelDef debugFlagList[] = {
{Tony::kTonyDebugAnimations, "animations", "Animations debugging"},
{Tony::kTonyDebugActions, "actions", "Actions debugging"},
{Tony::kTonyDebugSound, "sound", "Sound debugging"},
{Tony::kTonyDebugMusic, "music", "Music debugging"},
DEBUG_CHANNEL_END
};
#include "tony/detection_tables.h"
static const char *const directoryGlobs[] = {
"roasted",
"voices",
0
};
class TonyMetaEngineDetection : public AdvancedMetaEngineDetection<Tony::TonyGameDescription> {
public:
TonyMetaEngineDetection() : AdvancedMetaEngineDetection(Tony::gameDescriptions, tonyGames) {
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
const char *getName() const override {
return "tony";
}
const char *getEngineName() const override {
return "Tony Tough and the Night of Roasted Moths";
}
const char *getOriginalCopyright() const override {
return "Tony Tough and the Night of Roasted Moths (C) Protonic Interactive";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
REGISTER_PLUGIN_STATIC(TONY_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, TonyMetaEngineDetection);

39
engines/tony/detection.h Normal file
View File

@@ -0,0 +1,39 @@
/* 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 TONY_DETECTION_H
#define TONY_DETECTION_H
namespace Tony {
enum {
GF_COMPRESSED = (1 << 0)
};
struct TonyGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
};
} // End of namespace Tony
#endif // TONY_DETECTION_H

View File

@@ -0,0 +1,279 @@
/* 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/>.
*
*/
namespace Tony {
static const TonyGameDescription gameDescriptions[] = {
{
// Tony Tough English
{
"tony",
0,
{
{ "roasted.mpc", 0, "57c4a3860cf899443c357e0078ea6f49", 366773 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071 },
{ "voices.vdb", 0, "aebc6eb607ee19cc94bfe9c11898bb8c", 243003502 },
AD_LISTEND
},
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
{
// Tony Tough Czech
{
"tony",
0,
{
{ "roasted.mpc", 0, "a8283a101878f3ca105f1f83f07e2c40", 386491 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071 },
{ "voices.vdb", 0, "3384bdcb70d1e1ecedbde26e79683ede", 299019523 },
AD_LISTEND
},
Common::CS_CZE,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
{
// Tony Tough French "Collection Aventure" provided by Strangerke
{
"tony",
0,
{
{ "roasted.mpc", 0, "e890c6a41238827bdfa9874a65618b69", 374135 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071 },
{ "voices.vdb", 0, "aebc6eb607ee19cc94bfe9c11898bb8c", 243003502 },
AD_LISTEND
},
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
{
// Tony Tough Spanish provided by Pakolmo
{
"tony",
0,
{
{ "roasted.mpc", 0, "bcca7985db2fba9c1c4a0886618ec835", 515967 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071 },
{ "voices.vdb", 0, "aebc6eb607ee19cc94bfe9c11898bb8c", 243003502 },
AD_LISTEND
},
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
{
// Tony Tough German "Shoe Box" provided by Strangerke
{
"tony",
0,
{
{ "roasted.mpc", 0, "ccf7ab939a34de1b13df538596431684", 389554 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071 },
{ "voices.vdb", 0, "6a3c2f5426ab762bf4dc9826796aa320", 279745055 },
AD_LISTEND
},
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
{
// Tony Tough German "Gamestar" provided in bug #6138
{
"tony",
0,
{
{ "roasted.mpc", 0, "187de6f88f4083808cb66342ab55a7fd", 389904 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071 },
{ "voices.vdb", 0, 0, AD_NO_SIZE }, // FIXME
AD_LISTEND
},
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
{
// Tony Tough Italian provided by Fabio Barzagli
{
"tony",
0,
{
{ "roasted.mpc", 0, "1dc896cdb945170d7408598f803411c1", 380001 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071 },
{ "voices.vdb", 0, "f9f1ac8f63a909bb3ed972490dae65c4", 286130226 },
AD_LISTEND
},
Common::IT_ITA,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
{
// Tony Tough Italian provided by Giovanni Bajo
{
"tony",
0,
{
{ "roasted.mpc", 0, "6202816f991b15af82aab84e3e4be011", 380183 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071 },
{ "voices.vdb", 0, "f9f1ac8f63a909bb3ed972490dae65c4", 286130226 },
AD_LISTEND
},
Common::IT_ITA,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
{
// Tony Tough Polish provided by iGom bug #11546
{
"tony",
0,
{
{ "roasted.mpc", 0, "c212a81e34edf92bc177a80f24780bd2", 380200 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071 },
{ "voices.vdb", 0, "544b1db3a41b0f89567267d0664183bb", 321349288 },
AD_LISTEND
},
Common::PL_POL,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
{
// Tony Tough Polish provided by Fabio Barzagli
{
"tony",
0,
{
{ "roasted.mpc", 0, "89733ea710669acc8e7900b115f4afef", 389625 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071 },
{ "voices.vdb", 0, "544b1db3a41b0f89567267d0664183bb", 310906270 },
AD_LISTEND
},
Common::PL_POL,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
{
// Tony Tough Russian, reported in bug #6589
{
"tony",
0,
{
{ "roasted.mpc", 0, "377d6e24adeedc6c5c09c31b92231218", 391536 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071 },
{ "voices.vdb", 0, "af4061f49b934086710f0d41e6250a15", 325225827 },
AD_LISTEND
},
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
{
// Tony Tough English Demo
{
"tony",
"Extracted Demo",
{
{ "roasted.mpc", 0, "1e247922ec869712bfd96625bc4d3c7c", 39211 },
{ "roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 14972409 },
{ "voices.vdb", 0, "f9f1ac8f63a909bb3ed972490dae65c4", 20189260 },
AD_LISTEND
},
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DEMO | ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
#if 0
// Disabling this detection entry. The package contains _only_ standard list
// of InnoSetup files:
//
// DATA.TAG SETUP.INI _ISDEL.EXE _sys1.cab data1.cab layout.bin
// setup.bmp setup.lid SETUP.EXE _INST32I.EX_ _setup.dll _user1.cab
// lang.dat os.dat setup.ins
//
// As a result, adding this entry will lead to a false "unknown variant for tony-demo"
// For practically all games which use InnoSetup.
//
// The current AdvancedDetector code always will show the end user the detected
// game AND this entry, which is very annoying
//
// The only potential solution is to make AD work with the InnoSetup archives,
// that is, make it look inside of the archive and if it contains the matching
// file names, only then report it as an unknown variant.
{
// Tony Tough English Demo (Compressed)
{
"tony",
"Demo",
{
{ "data1.cab", 0, "7d8b6d308f96aee3968ad7910fb11e6d", 58660608 },
{ "data.tag", 0, "e9af151040745e83081e691356abeed7", 137 },
AD_LISTEND
},
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DEMO | GF_COMPRESSED | ADGF_DROPPLATFORM,
GUIO1(GUIO_NOMIDI)
},
},
#endif
{ AD_TABLE_END_MARKER }
};
} // End of namespace Tony

1175
engines/tony/font.cpp Normal file

File diff suppressed because it is too large Load Diff

374
engines/tony/font.h Normal file
View File

@@ -0,0 +1,374 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_FONT_H
#define TONY_FONT_H
#include "common/system.h"
#include "common/coroutines.h"
#include "tony/gfxcore.h"
#include "tony/resid.h"
namespace Tony {
class RMInput;
class RMInventory;
class RMItem;
class RMLoc;
class RMLocation;
class RMPointer;
/**
* Manages a font, in which there is a different surface for each letter
*/
class RMFont : public RMGfxTaskSetPrior {
protected:
int _nLetters;
RMGfxSourceBuffer8RLEByte *_letter;
public:
int _fontDimx, _fontDimy;
private:
int _dimx, _dimy;
class RMFontPrimitive : public RMGfxPrimitive {
public:
RMFontPrimitive() : RMGfxPrimitive() { _nChar = 0; }
RMFontPrimitive(RMGfxTask *task) : RMGfxPrimitive(task) { _nChar = 0; }
~RMFontPrimitive() override { }
RMGfxPrimitive *duplicate() override {
return new RMFontPrimitive(*this);
}
int _nChar;
};
protected:
// Loads the font
void load(uint32 resID, int nChars, int dimx, int dimy, uint32 palResID = RES_F_PAL);
void load(const byte *buf, int nChars, int dimx, int dimy, uint32 palResID = RES_F_PAL);
// Remove the font
void unload();
protected:
// Conversion form character to font index
virtual int convertToLetter(byte nChar) = 0;
// Character width
virtual int letterLength(int nChar, int nNext = 0) = 0;
public:
virtual int letterHeight() = 0;
public:
RMFont();
~RMFont() override;
// Initialization and closing
virtual void init() = 0;
virtual void close();
// Drawing
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBug, RMGfxPrimitive *prim) override;
// Create a primitive for a letter
RMGfxPrimitive *makeLetterPrimitive(byte bChar, int &nLength);
// Length in pixels of a string with the current font
int stringLen(const Common::String &text);
int stringLen(char bChar, char bNext = 0);
};
class RMFontColor : public virtual RMFont {
private:
byte _fontR, _fontG, _fontB;
public:
RMFontColor();
~RMFontColor() override;
virtual void setBaseColor(byte r, byte g, byte b);
};
class RMFontWithTables : public virtual RMFont {
protected:
int _cTable[256];
int _lTable[256];
int _lDefault;
int _hDefault;
signed char _l2Table[256][256];
protected:
// Overloaded methods
int convertToLetter(byte nChar) override;
int letterLength(int nChar, int nNext = 0) override;
public:
int letterHeight() override {
return _hDefault;
}
~RMFontWithTables() override {}
};
class RMFontDialog : public RMFontColor, public RMFontWithTables {
public:
void init() override;
~RMFontDialog() override {}
};
class RMFontObj : public RMFontColor, public RMFontWithTables {
private:
void setBothCase(int nChar, int nNext, signed char spiazz);
public:
void init() override;
~RMFontObj() override {}
};
class RMFontMacc : public RMFontColor, public RMFontWithTables {
public:
void init() override;
~RMFontMacc() override {}
};
class RMFontCredits : public RMFontColor, public RMFontWithTables {
public:
void init() override;
~RMFontCredits() override {}
void setBaseColor(byte r, byte g, byte b) override {}
};
/**
* Manages writing text onto9 the screen
*/
class RMText : public RMGfxWoodyBuffer {
private:
static RMFontColor *_fonts[4];
int _maxLineLength;
public:
enum HorAlign {
HLEFT,
HLEFTPAR,
HCENTER,
HRIGHT
};
enum VerAlign {
VTOP,
VCENTER,
VBOTTOM
};
private:
HorAlign _aHorType;
VerAlign _aVerType;
byte _textR, _textG, _textB;
protected:
virtual void clipOnScreen(RMGfxPrimitive *prim);
public:
RMText();
~RMText() override;
static void initStatics();
static void unload();
// Set the alignment type
void setAlignType(HorAlign aHor, VerAlign aVer);
// Sets the maximum length of a line in pixels (used to format the text)
void setMaxLineLength(int max);
// Write the text
void writeText(const Common::String &text, int font, int *time = NULL);
void writeText(Common::String text, RMFontColor *font, int *time = NULL);
// Overloaded function to decide when you delete the object from the OT list
void removeThis(CORO_PARAM, bool &result) override;
// Overloading of the Draw to center the text, if necessary
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
// Set the base color
void setColor(byte r, byte g, byte b);
};
/**
* Manages text in a dialog
*/
class RMTextDialog : public RMText {
protected:
int _startTime;
int _time;
bool _bSkipStatus;
RMPoint _dst;
uint32 _hEndDisplay;
bool _bShowed;
bool _bForceTime;
bool _bForceNoTime;
uint32 _hCustomSkip;
uint32 _hCustomSkip2;
RMInput *_input;
bool _bAlwaysDisplay;
bool _bNoTab;
public:
RMTextDialog();
~RMTextDialog() override;
// Write the text
void writeText(const Common::String &text, int font, int *time = NULL);
void writeText(const Common::String &text, RMFontColor *font, int *time = NULL);
// Overloaded function to decide when you delete the object from the OT list
void removeThis(CORO_PARAM, bool &result) override;
// Overloaded de-registration
void unregister() override;
// Overloading of the Draw to center the text, if necessary
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
// Set the position
void setPosition(const RMPoint &pt);
// Waiting
void waitForEndDisplay(CORO_PARAM);
void setCustomSkipHandle(uint32 hCustomSkip);
void setCustomSkipHandle2(uint32 hCustomSkip);
void setSkipStatus(bool bEnabled);
void setForcedTime(uint32 dwTime);
void setNoTab();
void forceTime();
void forceNoTime();
void setAlwaysDisplay();
// Set the input device, to allow skip from mouse
void setInput(RMInput *input);
void show();
void hide(CORO_PARAM);
};
class RMTextDialogScrolling : public RMTextDialog {
protected:
RMLocation *_curLoc;
RMPoint _startScroll;
void clipOnScreen(RMGfxPrimitive *prim) override;
public:
RMTextDialogScrolling();
RMTextDialogScrolling(RMLocation *loc);
~RMTextDialogScrolling() override;
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
};
/**
* Manages the name of a selected item on the screen
*/
class RMTextItemName : protected RMText {
protected:
RMPoint _mpos;
RMPoint _curscroll;
RMItem *_item;
public:
RMTextItemName();
~RMTextItemName() override;
void setMouseCoord(const RMPoint &m);
void doFrame(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMLocation &loc, RMPointer &ptr, RMInventory &inv);
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
RMPoint getHotspot();
RMItem *getSelectedItem();
bool isItemSelected();
void removeThis(CORO_PARAM, bool &result) override;
};
/**
* Manages the selection of screen items in a box
*/
class RMDialogChoice : public RMGfxWoodyBuffer {
private:
int _curSelection;
int _numChoices;
RMText *_drawedStrings;
RMPoint *_ptDrawStrings;
int _curAdded;
bool _bShow;
RMGfxSourceBuffer8 _dlgText;
RMGfxSourceBuffer8 _dlgTextLine;
RMPoint _ptDrawPos;
uint32 _hUnreg;
bool _bRemoveFromOT;
protected:
void prepare(CORO_PARAM);
void setSelected(CORO_PARAM, int pos);
public:
void removeThis(CORO_PARAM, bool &result) override;
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
void unregister() override;
public:
// Initialization
RMDialogChoice();
~RMDialogChoice() override;
// Initialization and closure
using RMGfxWoodyBuffer::init;
void init();
void close();
// Sets the number of possible sentences, which then be added with AddChoice()
void setNumChoices(int num);
// Adds a string with the choice
void addChoice(const Common::String &string);
// Show and hide the selection, with possible animations.
// NOTE: If no parameter is passed to Show(), it is the obligation of
// caller to ensure that the class is inserted into OT list
void show(CORO_PARAM, RMGfxTargetBuffer *bigBuf = NULL);
void hide(CORO_PARAM);
// Polling Update
void doFrame(CORO_PARAM, RMPoint ptMousePos);
// Returns the currently selected item, or -1 if none is selected
int getSelection();
};
} // End of namespace Tony
#endif

1609
engines/tony/game.cpp Normal file

File diff suppressed because it is too large Load Diff

339
engines/tony/game.h Normal file
View File

@@ -0,0 +1,339 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_GAME_H
#define TONY_GAME_H
#include "tony/gfxcore.h"
#include "tony/input.h"
#include "tony/loc.h"
#include "tony/utils.h"
namespace Tony {
#define INIT_GFX16_FROMRAW(dwRes, buf16) \
raw = new RMResRaw(dwRes); \
assert(raw->isValid()); \
assert((buf16) == NULL); \
(buf16) = new RMGfxSourceBuffer16(false); \
(buf16)->init(*raw, raw->width(), raw->height()); \
delete raw;
#define INIT_GFX8_FROMRAW(raw, dwRes, buf8) \
raw = new RMResRaw(dwRes); \
assert(raw->isValid()); \
assert((buf8) == NULL); \
(buf8) = new RMGfxSourceBuffer8RLEByte(); \
(buf8)->init(*raw, raw->width(), raw->height(), true); \
delete raw;
// X & Y dimensions of the adventure
#define RM_SX 640
#define RM_SY 480
// X & Y dimensions of bigbuf
#define RM_BBX (RM_SX)
#define RM_BBY (RM_SY)
// Skipping X & Y
#define RM_SKIPY ((RM_BBY - RM_SY) / 2)
#define RM_SKIPX 0
// Tony's actions
enum RMTonyAction {
TA_GOTO = 0,
TA_TAKE,
TA_USE,
TA_EXAMINE,
TA_TALK,
TA_PERORATE,
TA_COMBINE = 10,
TA_RECEIVECOMBINE,
TA_COMBINEGIVE,
TA_RECEIVECOMBINEGIVE
};
// Global Functions
void mainEnableGUI();
void mainDisableGUI();
// Classes
class RMPointer {
public:
enum PointerType {
PTR_NONE = 0,
PTR_ARROWUP,
PTR_ARROWDOWN,
PTR_ARROWLEFT,
PTR_ARROWRIGHT,
PTR_ARROWMAP,
PTR_CUSTOM
};
private:
RMGfxSourceBuffer8 *_pointer[16];
RMPoint _hotspot[16];
RMPoint _cursorHotspot;
RMItem *_specialPointer[16];
int _nCurPointer;
int _nCurSpecialPointer;
RMGfxSourceBuffer8 *_nCurCustomPointer;
public:
/**
* Constructor & destructor
*/
RMPointer();
virtual ~RMPointer();
/**
* Initialization
*/
void init();
/**
* Deinitialization
*/
void close();
/**
* Process a frame
*/
void doFrame();
/**
* draw method
*/
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
/**
* Sets a new action as current
*/
void setAction(RMTonyAction action);
/**
* Sets a new pointer
*/
void setSpecialPointer(PointerType ptr);
PointerType getSpecialPointer();
/**
* Set the new custom pointer
*/
void setCustomPointer(RMGfxSourceBuffer8 *ptr);
/**
* Return the current action to be applied according to the pointer
*/
int curAction();
/**
* Update the cursor
*/
void updateCursor();
/**
* Show the cursor
*/
void showCursor();
/**
* Hide the cursor
*/
void hideCursor();
};
class RMOptionButton: public RMGfxTaskSetPrior {
public:
RMRect _rect;
RMGfxSourceBuffer16 *_buf;
bool _bActive;
bool _bHasGfx;
bool _bDoubleState;
public:
RMOptionButton(uint32 dwRes, RMPoint pt, bool bDoubleState = false);
RMOptionButton(const RMRect &pt);
~RMOptionButton() override;
bool doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick);
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
void addToList(RMGfxTargetBuffer &bigBuf);
bool isActive();
void setActiveState(bool bState);
};
class RMOptionSlide : public RMGfxTaskSetPrior {
private:
RMOptionButton *_pushLeft;
RMOptionButton *_pushRight;
RMGfxSourceBuffer16 *_sliderCenter;
RMGfxSourceBuffer16 *_sliderLeft;
RMGfxSourceBuffer16 *_sliderRight;
RMGfxSourceBuffer16 *_sliderSingle;
int _nSlideSize;
RMPoint _pos;
int _nValue;
int _nMax;
int _nStep;
public:
RMOptionSlide(const RMPoint &pt, int m_nRange = 100, int m_nStartValue = 0, int slideSize = 300);
~RMOptionSlide() override;
bool doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick);
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
void addToList(RMGfxTargetBuffer &bigBuf);
int getValue();
};
class RMOptionScreen : public RMGfxWoodyBuffer {
private:
RMGfxSourceBuffer16 *_menu;
RMGfxSourceBuffer16 *_quitConfirm;
RMGfxSourceBuffer16 *_hideLoadSave;
RMOptionButton *_buttonQuitYes;
RMOptionButton *_buttonQuitNo;
RMOptionButton *_buttonExit;
RMOptionButton *_buttonQuit;
RMOptionButton *_buttonLoad;
RMOptionButton *_buttonSave;
RMOptionButton *_buttonGameMenu;
RMOptionButton *_buttonGfxMenu;
RMOptionButton *_buttonSoundMenu;
RMGfxSourceBuffer8 *_saveEasy;
RMGfxSourceBuffer8 *_saveHard;
RMGfxSourceBuffer16 *_curThumb[6];
Common::String _curThumbName[6];
byte _curThumbDiff[6];
RMOptionButton *_buttonSave_States[6];
RMOptionButton *_buttonSave_ArrowLeft;
RMOptionButton *_buttonSave_ArrowRight;
RMOptionButton *_buttonGfx_Tips;
RMOptionButton *_buttonSound_DubbingOn;
RMOptionButton *_buttonSound_MusicOn;
RMOptionButton *_buttonSound_SFXOn;
RMOptionSlide *_slideTonySpeed;
RMOptionSlide *_slideTextSpeed;
int _statePos;
bool _bEditSaveName;
int _nEditPos;
char _editName[256];
union {
RMOptionButton *_buttonGame_Lock;
RMOptionButton *_buttonGfx_Anni30;
RMOptionSlide *_sliderSound_Music;
};
union {
RMOptionButton *_buttonGame_TimerizedText;
RMOptionButton *_buttonGfx_AntiAlias;
RMOptionSlide *_sliderSound_SFX;
};
union {
RMOptionButton *_buttonGame_Scrolling;
RMOptionButton *_buttonGfx_Sottotitoli;
RMOptionSlide *_sliderSound_Dubbing;
};
union {
RMOptionButton *_buttonGame_InterUp;
RMOptionButton *_buttonGfx_Trans;
};
int _fadeStep;
bool _bExit;
bool _bQuitConfirm;
int _fadeY;
int _fadeTime;
bool _bLoadMenuOnly;
bool _bNoLoadSave;
bool _bAlterGfx;
enum OptionScreenState {
MENUGAME,
MENUGFX,
MENUSOUND,
MENULOAD,
MENUSAVE,
MENUNONE
};
OptionScreenState _nState;
OptionScreenState _nLastState;
public:
RMOptionScreen();
~RMOptionScreen() override;
using RMGfxWoodyBuffer::init;
void init(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result);
void initLoadMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result);
void initSaveMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result);
void initNoLoadSave(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result);
void reInit(RMGfxTargetBuffer &bigBuf);
bool close();
bool isClosing();
// Overloaded methods
int priority() override;
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
void removeThis(CORO_PARAM, bool &result) override;
/**
* Polling for the option screen
*/
void doFrame(CORO_PARAM, RMInput *m_input);
/**
* Retrieves a savegame's thumbnail, description, and difficulty level
*/
static bool loadThumbnailFromSaveState(int numState, byte *lpDestBuf, Common::String &name, byte &diff);
protected:
// Initialization and state change
void initState(CORO_PARAM);
void closeState();
void changeState(CORO_PARAM, OptionScreenState newState);
// Repaint the options menu
void refreshAll(CORO_PARAM);
void refreshThumbnails();
};
} // End of namespace Tony
#endif

2152
engines/tony/gfxcore.cpp Normal file

File diff suppressed because it is too large Load Diff

502
engines/tony/gfxcore.h Normal file
View File

@@ -0,0 +1,502 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_GFXCORE_H
#define TONY_GFXCORE_H
#include "common/system.h"
#include "common/coroutines.h"
#include "tony/utils.h"
namespace Tony {
/****************************************************************************\
* Class prototype
\****************************************************************************/
// Class Name Family Treee Abstract?
class RMGfxTask; // Yes
class RMGfxTaskSetPrior; // Task Yes
class RMGfxBuffer; //
class RMGfxSourceBuffer; // TaskP+[Buffer] Yes
class RMGfxTargetBuffer; // [Buffer]
class RMGfxSourceBufferPal; // Source Yes
class RMGfxSourceBuffer4; // SourcePal
class RMGfxSourceBuffer8; // SourcePal
class RMGfxSourceBuffer16; // Source
class RMGfxWoodyBuffer; // Source16+Target
class RMGfxClearTask; // Task
/**
* Graphics buffer
*/
class RMGfxBuffer {
protected:
int _dimx, _dimy;
byte *_buf;
byte *_origBuf;
public:
RMGfxBuffer();
RMGfxBuffer(int dimx, int dimy, int nBpp);
virtual ~RMGfxBuffer();
// Attributes
int getDimx();
int getDimy();
// Creation
void create(int dimx, int dimy, int nBpp);
virtual void destroy();
// These are valid only if the buffer is locked
operator byte *();
operator void *();
// Getting the offset for a given Y position
void offsetY(int nLines, int nBpp);
};
/**
* Graphics primitive
*/
class RMGfxPrimitive {
public:
RMGfxTask *_task;
protected:
RMRect _src;
RMRect _dst;
bool _bStretch;
byte _bFlag;
public:
RMGfxPrimitive();
RMGfxPrimitive(RMGfxTask *task);
RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMRect &dst);
RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMRect &dst);
RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMPoint &dst);
RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMPoint &dst);
RMGfxPrimitive(RMGfxTask *task, const RMRect &dst);
RMGfxPrimitive(RMGfxTask *task, const RMPoint &dst);
virtual ~RMGfxPrimitive();
void setFlag(byte bFlag);
void setTask(RMGfxTask *task);
void setSrc(const RMRect &src);
void setSrc(const RMPoint &src);
void setDst(const RMRect &dst);
void setDst(const RMPoint &dst);
void setStretch(bool bStretch);
bool haveDst();
RMRect &getDst();
bool haveSrc();
RMRect &getSrc();
// Flags
bool isFlipped();
// Duplicate
virtual RMGfxPrimitive *duplicate();
};
/**
* Graphic drawing task
*/
class RMGfxTask {
protected:
int _nPrior;
int _nInList;
public:
// Standard constructor
RMGfxTask();
virtual ~RMGfxTask() { }
virtual int priority();
virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) = 0;
virtual void removeThis(CORO_PARAM, bool &result);
// Registration
virtual void Register();
virtual void unregister();
};
/**
* Graphic drawing with priority
*/
class RMGfxTaskSetPrior : public RMGfxTask {
public:
~RMGfxTaskSetPrior() override { }
void setPriority(int nPrior);
};
/**
* Task that cleans the destination buffer
*/
class RMGfxClearTask : public RMGfxTask {
public:
~RMGfxClearTask() override { }
int priority() override;
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
void removeThis(CORO_PARAM, bool &result) override;
};
/**
* Task that draws a colored box
*/
class RMGfxBox : public RMGfxTaskSetPrior {
protected:
uint16 _wFillColor;
public:
~RMGfxBox() override { }
void setColor(byte r, byte g, byte b);
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
void removeThis(CORO_PARAM, bool &result) override;
};
/**
* Buffer source for the design, which is a task. This is an abstract base.
*/
class RMGfxSourceBuffer : public virtual RMGfxBuffer, public RMGfxTaskSetPrior {
public:
// Load the data for the surface
virtual int init(uint32 resID, int dimx, int dimy, bool bLoadPalette = false);
virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false);
virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false);
~RMGfxSourceBuffer() override;
protected:
virtual void prepareImage();
bool clip2D(int &x1, int &y1, int &u, int &v, int &width, int &height, bool bUseSrc, RMGfxTargetBuffer *buf);
void offsetY(int nLines);
public:
virtual int getBpp() = 0;
};
/**
* 16-bit color source
*/
class RMGfxSourceBuffer16 : public RMGfxSourceBuffer {
public:
void prepareImage() override;
protected:
bool _bTrasp0;
public:
RMGfxSourceBuffer16(bool bUseTrasp = false);
RMGfxSourceBuffer16(int dimx, int dimy);
~RMGfxSourceBuffer16() override;
// Initialization
void create(int dimx, int dimy);
int getBpp() override;
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
};
/**
* Buffer source with palette
*/
class RMGfxSourceBufferPal : public RMGfxSourceBuffer {
protected:
// The size of the palette is (1 << Bpp()) * 4
byte _pal[256 * 3];
uint16 _palFinal[256];
// Post process to prepare the palette for drawing
virtual void preparePalette();
public:
~RMGfxSourceBufferPal() override;
int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false) override;
void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false) override;
int loadPaletteWA(uint32 resID, bool bSwapped = false);
int loadPaletteWA(const byte *buf, bool bSwapped = false);
int loadPalette(uint32 resID);
int loadPalette(const byte *buf);
};
/**
* Buffer source with a 256 color palette
*/
class RMGfxSourceBuffer8 : public RMGfxSourceBufferPal {
protected:
bool _bTrasp0;
public:
RMGfxSourceBuffer8(bool bTrasp0 = true);
RMGfxSourceBuffer8(int dimx, int dimy);
~RMGfxSourceBuffer8() override;
// Initialization
void create(int dimx, int dimy);
int getBpp() override;
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
};
/**
* Buffer source with a 256 color palette, and alpha blending
*/
class RMGfxSourceBuffer8AB : public RMGfxSourceBuffer8 {
protected:
int calcTrasp(int f, int b);
public:
~RMGfxSourceBuffer8AB() override;
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
};
/**
* Buffer source with a 256 color palette, RLE compressed
*/
class RMGfxSourceBuffer8RLE : public virtual RMGfxSourceBuffer8 {
protected:
int _alphaBlendColor;
int _alphaR, _alphaB, _alphaG;
bool _bNeedRLECompress;
protected:
static byte _megaRLEBuf[];
virtual void rleWriteTrasp(byte *&cur, int rep) = 0;
virtual void rleWriteData(byte *&cur, int rep, byte *src) = 0;
virtual void rleWriteEOL(byte *&cur) = 0;
virtual void rleWriteAlphaBlend(byte *&cur, int rep) = 0;
virtual void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0;
virtual void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0;
// Perform image compression in RLE
void compressRLE();
protected:
// Overriding initialization methods
void prepareImage() override;
void preparePalette() override;
public:
RMGfxSourceBuffer8RLE();
~RMGfxSourceBuffer8RLE() override;
// Overload of the initialization method
void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false) override;
int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false) override;
// Draw image with RLE decompression
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
// Sets the color that will be alpha blended
void setAlphaBlendColor(int color);
// Warn if the data is already compressed
void setAlreadyCompressed();
};
class RMGfxSourceBuffer8RLEByte : public RMGfxSourceBuffer8RLE {
protected:
void rleWriteTrasp(byte * &cur, int rep) override;
void rleWriteAlphaBlend(byte * &cur, int rep) override;
void rleWriteData(byte * &cur, int rep, byte *src) override;
void rleWriteEOL(byte * &cur) override;
void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) override;
void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) override;
public:
~RMGfxSourceBuffer8RLEByte() override;
};
class RMGfxSourceBuffer8RLEWord : public RMGfxSourceBuffer8RLE {
protected:
void rleWriteTrasp(byte * &cur, int rep) override;
void rleWriteAlphaBlend(byte * &cur, int rep) override;
void rleWriteData(byte * &cur, int rep, byte *src) override;
void rleWriteEOL(byte * &cur) override;
void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) override;
void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) override;
public:
~RMGfxSourceBuffer8RLEWord() override;
};
class RMGfxSourceBuffer8RLEWordAB : public RMGfxSourceBuffer8RLEWord {
protected:
void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) override;
public:
~RMGfxSourceBuffer8RLEWordAB() override;
};
/**
* Buffer source with a 256 color palette, with anti-aliasing
*/
class RMGfxSourceBuffer8AA : public virtual RMGfxSourceBuffer8 {
protected:
static byte _megaAABuf[];
static byte _megaAABuf2[];
byte *_aabuf;
// Calculate the buffer for the anti-aliasing
void calculateAA();
// Draw the AA
void drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
protected:
void prepareImage() override;
public:
RMGfxSourceBuffer8AA();
~RMGfxSourceBuffer8AA() override;
// Draw with anti-aliasing
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
};
class RMGfxSourceBuffer8RLEByteAA : public RMGfxSourceBuffer8RLEByte, public RMGfxSourceBuffer8AA {
protected:
void prepareImage() override;
public:
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
// Overloaded initialization methods
void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false) override;
int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false) override;
~RMGfxSourceBuffer8RLEByteAA() override;
};
class RMGfxSourceBuffer8RLEWordAA : public RMGfxSourceBuffer8RLEWord, public RMGfxSourceBuffer8AA {
protected:
void prepareImage() override;
public:
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
// Overloaded initialization methods
void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false) override;
int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false) override;
~RMGfxSourceBuffer8RLEWordAA() override;
};
/**
* Source buffer with 16 colors
*/
class RMGfxSourceBuffer4 : public RMGfxSourceBufferPal {
public:
RMGfxSourceBuffer4();
RMGfxSourceBuffer4(int dimx, int dimy);
// Initialization
void create(int dimx, int dimy);
int getBpp() override;
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
};
/**
* Destination buffer which manages its own internal list of tasks
*/
class RMGfxTargetBuffer : public virtual RMGfxBuffer {
private:
struct OTList {
RMGfxPrimitive *_prim;
OTList *_next;
OTList();
OTList(RMGfxPrimitive *pr) {
_prim = pr;
_next = NULL;
}
};
bool _trackDirtyRects;
Common::List<Common::Rect> _currentDirtyRects, _previousDirtyRects, _dirtyRects;
void mergeDirtyRects();
private:
//Common::Mutex csModifyingOT;
protected:
OTList *_otlist;
int _otSize;
public:
RMGfxTargetBuffer();
~RMGfxTargetBuffer() override;
static uint16 *_precalcTable;
static void createBWPrecalcTable();
static void freeBWPrecalcTable();
// management of the OT list
void clearOT();
void drawOT(CORO_PARAM);
void addPrim(RMGfxPrimitive *prim); // The pointer must be delted
operator byte *();
operator void *();
operator uint16 *();
// Offseting buffer
void offsetY(int nLines);
// Dirty rect methods
void addDirtyRect(const Common::Rect &r);
Common::List<Common::Rect> &getDirtyRects();
void clearDirtyRects();
void setTrackDirtyRects(bool v);
bool getTrackDirtyRects() const;
};
/**
* Ring buffer, which is both source and by destination
*/
class RMGfxWoodyBuffer: public RMGfxSourceBuffer16, public RMGfxTargetBuffer {
public:
RMGfxWoodyBuffer();
RMGfxWoodyBuffer(int dimx, int dimy);
~RMGfxWoodyBuffer() override;
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
};
} // End of namespace Tony
#endif

839
engines/tony/gfxengine.cpp Normal file
View File

@@ -0,0 +1,839 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#include "common/savefile.h"
#include "tony/mpal/lzo.h"
#include "tony/mpal/mpalutils.h"
#include "tony/custom.h"
#include "tony/gfxengine.h"
#include "tony/tony.h"
namespace Tony {
/****************************************************************************\
* RMGfxEngine Methods
\****************************************************************************/
void exitAllIdles(CORO_PARAM, const void *param) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
int nCurLoc = *(const int *)param;
CORO_BEGIN_CODE(_ctx);
// Closes idle
GLOBALS._bSkipSfxNoLoop = true;
CORO_INVOKE_2(mpalEndIdlePoll, nCurLoc, NULL);
GLOBALS._bIdleExited = true;
GLOBALS._bSkipSfxNoLoop = false;
CORO_END_CODE;
}
RMGfxEngine::RMGfxEngine() {
// Create big buffer where the frame will be rendered
_bigBuf.create(RM_BBX, RM_BBY, 16);
_bigBuf.offsetY(RM_SKIPY);
_bigBuf.setTrackDirtyRects(true);
_nCurLoc = 0;
_curAction = TA_GOTO;
_curActionObj = 0;
_nWipeType = 0;
_hWipeEvent = 0;
_nWipeStep = 0;
_bMustEnterMenu = false;
_bWiping = false;
_bGUIOption = false;
_bGUIInterface = false;
_bGUIInventory = false;
_bAlwaysDrawMouse = false;
_bOption = false;
_bLocationLoaded = false;
_bInput = false;
}
RMGfxEngine::~RMGfxEngine() {
// Close the buffer
_bigBuf.destroy();
}
void RMGfxEngine::openOptionScreen(CORO_PARAM, int type) {
CORO_BEGIN_CONTEXT;
bool bRes;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->bRes = false;
if (type == 0)
CORO_INVOKE_2(_opt.init, _bigBuf, _ctx->bRes);
else if (type == 1)
CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, true, _ctx->bRes);
else if (type == 2)
CORO_INVOKE_2(_opt.initNoLoadSave, _bigBuf, _ctx->bRes);
else if (type == 3)
CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, false, _ctx->bRes);
else if (type == 4)
CORO_INVOKE_3(_opt.initSaveMenuOnly, _bigBuf, false, _ctx->bRes);
if (_ctx->bRes) {
g_vm->pauseSound(true);
disableInput();
_inv.endCombine();
_curActionObj = 0;
_curAction = TA_GOTO;
_point.setAction(_curAction);
_point.setSpecialPointer(RMPointer::PTR_NONE);
_point.setCustomPointer(NULL);
enableMouse();
g_vm->grabThumbnail();
// Exists the IDLE to avoid premature death in loading
_bMustEnterMenu = true;
if (type == 1 || type == 2) {
GLOBALS._bIdleExited = true;
} else {
CORO_INVOKE_0(_tony.stopNoAction);
GLOBALS._bIdleExited = false;
CoroScheduler.createProcess(exitAllIdles, &_nCurLoc, sizeof(int));
}
}
CORO_END_CODE;
}
void RMGfxEngine::doFrame(CORO_PARAM, bool bDrawLocation) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Poll of input devices
_input.poll();
if (_bMustEnterMenu && GLOBALS._bIdleExited) {
_bOption = true;
_bMustEnterMenu = false;
GLOBALS._bIdleExited = false;
}
if (_bOption) {
CORO_INVOKE_1(_opt.doFrame, &_input);
_bOption = !_opt.isClosing();
if (!_bOption) {
disableMouse();
enableInput();
mpalStartIdlePoll(_nCurLoc);
g_vm->pauseSound(false);
}
}
if (bDrawLocation && _bLocationLoaded) {
// Location and objects
_loc.doFrame(&_bigBuf);
// Check the mouse input
if (_bInput && !_tony.inAction()) {
// If we are on the inventory, it is it who controls all input
if (_inv.haveFocus(_input.mousePos()) && !_inter.active()) {
// Left Click
// **********
if (_input.mouseLeftClicked()/* && m_itemName.IsItemSelected()*/) {
// Left click activates the combine, if we are on an object
if (_inv.leftClick(_input.mousePos(), _curActionObj)) {
_curAction = TA_COMBINE;
_point.setAction(_curAction);
}
} else
// Right Click
// ***********
if (_input.mouseRightClicked()) {
if (_itemName.isItemSelected()) {
_curActionObj = 0;
_inv.rightClick(_input.mousePos());
} else
_inv.rightClick(_input.mousePos());
} else
// Right Release
// *************
if (_input.mouseRightReleased()) {
if (_inv.rightRelease(_input.mousePos(), _curAction)) {
CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction);
_curAction = TA_GOTO;
_point.setAction(_curAction);
}
}
} else {
// Options Menu
// ************
if (_bGUIOption) {
if (!_tony.inAction() && _bInput) {
if ((_input.mouseLeftClicked() && _input.mousePos()._x < 3 && _input.mousePos()._y < 3)) {
CORO_INVOKE_1(openOptionScreen, 0);
goto SKIPCLICKSINISTRO;
} else if (_input.getAsyncKeyState(Common::KEYCODE_ESCAPE))
CORO_INVOKE_1(openOptionScreen, 0);
else if (!g_vm->getIsDemo()) {
if (_input.getAsyncKeyState(Common::KEYCODE_F3) || _input.getAsyncKeyState(Common::KEYCODE_F5))
// Save game screen
CORO_INVOKE_1(openOptionScreen, 4);
else if (_input.getAsyncKeyState(Common::KEYCODE_F2) || _input.getAsyncKeyState(Common::KEYCODE_F7))
// Load game screen
CORO_INVOKE_1(openOptionScreen, 3);
}
}
}
// Left Click
// **************
if (_input.mouseLeftClicked() && !_inter.active()) {
if (_curAction != TA_COMBINE)
CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _point.curAction());
else if (_itemName.getSelectedItem() != NULL)
CORO_INVOKE_4(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), TA_COMBINE, _curActionObj);
if (_curAction == TA_COMBINE) {
_inv.endCombine();
_point.setSpecialPointer(RMPointer::PTR_NONE);
}
_curAction = TA_GOTO;
_point.setAction(_curAction);
}
SKIPCLICKSINISTRO:
// Right Click
// ************
if (_curAction == TA_COMBINE) {
// During a combine, it cancels it
if (_input.mouseRightClicked()) {
_inv.endCombine();
_curActionObj = 0;
_curAction = TA_GOTO;
_point.setAction(_curAction);
_point.setSpecialPointer(RMPointer::PTR_NONE);
}
} else if (_input.mouseRightClicked() && _itemName.isItemSelected() && _point.getSpecialPointer() == RMPointer::PTR_NONE) {
if (_bGUIInterface) {
// Before opening the interface, replaces GOTO
_curAction = TA_GOTO;
_curActionObj = 0;
_point.setAction(_curAction);
_inter.clicked(_input.mousePos());
}
}
// Right Release
// *************
if (_input.mouseRightReleased()) {
if (_bGUIInterface) {
if (_inter.released(_input.mousePos(), _curAction)) {
_point.setAction(_curAction);
CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction);
_curAction = TA_GOTO;
_point.setAction(_curAction);
}
}
}
}
// Update the name under the mouse pointer
_itemName.setMouseCoord(_input.mousePos());
if (!_inter.active() && !_inv.miniActive())
CORO_INVOKE_4(_itemName.doFrame, _bigBuf, _loc, _point, _inv);
}
// Interface & Inventory
_inter.doFrame(_bigBuf, _input.mousePos());
_inv.doFrame(_bigBuf, _point, _input.mousePos(), (!_tony.inAction() && !_inter.active() && _bGUIInventory));
}
// Animate Tony
CORO_INVOKE_2(_tony.doFrame, &_bigBuf, _nCurLoc);
// Update screen scrolling to keep Tony in focus
if (_tony.mustUpdateScrolling() && _bLocationLoaded) {
RMPoint showThis = _tony.position();
showThis._y -= 60;
_loc.updateScrolling(showThis);
}
if (_bLocationLoaded)
_tony.setScrollPosition(_loc.scrollPosition());
if ((!_tony.inAction() && _bInput) || _bAlwaysDrawMouse) {
_point.showCursor();
} else {
_point.hideCursor();
}
_point.doFrame();
// **********************
// Draw the list in the OT
// **********************
CORO_INVOKE_0(_bigBuf.drawOT);
#define FSTEP (480/32)
// Wipe
if (_bWiping) {
switch (_nWipeType) {
case 1:
if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top >= FSTEP * 2)) {
CoroScheduler.setEvent(_hWipeEvent);
_nWipeType = 3;
break;
}
_rcWipeEllipse.top += FSTEP;
_rcWipeEllipse.left += FSTEP;
_rcWipeEllipse.right -= FSTEP;
_rcWipeEllipse.bottom -= FSTEP;
break;
case 2:
if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top < 480 - FSTEP)) {
CoroScheduler.setEvent(_hWipeEvent);
_nWipeType = 3;
break;
}
_rcWipeEllipse.top -= FSTEP;
_rcWipeEllipse.left -= FSTEP;
_rcWipeEllipse.right += FSTEP;
_rcWipeEllipse.bottom += FSTEP;
break;
default:
break;
}
}
CORO_END_CODE;
}
void RMGfxEngine::initCustomDll() {
setupGlobalVars(&_tony, &_point, &g_vm->_theBoxes, &_loc, &_inv, &_input);
}
void RMGfxEngine::itemIrq(uint32 dwItem, int nPattern, int nStatus) {
assert(GLOBALS._gfxEngine);
if (GLOBALS._gfxEngine->_bLocationLoaded) {
RMItem *item = GLOBALS._gfxEngine->_loc.getItemFromCode(dwItem);
if (item != NULL) {
if (nPattern != -1) {
item->setPattern(nPattern, true);
}
if (nStatus != -1)
item->setStatus(nStatus);
}
}
}
void RMGfxEngine::initForNewLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) {
if (start._x == -1 || start._y == -1) {
start._x = ptTonyStart._x - RM_SX / 2;
start._y = ptTonyStart._y - RM_SY / 2;
}
_loc.setScrollPosition(start);
if (ptTonyStart._x == 0 && ptTonyStart._y == 0) {
} else {
_tony.setPosition(ptTonyStart, nLoc);
_tony.setScrollPosition(start);
}
_curAction = TA_GOTO;
_point.setCustomPointer(NULL);
_point.setSpecialPointer(RMPointer::PTR_NONE);
_point.setAction(_curAction);
_inter.reset();
_inv.reset();
mpalStartIdlePoll(_nCurLoc);
}
uint32 RMGfxEngine::loadLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) {
_nCurLoc = nLoc;
bool bLoaded = false;
for (int i = 0; i < 5; i++) {
// Try the loading of the location
RMRes res(_nCurLoc);
if (!res.isValid())
continue;
Common::SeekableReadStream *ds = res.getReadStream();
_loc.load(*ds);
delete ds;
initForNewLocation(nLoc, ptTonyStart, start);
bLoaded = true;
break;
}
if (!bLoaded)
error("Location was not loaded");
if (_bOption)
_opt.reInit(_bigBuf);
_bLocationLoaded = true;
// On entering the location
return CORO_INVALID_PID_VALUE; //mpalQueryDoAction(0, m_nCurLoc, 0);
}
void RMGfxEngine::unloadLocation(CORO_PARAM, bool bDoOnExit, uint32 *result) {
CORO_BEGIN_CONTEXT;
uint32 h;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Release the location
CORO_INVOKE_2(mpalEndIdlePoll, _nCurLoc, NULL);
// On Exit?
if (bDoOnExit) {
_ctx->h = mpalQueryDoAction(1, _nCurLoc, 0);
if (_ctx->h != CORO_INVALID_PID_VALUE)
CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
}
_bLocationLoaded = false;
_bigBuf.clearOT();
_loc.unload();
if (result != NULL)
*result = CORO_INVALID_PID_VALUE;
CORO_END_CODE;
}
void RMGfxEngine::init() {
// Screen loading
RMGfxSourceBuffer16 *load = NULL;
RMResRaw *raw;
INIT_GFX16_FROMRAW(20038, load);
_bigBuf.addPrim(new RMGfxPrimitive(load));
_bigBuf.drawOT(Common::nullContext);
_bigBuf.clearOT();
delete load;
// Display 'Loading' screen
_bigBuf.addDirtyRect(Common::Rect(0, 0, RM_SX, RM_SY));
g_vm->_window.getNewFrame(*this, NULL);
g_vm->_window.repaint();
// Activate GUI
_bGUIOption = true;
_bGUIInterface = true;
_bGUIInventory = true;
GLOBALS._bSkipSfxNoLoop = false;
_bMustEnterMenu = false;
GLOBALS._bIdleExited = false;
_bOption = false;
_bWiping = false;
_hWipeEvent = CoroScheduler.createEvent(false, false);
// Initialize the IRQ function for items for MPAL
GLOBALS._gfxEngine = this;
mpalInstallItemIrq(itemIrq);
// Initialize the mouse pointer
_point.init();
// Initialize Tony
_tony.init();
_tony.linkToBoxes(&g_vm->_theBoxes);
// Initialize the inventory and the interface
_inv.init();
_inter.init();
// Download the location and set priorities @@@@@
_bLocationLoaded = false;
enableInput();
// Starting the game
_tony.executeAction(20, 1, 0);
}
void RMGfxEngine::close() {
_bigBuf.clearOT();
_inter.close();
_inv.close();
_tony.close();
_point.close();
}
void RMGfxEngine::enableInput() {
_bInput = true;
}
void RMGfxEngine::disableInput() {
_bInput = false;
_inter.reset();
}
void RMGfxEngine::enableMouse() {
_bAlwaysDrawMouse = true;
}
void RMGfxEngine::disableMouse() {
_bAlwaysDrawMouse = false;
}
#define TONY_SAVEGAME_VERSION 8
void RMGfxEngine::saveState(const Common::String &fn, byte *curThumb, const Common::String &name) {
Common::OutSaveFile *f = g_system->getSavefileManager()->openForSaving(fn);
if (f == NULL)
return;
byte *state;
char buf[4];
RMPoint tp = _tony.position();
// Saving: MPAL variables, current location, and Tony inventory position
// For now, we only save the MPAL state
uint size = mpalGetSaveStateSize();
state = new byte[size];
mpalSaveState(state);
uint thumbsize = 160 * 120 * 2;
buf[0] = 'R';
buf[1] = 'M';
buf[2] = 'S';
buf[3] = TONY_SAVEGAME_VERSION;
f->write(buf, 4);
f->writeUint32LE(thumbsize);
f->write(curThumb, thumbsize);
// Difficulty level
int i = mpalQueryGlobalVar("VERSIONEFACILE");
f->writeByte(i);
i = strlen(name.c_str());
f->writeByte(i);
f->write(name.c_str(), i);
f->writeUint32LE(_nCurLoc);
f->writeUint32LE(tp._x);
f->writeUint32LE(tp._y);
f->writeUint32LE(size);
f->write(state, size);
delete[] state;
// Inventory
size = _inv.getSaveStateSize();
state = new byte[size];
_inv.saveState(state);
f->writeUint32LE(size);
f->write(state, size);
delete[] state;
// boxes
size = g_vm->_theBoxes.getSaveStateSize();
state = new byte[size];
g_vm->_theBoxes.saveState(state);
f->writeUint32LE(size);
f->write(state, size);
delete[] state;
// New Ver5
// Saves the state of the shepherdess and show yourself
bool bStat = _tony.getShepherdess();
f->writeByte(bStat);
bStat = _inter.getPerorate();
f->writeByte(bStat);
// Save the chars
charsSaveAll(f);
// Save the options
f->writeByte(GLOBALS._bCfgInvLocked);
f->writeByte(GLOBALS._bCfgInvNoScroll);
f->writeByte(GLOBALS._bCfgTimerizedText);
f->writeByte(GLOBALS._bCfgInvUp);
f->writeByte(GLOBALS._bCfgAnni30);
f->writeByte(GLOBALS._bCfgAntiAlias);
f->writeByte(GLOBALS._bShowSubtitles);
f->writeByte(GLOBALS._bCfgTransparence);
f->writeByte(GLOBALS._bCfgInterTips);
f->writeByte(GLOBALS._bCfgDubbing);
f->writeByte(GLOBALS._bCfgMusic);
f->writeByte(GLOBALS._bCfgSFX);
f->writeByte(GLOBALS._nCfgTonySpeed);
f->writeByte(GLOBALS._nCfgTextSpeed);
f->writeByte(GLOBALS._nCfgDubbingVolume);
f->writeByte(GLOBALS._nCfgMusicVolume);
f->writeByte(GLOBALS._nCfgSFXVolume);
// Save the hotspots
saveChangedHotspot(f);
// Save the music
saveMusic(f);
f->finalize();
delete f;
}
void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) {
// PROBLEM: You should change the location in a separate process to do the OnEnter
CORO_BEGIN_CONTEXT;
Common::InSaveFile *f;
byte *state, *statecmp;
uint32 size, sizecmp;
char buf[4];
RMPoint tp;
int loc;
int ver;
int i;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->f = g_system->getSavefileManager()->openForLoading(fn);
if (_ctx->f == NULL)
return;
_ctx->f->read(_ctx->buf, 4);
if (_ctx->buf[0] != 'R' || _ctx->buf[1] != 'M' || _ctx->buf[2] != 'S') {
delete _ctx->f;
return;
}
_ctx->ver = _ctx->buf[3];
if (_ctx->ver == 0 || _ctx->ver > TONY_SAVEGAME_VERSION) {
delete _ctx->f;
return;
}
if (_ctx->ver >= 0x3) {
// There is a thumbnail. If the version is between 5 and 7, it's compressed
if ((_ctx->ver >= 0x5) && (_ctx->ver <= 0x7)) {
_ctx->i = 0;
_ctx->i = _ctx->f->readUint32LE();
_ctx->f->seek(_ctx->i);
} else {
if (_ctx->ver >= 8)
// Skip thumbnail size
_ctx->f->skip(4);
_ctx->f->seek(160 * 120 * 2, SEEK_CUR);
}
}
if (_ctx->ver >= 0x5) {
// Skip the difficulty level
_ctx->f->seek(1, SEEK_CUR);
}
if (_ctx->ver >= 0x4) { // Skip the savegame name, which serves no purpose
_ctx->i = _ctx->f->readByte();
_ctx->f->seek(_ctx->i, SEEK_CUR);
}
_ctx->loc = _ctx->f->readUint32LE();
_ctx->tp._x = _ctx->f->readUint32LE();
_ctx->tp._y = _ctx->f->readUint32LE();
_ctx->size = _ctx->f->readUint32LE();
if ((_ctx->ver >= 0x5) && (_ctx->ver <= 7)) {
// MPAL was packed!
_ctx->sizecmp = _ctx->f->readUint32LE();
_ctx->state = new byte[_ctx->size];
_ctx->statecmp = new byte[_ctx->sizecmp];
_ctx->f->read(_ctx->statecmp, _ctx->sizecmp);
lzo1x_decompress(_ctx->statecmp, _ctx->sizecmp, _ctx->state, &_ctx->size);
delete[] _ctx->statecmp;
} else {
// Read uncompressed MPAL data
_ctx->state = new byte[_ctx->size];
_ctx->f->read(_ctx->state, _ctx->size);
}
mpalLoadState(_ctx->state);
delete[] _ctx->state;
// Inventory
_ctx->size = _ctx->f->readUint32LE();
_ctx->state = new byte[_ctx->size];
_ctx->f->read(_ctx->state, _ctx->size);
_inv.loadState(_ctx->state);
delete[] _ctx->state;
if (_ctx->ver >= 0x2) { // Version 2: box please
_ctx->size = _ctx->f->readUint32LE();
_ctx->state = new byte[_ctx->size];
_ctx->f->read(_ctx->state, _ctx->size);
g_vm->_theBoxes.loadState(_ctx->state);
delete[] _ctx->state;
}
if (_ctx->ver >= 5) {
// Version 5
bool bStat = _ctx->f->readByte();
_tony.setShepherdess(bStat);
bStat = _ctx->f->readByte();
_inter.setPerorate(bStat);
charsLoadAll(_ctx->f);
}
if (_ctx->ver >= 6) {
// Load options
GLOBALS._bCfgInvLocked = _ctx->f->readByte();
GLOBALS._bCfgInvNoScroll = _ctx->f->readByte();
GLOBALS._bCfgTimerizedText = _ctx->f->readByte();
GLOBALS._bCfgInvUp = _ctx->f->readByte();
GLOBALS._bCfgAnni30 = _ctx->f->readByte();
GLOBALS._bCfgAntiAlias = _ctx->f->readByte();
GLOBALS._bShowSubtitles = _ctx->f->readByte();
GLOBALS._bCfgTransparence = _ctx->f->readByte();
GLOBALS._bCfgInterTips = _ctx->f->readByte();
GLOBALS._bCfgDubbing = _ctx->f->readByte();
GLOBALS._bCfgMusic = _ctx->f->readByte();
GLOBALS._bCfgSFX = _ctx->f->readByte();
GLOBALS._nCfgTonySpeed = _ctx->f->readByte();
GLOBALS._nCfgTextSpeed = _ctx->f->readByte();
GLOBALS._nCfgDubbingVolume = _ctx->f->readByte();
GLOBALS._nCfgMusicVolume = _ctx->f->readByte();
GLOBALS._nCfgSFXVolume = _ctx->f->readByte();
// Load hotspots
loadChangedHotspot(_ctx->f);
}
if (_ctx->ver >= 7) {
loadMusic(_ctx->f);
}
delete _ctx->f;
CORO_INVOKE_2(unloadLocation, false, NULL);
loadLocation(_ctx->loc, _ctx->tp, RMPoint(-1, -1));
_tony.setPattern(RMTony::PAT_STANDRIGHT);
// On older versions, need to an enter action
if (_ctx->ver < 5)
mpalQueryDoAction(0, _ctx->loc, 0);
else {
// In the new ones, we just reset the mcode
mCharResetCodes();
}
if (_ctx->ver >= 6)
reapplyChangedHotspot();
CORO_INVOKE_0(restoreMusic);
_bGUIInterface = true;
_bGUIInventory = true;
_bGUIOption = true;
CORO_END_CODE;
}
void RMGfxEngine::pauseSound(bool bPause) {
if (_bLocationLoaded)
_loc.pauseSound(bPause);
}
void RMGfxEngine::initWipe(int type) {
_bWiping = true;
_nWipeType = type;
_nWipeStep = 0;
if (_nWipeType == 1)
_rcWipeEllipse = Common::Rect(80, 0, 640 - 80, 480);
else if (_nWipeType == 2)
_rcWipeEllipse = Common::Rect(320 - FSTEP, 240 - FSTEP, 320 + FSTEP, 240 + FSTEP);
}
void RMGfxEngine::closeWipe() {
_bWiping = false;
}
void RMGfxEngine::waitWipeEnd(CORO_PARAM) {
CoroScheduler.waitForSingleObject(coroParam, _hWipeEvent, CORO_INFINITE);
}
bool RMGfxEngine::canLoadSave() {
return _bInput && !_tony.inAction() && !g_vm->getIsDemo();
}
RMGfxEngine::operator RMGfxTargetBuffer &() {
return _bigBuf;
}
RMInput &RMGfxEngine::getInput() {
return _input;
}
RMPointer &RMGfxEngine::getPointer() {
return _point;
}
/**
* Link to graphic task
*/
void RMGfxEngine::linkGraphicTask(RMGfxTask *task) {
_bigBuf.addPrim(new RMGfxPrimitive(task));
}
void RMGfxEngine::setPerorate(bool bpal) {
_inter.setPerorate(bpal);
}
} // End of namespace Tony

138
engines/tony/gfxengine.h Normal file
View File

@@ -0,0 +1,138 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_GFXENGINE_H
#define TONY_GFXENGINE_H
#include "common/scummsys.h"
#include "common/system.h"
#include "common/rect.h"
#include "tony/mpal/memory.h"
#include "tony/game.h"
#include "tony/gfxcore.h"
#include "tony/input.h"
#include "tony/inventory.h"
#include "tony/tonychar.h"
#include "tony/utils.h"
namespace Tony {
class RMGfxEngine {
private:
RMGfxTargetBuffer _bigBuf;
RMInput _input;
RMPointer _point;
RMLocation _loc;
RMOptionScreen _opt;
RMTony _tony;
RMInventory _inv;
RMInterface _inter;
RMTextItemName _itemName;
bool _bOption;
bool _bLocationLoaded;
bool _bInput;
bool _bAlwaysDrawMouse;
int _nCurLoc;
RMTonyAction _curAction;
int _curActionObj;
int _nWipeType;
uint32 _hWipeEvent;
int _nWipeStep;
bool _bMustEnterMenu;
protected:
static void itemIrq(uint32 dwItem, int nPattern, int nStatus);
void initForNewLocation(int nLoc, RMPoint ptTonyStart, RMPoint start);
public:
bool _bWiping;
Common::Rect _rcWipeEllipse;
bool _bGUIOption;
bool _bGUIInterface;
bool _bGUIInventory;
public:
RMGfxEngine();
virtual ~RMGfxEngine();
// Draw the next frame
void doFrame(CORO_PARAM, bool bDrawLocation);
// Initializes the graphics engine
void init();
// Closes the graphics engine
void close();
// Warns when entering or exits the options menu
void openOptionScreen(CORO_PARAM, int type);
// Enables or disables mouse input
void enableInput();
void disableInput();
// Enables and disables mouse draw
void enableMouse();
void disableMouse();
operator RMGfxTargetBuffer &();
RMInput &getInput();
RMPointer &getPointer();
// Link to the custom function list
void initCustomDll();
// Link to graphic task
void linkGraphicTask(RMGfxTask *task);
// Manage a location
uint32 loadLocation(int nLoc, RMPoint ptTonyStart, RMPoint start);
void unloadLocation(CORO_PARAM, bool bDoOnExit, uint32 *result);
int getCurrentLocation() const { return _nCurLoc; }
// State management
void saveState(const Common::String &fn, byte *curThumb, const Common::String &name);
void loadState(CORO_PARAM, const Common::String &fn);
// Pauses sound
void pauseSound(bool bPause);
// Wipe
void initWipe(int type);
void closeWipe();
void waitWipeEnd(CORO_PARAM);
void setPerorate(bool bpal);
bool canLoadSave();
};
} // End of namespace Tony
#endif

134
engines/tony/globals.cpp Normal file
View File

@@ -0,0 +1,134 @@
/* 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/algorithm.h"
#include "tony/globals.h"
namespace Tony {
Globals::Globals() {
_nextLoop = false;
_nextChannel = 0;
_nextSync = 0;
_curChannel = 0;
_flipflop = 0;
_curBackText = NULL;
_bTonyIsSpeaking = false;
_curChangedHotspot = 0;
_tony = NULL;
_pointer = NULL;
_boxes = NULL;
_loc = NULL;
_inventory = NULL;
_input = NULL;
_gfxEngine = NULL;
EnableGUI = NULL;
DisableGUI = NULL;
_dwTonyNumTexts = 0;
_bTonyInTexts = false;
_bStaticTalk = false;
_bAlwaysDisplay = false;
_bIdleExited = false;
_bSkipSfxNoLoop = false;
_bNoBullsEye = false;
_curDialog = 0;
_curSoundEffect = 0;
_bFadeOutStop = false;
_bSkipIdle = false;
_hSkipIdle = 0;
_lastMusic = 0;
_lastTappeto = 0;
Common::fill(&_ambiance[0], &_ambiance[200], 0);
_fullScreenMessageLoc = 0;
// MPAL global variables
_mpalError = 0;
_lpiifCustom = NULL;
_lplpFunctions = NULL;
_lplpFunctionStrings = NULL;
_nObjs = 0;
_nVars = 0;
_hVars = NULL;
_lpmvVars = NULL;
_nMsgs = 0;
_hMsgs = NULL;
_lpmmMsgs = NULL;
_nDialogs = 0;
_hDialogs = NULL;
_lpmdDialogs = NULL;
_nItems = 0;
_hItems = NULL;
_lpmiItems = NULL;
_nLocations = 0;
_hLocations = NULL;
_lpmlLocations = NULL;
_nScripts = 0;
_hScripts = NULL;
_lpmsScripts = NULL;
_nResources = 0;
_lpResources = NULL;
_bExecutingAction = false;
_bExecutingDialog = false;
Common::fill(&_nPollingLocations[0], &_nPollingLocations[MAXPOLLINGLOCATIONS], 0);
Common::fill(&_hEndPollingLocations[0], &_hEndPollingLocations[MAXPOLLINGLOCATIONS], 0);
Common::fill(&_pollingThreads[0], &_pollingThreads[MAXPOLLINGLOCATIONS], 0);
_hAskChoice = 0;
_hDoneChoice = 0;
_nExecutingAction = 0;
_nExecutingDialog = 0;
_nExecutingChoice = 0;
_nSelectedChoice = 0;
_nTonyNextTalkType = RMTony::TALK_NORMAL;
_saveTonyLoc = 0;
for (int i = 0; i < 16; ++i) {
Common::fill((byte *)&_character[i], (byte *)&_character[i] + sizeof(CharacterStruct), 0);
_isMChar[i] = false;
}
for (int i = 0; i < 10; ++i)
Common::fill((byte *)&_mCharacter[i], (byte *)&_mCharacter[i] + sizeof(MCharacterStruct), 0);
for (int i = 0; i < 256; ++i)
Common::fill((byte *)&_changedHotspot[i], (byte *)&_changedHotspot[i] + sizeof(ChangedHotspotStruct), 0);
// Set up globals that have explicit initial values
_bCfgInvLocked = false;
_bCfgInvNoScroll = false;
_bCfgTimerizedText = true;
_bCfgInvUp = false;
_bCfgAnni30 = false;
_bCfgAntiAlias = false;
_bCfgTransparence = true;
_bCfgInterTips = true;
_bShowSubtitles = true;
_nCfgTonySpeed = 3;
_nCfgTextSpeed = 5;
_bCfgDubbing = true;
_bCfgMusic = true;
_bCfgSFX = true;
_nCfgDubbingVolume = 10;
_nCfgMusicVolume = 7;
_nCfgSFXVolume = 10;
}
} // End of namespace Tony

287
engines/tony/globals.h Normal file
View File

@@ -0,0 +1,287 @@
/* 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 TONY_GLOBALS
#define TONY_GLOBALS
#include "common/savefile.h"
#include "tony/gfxengine.h"
#include "tony/input.h"
#include "tony/inventory.h"
#include "tony/loc.h"
#include "tony/tonychar.h"
#include "tony/mpal/mpal.h"
#include "tony/mpal/mpaldll.h"
namespace Tony {
#define AMBIANCE_CRICKETS 1
#define AMBIANCE_CRICKETSMUFFLED 2
#define AMBIANCE_CRICKETSWIND 3
#define AMBIANCE_CRICKETSWIND1 4
#define AMBIANCE_WIND 5
#define AMBIANCE_SEA 6
#define AMBIANCE_SEAHALFVOLUME 7
struct CharacterStruct {
uint32 _code;
RMItem *_item;
byte _r, _g, _b;
int _talkPattern;
int _standPattern;
int _startTalkPattern, _endTalkPattern;
int _numTexts;
void save(Common::OutSaveFile *f) {
f->writeUint32LE(_code);
f->writeUint32LE(0);
f->writeByte(_r);
f->writeByte(_g);
f->writeByte(_b);
f->writeUint32LE(_talkPattern);
f->writeUint32LE(_standPattern);
f->writeUint32LE(_startTalkPattern);
f->writeUint32LE(_endTalkPattern);
f->writeUint32LE(_numTexts);
}
void load(Common::InSaveFile *f) {
_code = f->readUint32LE();
f->readUint32LE();
_item = NULL;
_r = f->readByte();
_g = f->readByte();
_b = f->readByte();
_talkPattern = f->readUint32LE();
_standPattern = f->readUint32LE();
_startTalkPattern = f->readUint32LE();
_endTalkPattern = f->readUint32LE();
_numTexts = f->readUint32LE();
}
};
struct MCharacterStruct {
uint32 _code;
RMItem *_item;
byte _r, _g, _b;
int _x, _y;
int _numTalks[10];
int _curGroup;
int _numTexts;
bool _bInTexts;
int _curTalk;
bool _bAlwaysBack;
void save(Common::OutSaveFile *f) {
f->writeUint32LE(_code);
f->writeUint32LE(0);
f->writeByte(_r);
f->writeByte(_g);
f->writeByte(_b);
f->writeUint32LE(_x);
f->writeUint32LE(_y);
for (int i = 0; i < 10; ++i)
f->writeUint32LE(_numTalks[i]);
f->writeUint32LE(_curGroup);
f->writeUint32LE(_numTexts);
f->writeByte(_bInTexts);
f->writeUint32LE(_curTalk);
f->writeByte(_bAlwaysBack);
}
void load(Common::InSaveFile *f) {
_code = f->readUint32LE();
f->readUint32LE();
_item = NULL;
_r = f->readByte();
_g = f->readByte();
_b = f->readByte();
_x = f->readUint32LE();
_y = f->readUint32LE();
for (int i = 0; i < 10; ++i)
_numTalks[i] = f->readUint32LE();
_curGroup = f->readUint32LE();
_numTexts = f->readUint32LE();
_bInTexts = f->readByte();
_curTalk = f->readUint32LE();
_bAlwaysBack = f->readByte();
}
};
struct ChangedHotspotStruct {
uint32 _dwCode;
uint32 _nX, _nY;
void save(Common::OutSaveFile *f) {
f->writeUint32LE(_dwCode);
f->writeUint32LE(_nX);
f->writeUint32LE(_nY);
}
void load(Common::InSaveFile *f) {
_dwCode = f->readUint32LE();
_nX = f->readUint32LE();
_nY = f->readUint32LE();
}
};
/**
* Description of a call to a custom function.
*/
typedef struct {
int _nCf;
int _arg1, _arg2, _arg3, _arg4;
} CfCall;
typedef CfCall *LpCfCall;
struct CoroutineMutex {
CoroutineMutex() : _eventId(0), _ownerPid(0), _lockCount(0) { }
uint32 _eventId;
uint32 _ownerPid;
uint32 _lockCount;
};
/****************************************************************************\
* Global variables
\****************************************************************************/
/**
* Globals class
*/
class Globals {
public:
Globals();
Common::String _nextMusic;
bool _nextLoop;
int _nextChannel;
int _nextSync;
int _curChannel;
int _flipflop;
CharacterStruct _character[16];
MCharacterStruct _mCharacter[10];
ChangedHotspotStruct _changedHotspot[256];
bool _isMChar[16];
bool _bAlwaysDisplay;
RMPoint _saveTonyPos;
int _saveTonyLoc;
RMTextDialog *_curBackText;
bool _bTonyIsSpeaking;
int _curChangedHotspot;
bool _bCfgInvLocked;
bool _bCfgInvNoScroll;
bool _bCfgTimerizedText;
bool _bCfgInvUp;
bool _bCfgAnni30;
bool _bCfgAntiAlias;
bool _bShowSubtitles;
bool _bCfgTransparence;
bool _bCfgInterTips;
bool _bCfgDubbing;
bool _bCfgMusic;
bool _bCfgSFX;
int _nCfgTonySpeed;
int _nCfgTextSpeed;
int _nCfgDubbingVolume;
int _nCfgMusicVolume;
int _nCfgSFXVolume;
bool _bSkipSfxNoLoop;
bool _bIdleExited;
bool _bNoBullsEye;
int _curDialog;
int _curSoundEffect;
bool _bFadeOutStop;
RMTony *_tony;
RMPointer *_pointer;
RMGameBoxes *_boxes;
RMLocation *_loc;
RMInventory *_inventory;
RMInput *_input;
RMGfxEngine *_gfxEngine;
void (*EnableGUI)();
void (*DisableGUI)();
uint32 _dwTonyNumTexts;
bool _bTonyInTexts;
bool _bStaticTalk;
RMTony::CharacterTalkType _nTonyNextTalkType;
RMPoint _startLocPos[256];
CoroutineMutex _mut[10];
bool _bSkipIdle;
uint32 _hSkipIdle;
int _lastMusic, _lastTappeto;
int _ambiance[200];
RMPoint _fullScreenMessagePt;
int _fullScreenMessageLoc;
/**
* @defgroup MPAL variables
*/
uint32 _mpalError;
LPITEMIRQFUNCTION _lpiifCustom;
LPLPCUSTOMFUNCTION _lplpFunctions;
Common::String *_lplpFunctionStrings;
uint16 _nObjs;
uint16 _nVars;
MpalHandle _hVars;
LpMpalVar _lpmvVars;
uint16 _nMsgs;
MpalHandle _hMsgs;
LpMpalMsg _lpmmMsgs;
uint16 _nDialogs;
MpalHandle _hDialogs;
LpMpalDialog _lpmdDialogs;
uint16 _nItems;
MpalHandle _hItems;
LpMpalItem _lpmiItems;
uint16 _nLocations;
MpalHandle _hLocations;
LpMpalLocation _lpmlLocations;
uint16 _nScripts;
MpalHandle _hScripts;
LpMpalScript _lpmsScripts;
Common::File _hMpr;
uint16 _nResources;
uint32 *_lpResources;
bool _bExecutingAction;
bool _bExecutingDialog;
uint32 _nPollingLocations[MAXPOLLINGLOCATIONS];
uint32 _hEndPollingLocations[MAXPOLLINGLOCATIONS];
uint32 _pollingThreads[MAXPOLLINGLOCATIONS];
uint32 _hAskChoice;
uint32 _hDoneChoice;
uint32 _nExecutingAction;
uint32 _nExecutingDialog;
uint32 _nExecutingChoice;
uint32 _nSelectedChoice;
};
} // End of namespace Tony
#endif // TONY_GLOBALS

127
engines/tony/input.cpp Normal file
View File

@@ -0,0 +1,127 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#include "tony/gfxengine.h"
#include "tony/tony.h"
namespace Tony {
RMInput::RMInput() {
_leftClickMouse = _leftReleaseMouse = false;
_rightClickMouse = _rightReleaseMouse = false;
}
void RMInput::poll() {
_leftClickMouse = _leftReleaseMouse = _rightClickMouse = _rightReleaseMouse = false;
// Get pending events
while (g_system->getEventManager()->pollEvent(_event) && !g_vm->shouldQuit()) {
switch (_event.type) {
case Common::EVENT_MOUSEMOVE:
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_LBUTTONUP:
case Common::EVENT_RBUTTONDOWN:
case Common::EVENT_RBUTTONUP:
_mousePos = _event.mouse;
if (_event.type == Common::EVENT_LBUTTONDOWN) {
_leftClickMouse = true;
} else if (_event.type == Common::EVENT_LBUTTONUP) {
_leftReleaseMouse = true;
} else if (_event.type == Common::EVENT_RBUTTONDOWN) {
_rightClickMouse = true;
} else if (_event.type == Common::EVENT_RBUTTONUP) {
_rightReleaseMouse = true;
} else
continue;
// Since a mouse button has changed, don't do any further event processing this frame
return;
case Common::EVENT_KEYDOWN:
// Flag the given key as being down
_keyDown.push_back(_event.kbd.keycode);
return;
case Common::EVENT_KEYUP:
for (uint i = 0; i < _keyDown.size(); i++) {
if (_keyDown[i] == _event.kbd.keycode) {
_keyDown.remove_at(i);
break;
}
}
return;
default:
break;
}
}
}
/**
* Return true if a key has been pressed
*/
bool RMInput::getAsyncKeyState(Common::KeyCode kc) {
// The act of testing for a particular key automatically clears the state, to prevent
// the same key being registered in multiple different frames
for (uint i = 0; i < _keyDown.size(); i++) {
if (_keyDown[i] == kc) {
_keyDown.remove_at(i);
return true;
}
}
return false;
}
/**
* Reading of the mouse
*/
RMPoint RMInput::mousePos() {
RMPoint p(_mousePos.x, _mousePos.y);
return p;
}
/**
* Events of mouse clicks
*/
bool RMInput::mouseLeftClicked() {
return _leftClickMouse;
}
bool RMInput::mouseRightClicked() {
return _rightClickMouse;
}
bool RMInput::mouseLeftReleased() {
return _leftReleaseMouse;
}
bool RMInput::mouseRightReleased() {
return _rightReleaseMouse;
}
} // End of namespace Tony

79
engines/tony/input.h Normal file
View File

@@ -0,0 +1,79 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_INPUT_H
#define TONY_INPUT_H
#include "common/events.h"
#include "common/rect.h"
#include "common/array.h"
#include "common/keyboard.h"
#include "tony/utils.h"
namespace Tony {
class RMInput {
private:
Common::Event _event;
// Mouse related fields
Common::Point _mousePos;
bool _leftClickMouse, _leftReleaseMouse, _rightClickMouse, _rightReleaseMouse;
// Keyboard related fields
Common::Array<Common::KeyCode> _keyDown;
public:
RMInput();
/**
* Polling (must be performed once per frame)
*/
void poll();
/**
* Reading of the mouse
*/
RMPoint mousePos();
/**
* Events of mouse clicks
*/
bool mouseLeftClicked();
bool mouseRightClicked();
bool mouseLeftReleased();
bool mouseRightReleased();
/**
* Returns true if the given key is pressed
*/
bool getAsyncKeyState(Common::KeyCode kc);
};
} // End of namespace Tony
#endif

934
engines/tony/inventory.cpp Normal file
View File

@@ -0,0 +1,934 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#include "common/textconsole.h"
#include "tony/mpal/mpalutils.h"
#include "tony/inventory.h"
#include "tony/game.h"
#include "tony/tony.h"
namespace Tony {
/****************************************************************************\
* RMInventory Methods
\****************************************************************************/
RMInventory::RMInventory() {
_items = NULL;
_state = CLOSED;
_bCombining = false;
_nItems = 0;
Common::fill(_inv, _inv + 256, 0);
_nInv = 0;
_curPutY = 0;
_curPutTime = 0;
_curPos = 0;
_bHasFocus = false;
_nSelectObj = 0;
_nCombine = 0;
_bBlinkingRight = false;
_bBlinkingLeft = false;
_miniAction = 0;
}
RMInventory::~RMInventory() {
close();
}
bool RMInventory::checkPointInside(const RMPoint &pt) {
if (!GLOBALS._bCfgInvUp)
return pt._y > RM_SY - 70;
else
return pt._y < 70;
}
void RMInventory::init() {
// Create the main buffer
create(RM_SX, 68);
setPriority(185);
// Setup the inventory
_nInv = 0;
_curPos = 0;
_bCombining = false;
// New items
_nItems = 78; // @@@ Number of takeable items
_items = new RMInventoryItem[_nItems + 1];
int curres = 10500;
// Loop through the items
for (int i = 0; i <= _nItems; i++) {
// Load the items from the resource
RMRes res(curres);
assert(res.isValid());
Common::SeekableReadStream *ds = res.getReadStream();
// Initialize the MPAL inventory item by reading it in.
_items[i]._icon.setInitCurPattern(false);
_items[i]._icon.readFromStream(*ds);
delete ds;
// Puts in the default pattern 1
_items[i]._pointer = NULL;
_items[i]._status = 1;
_items[i]._icon.setPattern(1);
_items[i]._icon.doFrame(this, false);
curres++;
if (i == 0 || i == 28 || i == 29)
continue;
_items[i]._pointer = new RMGfxSourceBuffer8RLEByteAA[_items[i]._icon.numPattern()];
for (int j = 0; j < _items[i]._icon.numPattern(); j++) {
RMResRaw raw(curres);
assert(raw.isValid());
_items[i]._pointer[j].init((const byte *)raw, raw.width(), raw.height(), true);
curres++;
}
}
_items[28]._icon.setPattern(1);
_items[29]._icon.setPattern(1);
// Download interface
RMRes res(RES_I_MINIINTER);
assert(res.isValid());
Common::SeekableReadStream *ds = res.getReadStream();
_miniInterface.readFromStream(*ds);
_miniInterface.setPattern(1);
delete ds;
// Create the text for hints on the mini interface
_hints[0].setAlignType(RMText::HCENTER, RMText::VTOP);
_hints[1].setAlignType(RMText::HCENTER, RMText::VTOP);
_hints[2].setAlignType(RMText::HCENTER, RMText::VTOP);
// The text is taken from MPAL for translation
RMMessage msg1(15);
RMMessage msg2(13);
RMMessage msg3(14);
_hints[0].writeText(msg1[0], 1); // Examine
_hints[1].writeText(msg2[0], 1); // Take
_hints[2].writeText(msg3[0], 1); // Use
// Prepare initial inventory
prepare();
drawOT(Common::nullContext);
clearOT();
}
void RMInventory::close() {
// Has memory
if (_items != NULL) {
// Delete the item pointers
for (int i = 0; i <= _nItems; i++)
delete[] _items[i]._pointer;
// Delete the items array
delete[] _items;
_items = NULL;
}
destroy();
}
void RMInventory::reset() {
_state = CLOSED;
endCombine();
}
void RMInventory::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
CORO_BEGIN_CONTEXT;
RMPoint pos;
RMPoint pos2;
RMGfxPrimitive *p;
RMGfxPrimitive *p2;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
prim->setDst(RMPoint(0, _curPutY));
_csModifyInterface.lock();
CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim);
_csModifyInterface.unlock();
if (_state == SELECTING) {
if (!GLOBALS._bCfgInvUp) {
_ctx->pos.set((_nSelectObj + 1) * 64 - 20, RM_SY - 113);
_ctx->pos2.set((_nSelectObj + 1) * 64 + 34, RM_SY - 150);
} else {
_ctx->pos.set((_nSelectObj + 1) * 64 - 20, 72 - 4); // The brown part is at the top :(
_ctx->pos2.set((_nSelectObj + 1) * 64 + 34, 119 - 4);
}
_ctx->p = new RMGfxPrimitive(prim->_task, _ctx->pos);
_ctx->p2 = new RMGfxPrimitive(prim->_task, _ctx->pos2);
// Draw the mini interface
CORO_INVOKE_2(_miniInterface.draw, bigBuf, _ctx->p);
if (GLOBALS._bCfgInterTips) {
if (_miniAction == 1) // Examine
CORO_INVOKE_2(_hints[0].draw, bigBuf, _ctx->p2);
else if (_miniAction == 2) // Talk
CORO_INVOKE_2(_hints[1].draw, bigBuf, _ctx->p2);
else if (_miniAction == 3) // Use
CORO_INVOKE_2(_hints[2].draw, bigBuf, _ctx->p2);
}
delete _ctx->p;
delete _ctx->p2;
}
CORO_END_CODE;
}
void RMInventory::removeThis(CORO_PARAM, bool &result) {
if (_state == CLOSED)
result = true;
else
result = false;
}
void RMInventory::removeItem(int code) {
for (int i = 0; i < _nInv; i++) {
if (_inv[i] == code - 10000) {
_csModifyInterface.lock();
Common::copy(&_inv[i + 1], &_inv[i + 1] + (_nInv - i), &_inv[i]);
_nInv--;
prepare();
drawOT(Common::nullContext);
clearOT();
_csModifyInterface.unlock();
return;
}
}
}
void RMInventory::addItem(int code) {
if (code <= 10000 || code >= 10101) {
// If we are here, it means that we are adding an item that should not be in the inventory
warning("RMInventory::addItem(%d) - Cannot find a valid icon for this item, and then it will not be added to the inventory", code);
} else {
_csModifyInterface.lock();
if (_curPos + 8 == _nInv) {
// Break through the inventory! On the flashing pattern
_items[28]._icon.setPattern(2);
}
_inv[_nInv++] = code - 10000;
prepare();
drawOT(Common::nullContext);
clearOT();
_csModifyInterface.unlock();
}
}
void RMInventory::changeItemStatus(uint32 code, uint32 dwStatus) {
if (code <= 10000 || code >= 10101) {
error("RMInventory::changeItemStatus(%d) - Specified object code is not valid", code);
} else {
_csModifyInterface.lock();
_items[code - 10000]._icon.setPattern(dwStatus);
_items[code - 10000]._status = dwStatus;
prepare();
drawOT(Common::nullContext);
clearOT();
_csModifyInterface.unlock();
}
}
void RMInventory::prepare() {
for (int i = 1; i < RM_SX / 64 - 1; i++) {
if (i - 1 + _curPos < _nInv)
addPrim(new RMGfxPrimitive(&_items[_inv[i - 1 + _curPos]]._icon, RMPoint(i * 64, 0)));
else
addPrim(new RMGfxPrimitive(&_items[0]._icon, RMPoint(i * 64, 0)));
}
// Frecce
addPrim(new RMGfxPrimitive(&_items[29]._icon, RMPoint(0, 0)));
addPrim(new RMGfxPrimitive(&_items[28]._icon, RMPoint(640 - 64, 0)));
}
bool RMInventory::miniActive() {
return _state == SELECTING;
}
bool RMInventory::haveFocus(const RMPoint &mpos) {
// When we combine, have the focus only if we are on an arrow (to scroll)
if (_state == OPENED && _bCombining && checkPointInside(mpos) && (mpos._x < 64 || mpos._x > RM_SX - 64))
return true;
// If the inventory is open, focus we we go over it
if (_state == OPENED && !_bCombining && checkPointInside(mpos))
return true;
// If we are selecting a verb (and then right down), we always focus
if (_state == SELECTING)
return true;
return false;
}
void RMInventory::endCombine() {
_bCombining = false;
}
bool RMInventory::leftClick(const RMPoint &mpos, int &nCombineObj) {
// The left click picks an item from your inventory to use it with the background
int n = mpos._x / 64;
if (_state == OPENED) {
if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0) {
_bCombining = true; //m_state = COMBINING;
_nCombine = _inv[n - 1 + _curPos];
nCombineObj = _nCombine + 10000;
g_vm->playUtilSFX(1);
return true;
}
}
// Click the right arrow
if ((_state == OPENED) && _bBlinkingRight) {
_csModifyInterface.lock();
_curPos++;
if (_curPos + 8 >= _nInv) {
_bBlinkingRight = false;
_items[28]._icon.setPattern(1);
}
if (_curPos > 0) {
_bBlinkingLeft = true;
_items[29]._icon.setPattern(2);
}
prepare();
drawOT(Common::nullContext);
clearOT();
_csModifyInterface.unlock();
}
// Click the left arrow
else if ((_state == OPENED) && _bBlinkingLeft) {
assert(_curPos > 0);
_csModifyInterface.lock();
_curPos--;
if (_curPos == 0) {
_bBlinkingLeft = false;
_items[29]._icon.setPattern(1);
}
if (_curPos + 8 < _nInv) {
_bBlinkingRight = true;
_items[28]._icon.setPattern(2);
}
prepare();
drawOT(Common::nullContext);
clearOT();
_csModifyInterface.unlock();
}
return false;
}
void RMInventory::rightClick(const RMPoint &mpos) {
assert(checkPointInside(mpos));
if (_state == OPENED && !_bCombining) {
// Open the context interface
int n = mpos._x / 64;
if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0) {
_state = SELECTING;
_miniAction = 0;
_nSelectObj = n - 1;
g_vm->playUtilSFX(0);
}
}
if ((_state == OPENED) && _bBlinkingRight) {
_csModifyInterface.lock();
_curPos += 7;
if (_curPos + 8 > _nInv)
_curPos = _nInv - 8;
if (_curPos + 8 <= _nInv) {
_bBlinkingRight = false;
_items[28]._icon.setPattern(1);
}
if (_curPos > 0) {
_bBlinkingLeft = true;
_items[29]._icon.setPattern(2);
}
prepare();
drawOT(Common::nullContext);
clearOT();
_csModifyInterface.unlock();
} else if ((_state == OPENED) && _bBlinkingLeft) {
assert(_curPos > 0);
_csModifyInterface.lock();
_curPos -= 7;
if (_curPos < 0)
_curPos = 0;
if (_curPos == 0) {
_bBlinkingLeft = false;
_items[29]._icon.setPattern(1);
}
if (_curPos + 8 < _nInv) {
_bBlinkingRight = true;
_items[28]._icon.setPattern(2);
}
prepare();
drawOT(Common::nullContext);
clearOT();
_csModifyInterface.unlock();
}
}
bool RMInventory::rightRelease(const RMPoint &mpos, RMTonyAction &curAction) {
if (_state == SELECTING) {
_state = OPENED;
if (_miniAction == 1) { // Examine
curAction = TA_EXAMINE;
return true;
} else if (_miniAction == 2) { // Talk
curAction = TA_TALK;
return true;
} else if (_miniAction == 3) { // Use
curAction = TA_USE;
return true;
}
}
return false;
}
#define INVSPEED 20
void RMInventory::doFrame(RMGfxTargetBuffer &bigBuf, RMPointer &ptr, RMPoint mpos, bool bCanOpen) {
if (_state != CLOSED) {
// Clean up the OT list
_csModifyInterface.lock();
clearOT();
// DoFrame makes all the objects currently in the inventory be displayed
// @@@ Maybe we should do all takeable objects? Please does not help
bool bNeedRedraw = false;
for (int i = 0; i < _nInv; i++) {
if (_items[_inv[i]]._icon.doFrame(this, false) && (i >= _curPos && i <= _curPos + 7))
bNeedRedraw = true;
}
if ((_state == CLOSING || _state == OPENING || _state == OPENED) && checkPointInside(mpos)) {
if (mpos._x > RM_SX - 64) {
if (_curPos + 8 < _nInv && !_bBlinkingRight) {
_items[28]._icon.setPattern(3);
_bBlinkingRight = true;
bNeedRedraw = true;
}
} else if (_bBlinkingRight) {
_items[28]._icon.setPattern(2);
_bBlinkingRight = false;
bNeedRedraw = true;
}
if (mpos._x < 64) {
if (_curPos > 0 && !_bBlinkingLeft) {
_items[29]._icon.setPattern(3);
_bBlinkingLeft = true;
bNeedRedraw = true;
}
} else if (_bBlinkingLeft) {
_items[29]._icon.setPattern(2);
_bBlinkingLeft = false;
bNeedRedraw = true;
}
}
if (_items[28]._icon.doFrame(this, false))
bNeedRedraw = true;
if (_items[29]._icon.doFrame(this, false))
bNeedRedraw = true;
if (bNeedRedraw)
prepare();
_csModifyInterface.unlock();
}
if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_i)) {
GLOBALS._bCfgInvLocked = !GLOBALS._bCfgInvLocked;
}
if (_bCombining) { // m_state == COMBINING)
ptr.setCustomPointer(&_items[_nCombine]._pointer[_items[_nCombine]._status - 1]);
ptr.setSpecialPointer(RMPointer::PTR_CUSTOM);
}
if (!GLOBALS._bCfgInvUp) {
if ((_state == CLOSED) && (mpos._y > RM_SY - 10 || GLOBALS._bCfgInvLocked) && bCanOpen) {
if (!GLOBALS._bCfgInvNoScroll) {
_state = OPENING;
_curPutY = RM_SY - 1;
_curPutTime = g_vm->getTime();
} else {
_state = OPENED;
_curPutY = RM_SY - 68;
}
} else if (_state == OPENED) {
if ((mpos._y < RM_SY - 70 && !GLOBALS._bCfgInvLocked) || !bCanOpen) {
if (!GLOBALS._bCfgInvNoScroll) {
_state = CLOSING;
_curPutY = RM_SY - 68;
_curPutTime = g_vm->getTime();
} else {
_state = CLOSED;
}
}
} else if (_state == OPENING) {
while (_curPutTime + INVSPEED < g_vm->getTime()) {
_curPutY -= 3;
_curPutTime += INVSPEED;
}
if (_curPutY <= RM_SY - 68) {
_state = OPENED;
_curPutY = RM_SY - 68;
}
} else if (_state == CLOSING) {
while (_curPutTime + INVSPEED < g_vm->getTime()) {
_curPutY += 3;
_curPutTime += INVSPEED;
}
if (_curPutY > 480)
_state = CLOSED;
}
} else {
if ((_state == CLOSED) && (mpos._y < 10 || GLOBALS._bCfgInvLocked) && bCanOpen) {
if (!GLOBALS._bCfgInvNoScroll) {
_state = OPENING;
_curPutY = - 68;
_curPutTime = g_vm->getTime();
} else {
_state = OPENED;
_curPutY = 0;
}
} else if (_state == OPENED) {
if ((mpos._y > 70 && !GLOBALS._bCfgInvLocked) || !bCanOpen) {
if (!GLOBALS._bCfgInvNoScroll) {
_state = CLOSING;
_curPutY = -2;
_curPutTime = g_vm->getTime();
} else {
_state = CLOSED;
}
}
} else if (_state == OPENING) {
while (_curPutTime + INVSPEED < g_vm->getTime()) {
_curPutY += 3;
_curPutTime += INVSPEED;
}
if (_curPutY >= 0) {
_state = OPENED;
_curPutY = 0;
}
} else if (_state == CLOSING) {
while (_curPutTime + INVSPEED < g_vm->getTime()) {
_curPutY -= 3;
_curPutTime += INVSPEED;
}
if (_curPutY < -68)
_state = CLOSED;
}
}
if (_state == SELECTING) {
int startx = (_nSelectObj + 1) * 64 - 20;
int starty;
if (!GLOBALS._bCfgInvUp)
starty = RM_SY - 109;
else
starty = 70;
// Make sure it is on one of the verbs
if (mpos._y > starty && mpos._y < starty + 45) {
if (mpos._x > startx && mpos._x < startx + 40) {
if (_miniAction != 1) {
_miniInterface.setPattern(2);
_miniAction = 1;
g_vm->playUtilSFX(1);
}
} else if (mpos._x >= startx + 40 && mpos._x < startx + 80) {
if (_miniAction != 2) {
_miniInterface.setPattern(3);
_miniAction = 2;
g_vm->playUtilSFX(1);
}
} else if (mpos._x >= startx + 80 && mpos._x < startx + 108) {
if (_miniAction != 3) {
_miniInterface.setPattern(4);
_miniAction = 3;
g_vm->playUtilSFX(1);
}
} else {
_miniInterface.setPattern(1);
_miniAction = 0;
}
} else {
_miniInterface.setPattern(1);
_miniAction = 0;
}
// Update the mini-interface
_miniInterface.doFrame(&bigBuf, false);
}
if ((_state != CLOSED) && !_nInList) {
bigBuf.addPrim(new RMGfxPrimitive(this));
}
}
bool RMInventory::itemInFocus(const RMPoint &mpt) {
if ((_state == OPENED || _state == OPENING) && checkPointInside(mpt))
return true;
else
return false;
}
RMItem *RMInventory::whichItemIsIn(const RMPoint &mpt) {
if (_state == OPENED) {
if (checkPointInside(mpt)) {
int n = mpt._x / 64;
if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0 && (!_bCombining || _inv[n - 1 + _curPos] != _nCombine))
return &_items[_inv[n - 1 + _curPos]]._icon;
}
}
return NULL;
}
int RMInventory::getSaveStateSize() {
// m_inv pattern m_nInv
return 256 * 4 + 256 * 4 + 4;
}
void RMInventory::saveState(byte *state) {
WRITE_LE_UINT32(state, _nInv);
state += 4;
for (int i = 0; i < 256; ++i) {
WRITE_LE_UINT32(state, _inv[i]);
state += 4;
}
int x;
for (int i = 0; i < 256; i++) {
if (i < _nItems)
x = _items[i]._status;
else
x = 0;
WRITE_LE_UINT32(state, x);
state += 4;
}
}
int RMInventory::loadState(byte *state) {
_nInv = READ_LE_UINT32(state);
state += 4;
for (int i = 0; i < 256; ++i) {
_inv[i] = READ_LE_UINT32(state);
state += 4;
}
for (int i = 0; i < 256; i++) {
int x = READ_LE_UINT32(state);
state += 4;
if (i < _nItems) {
_items[i]._status = x;
_items[i]._icon.setPattern(x);
}
}
_curPos = 0;
_bCombining = false;
_items[29]._icon.setPattern(1);
if (_nInv > 8)
_items[28]._icon.setPattern(2);
else
_items[28]._icon.setPattern(1);
prepare();
drawOT(Common::nullContext);
clearOT();
return getSaveStateSize();
}
RMInventory &RMInventory::operator+=(RMItem *item) {
addItem(item->mpalCode());
return *this;
}
RMInventory &RMInventory::operator+=(RMItem &item) {
addItem(item.mpalCode());
return *this;
}
RMInventory &RMInventory::operator+=(int code) {
addItem(code);
return *this;
}
/****************************************************************************\
* RMInterface methods
\****************************************************************************/
RMInterface::RMInterface() : RMGfxSourceBuffer8RLEByte() {
_bActive = _bPerorate = false;
_lastHotZone = -1;
}
RMInterface::~RMInterface() {
}
bool RMInterface::active() {
return _bActive;
}
int RMInterface::onWhichBox(RMPoint pt) {
pt -= _openStart;
// Check how many verbs you have to consider
int max = 4;
if (_bPerorate)
max = 5;
// Find the verb
for (int i = 0; i < max; i++) {
if (_hotbbox[i].ptInRect(pt))
return i;
}
// Found no verb
return -1;
}
void RMInterface::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
CORO_BEGIN_CONTEXT;
int h;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
prim->getDst().topLeft() = _openStart;
CORO_INVOKE_2(RMGfxSourceBuffer8RLEByte::draw, bigBuf, prim);
// Check if there is a draw hot zone
_ctx->h = onWhichBox(_mpos);
if (_ctx->h != -1) {
prim->getDst().topLeft() = _openStart;
CORO_INVOKE_2(_hotzone[_ctx->h].draw, bigBuf, prim);
if (_lastHotZone != _ctx->h) {
_lastHotZone = _ctx->h;
g_vm->playUtilSFX(1);
}
if (GLOBALS._bCfgInterTips) {
prim->getDst().topLeft() = _openStart + RMPoint(70, 177);
CORO_INVOKE_2(_hints[_ctx->h].draw, bigBuf, prim);
}
} else
_lastHotZone = -1;
CORO_END_CODE;
}
void RMInterface::doFrame(RMGfxTargetBuffer &bigBuf, RMPoint mousepos) {
// If needed, add to the OT schedule list
if (!_nInList && _bActive)
bigBuf.addPrim(new RMGfxPrimitive(this));
_mpos = mousepos;
}
void RMInterface::clicked(const RMPoint &mousepos) {
_bActive = true;
_openPos = mousepos;
// Calculate the top left corner of the interface
_openStart = _openPos - RMPoint(_dimx / 2, _dimy / 2);
_lastHotZone = -1;
// Keep it inside the screen
if (_openStart._x < 0)
_openStart._x = 0;
if (_openStart._y < 0)
_openStart._y = 0;
if (_openStart._x + _dimx > RM_SX)
_openStart._x = RM_SX - _dimx;
if (_openStart._y + _dimy > RM_SY)
_openStart._y = RM_SY - _dimy;
// Play the sound effect
g_vm->playUtilSFX(0);
}
bool RMInterface::released(const RMPoint &mousepos, RMTonyAction &action) {
if (!_bActive)
return false;
_bActive = false;
switch (onWhichBox(mousepos)) {
case 0:
action = TA_TAKE;
break;
case 1:
action = TA_TALK;
break;
case 2:
action = TA_USE;
break;
case 3:
action = TA_EXAMINE;
break;
case 4:
action = TA_PERORATE;
break;
default: // No verb
return false;
}
return true;
}
void RMInterface::reset() {
_bActive = false;
}
void RMInterface::setPerorate(bool bOn) {
_bPerorate = bOn;
}
bool RMInterface::getPerorate() {
return _bPerorate;
}
void RMInterface::init() {
RMResRaw inter(RES_I_INTERFACE);
RMRes pal(RES_I_INTERPPAL);
setPriority(191);
RMGfxSourceBuffer::init(inter, inter.width(), inter.height());
loadPaletteWA(RES_I_INTERPAL);
for (int i = 0; i < 5; i++) {
RMResRaw part(RES_I_INTERP1 + i);
_hotzone[i].init(part, part.width(), part.height());
_hotzone[i].loadPaletteWA(pal);
}
_hotbbox[0].setRect(126, 123, 159, 208); // Take
_hotbbox[1].setRect(90, 130, 125, 186); // About
_hotbbox[2].setRect(110, 60, 152, 125);
_hotbbox[3].setRect(56, 51, 93, 99);
_hotbbox[4].setRect(51, 105, 82, 172);
_hints[0].setAlignType(RMText::HRIGHT, RMText::VTOP);
_hints[1].setAlignType(RMText::HRIGHT, RMText::VTOP);
_hints[2].setAlignType(RMText::HRIGHT, RMText::VTOP);
_hints[3].setAlignType(RMText::HRIGHT, RMText::VTOP);
_hints[4].setAlignType(RMText::HRIGHT, RMText::VTOP);
// The text is taken from MPAL for translation
RMMessage msg0(12);
RMMessage msg1(13);
RMMessage msg2(14);
RMMessage msg3(15);
RMMessage msg4(16);
_hints[0].writeText(msg0[0], 1); // Take
_hints[1].writeText(msg1[0], 1); // Talk
_hints[2].writeText(msg2[0], 1); // Use
_hints[3].writeText(msg3[0], 1); // Examine
_hints[4].writeText(msg4[0], 1); // Show Yourself
_bActive = false;
_bPerorate = false;
_lastHotZone = 0;
}
void RMInterface::close() {
destroy();
for (int i = 0; i < 5; i++)
_hotzone[i].destroy();
}
} // End of namespace Tony

241
engines/tony/inventory.h Normal file
View File

@@ -0,0 +1,241 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_INVENTORY_H
#define TONY_INVENTORY_H
#include "common/scummsys.h"
#include "common/mutex.h"
#include "tony/font.h"
#include "tony/game.h"
#include "tony/gfxcore.h"
#include "tony/loc.h"
namespace Tony {
struct RMInventoryItem {
RMItem _icon;
RMGfxSourceBuffer8RLEByteAA *_pointer;
int _status;
};
class RMInventory : public RMGfxWoodyBuffer {
private:
enum InventoryState {
CLOSED,
OPENING,
OPENED,
CLOSING,
SELECTING
};
protected:
int _nItems;
RMInventoryItem *_items;
int _inv[256];
int _nInv;
int _curPutY;
uint32 _curPutTime;
int _curPos;
InventoryState _state;
bool _bHasFocus;
int _nSelectObj;
int _nCombine;
bool _bCombining;
bool _bBlinkingRight, _bBlinkingLeft;
int _miniAction;
RMItem _miniInterface;
RMText _hints[3];
Common::Mutex _csModifyInterface;
protected:
/**
* Prepare the image inventory. It should be recalled whenever the inventory changes
*/
void prepare();
/**
* Check if the mouse Y position is conrrect, even under the inventory portion of the screen
*/
bool checkPointInside(const RMPoint &pt);
public:
RMInventory();
~RMInventory() override;
/**
* Prepare a frame
*/
void doFrame(RMGfxTargetBuffer &bigBuf, RMPointer &ptr, RMPoint mpos, bool bCanOpen);
/**
* Initialization and closing
*/
using RMGfxWoodyBuffer::init;
void init();
void close();
void reset();
/**
* Overload test for removal from OT list
*/
void removeThis(CORO_PARAM, bool &result) override;
/**
* Overload the drawing of the inventory
*/
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
/**
* Method for determining whether the inventory currently has the focus
*/
bool haveFocus(const RMPoint &mpos);
/**
* Method for determining if the mini interface is active
*/
bool miniActive();
/**
* Handle the left mouse click (only when the inventory has the focus)
*/
bool leftClick(const RMPoint &mpos, int &nCombineObj);
/**
* Handle the right mouse button (only when the inventory has the focus)
*/
void rightClick(const RMPoint &mpos);
bool rightRelease(const RMPoint &mpos, RMTonyAction &curAction);
/**
* Warn that an item combine is over
*/
void endCombine();
public:
/**
* Add an item to the inventory
*/
void addItem(int code);
RMInventory &operator+=(RMItem *item);
RMInventory &operator+=(RMItem &item);
RMInventory &operator+=(int code);
/**
* Removes an item
*/
void removeItem(int code);
/**
* We are on an object?
*/
RMItem *whichItemIsIn(const RMPoint &mpt);
bool itemInFocus(const RMPoint &mpt);
/**
* Change the icon of an item
*/
void changeItemStatus(uint32 dwCode, uint32 dwStatus);
/**
* Save methods
*/
int getSaveStateSize();
void saveState(byte *state);
int loadState(byte *state);
};
class RMInterface : public RMGfxSourceBuffer8RLEByte {
private:
bool _bActive;
RMPoint _mpos;
RMPoint _openPos;
RMPoint _openStart;
RMText _hints[5];
RMGfxSourceBuffer8RLEByte _hotzone[5];
RMRect _hotbbox[5];
bool _bPerorate;
int _lastHotZone;
protected:
/**
* Return which box a given point is in
*/
int onWhichBox(RMPoint pt);
public:
RMInterface();
~RMInterface() override;
/**
* The usual DoFrame (poll the graphics engine)
*/
void doFrame(RMGfxTargetBuffer &bigBuf, RMPoint mousepos);
/**
* TRUE if it is active (you can select items)
*/
bool active();
/**
* Initialization
*/
using RMGfxSourceBuffer8RLEByte::init;
void init();
void close();
/**
* Reset the interface
*/
void reset();
/**
* Warns of mouse clicks and releases
*/
void clicked(const RMPoint &mousepos);
bool released(const RMPoint &mousepos, RMTonyAction &action);
/**
* Enables or disables the fifth verb
*/
void setPerorate(bool bOn);
bool getPerorate();
/**
* Overloaded Draw
*/
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
};
} // End of namespace Tony
#endif

2248
engines/tony/loc.cpp Normal file

File diff suppressed because it is too large Load Diff

571
engines/tony/loc.h Normal file
View File

@@ -0,0 +1,571 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_LOC_H
#define TONY_LOC_H
#include "common/scummsys.h"
#include "common/mutex.h"
#include "common/file.h"
#include "tony/sound.h"
#include "tony/utils.h"
namespace Tony {
/****************************************************************************\
* Various defines
\****************************************************************************/
/**
* Valid color modes
*/
typedef enum {
CM_256,
CM_65K
} RMColorMode;
/****************************************************************************\
* Class declarations
\****************************************************************************/
/**
* Generic palette
*/
class RMPalette {
public:
byte _data[1024];
public:
void readFromStream(Common::ReadStream &ds);
};
/**
* Sound effect of an object
*/
class RMSfx {
public:
Common::String _name;
FPSfx *_fx;
bool _bPlayingLoop;
public:
RMSfx();
virtual ~RMSfx();
void play(bool bLoop = false);
void setVolume(int vol);
void pause(bool bPause);
void stop();
void readFromStream(Common::ReadStream &ds, bool bLOX = false);
};
/**
* Object pattern
*/
class RMPattern {
public:
// Type of slot
enum RMSlotType {
DUMMY1 = 0,
DUMMY2,
SPRITE,
SOUND,
COMMAND,
SPECIAL
};
// Class slot
class RMSlot {
private:
RMPoint _pos; // Child co-ordinates
public:
RMSlotType _type;
int _data;
byte _flag;
public:
RMPoint pos() {
return _pos;
}
void readFromStream(Common::ReadStream &ds, bool bLOX = false);
};
public:
Common::String _name;
private:
int _speed;
RMPoint _pos; // Parent coordinates
RMPoint _curPos; // Parent + child coordinates
int _bLoop;
int _nSlots;
int _nCurSlot;
int _nCurSprite;
RMSlot *_slots;
uint32 _nStartTime;
public:
RMPattern();
virtual ~RMPattern();
// A warning that the pattern now and the current
int init(RMSfx *sfx, bool bPlayP0 = false, byte *bFlag = NULL);
// Update the pattern, checking to see if it's time to change slot and executing
// any associated commands
int update(uint32 hEndPattern, byte &bFlag, RMSfx *sfx);
// Stop a sound effect
void stopSfx(RMSfx *sfx);
// Reads the position of the pattern
RMPoint pos();
void readFromStream(Common::ReadStream &ds, bool bLOX);
private:
void updateCoord();
};
/**
* Sprite (frame) animation of an item
*/
class RMSprite : public RMGfxTask {
public:
Common::String _name;
RMRect _rcBox;
protected:
RMGfxSourceBuffer *_buf;
public:
RMSprite();
~RMSprite() override;
void init(RMGfxSourceBuffer *buf);
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
void setPalette(byte *lpBuf);
void getSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy);
void LOXGetSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy);
void readFromStream(Common::SeekableReadStream &ds, bool bLOX = false);
};
/**
* Data on an item
*/
class RMItem : public RMGfxTask {
public:
Common::String _name;
protected:
int _z;
RMPoint _pos; // Coordinate ancestor
RMColorMode _cm;
RMPoint _curScroll;
byte _FX;
byte _FXparm;
virtual int getCurPattern();
private:
int _nCurPattern;
int _mpalCode;
RMPoint _hot;
RMRect _rcBox;
int _nSprites, _nSfx, _nPatterns;
byte _bPal;
RMPalette _pal;
RMSprite *_sprites;
RMSfx *_sfx;
RMPattern *_patterns;
byte _bCurFlag;
int _nCurSprite;
bool _bIsActive;
uint32 _hEndPattern;
bool _bInitCurPattern;
public:
RMPoint calculatePos();
public:
RMItem();
~RMItem() override;
// Process to make the object move on any animations.
// Returns TRUE if it should be redrawn on the next frame
bool doFrame(RMGfxTargetBuffer *bigBuf, bool bAddToList = true);
// Sets the current scrolling position
void setScrollPosition(const RMPoint &scroll);
// Overloading of check whether to remove from active list
void removeThis(CORO_PARAM, bool &result) override;
// Overloaded Draw
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
// Overloaded priority: it's based on Z ordering
int priority() override;
// Pattern number
int numPattern();
// Set anew animation pattern, changing abruptly from the current
virtual void setPattern(int nPattern, bool bPlayP0 = false);
// Set a new status
void setStatus(int nStatus);
bool isIn(const RMPoint &pt, int *size = NULL);
RMPoint getHotspot();
bool getName(Common::String &name);
int mpalCode();
// Unload
void unload();
// Wait for the end of the current pattern
void waitForEndPattern(CORO_PARAM, uint32 hCustomSkip = CORO_INVALID_PID_VALUE);
// Sets a new hotspot fro the object
void changeHotspot(const RMPoint &pt);
void setInitCurPattern(bool status);
void playSfx(int nSfx);
void readFromStream(Common::SeekableReadStream &ds, bool bLOX = false);
void pauseSound(bool bPause);
protected:
// Create a primitive that has as it's task this item
virtual RMGfxPrimitive *newItemPrimitive();
// Allocate memory for the sprites
virtual RMGfxSourceBuffer *newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE);
};
#define MAXBOXES 50 // Maximum number of allowed boxes
#define MAXHOTSPOT 20 // Maximum nimber of allowed hotspots
class RMBox {
public:
struct Hotspot {
int _hotx, _hoty; // Hotspot coordinates
int _destination; // Hotspot destination
};
public:
int _left, _top, _right, _bottom; // Vertici bounding boxes
int _adj[MAXBOXES]; // List of adjacent bounding boxes
int _numHotspot; // Hotspot number
uint8 _destZ; // Z value for the bounding box
Hotspot _hotspot[MAXHOTSPOT]; // List of hotspots
bool _bActive;
bool _bReversed;
void readFromStream(Common::ReadStream &ds);
};
class RMBoxLoc {
public:
int _numbBox;
RMBox *_boxes;
void readFromStream(Common::ReadStream &ds);
public:
RMBoxLoc();
virtual ~RMBoxLoc();
void recalcAllAdj();
};
#define GAME_BOXES_SIZE 200
class RMGameBoxes {
protected:
RMBoxLoc *_allBoxes[GAME_BOXES_SIZE];
int _nLocBoxes;
public:
RMGameBoxes();
~RMGameBoxes();
void init();
void close();
// Get binding boxes for a given location
RMBoxLoc *getBoxes(int nLoc);
int getLocBoxesCount() const;
// Return the box which contains a given point
int whichBox(int nLoc, const RMPoint &pt);
// Check whether a point is inside a given box
bool isInBox(int nLoc, int nBox, const RMPoint &pt);
// Change the status of a box
void changeBoxStatus(int nLoc, int nBox, int status);
// Save state handling
int getSaveStateSize();
void saveState(byte *buf);
void loadState(byte *buf);
};
class RMCharacter : protected RMItem {
public:
enum Patterns {
PAT_STANDUP = 1,
PAT_STANDDOWN,
PAT_STANDLEFT,
PAT_STANDRIGHT,
PAT_WALKUP,
PAT_WALKDOWN,
PAT_WALKLEFT,
PAT_WALKRIGHT
};
private:
enum CharacterStatus {
STAND,
WALK
};
signed short _walkCount;
int _dx, _dy, _olddx, _olddy;
float _fx, _fy, _slope;
RMPoint _lineStart, _lineEnd, _pathEnd;
signed char _walkSpeed, _walkStatus;
char _minPath;
short _nextBox;
short _path[MAXBOXES];
short _pathLength, _pathCount;
int _curBox;
CharacterStatus _status;
int _curSpeed;
bool _bEndOfPath;
uint32 _hEndOfPath;
Common::Mutex _csMove;
int _curLocation;
bool _bRemoveFromOT;
bool _bMovingWithoutMinpath;
RMGameBoxes *_theBoxes;
RMPoint _fixedScroll;
private:
int inWhichBox(const RMPoint &pt);
bool findPath(short source, short destination);
RMPoint searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint point);
RMPoint nearestPoint(const RMPoint &punto);
void goTo(CORO_PARAM, RMPoint destcoord, bool bReversed = false);
short scanLine(const RMPoint &point);
RMPoint invScanLine(const RMPoint &point);
RMPoint nearestHotSpot(int sourcebox, int destbox);
void newBoxEntered(int nBox);
protected:
bool _bMoving;
bool _bDrawNow;
bool _bNeedToStop;
public:
RMCharacter();
~RMCharacter() override;
void linkToBoxes(RMGameBoxes *theBoxes);
void removeThis(CORO_PARAM, bool &result) override;
// Update the position of a character
void doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int loc);
// Overloaded draw
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
// TRUE if you just stopped
bool endOfPath();
// Change the pattern of a character to STOP
virtual void stop(CORO_PARAM);
// Check if the character is moving
bool isMoving();
// Move the character to a certain position
void move(CORO_PARAM, RMPoint pt, bool *result = NULL);
// Place the character in a certain position WITHOUT moving
void setPosition(const RMPoint &pt, int newloc = -1);
// Wait for the end of movement
void waitForEndMovement(CORO_PARAM);
void setFixedScroll(const RMPoint &fix);
void setSpeed(int speed);
};
class RMWipe : public RMGfxTask {
private:
bool _bFading;
bool _bEndFade;
bool _bUnregister;
uint32 _hUnregistered;
int _nFadeStep;
uint32 _hEndOfFade;
bool _bMustRegister;
RMItem _wip0r;
public:
RMWipe();
~RMWipe() override;
void doFrame(RMGfxTargetBuffer &bigBuf);
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
void initFade(int type);
void closeFade();
void waitForFadeEnd(CORO_PARAM);
void unregister() override;
void removeThis(CORO_PARAM, bool &result) override;
int priority() override;
};
/**
* Location
*/
class RMLocation : public RMGfxTaskSetPrior {
public:
Common::String _name; // Name
private:
RMColorMode _cmode; // Color mode
RMGfxSourceBuffer *_buf; // Location picture
int _nItems; // Number of objects
RMItem *_items; // Objects
RMPoint _curScroll; // Current scroll position
RMPoint _fixedScroll;
RMPoint _prevScroll; // Previous scroll position
RMPoint _prevFixedScroll;
public:
// @@@@@@@@@@@@@@@@@@@@@@@
RMPoint TEMPTonyStart;
RMPoint TEMPGetTonyStart();
int TEMPNumLoc;
int TEMPGetNumLoc();
public:
RMLocation();
~RMLocation() override;
// Load variations
bool load(Common::SeekableReadStream &ds);
bool loadLOX(Common::SeekableReadStream &ds);
// Unload
void unload();
// Overloaded draw
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
// Prepare a frame by drawing the location and all it's items
void doFrame(RMGfxTargetBuffer *bigBuf);
// Return the item at a given point
RMItem *whichItemIsIn(const RMPoint &pt);
// Return the item based on it's MPAL code
RMItem *getItemFromCode(uint32 dwCode);
// Set the current scroll position
void setScrollPosition(const RMPoint &scroll);
// Sets an additinal offset for scrolling
void setFixedScroll(const RMPoint &scroll);
// Update the scrolling coordinates to display the specified point
void updateScrolling(const RMPoint &ptShowThis);
// Read the current scroll position
RMPoint scrollPosition();
// Pause sound
void pauseSound(bool bPause);
};
/**
* MPAL message, composed of more ASCIIZ
*/
class RMMessage {
private:
char *_lpMessage;
char *_lpPeriods[256];
int _nPeriods;
private:
void parseMessage();
public:
RMMessage();
RMMessage(uint32 dwId);
virtual ~RMMessage();
void load(uint32 dwId);
bool isValid();
int numPeriods();
char *period(int num);
char *operator[](int num);
};
} // End of namespace Tony
#endif /* TONY_H */

160
engines/tony/metaengine.cpp Normal file
View File

@@ -0,0 +1,160 @@
/* 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 "base/plugins.h"
#include "common/memstream.h"
#include "engines/advancedDetector.h"
#include "common/system.h"
#include "graphics/surface.h"
#include "tony/tony.h"
#include "tony/game.h"
#include "tony/detection.h"
namespace Tony {
uint32 TonyEngine::getFeatures() const {
return _gameDescription->desc.flags;
}
Common::Language TonyEngine::getLanguage() const {
return _gameDescription->desc.language;
}
bool TonyEngine::getIsDemo() const {
return _gameDescription->desc.flags & ADGF_DEMO;
}
bool TonyEngine::isCompressed() const {
return _gameDescription->desc.flags & GF_COMPRESSED;
}
} // End of namespace Tony
class TonyMetaEngine : public AdvancedMetaEngine<Tony::TonyGameDescription> {
public:
const char *getName() const override {
return "tony";
}
bool hasFeature(MetaEngineFeature f) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const Tony::TonyGameDescription *desc) const override;
SaveStateList listSaves(const char *target) const override;
int getMaximumSaveSlot() const override;
bool removeSaveState(const char *target, int slot) const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
};
bool TonyMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSimpleSavesNames) ||
(f == kSavesSupportThumbnail);
}
bool Tony::TonyEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
Common::Error TonyMetaEngine::createInstance(OSystem *syst, Engine **engine, const Tony::TonyGameDescription *desc) const {
*engine = new Tony::TonyEngine(syst,desc);
return Common::kNoError;
}
SaveStateList TonyMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::StringArray filenames;
Common::String saveDesc;
Common::String pattern = "tony.0##";
filenames = saveFileMan->listSavefiles(pattern);
SaveStateList saveList;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
int slotNum = atoi(file->c_str() + file->size() - 3);
if (slotNum >= 0 && slotNum <= 999) {
byte thumbnailData[160 * 120 * 2];
Common::String saveName;
byte difficulty;
if (Tony::RMOptionScreen::loadThumbnailFromSaveState(slotNum, thumbnailData, saveName, difficulty)) {
// Add the save name to the savegame list
saveList.push_back(SaveStateDescriptor(this, slotNum, saveName));
}
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
int TonyMetaEngine::getMaximumSaveSlot() const {
return 99;
}
bool TonyMetaEngine::removeSaveState(const char *target, int slot) const {
Common::String filename = Tony::TonyEngine::getSaveStateFileName(slot);
return g_system->getSavefileManager()->removeSavefile(filename);
}
SaveStateDescriptor TonyMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::String saveName;
byte difficulty;
Graphics::Surface *to = new Graphics::Surface();
to->create(160, 120, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
if (Tony::RMOptionScreen::loadThumbnailFromSaveState(slot, (byte *)to->getPixels(), saveName, difficulty)) {
#ifdef SCUMM_BIG_ENDIAN
uint16 *pixels = (uint16 *)to->getPixels();
for (int i = 0; i < to->w * to->h; ++i)
pixels[i] = READ_LE_UINT16(pixels + i);
#endif
// Create the return descriptor
SaveStateDescriptor desc(this, slot, saveName);
desc.setDeletableFlag(true);
desc.setWriteProtectedFlag(false);
desc.setThumbnail(to);
return desc;
}
delete to;
return SaveStateDescriptor();
}
#if PLUGIN_ENABLED_DYNAMIC(TONY)
REGISTER_PLUGIN_DYNAMIC(TONY, PLUGIN_TYPE_ENGINE, TonyMetaEngine);
#else
REGISTER_PLUGIN_STATIC(TONY, PLUGIN_TYPE_ENGINE, TonyMetaEngine);
#endif

36
engines/tony/module.mk Normal file
View File

@@ -0,0 +1,36 @@
MODULE := engines/tony
MODULE_OBJS := \
custom.o \
debugger.o \
font.o \
game.o \
gfxcore.o \
gfxengine.o \
globals.o \
input.o \
inventory.o \
loc.o \
metaengine.o \
sound.o \
tony.o \
tonychar.o \
utils.o \
window.o \
mpal/expr.o \
mpal/loadmpc.o \
mpal/memory.o \
mpal/mpal.o \
mpal/mpalutils.o \
mpal/lzo.o
# This module can be built as a plugin
ifeq ($(ENABLE_TONY), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

372
engines/tony/mpal/expr.cpp Normal file
View File

@@ -0,0 +1,372 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#include "tony/mpal/mpal.h"
#include "tony/mpal/memory.h"
#include "tony/mpal/mpaldll.h"
#include "tony/tony.h"
namespace Tony {
namespace MPAL {
static const size_t EXPR_LENGTH_SIZE =
#ifndef NO_CXX11_ALIGNAS
alignof(max_align_t)
#else
sizeof(byte)
#endif
;
/**
* Duplicate a mathematical expression.
*
* @param h Handle to the original expression
* @retruns Pointer to the cloned expression
*/
static void *duplicateExpression(MpalHandle h) {
byte *orig, *clone;
orig = (byte *)globalLock(h);
int num = *orig;
LpExpression one = (LpExpression)(orig + EXPR_LENGTH_SIZE);
clone = (byte *)globalAlloc(GMEM_FIXED, sizeof(Expression) * num + EXPR_LENGTH_SIZE);
LpExpression two = (LpExpression)(clone + EXPR_LENGTH_SIZE);
memcpy(clone, orig, sizeof(Expression) * num + EXPR_LENGTH_SIZE);
for (int i = 0; i < num; i++) {
if (one->_type == ELT_PARENTH) {
two->_type = ELT_PARENTH2;
two->_val._pson = duplicateExpression(two->_val._son);
}
++one;
++two;
}
globalUnlock(h);
return clone;
}
static int32 Compute(int32 a, int32 b, byte symbol) {
switch (symbol) {
case OP_MUL:
return a * b;
case OP_DIV:
return a / b;
case OP_MODULE:
return a % b;
case OP_ADD:
return a + b;
case OP_SUB:
return a - b;
case OP_SHL:
return a << b;
case OP_SHR:
return a >> b;
case OP_MINOR:
return a < b;
case OP_MAJOR:
return a > b;
case OP_MINEQ:
return a <= b;
case OP_MAJEQ:
return a >= b;
case OP_EQUAL:
return a == b;
case OP_NOEQUAL:
return a != b;
case OP_BITAND:
return a & b;
case OP_BITXOR:
return a ^ b;
case OP_BITOR:
return a | b;
case OP_AND:
return a && b;
case OP_OR:
return a || b;
default:
GLOBALS._mpalError = 1;
break;
}
return 0;
}
static void solve(LpExpression one, int num) {
LpExpression two, three;
while (num > 1) {
two = one + 1;
if ((two->_symbol == 0) || (one->_symbol & 0xF0) <= (two->_symbol & 0xF0)) {
two->_val._num = Compute(one->_val._num, two->_val._num, one->_symbol);
memmove(one, two, (num - 1) * sizeof(Expression));
--num;
} else {
int j = 1;
three = two + 1;
while ((three->_symbol != 0) && (two->_symbol & 0xF0) > (three->_symbol & 0xF0)) {
++two;
++three;
++j;
}
three->_val._num = Compute(two->_val._num, three->_val._num, two->_symbol);
memmove(two, three, (num - j - 1) * sizeof(Expression));
--num;
}
}
}
/**
* Calculates the result of a mathematical expression, replacing the current
* value of any variable.
*
* @param expr Pointer to an expression duplicated by DuplicateExpression
* @returns Value
*/
static int32 evaluateAndFreeExpression(void *expr) {
int num = *(byte *)expr;
LpExpression one = (LpExpression)((byte *)expr + EXPR_LENGTH_SIZE);
// 1) Substitutions of variables
LpExpression cur = one;
for (int i = 0; i < num; i++, cur++) {
if (cur->_type == ELT_VAR) {
cur->_type = ELT_NUMBER;
cur->_val._num = varGetValue(cur->_val._name);
}
}
// 2) Replacement of brackets (using recursive calls)
cur = one;
for (int i = 0; i < num; i++, cur++) {
if (cur->_type == ELT_PARENTH2) {
cur->_type = ELT_NUMBER;
cur->_val._num = evaluateAndFreeExpression(cur->_val._pson);
}
}
// 3) algebraic resolution
solve(one, num);
int32 val = one->_val._num;
globalDestroy(expr);
return val;
}
/**
* Parses a mathematical expression from the MPC file
*
* @param buf Buffer containing the expression to evaluate
* @param h Pointer to a handle that, at the end of execution,
* will point to the area of memory containing the parsed expression
* @returns Pointer to the buffer immediately after the expression, or NULL if error.
*/
const byte *parseExpression(const byte *lpBuf, const Common::UnalignedPtr<MpalHandle> &h) {
byte *start;
byte num = *lpBuf;
lpBuf++;
if (num == 0)
return NULL;
h.store(globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, num * sizeof(Expression) + EXPR_LENGTH_SIZE));
if (h.load() == NULL)
return NULL;
start = (byte *)globalLock(h.load());
*start = num;
LpExpression cur = (LpExpression)(start + EXPR_LENGTH_SIZE);
for (uint32 i = 0;i < num; i++) {
cur->_type = *(lpBuf);
// *(lpBuf + 1) contains the unary operator, unused => skipped
lpBuf += 2;
switch (cur->_type) {
case ELT_NUMBER:
cur->_val._num = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
break;
case ELT_VAR:
// As name is a byte, there is no alignment rule
cur->_val._name = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, (*lpBuf) + 1);
if (cur->_val._name == NULL)
return NULL;
memcpy(cur->_val._name, lpBuf + 1, *lpBuf);
lpBuf += *lpBuf + 1;
break;
case ELT_PARENTH:
lpBuf = parseExpression(lpBuf, &cur->_val._son);
if (lpBuf == NULL)
return NULL;
break;
default:
return NULL;
}
cur->_symbol = *lpBuf;
lpBuf++;
cur++;
}
if (*lpBuf != 0)
return NULL;
lpBuf++;
return lpBuf;
}
/**
* Calculate the value of a mathematical expression
*
* @param h Handle to the expression
* @returns Numeric value
*/
int32 evaluateExpression(MpalHandle h) {
lockVar();
int32 ret = evaluateAndFreeExpression(duplicateExpression(h));
unlockVar();
return ret;
}
/**
* Compare two mathematical expressions together
*
* @param h1 Expression to be compared
* @param h2 Expression to be compared
*/
bool compareExpressions(MpalHandle h1, MpalHandle h2) {
byte *e1, *e2;
e1 = (byte *)globalLock(h1);
e2 = (byte *)globalLock(h2);
int num1 = *e1;
int num2 = *e2;
if (num1 != num2) {
globalUnlock(h1);
globalUnlock(h2);
return false;
}
LpExpression one = (LpExpression)(e1 + EXPR_LENGTH_SIZE);
LpExpression two = (LpExpression)(e2 + EXPR_LENGTH_SIZE);
for (int i = 0; i < num1; i++) {
if (one->_type != two->_type || (i != num1 - 1 && one->_symbol != two->_symbol)) {
globalUnlock(h1);
globalUnlock(h2);
return false;
}
switch (one->_type) {
case ELT_NUMBER:
if (one->_val._num != two->_val._num) {
globalUnlock(h1);
globalUnlock(h2);
return false;
}
break;
case ELT_VAR:
if (strcmp(one->_val._name, two->_val._name) != 0) {
globalUnlock(h1);
globalUnlock(h2);
return false;
}
break;
case ELT_PARENTH:
if (!compareExpressions(one->_val._son, two->_val._son)) {
globalUnlock(h1);
globalUnlock(h2);
return false;
}
break;
default:
break;
}
++one;
++two;
}
globalUnlock(h1);
globalUnlock(h2);
return true;
}
/**
* Frees an expression that was previously parsed
*
* @param h Handle for the expression
*/
void freeExpression(MpalHandle h) {
byte *data = (byte *)globalLock(h);
int num = *data;
LpExpression cur = (LpExpression)(data + EXPR_LENGTH_SIZE);
for (int i = 0; i < num; ++i, ++cur) {
switch (cur->_type) {
case ELT_VAR:
globalDestroy(cur->_val._name);
break;
case ELT_PARENTH:
freeExpression(cur->_val._son);
break;
default:
break;
}
}
globalUnlock(h);
globalFree(h);
}
} // end of namespace MPAL
} // end of namespace Tony

138
engines/tony/mpal/expr.h Normal file
View File

@@ -0,0 +1,138 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef MPAL_EXPR_H
#define MPAL_EXPR_H
#include "tony/mpal/memory.h"
namespace Tony {
namespace MPAL {
/**
* @defgroup Mathematical operations
*/
//@{
#define OP_MUL ((1 << 4) | 0)
#define OP_DIV ((1 << 4) | 1)
#define OP_MODULE ((1 << 4) | 2)
#define OP_ADD ((2 << 4) | 0)
#define OP_SUB ((2 << 4) | 1)
#define OP_SHL ((3 << 4) | 0)
#define OP_SHR ((3 << 4) | 1)
#define OP_MINOR ((4 << 4) | 0)
#define OP_MAJOR ((4 << 4) | 1)
#define OP_MINEQ ((4 << 4) | 2)
#define OP_MAJEQ ((4 << 4) | 3)
#define OP_EQUAL ((5 << 4) | 0)
#define OP_NOEQUAL ((5 << 4) | 1)
#define OP_BITAND ((6 << 4) | 0)
#define OP_BITXOR ((7 << 4) | 0)
#define OP_BITOR ((8 << 4) | 0)
#define OP_AND ((9 << 4) | 0)
#define OP_OR ((10 << 4) | 0)
//@}
/**
* @defgroup Structures
*/
//@{
/**
* Mathematical framework to manage operations
*/
typedef struct {
byte _type; // Object Type (see enum ExprListTypes)
union {
int32 _num; // Identifier (if type == ELT_NUMBER)
char *_name; // Variable name (if type == ELT_VAR)
MpalHandle _son; // Handle expressions (if type == ELT_PARENTH)
void *_pson; // Handle lockato (if type == ELT_PARENTH2)
} _val;
byte _symbol; // Mathematic symbols (see #define OP_*)
} Expression;
typedef Expression *LpExpression;
//@}
/**
* Object types that can be contained in an EXPRESSION structure
*/
enum ExprListTypes {
ELT_NUMBER = 1,
ELT_VAR = 2,
ELT_PARENTH = 3,
ELT_PARENTH2 = 4
};
/****************************************************************************\
* Function Prototypes
\****************************************************************************/
/**
* Parses a mathematical expression from the MPC file
*
* @param buf Buffer containing the expression to evaluate
* @param h Pointer to a handle that, at the end of execution,
* will point to the area of memory containing the parsed expression
* @returns Pointer to the buffer immediately after the expression, or NULL if error.
*/
const byte *parseExpression(const byte *lpBuf, const Common::UnalignedPtr<MpalHandle> &h);
/**
* Calculate the value of a mathematical expression
*
* @param h Handle to the expression
* @returns Numeric value
*/
int32 evaluateExpression(MpalHandle h);
/**
* Compare two mathematical expressions together
*
* @param h1 Expression to be compared
* @param h2 Expression to be compared
*/
bool compareExpressions(MpalHandle h1, MpalHandle h2);
/**
* Frees an expression that was previously parsed
*
* @param h Handle for the expression
*/
void freeExpression(MpalHandle h);
} // end of namespace MPAL
} // end of namespace Tony
#endif

View File

@@ -0,0 +1,786 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#include "mpal.h"
#include "mpaldll.h"
#include "memory.h"
#include "tony/tony.h"
namespace Tony {
namespace MPAL {
/****************************************************************************\
* Static functions
\****************************************************************************/
static bool compareCommands(struct Command *cmd1, struct Command *cmd2) {
if (cmd1->_type == 2 && cmd2->_type == 2) {
if (strcmp(cmd1->_lpszVarName, cmd2->_lpszVarName) == 0 &&
compareExpressions(cmd1->_expr, cmd2->_expr))
return true;
else
return false;
} else
return (memcmp(cmd1, cmd2, sizeof(struct Command)) == 0);
}
/**
* Parses a script from the MPC file, and inserts its data into a structure
*
* @param lpBuf Buffer containing the compiled script.
* @param lpmsScript Pointer to a structure that will be filled with the
* data of the script.
* @returns Pointer to the buffer after the item, or NULL on failure.
*/
static const byte *ParseScript(const byte *lpBuf, LpMpalScript lpmsScript) {
lpmsScript->_nObj = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmsScript->_nMoments = READ_LE_UINT16(lpBuf);
lpBuf += 2;
int curCmd = 0;
for (uint i = 0; i < lpmsScript->_nMoments; i++) {
lpmsScript->_moment[i]._dwTime = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmsScript->_moment[i]._nCmds = *lpBuf;
lpBuf++;
for (int j = 0; j < lpmsScript->_moment[i]._nCmds; j++) {
lpmsScript->_command[curCmd]._type = *lpBuf;
lpBuf++;
switch (lpmsScript->_command[curCmd]._type) {
case 1:
lpmsScript->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
lpBuf += 2;
lpmsScript->_command[curCmd]._arg1 = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmsScript->_command[curCmd]._arg2 = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmsScript->_command[curCmd]._arg3 = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmsScript->_command[curCmd]._arg4 = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
break;
case 2: { // Variable assign
int len = *lpBuf;
lpBuf++;
lpmsScript->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
if (lpmsScript->_command[curCmd]._lpszVarName == NULL)
return NULL;
memcpy(lpmsScript->_command[curCmd]._lpszVarName, lpBuf, len);
lpBuf += len;
lpBuf = parseExpression(lpBuf, &lpmsScript->_command[curCmd]._expr);
if (lpBuf == NULL)
return NULL;
break;
}
default:
return NULL;
}
lpmsScript->_moment[i]._cmdNum[j] = curCmd;
curCmd++;
}
}
return lpBuf;
}
/**
* Frees a script allocated via a previous call to ParseScript
*
* @param lpmsScript Pointer to a script structure
*/
static void FreeScript(LpMpalScript lpmsScript) {
for (int i = 0; i < MAX_COMMANDS_PER_SCRIPT && (lpmsScript->_command[i]._type); ++i, ++lpmsScript) {
if (lpmsScript->_command[i]._type == 2) {
// Variable Assign
globalDestroy(lpmsScript->_command[i]._lpszVarName);
freeExpression(lpmsScript->_command[i]._expr);
}
}
}
/**
* Parses a dialog from the MPC file, and inserts its data into a structure
*
* @param lpBuf Buffer containing the compiled dialog.
* @param lpmdDialog Pointer to a structure that will be filled with the
* data of the dialog.
* @returns Pointer to the buffer after the item, or NULL on failure.
*/
static const byte *parseDialog(const byte *lpBuf, LpMpalDialog lpmdDialog) {
lpmdDialog->_nObj = READ_LE_UINT32(lpBuf);
lpBuf += 4;
// Periods
uint32 num = READ_LE_UINT16(lpBuf);
lpBuf += 2;
if (num >= MAX_PERIODS_PER_DIALOG - 1)
error("Too much periods in dialog #%d", lpmdDialog->_nObj);
uint32 i;
for (i = 0; i < num; i++) {
lpmdDialog->_periodNums[i] = READ_LE_UINT16(lpBuf);
lpBuf += 2;
lpmdDialog->_periods[i] = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, *lpBuf + 1);
byte *lpLock = (byte *)globalLock(lpmdDialog->_periods[i]);
Common::copy(lpBuf + 1, lpBuf + 1 + *lpBuf, lpLock);
globalUnlock(lpmdDialog->_periods[i]);
lpBuf += (*lpBuf) + 1;
}
lpmdDialog->_periodNums[i] = 0;
lpmdDialog->_periods[i] = NULL;
// Groups
num = READ_LE_UINT16(lpBuf);
lpBuf += 2;
uint32 curCmd = 0;
if (num >= MAX_GROUPS_PER_DIALOG)
error("Too much groups in dialog #%d", lpmdDialog->_nObj);
for (i = 0; i < num; i++) {
lpmdDialog->_group[i]._num = READ_LE_UINT16(lpBuf);
lpBuf += 2;
lpmdDialog->_group[i]._nCmds = *lpBuf; lpBuf++;
if (lpmdDialog->_group[i]._nCmds >= MAX_COMMANDS_PER_GROUP)
error("Too much commands in group #%d in dialog #%d", lpmdDialog->_group[i]._num, lpmdDialog->_nObj);
for (uint32 j = 0; j < lpmdDialog->_group[i]._nCmds; j++) {
lpmdDialog->_command[curCmd]._type = *lpBuf;
lpBuf++;
switch (lpmdDialog->_command[curCmd]._type) {
// Call custom function
case 1:
lpmdDialog->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
lpBuf += 2;
lpmdDialog->_command[curCmd]._arg1 = READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmdDialog->_command[curCmd]._arg2 = READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmdDialog->_command[curCmd]._arg3 = READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmdDialog->_command[curCmd]._arg4 = READ_LE_UINT32(lpBuf);
lpBuf += 4;
break;
// Variable assign
case 2: {
uint32 len = *lpBuf;
lpBuf++;
lpmdDialog->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
if (lpmdDialog->_command[curCmd]._lpszVarName == NULL)
return NULL;
Common::copy(lpBuf, lpBuf + len, lpmdDialog->_command[curCmd]._lpszVarName);
lpBuf += len;
lpBuf = parseExpression(lpBuf, &lpmdDialog->_command[curCmd]._expr);
if (lpBuf == NULL)
return NULL;
break;
}
// Do Choice
case 3:
lpmdDialog->_command[curCmd]._nChoice = READ_LE_UINT16(lpBuf);
lpBuf += 2;
break;
default:
return NULL;
}
uint32 kk;
for (kk = 0;kk < curCmd; kk++) {
if (compareCommands(&lpmdDialog->_command[kk], &lpmdDialog->_command[curCmd])) {
lpmdDialog->_group[i]._cmdNum[j] = kk;
// Free any data allocated for the duplictaed command
if (lpmdDialog->_command[curCmd]._type == 2) {
globalDestroy(lpmdDialog->_command[curCmd]._lpszVarName);
freeExpression(lpmdDialog->_command[curCmd]._expr);
lpmdDialog->_command[curCmd]._lpszVarName = NULL;
lpmdDialog->_command[curCmd]._expr = 0;
lpmdDialog->_command[curCmd]._type = 0;
}
break;
}
}
if (kk == curCmd) {
lpmdDialog->_group[i]._cmdNum[j] = curCmd;
curCmd++;
}
}
}
if (curCmd >= MAX_COMMANDS_PER_DIALOG)
error("Too much commands in dialog #%d", lpmdDialog->_nObj);
// Choices
num = READ_LE_UINT16(lpBuf);
lpBuf += 2;
if (num >= MAX_CHOICES_PER_DIALOG)
error("Too much choices in dialog #%d", lpmdDialog->_nObj);
for (i = 0; i < num; i++) {
lpmdDialog->_choice[i]._nChoice = READ_LE_UINT16(lpBuf);
lpBuf += 2;
uint32 num2 = *lpBuf++;
if (num2 >= MAX_SELECTS_PER_CHOICE)
error("Too much selects in choice #%d in dialog #%d", lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj);
for (uint32 j = 0; j < num2; j++) {
// When
switch (*lpBuf++) {
case 0:
lpmdDialog->_choice[i]._select[j]._when = NULL;
break;
case 1:
lpBuf = parseExpression(lpBuf, &lpmdDialog->_choice[i]._select[j]._when);
if (lpBuf == NULL)
return NULL;
break;
case 2:
return NULL;
default:
break;
}
// Attrib
lpmdDialog->_choice[i]._select[j]._attr = *lpBuf++;
// Data
lpmdDialog->_choice[i]._select[j]._dwData = READ_LE_UINT32(lpBuf);
lpBuf += 4;
// PlayGroup
uint32 num3 = *lpBuf++;
if (num3 >= MAX_PLAYGROUPS_PER_SELECT)
error("Too much playgroups in select #%d in choice #%d in dialog #%d", j, lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj);
for (uint32 z = 0; z < num3; z++) {
lpmdDialog->_choice[i]._select[j]._wPlayGroup[z] = READ_LE_UINT16(lpBuf);
lpBuf += 2;
}
lpmdDialog->_choice[i]._select[j]._wPlayGroup[num3] = 0;
}
// Mark the last selection
lpmdDialog->_choice[i]._select[num2]._dwData = 0;
}
lpmdDialog->_choice[num]._nChoice = 0;
return lpBuf;
}
/**
* Parses an item from the MPC file, and inserts its data into a structure
*
* @param lpBuf Buffer containing the compiled dialog.
* @param lpmiItem Pointer to a structure that will be filled with the
* data of the item.
* @returns Pointer to the buffer after the item, or NULL on failure.
* @remarks It's necessary that the structure that is passed has been
* completely initialized to 0 beforehand.
*/
static const byte *parseItem(const byte *lpBuf, LpMpalItem lpmiItem) {
lpmiItem->_nObj = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
byte len = *lpBuf;
lpBuf++;
memcpy(lpmiItem->_lpszDescribe, lpBuf, MIN((byte)MAX_DESCRIBE_SIZE, len));
lpBuf += len;
if (len >= MAX_DESCRIBE_SIZE)
error("Describe too long in item #%d", lpmiItem->_nObj);
lpmiItem->_nActions=*lpBuf;
lpBuf++;
// Allocation action
if (lpmiItem->_nActions > 0)
lpmiItem->_action = (ItemAction *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(struct ItemAction) * (int)lpmiItem->_nActions);
uint32 curCmd = 0;
for (uint32 i = 0; i < lpmiItem->_nActions; i++) {
lpmiItem->_action[i]._num = *lpBuf;
lpBuf++;
lpmiItem->_action[i]._wParm = READ_LE_UINT16(lpBuf);
lpBuf += 2;
if (lpmiItem->_action[i]._num == 0xFF) {
lpmiItem->_action[i]._wTime = READ_LE_UINT16(lpBuf);
lpBuf += 2;
lpmiItem->_action[i]._perc = *lpBuf;
lpBuf++;
}
if (*lpBuf == 0) {
lpBuf++;
lpmiItem->_action[i]._when = NULL;
} else {
lpBuf++;
lpBuf = parseExpression(lpBuf,&lpmiItem->_action[i]._when);
if (lpBuf == NULL)
return NULL;
}
lpmiItem->_action[i]._nCmds=*lpBuf;
lpBuf++;
if (lpmiItem->_action[i]._nCmds >= MAX_COMMANDS_PER_ACTION)
error("Too much commands in action #%d in item #%d", lpmiItem->_action[i]._num, lpmiItem->_nObj);
for (uint32 j = 0; j < lpmiItem->_action[i]._nCmds; j++) {
lpmiItem->_command[curCmd]._type = *lpBuf;
lpBuf++;
switch (lpmiItem->_command[curCmd]._type) {
case 1: // Call custom function
lpmiItem->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
lpBuf += 2;
lpmiItem->_command[curCmd]._arg1 = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmiItem->_command[curCmd]._arg2 = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmiItem->_command[curCmd]._arg3 = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmiItem->_command[curCmd]._arg4 = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
break;
case 2: // Variable assign
len = *lpBuf;
lpBuf++;
lpmiItem->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
if (lpmiItem->_command[curCmd]._lpszVarName == NULL)
return NULL;
memcpy(lpmiItem->_command[curCmd]._lpszVarName, lpBuf, len);
lpBuf += len;
lpBuf = parseExpression(lpBuf, &lpmiItem->_command[curCmd]._expr);
if (lpBuf == NULL)
return NULL;
break;
default:
return NULL;
}
uint32 kk;
for (kk = 0; kk < curCmd; kk++) {
if (compareCommands(&lpmiItem->_command[kk], &lpmiItem->_command[curCmd])) {
lpmiItem->_action[i]._cmdNum[j] = kk;
// Free any data allocated for the duplictaed command
if (lpmiItem->_command[curCmd]._type == 2) {
globalDestroy(lpmiItem->_command[curCmd]._lpszVarName);
freeExpression(lpmiItem->_command[curCmd]._expr);
lpmiItem->_command[curCmd]._lpszVarName = NULL;
lpmiItem->_command[curCmd]._expr = 0;
lpmiItem->_command[curCmd]._type = 0;
}
break;
}
}
if (kk == curCmd) {
lpmiItem->_action[i]._cmdNum[j] = curCmd;
curCmd++;
if (curCmd >= MAX_COMMANDS_PER_ITEM) {
error("Too much commands in item #%d", lpmiItem->_nObj);
//curCmd=0;
}
}
}
}
lpmiItem->_dwRes = READ_LE_UINT32(lpBuf);
lpBuf += 4;
return lpBuf;
}
/**
* Frees an item parsed from a prior call to ParseItem
*
* @param lpmiItem Pointer to an item structure
*/
static void freeItem(LpMpalItem lpmiItem) {
// Free the actions
if (lpmiItem->_action) {
for (int i = 0; i < lpmiItem->_nActions; ++i) {
if (lpmiItem->_action[i]._when != 0)
freeExpression(lpmiItem->_action[i]._when);
}
globalDestroy(lpmiItem->_action);
}
// Free the commands
for (int i = 0; i < MAX_COMMANDS_PER_ITEM && (lpmiItem->_command[i]._type); ++i) {
if (lpmiItem->_command[i]._type == 2) {
// Variable Assign
globalDestroy(lpmiItem->_command[i]._lpszVarName);
freeExpression(lpmiItem->_command[i]._expr);
}
}
}
/**
* Parses a location from the MPC file, and inserts its data into a structure
*
* @param lpBuf Buffer containing the compiled location.
* @param lpmiLocation Pointer to a structure that will be filled with the
* data of the location.
* @returns Pointer to the buffer after the location, or NULL on failure.
*/
static const byte *ParseLocation(const byte *lpBuf, LpMpalLocation lpmlLocation) {
lpmlLocation->_nObj = (int32)READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpmlLocation->_dwXlen = READ_LE_UINT16(lpBuf);
lpBuf += 2;
lpmlLocation->_dwYlen = READ_LE_UINT16(lpBuf);
lpBuf += 2;
lpmlLocation->_dwPicRes = READ_LE_UINT32(lpBuf);
lpBuf += 4;
return lpBuf;
}
/****************************************************************************\
* Exported functions
\****************************************************************************/
/**
* @defgroup Exported functions
*/
//@{
/**
* Reads and interprets the MPC file, and create structures for various directives
* in the global variables
*
* @param lpBuf Buffer containing the MPC file data, excluding the header.
* @returns True if succeeded OK, false if failure.
*/
bool parseMpc(const byte *lpBuf) {
byte *lpTemp;
// 1. Variables
if (lpBuf[0] != 'V' || lpBuf[1] != 'A' || lpBuf[2] != 'R' || lpBuf[3] != 'S')
return false;
lpBuf += 4;
GLOBALS._nVars = READ_LE_UINT16(lpBuf);
lpBuf += 2;
GLOBALS._hVars = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MpalVar) * (uint32)GLOBALS._nVars);
if (GLOBALS._hVars == NULL)
return false;
GLOBALS._lpmvVars = (LpMpalVar)globalLock(GLOBALS._hVars);
for (uint16 i = 0; i < GLOBALS._nVars; i++) {
uint16 wLen = *(const byte *)lpBuf;
lpBuf++;
memcpy(GLOBALS._lpmvVars->_lpszVarName, lpBuf, MIN(wLen, (uint16)32));
lpBuf += wLen;
GLOBALS._lpmvVars->_dwVal = READ_LE_UINT32(lpBuf);
lpBuf += 4;
lpBuf++; // Skip 'ext'
GLOBALS._lpmvVars++;
}
globalUnlock(GLOBALS._hVars);
// 2. Messages
if (lpBuf[0] != 'M' || lpBuf[1] != 'S' || lpBuf[2] != 'G' || lpBuf[3] != 'S')
return false;
lpBuf += 4;
GLOBALS._nMsgs = READ_LE_UINT16(lpBuf);
lpBuf += 2;
#ifdef NEED_LOCK_MSGS
GLOBALS._hMsgs = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MpalMsg) * (uint32)GLOBALS._nMsgs);
if (GLOBALS._hMsgs == NULL)
return false;
GLOBALS._lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs);
#else
GLOBALS._lpmmMsgs=(LPMPALMSG)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MPALMSG) * (uint32)GLOBALS._nMsgs);
if (GLOBALS._lpmmMsgs==NULL)
return false;
#endif
for (uint16 i = 0; i < GLOBALS._nMsgs; i++) {
GLOBALS._lpmmMsgs->_wNum = READ_LE_UINT16(lpBuf);
lpBuf += 2;
uint16 j;
for (j = 0; lpBuf[j] != 0;)
j += lpBuf[j] + 1;
GLOBALS._lpmmMsgs->_hText = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, j + 1);
lpTemp = (byte *)globalLock(GLOBALS._lpmmMsgs->_hText);
for (j = 0; lpBuf[j] != 0;) {
memcpy(lpTemp, &lpBuf[j + 1], lpBuf[j]);
lpTemp += lpBuf[j];
*lpTemp ++= '\0';
j += lpBuf[j] + 1;
}
lpBuf += j + 1;
*lpTemp = '\0';
globalUnlock(GLOBALS._lpmmMsgs->_hText);
GLOBALS._lpmmMsgs++;
}
#ifdef NEED_LOCK_MSGS
globalUnlock(GLOBALS._hMsgs);
#endif
// 3. Objects
if (lpBuf[0] != 'O' || lpBuf[1] != 'B' || lpBuf[2] != 'J' || lpBuf[3] != 'S')
return false;
lpBuf += 4;
GLOBALS._nObjs = READ_LE_UINT16(lpBuf);
lpBuf += 2;
// Check out the dialogs
GLOBALS._nDialogs = 0;
GLOBALS._hDialogs = GLOBALS._lpmdDialogs = NULL;
if (*((const byte *)lpBuf + 2) == 6 && strncmp((const char *)lpBuf + 3, "Dialog", 6) == 0) {
GLOBALS._nDialogs = READ_LE_UINT16(lpBuf);
lpBuf += 2;
GLOBALS._hDialogs = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nDialogs * sizeof(MpalDialog));
if (GLOBALS._hDialogs == NULL)
return false;
GLOBALS._lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs);
for (uint16 i = 0; i < GLOBALS._nDialogs; i++) {
if ((lpBuf = parseDialog(lpBuf + 7, &GLOBALS._lpmdDialogs[i])) == NULL)
return false;
}
globalUnlock(GLOBALS._hDialogs);
}
// Check the items
GLOBALS._nItems = 0;
GLOBALS._hItems = GLOBALS._lpmiItems = NULL;
if (*(lpBuf + 2) == 4 && strncmp((const char *)lpBuf + 3, "Item", 4) == 0) {
GLOBALS._nItems = READ_LE_UINT16(lpBuf);
lpBuf += 2;
// Allocate memory and read them in
GLOBALS._hItems = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nItems * sizeof(MpalItem));
if (GLOBALS._hItems == NULL)
return false;
GLOBALS._lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems);
for (uint16 i = 0; i < GLOBALS._nItems; i++) {
if ((lpBuf = parseItem(lpBuf + 5, &GLOBALS._lpmiItems[i])) == NULL)
return false;
}
globalUnlock(GLOBALS._hItems);
}
// Check the locations
GLOBALS._nLocations = 0;
GLOBALS._hLocations = GLOBALS._lpmlLocations = NULL;
if (*(lpBuf + 2) == 8 && strncmp((const char *)lpBuf + 3, "Location", 8) == 0) {
GLOBALS._nLocations = READ_LE_UINT16(lpBuf);
lpBuf += 2;
// Allocate memory and read them in
GLOBALS._hLocations = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nLocations * sizeof(MpalLocation));
if (GLOBALS._hLocations == NULL)
return false;
GLOBALS._lpmlLocations = (LpMpalLocation)globalLock(GLOBALS._hLocations);
for (uint16 i = 0; i < GLOBALS._nLocations; i++) {
if ((lpBuf = ParseLocation(lpBuf + 9, &GLOBALS._lpmlLocations[i])) == NULL)
return false;
}
globalUnlock(GLOBALS._hLocations);
}
// Check the scripts
GLOBALS._nScripts = 0;
GLOBALS._hScripts = GLOBALS._lpmsScripts = NULL;
if (*(lpBuf + 2) == 6 && strncmp((const char *)lpBuf + 3, "Script", 6) == 0) {
GLOBALS._nScripts = READ_LE_UINT16(lpBuf);
lpBuf += 2;
// Allocate memory
GLOBALS._hScripts = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nScripts * sizeof(MpalScript));
if (GLOBALS._hScripts == NULL)
return false;
GLOBALS._lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts);
for (uint16 i = 0; i < GLOBALS._nScripts; i++) {
if ((lpBuf = ParseScript(lpBuf + 7, &GLOBALS._lpmsScripts[i])) == NULL)
return false;
// Sort the various moments of the script
//qsort(
//GLOBALS.lpmsScripts[i].Moment,
//GLOBALS.lpmsScripts[i].nMoments,
//sizeof(GLOBALS.lpmsScripts[i].Moment[0]),
//(int (*)(const void *, const void *))CompareMoments
//);
}
globalUnlock(GLOBALS._hScripts);
}
if (lpBuf[0] != 'E' || lpBuf[1] != 'N' || lpBuf[2] != 'D' || lpBuf[3] != '0')
return false;
return true;
}
/**
* Free the given dialog
*/
static void freeDialog(LpMpalDialog lpmdDialog) {
// Free the periods
for (int i = 0; i < MAX_PERIODS_PER_DIALOG && (lpmdDialog->_periods[i]); ++i)
globalFree(lpmdDialog->_periods[i]);
for (int i = 0; i < MAX_COMMANDS_PER_DIALOG && (lpmdDialog->_command[i]._type); i++) {
if (lpmdDialog->_command[i]._type == 2) {
// Variable assign
globalDestroy(lpmdDialog->_command[i]._lpszVarName);
freeExpression(lpmdDialog->_command[i]._expr);
}
}
// Free the choices
for (int i = 0; i < MAX_CHOICES_PER_DIALOG; ++i) {
for (int j = 0; j < MAX_SELECTS_PER_CHOICE; j++) {
if (lpmdDialog->_choice[i]._select[j]._when)
freeExpression(lpmdDialog->_choice[i]._select[j]._when);
}
}
}
/**
* Frees any data allocated from the parsing of the MPC file
*/
void freeMpc() {
// Free variables
globalFree(GLOBALS._hVars);
// Free messages
LpMpalMsg lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs);
for (int i = 0; i < GLOBALS._nMsgs; i++, ++lpmmMsgs)
globalFree(lpmmMsgs->_hText);
globalUnlock(GLOBALS._hMsgs);
globalFree(GLOBALS._hMsgs);
// Free objects
if (GLOBALS._hDialogs) {
LpMpalDialog lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs);
for (int i = 0; i < GLOBALS._nDialogs; i++, ++lpmdDialogs)
freeDialog(lpmdDialogs);
globalFree(GLOBALS._hDialogs);
}
// Free items
if (GLOBALS._hItems) {
LpMpalItem lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems);
for (int i = 0; i < GLOBALS._nItems; ++i, ++lpmiItems)
freeItem(lpmiItems);
globalUnlock(GLOBALS._hItems);
globalFree(GLOBALS._hItems);
}
// Free the locations
if (GLOBALS._hLocations) {
globalFree(GLOBALS._hLocations);
}
// Free the scripts
if (GLOBALS._hScripts) {
LpMpalScript lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts);
for (int i = 0; i < GLOBALS._nScripts; ++i, ++lpmsScripts) {
FreeScript(lpmsScripts);
}
globalUnlock(GLOBALS._hScripts);
}
}
//@}
} // end of namespace MPAL
} // end of namespace Tony

View File

@@ -0,0 +1,57 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef __LOADMPC_H
#define __LOADMPC_H
namespace Tony {
namespace MPAL {
/****************************************************************************\
* Function prototypes
\****************************************************************************/
/**
* Reads and interprets the MPC file, and create structures for various directives
* in the global variables
*
* @param lpBuf Buffer containing the MPC file data, excluding the header.
* @returns True if succeeded OK, false if failure.
*/
bool parseMpc(const byte *lpBuf);
/**
* Frees any data allocated from the parsing of the MPC file
*/
void freeMpc();
} // end of namespace MPAL
} // end of namespace Tony
#endif

220
engines/tony/mpal/lzo.cpp Normal file
View File

@@ -0,0 +1,220 @@
/* 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/>.
*
*/
/* minilzo.c -- mini subset of the LZO real-time data compression library
This file is part of the LZO real-time data compression library.
Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
All Rights Reserved.
The LZO library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
The LZO library 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 the LZO library; see the file COPYING.
If not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Markus F.X.J. Oberhumer
<markus@oberhumer.com>
http://www.oberhumer.com/opensource/lzo/
*/
#include "lzo.h"
#include "common/textconsole.h"
namespace Tony {
namespace MPAL {
#define pd(a, b) ((uint32) ((a) - (b)))
#define TEST_IP (ip < ip_end)
#define M2_MAX_OFFSET 0x0800
/**
* Decompresses an LZO compressed resource
*/
int lzo1x_decompress(const byte *in, uint32 in_len, byte *out, uint32 *out_len) {
byte *op;
const byte *ip;
uint32 t = 0;
const byte *m_pos;
const byte * const ip_end = in + in_len;
*out_len = 0;
op = out;
ip = in;
if (*ip > 17) {
t = *ip++ - 17;
if (t < 4)
goto match_next;
assert(t > 0);
do {
*op++ = *ip++;
} while (--t > 0);
goto first_literal_run;
}
while (TEST_IP) {
t = *ip++;
if (t >= 16)
goto match;
if (t == 0) {
while (*ip == 0) {
t += 255;
ip++;
}
t += 15 + *ip++;
}
assert(t > 0);
*op++ = *ip++;
*op++ = *ip++;
*op++ = *ip++;
do {
*op++ = *ip++;
} while (--t > 0);
first_literal_run:
t = *ip++;
if (t >= 16)
goto match;
m_pos = op - (1 + M2_MAX_OFFSET);
m_pos -= t >> 2;
m_pos -= *ip++ << 2;
*op++ = *m_pos++;
*op++ = *m_pos++;
*op++ = *m_pos;
goto match_done;
do {
match:
if (t >= 64) {
m_pos = op - 1;
m_pos -= (t >> 2) & 7;
m_pos -= *ip++ << 3;
t = (t >> 5) - 1;
assert(t > 0);
goto copy_match;
} else if (t >= 32) {
t &= 31;
if (t == 0) {
while (*ip == 0) {
t += 255;
ip++;
}
t += 31 + *ip++;
}
m_pos = op - 1;
m_pos -= (ip[0] >> 2) + (ip[1] << 6);
ip += 2;
} else if (t >= 16) {
m_pos = op;
m_pos -= (t & 8) << 11;
t &= 7;
if (t == 0) {
while (*ip == 0) {
t += 255;
ip++;
}
t += 7 + *ip++;
}
m_pos -= (ip[0] >> 2) + (ip[1] << 6);
ip += 2;
if (m_pos == op)
goto eof_found;
m_pos -= 0x4000;
} else {
m_pos = op - 1;
m_pos -= t >> 2;
m_pos -= *ip++ << 2;
*op++ = *m_pos++;
*op++ = *m_pos;
goto match_done;
}
assert(t > 0);
{
copy_match:
*op++ = *m_pos++;
*op++ = *m_pos++;
do {
*op++ = *m_pos++;
} while (--t > 0);
}
match_done:
t = ip[-2] & 3;
if (t == 0)
break;
match_next:
assert(t > 0);
assert(t < 4);
*op++ = *ip++;
if (t > 1) {
*op++ = *ip++;
if (t > 2)
*op++ = *ip++;
}
t = *ip++;
} while (TEST_IP);
}
eof_found:
assert(t == 1);
*out_len = pd(op, out);
return (ip == ip_end ? LZO_E_OK :
(ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
}
} // end of namespace MPAL
} // end of namespace Tony

88
engines/tony/mpal/lzo.h Normal file
View File

@@ -0,0 +1,88 @@
/* 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/>.
*
*/
/* minilzo.c -- mini subset of the LZO real-time data compression library
This file is part of the LZO real-time data compression library.
Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
All Rights Reserved.
The LZO library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
The LZO library 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 the LZO library; see the file COPYING.
If not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Markus F.X.J. Oberhumer
<markus@oberhumer.com>
http://www.oberhumer.com/opensource/lzo/
*/
#ifndef TONY_MPAL_LZO_H
#define TONY_MPAL_LZO_H
#include "common/scummsys.h"
namespace Tony {
namespace MPAL {
/* Error codes for the compression/decompression functions. Negative
* values are errors, positive values will be used for special but
* normal events.
*/
#define LZO_E_OK 0
#define LZO_E_INPUT_OVERRUN (-4)
#define LZO_E_INPUT_NOT_CONSUMED (-8)
/**
* Decompresses an LZO compressed resource
*/
int lzo1x_decompress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len);
} // end of namespace MPAL
} // end of namespace Tony
#endif /* already included */

View File

@@ -0,0 +1,124 @@
/* 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/algorithm.h"
#include "common/textconsole.h"
#include "tony/mpal/memory.h"
namespace Tony {
namespace MPAL {
/****************************************************************************\
* MemoryManager methods
\****************************************************************************/
/**
* Allocates a new memory block
* @return Returns a MemoryItem instance for the new block
*/
MpalHandle MemoryManager::allocate(uint32 size, uint flags) {
MemoryItem *newItem = (MemoryItem *)malloc(sizeof(MemoryItem) - sizeof(byte[1]) + size);
newItem->_id = BLOCK_ID;
newItem->_size = size;
newItem->_lockCount = 0;
// If requested, clear the allocated data block
if ((flags & GMEM_ZEROINIT) != 0) {
byte *dataP = newItem->_data;
Common::fill(dataP, dataP + size, 0);
}
return (MpalHandle)newItem;
}
/**
* Allocates a new memory block and returns its data pointer
* @return Data pointer to allocated block
*/
void *MemoryManager::alloc(uint32 size, uint flags) {
MemoryItem *item = (MemoryItem *)allocate(size, flags);
++item->_lockCount;
return &item->_data[0];
}
#define OFFSETOF(type, field) ((size_t) &(((type *) 0)->field))
/**
* Returns a reference to the MemoryItem for a gien byte pointer
* @param block Byte pointer
*/
MemoryItem *MemoryManager::getItem(MpalHandle handle) {
MemoryItem *rec = (MemoryItem *)((byte *)handle - OFFSETOF(MemoryItem, _data));
assert(rec->_id == BLOCK_ID);
return rec;
}
/**
* Returns a size of a memory block given its pointer
*/
uint32 MemoryManager::getSize(MpalHandle handle) {
MemoryItem *item = (MemoryItem *)handle;
assert(item->_id == BLOCK_ID);
return item->_size;
}
/**
* Erases a given item
*/
void MemoryManager::freeBlock(MpalHandle handle) {
MemoryItem *item = (MemoryItem *)handle;
assert(item->_id == BLOCK_ID);
free(item);
}
/**
* Erases a given item
*/
void MemoryManager::destroyItem(MpalHandle handle) {
MemoryItem *item = getItem(handle);
assert(item->_id == BLOCK_ID);
free(item);
}
/**
* Locks an item for access
*/
void *MemoryManager::lockItem(MpalHandle handle) {
MemoryItem *item = (MemoryItem *)handle;
assert(item->_id == BLOCK_ID);
++item->_lockCount;
return &item->_data[0];
}
/**
* Unlocks a locked item
*/
void MemoryManager::unlockItem(MpalHandle handle) {
MemoryItem *item = (MemoryItem *)handle;
assert(item->_id == BLOCK_ID);
assert(item->_lockCount > 0);
--item->_lockCount;
}
} // end of namespace MPAL
} // end of namespace Tony

View File

@@ -0,0 +1,79 @@
/* 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 TONY_MPAL_MEMORY
#define TONY_MPAL_MEMORY
#include "common/scummsys.h"
#include "common/list.h"
namespace Tony {
namespace MPAL {
typedef void *MpalHandle;
struct MemoryItem {
uint32 _id;
uint32 _size;
int _lockCount;
#ifndef NO_CXX11_ALIGNAS
alignas(max_align_t)
#endif
byte _data[1];
// Casting for access to data
operator void *() { return &_data[0]; }
};
class MemoryManager {
private:
static MemoryItem *getItem(MpalHandle handle);
public:
static MpalHandle allocate(uint32 size, uint flags);
static void *alloc(uint32 size, uint flags);
static void freeBlock(MpalHandle handle);
static void destroyItem(MpalHandle handle);
static uint32 getSize(MpalHandle handle);
static void *lockItem(MpalHandle handle);
static void unlockItem(MpalHandle handle);
};
// defines
#define globalAlloc(flags, size) MemoryManager::alloc(size, flags)
#define globalAllocate(flags, size) MemoryManager::allocate(size, flags)
#define globalFree(handle) MemoryManager::freeBlock(handle)
#define globalDestroy(handle) MemoryManager::destroyItem(handle)
#define globalLock(handle) MemoryManager::lockItem(handle)
#define globalUnlock(handle) MemoryManager::unlockItem(handle)
#define globalSize(handle) MemoryManager::getSize(handle)
#define GMEM_FIXED 1
#define GMEM_MOVEABLE 2
#define GMEM_ZEROINIT 4
const uint32 BLOCK_ID = 0x12345678;
} // end of namespace MPAL
} // end of namespace Tony
#endif

2096
engines/tony/mpal/mpal.cpp Normal file

File diff suppressed because it is too large Load Diff

501
engines/tony/mpal/mpal.h Normal file
View File

@@ -0,0 +1,501 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
/****************************************************************************\
* General Introduction
\****************************************************************************/
/*
* MPAL (MultiPurpose Adventure Language) is a high level language
* for the definition of adventure. Through the use of MPAL you can describe
* storyboard the adventure, and then use it with any user interface.
* In fact, unlike many other similar products, MPAL is not programmed through
* the whole adventure, but are defined only the locations, objects, as they may
* interact with each other, etc.. thus making MPAL useful for any type of adventure.
*/
/****************************************************************************\
* Structure
\****************************************************************************/
/*
* MPAL consists of two main files: MPAL.DLL and MPAL.H
* The first is the DLL that contains the code to interface with MPAL
* adventures, the second is the header that defines the prototypes
* functions. MPAL is compiled for Win32, and it can therefore be used with
* any compiler that supports Win32 DLL (Watcom C++, Visual C++,
* Delphi, etc.), and therefore compatible with both Windows 95 and Windows NT.
*
* To use the DLL, and 'obviously need to create a library for symbols to export.
*
*/
/****************************************************************************\
* Custom Functions
\****************************************************************************/
/*
* A custom function and a function specified by the program that uses the
* library, to perform the particular code. The custom functions are
* retrieved from the library as specified in the source MPAL, and in particular
* in defining the behavior of an item with some action.
*
* To use the custom functions, you need to prepare an array of
* pointers to functions (such as using the type casting LPCUSTOMFUNCTION,
* (defined below), and pass it as second parameter to mpalInit (). Note you
* must specify the size of the array, as elements of pointers and which do not
* contain the same: the library will call it only those functions specified in
* the source MPAL. It can be useful, for debugging reasons, do not bet
* the shares of arrays used to debugging function, to avoid unpleasant crash,
* if it has been made an error in source and / or some oversight in the code.
*
*/
#ifndef TONY_MPAL_H
#define TONY_MPAL_H
#include "common/scummsys.h"
#include "common/coroutines.h"
#include "common/rect.h"
#include "common/str.h"
#include "tony/mpal/memory.h"
namespace Tony {
namespace MPAL {
/****************************************************************************\
* Macro definitions and structures
\****************************************************************************/
// OK value for the error codes
#define OK 0
#define MAXFRAMES 400 // frame animation of an object
#define MAXPATTERN 40 // pattern of animation of an object
#define MAXPOLLINGLOCATIONS 64
#define GETARG(type) va_arg(v, type)
/**
* Macro for use with queries that may refer to X and Y co-ordinates
*/
enum QueryCoordinates {
MPQ_X,
MPQ_Y
};
/**
* Query can be used with mpalQuery methods. In practice corresponds all claims
* that can do at the library
*/
enum QueryTypes {
// General Query
MPQ_VERSION = 10,
MPQ_GLOBAL_VAR = 50,
MPQ_RESOURCE,
MPQ_MESSAGE,
// Query on leases
MPQ_LOCATION_IMAGE = 100,
MPQ_LOCATION_SIZE,
// Queries about items
MPQ_ITEM_LIST = 200,
MPQ_ITEM_DATA,
MPQ_ITEM_PATTERN,
MPQ_ITEM_NAME,
MPQ_ITEM_IS_ACTIVE,
// Query dialog
MPQ_DIALOG_PERIOD = 300,
MPQ_DIALOG_WAITFORCHOICE,
MPQ_DIALOG_SELECTLIST,
MPQ_DIALOG_SELECTION,
// Query execution
MPQ_DO_ACTION = 400,
MPQ_DO_DIALOG
};
/**
* Framework to manage the animation of an item
*/
typedef struct {
char *_frames[MAXFRAMES];
Common::Rect _frameslocations[MAXFRAMES];
Common::Rect _bbox[MAXFRAMES];
short _pattern[MAXPATTERN][MAXFRAMES];
short _speed;
char _numframe;
char _numpattern;
char _curframe;
char _curpattern;
short _destX, _destY;
signed char _destZ;
short _objectID;
} Item;
typedef Item *LpItem;
/**
* Define a custom function, to use the language MPAL to perform various controls as a result of an action
*/
typedef void (*LPCUSTOMFUNCTION)(CORO_PARAM, uint32, uint32, uint32, uint32);
typedef LPCUSTOMFUNCTION *LPLPCUSTOMFUNCTION;
/**
*
* Define an IRQ of an item that is called when the pattern changes or the status of an item
*/
typedef void (*LPITEMIRQFUNCTION)(uint32, int, int);
typedef LPITEMIRQFUNCTION* LPLPITEMIRQFUNCTION;
/**
* @defgroup Macrofunctions query
*
* The following are defines used for simplifying calling the mpalQuery variants
*/
//@{
/**
* Gets the current version of MPAL
*
* @returns Version number (0x1232 = 1.2.3b)
*/
#define mpalQueryVersion() \
(uint16)mpalQueryDWORD(MPQ_VERSION)
/**
* Gets the numerical value of a global variable
*
* @param lpszVarName Variable name (ASCIIZ)
* @returns Global variable value
* @remarks This query was implemented for debugging. The program,
* if well designed, should not need to access variables from
* within the library.
*/
#define mpalQueryGlobalVar(lpszVarName) \
mpalQueryDWORD(MPQ_GLOBAL_VAR, (const char *)(lpszVarName))
/**
* Provides access to a resource inside the .MPC file
*
* @param dwResId Resource Id
* @returns Handle to a memory area containing the resource, ready for use.
*/
#define mpalQueryResource(dwResId) \
mpalQueryHANDLE(MPQ_RESOURCE, (uint32)(dwResId))
/**
* Returns a message.
*
* @param nMsg Message number
* @returns ASCIIZ message
* @remarks The returned pointer must be freed via the memory manager
* after use. The message will be in ASCIIZ format.
*/
#define mpalQueryMessage(nMsg) \
(char *)mpalQueryHANDLE(MPQ_MESSAGE, (uint32)(nMsg))
/**
* Provides a location image
* @return Returns a picture handle
*/
#define mpalQueryLocationImage(nLoc) \
mpalQueryHANDLE(MPQ_LOCATION_IMAGE, (uint32)(nLoc))
/**
* Request the x or y size of a location in pixels
*
* @param nLoc Location number
* @param dwCoord MPQ_X or MPQ_Y coordinate to retrieve
* @returns Size
*/
#define mpalQueryLocationSize(nLoc, dwCoord) \
mpalQueryDWORD(MPQ_LOCATION_SIZE, (uint32)(nLoc), (uint32)(dwCoord))
/**
* Provides the list of objects in a location.
*
* @param nLoc Location number
* @returns List of objects (accessible by Item [0], Item [1], etc.)
*/
// TODO: Determine if this is endian safe
#define mpalQueryItemList(nLoc) \
(uint32 *)mpalQueryHANDLE(MPQ_ITEM_LIST, (uint32)(nLoc))
/**
* Provides information on an item
*
* @param nItem Item number
* @returns Structure filled with requested information
*/
#define mpalQueryItemData(nItem) \
(LpItem)mpalQueryHANDLE(MPQ_ITEM_DATA, (uint32)(nItem))
/**
* Provides the current pattern of an item
*
* @param nItem Item number
* @returns Number of animation patterns to be executed.
* @remarks By default, the pattern of 0 indicates that we should do nothing.
*/
#define mpalQueryItemPattern(nItem) \
mpalQueryDWORD(MPQ_ITEM_PATTERN, (uint32)(nItem))
/**
* Returns true if an item is active
*
* @param nItem Item number
* @returns TRUE if the item is active, FALSE otherwise
*/
#define mpalQueryItemIsActive(nItem) \
(bool)mpalQueryDWORD(MPQ_ITEM_IS_ACTIVE, (uint32)(nItem))
/**
* Returns the name of an item
*
* @param nItem Item number
* @param lpszName Pointer to a buffer of at least 33 bytes
* that will be filled with the name
* @remarks If the item is not active (ie. if its status or number
* is less than or equal to 0), the string will be empty.
*/
#define mpalQueryItemName(nItem, lpszName) \
mpalQueryHANDLE(MPQ_ITEM_NAME, (uint32)(nItem), (char *)(lpszName))
/**
* Returns a sentence of dialog.
*
* @param nDialog Dialog number
* @param nPeriod Number of words
* @returns A pointer to the string of words, or NULL on failure.
* @remarks The string must be freed after use using the memory manager.
* Unlike normal messages, the sentences of dialogue are formed by a single
* string terminated with 0.
*/
#define mpalQueryDialogPeriod(nPeriod) \
(char *)mpalQueryHANDLE(MPQ_DIALOG_PERIOD, (uint32)(nPeriod))
/**
* Wait until the moment in which the need is signaled to make a choice by the user.
* @returns Number of choice to be made, or -1 if the dialogue is finished.
*/
#define mpalQueryDialogWaitForChoice(dwRet) \
CORO_INVOKE_2(mpalQueryCORO, MPQ_DIALOG_WAITFORCHOICE, dwRet)
/**
* Requires a list of various options for some choice within the current dialog.
*
* @param nChoice Choice number
* @returns A pointer to an array containing the data matched to each option.
* @remarks The figure 'a uint32 specified in the source to which MPAL
* You can assign meaning that the more' suits.
* The pointer msut be freed after use using the memory memory.
*/
#define mpalQueryDialogSelectList(nChoice) \
(uint32 *)mpalQueryHANDLE(MPQ_DIALOG_SELECTLIST, (uint32)(nChoice))
/**
* Warns the library that the user has selected, in a certain choice of the current dialog,
* corresponding option at a certain given.
*
* @param nChoice Choice number of the choice that was in progress
* @param dwData Option that was selected by the user.
* @returns TRUE if all OK, FALSE on failure.
* @remarks After execution of this query, MPAL continue
* Groups according to the execution of the dialogue. And necessary so the game
* remains on hold again for another chosen by mpalQueryDialogWaitForChoice ().
*/
#define mpalQueryDialogSelection(nChoice, dwData) \
(bool)mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData))
#define mpalQueryDialogSelectionDWORD(nChoice, dwData) \
mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData))
/**
* Warns the library an action was performed on a Object.
* The library will call custom functions, if necessary.
*
* @param nAction Action number
* @param nItem Item number
* @param dwParam Action parameter
* @returns Handle to the thread that is performing the action, or CORO_INVALID_PID_VALUE
* if the action is not defined for the item, or the item is inactive.
* @remarks The parameter is used primarily to implement actions
* as "U.S." involving two objects together. The action will be executed only
* if the item is active, ie if its status is a positive number greater than 0.
*/
#define mpalQueryDoAction(nAction, nItem, dwParam) \
mpalQueryDWORD(MPQ_DO_ACTION, (uint32)(nAction), (uint32)(nItem), (uint32)(dwParam))
/**
* Warns the library a dialogue was required.
*
* @param nDialog Dialog number
* @param nGroup Group number to use
* @returns Handle to the thread that is running the box, or
* CORO_INVALID_PID_VALUE if the dialogue does not exist.
*/
#define mpalQueryDoDialog(nDialog, nGroup) \
mpalQueryDWORD(MPQ_DO_DIALOG, (uint32)(nDialog), (uint32)(nGroup))
/**
* @defgroup Functions exported to the main game
*/
//@{
/**
* Initializes the MPAL library, and opens an .MPC file, which will be 'used for all queries
* @param lpszMpcFileName Name of the .MPC file, including extension
* @param lpszMprFileName Name of the .MPR file, including extension
* @param lplpcfArray Array of pointers to custom functions
* @returns TRUE if all OK, FALSE on failure
*/
bool mpalInit(const char *lpszFileName, const char *lpszMprFileName,
LPLPCUSTOMFUNCTION lplpcfArray, Common::String *lpcfStrings);
/**
* Frees resources allocated by the MPAL subsystem
*/
void mpalFree();
/**
* This is a general function to communicate with the library, to request information
* about what is in the .MPC file
*
* @param wQueryType Type of query. The list is in the QueryTypes enum.
* @returns 4 bytes depending on the type of query
* @remarks This is the specialized version of the original single mpalQuery
* method that returns numeric results.
*/
uint32 mpalQueryDWORD(uint wQueryType, ...);
/**
* This is a general function to communicate with the library, to request information
* about what is in the .MPC file
*
* @param wQueryType Type of query. The list is in the QueryTypes enum.
* @returns 4 bytes depending on the type of query
* @remarks This is the specialized version of the original single mpalQuery
* method that returns a pointer or handle.
*/
MpalHandle mpalQueryHANDLE(uint wQueryType, ...);
/**
* This is a general function to communicate with the library, to request information
* about what is in the .MPC file
*
* @param wQueryType Type of query. The list is in the QueryTypes enum.
* @returns 4 bytes depending on the type of query
* @remarks This is the specialized version of the original single mpalQuery
* method that needs to run within a co-routine context.
*/
void mpalQueryCORO(CORO_PARAM, uint16 wQueryType, uint32 *dwRet);
/**
* Execute a script. The script runs on multitasking by a thread.
*
* @param nScript Script number to run
* @returns TRUE if the script 'was launched, FALSE on failure
*/
bool mpalExecuteScript(int nScript);
/**
* Returns the current MPAL error code
*
* @returns Error code
*/
uint32 mpalGetError();
/**
* Install a custom routine That will be called by MPAL every time the pattern
* of an item has been changed.
*
* @param lpiifCustom Custom function to install
*/
void mpalInstallItemIrq(LPITEMIRQFUNCTION lpiifCustom);
/**
* Process the idle actions of the items on one location.
*
* @param nLoc Number of the location whose items must be processed
* for idle actions.
* @returns TRUE if all OK, and FALSE if it exceeded the maximum limit.
* @remarks The maximum number of locations that can be polled
* simultaneously is defined defined by MAXPOLLINGFUNCIONS
*/
bool mpalStartIdlePoll(int nLoc);
/**
* Stop processing the idle actions of the items on one location.
*
* @param nLo Number of the location
* @returns TRUE if all OK, FALSE if the specified location was not
* in the process of polling
*/
void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result);
/**
* Load a save state from a buffer.
*
* @param buf Buffer where to store the state
* @returns Length of the state buffer in bytes
*/
int mpalLoadState(byte *buf);
/**
* Store the save state into a buffer. The buffer must be
* length at least the size specified with mpalGetSaveStateSize
*
* @param buf Buffer where to store the state
*/
void mpalSaveState(byte *buf);
/**
* Retrieve the length of a save state
*
* @returns Length in bytes
*/
int mpalGetSaveStateSize();
/**
* Locks the variables for access
*/
void lockVar();
/**
* Unlocks variables after use
*/
void unlockVar();
} // end of namespace MPAL
} // end of namespace Tony
#endif

246
engines/tony/mpal/mpaldll.h Normal file
View File

@@ -0,0 +1,246 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef __MPALDLL_H
#define __MPALDLL_H
#include "common/file.h"
#include "tony/mpal/memory.h"
#include "tony/mpal/loadmpc.h"
#include "tony/mpal/expr.h"
namespace Tony {
namespace MPAL {
/****************************************************************************\
* Defines
\****************************************************************************/
#define HEX_VERSION 0x0170
#define MAX_ACTIONS_PER_ITEM 40
#define MAX_COMMANDS_PER_ITEM 128
#define MAX_COMMANDS_PER_ACTION 128
#define MAX_DESCRIBE_SIZE 64
#define MAX_MOMENTS_PER_SCRIPT 256
#define MAX_COMMANDS_PER_SCRIPT 256
#define MAX_COMMANDS_PER_MOMENT 32
#define MAX_GROUPS_PER_DIALOG 128
#define MAX_COMMANDS_PER_DIALOG 480
#define MAX_COMMANDS_PER_GROUP 64
#define MAX_CHOICES_PER_DIALOG 64
#define MAX_SELECTS_PER_CHOICE 64
#define MAX_PLAYGROUPS_PER_SELECT 9
#define MAX_PERIODS_PER_DIALOG 400
#define NEED_LOCK_MSGS
/****************************************************************************\
* Structures
\****************************************************************************/
#include "common/pack-start.h"
/**
* MPAL global variables
*/
struct MpalVar {
uint32 _dwVal; // Variable value
char _lpszVarName[33]; // Variable name
} PACKED_STRUCT;
typedef MpalVar *LpMpalVar;
/**
* MPAL Messages
*/
struct MpalMsg {
MpalHandle _hText; // Handle to the message text
uint16 _wNum; // Message number
} PACKED_STRUCT;
typedef MpalMsg *LpMpalMsg;
/**
* MPAL Locations
*/
struct MpalLocation {
uint32 _nObj; // Location number
uint32 _dwXlen, _dwYlen; // Dimensions
uint32 _dwPicRes; // Resource that contains the image
} PACKED_STRUCT;
typedef MpalLocation *LpMpalLocation;
/**
* All the data for a command, ie. tags used by OnAction in the item, the time
* in the script, and in the group dialog.
*/
struct Command {
/*
* Types of commands that are recognized
*
* #1 -> Custom function call (ITEM, SCRIPT, DIALOG)
* #2 -> Variable assignment (ITEM, SCRIPT, DIALOG)
* #3 -> Making a choice (DIALOG)
*
*/
byte _type; // Type of control
union {
int32 _nCf; // Custom function call [#1]
char *_lpszVarName; // Variable name [#2]
int32 _nChoice; // Number of choice you make [#3]
};
union {
int32 _arg1; // Argument for custom function [#1]
MpalHandle _expr; // Expression to assign to a variable [#2]
};
int32 _arg2, _arg3, _arg4; // Arguments for custom function [#1]
} PACKED_STRUCT;
/**
* MPAL dialog
*/
struct MpalDialog {
uint32 _nObj; // Dialog number
struct Command _command[MAX_COMMANDS_PER_DIALOG];
struct {
uint16 _num;
byte _nCmds;
uint16 _cmdNum[MAX_COMMANDS_PER_GROUP];
} _group[MAX_GROUPS_PER_DIALOG];
struct {
// The last choice has nChoice == 0
uint16 _nChoice;
// The select number (we're pretty stingy with RAM). The last select has dwData == 0
struct {
MpalHandle _when;
uint32 _dwData;
uint16 _wPlayGroup[MAX_PLAYGROUPS_PER_SELECT];
// Bit 0=endchoice Bit 1=enddialog
byte _attr;
// Modified at run-time: 0 if the select is currently disabled,
// and 1 if currently active
byte _curActive;
} _select[MAX_SELECTS_PER_CHOICE];
} _choice[MAX_CHOICES_PER_DIALOG];
uint16 _periodNums[MAX_PERIODS_PER_DIALOG];
MpalHandle _periods[MAX_PERIODS_PER_DIALOG];
} PACKED_STRUCT;
typedef MpalDialog *LpMpalDialog;
/**
* MPAL Item
*/
struct ItemAction {
byte _num; // Action number
uint16 _wTime; // If idle, the time which must pass
byte _perc; // Percentage of the idle run
MpalHandle _when; // Expression to compute. If != 0, then action can be done
uint16 _wParm; // Parameter for action
byte _nCmds; // Number of commands to be executed
uint32 _cmdNum[MAX_COMMANDS_PER_ACTION]; // Commands to execute
} PACKED_STRUCT;
struct MpalItem {
uint32 _nObj; // Item number
byte _lpszDescribe[MAX_DESCRIBE_SIZE]; // Name
byte _nActions; // Number of managed actions
uint32 _dwRes; // Resource that contains frames and patterns
struct Command _command[MAX_COMMANDS_PER_ITEM];
// Pointer to array of structures containing various managed activities. In practice, of
// every action we know what commands to run, including those defined in structures above
struct ItemAction *_action;
} PACKED_STRUCT;
typedef MpalItem *LpMpalItem;
/**
* MPAL Script
*/
struct MpalScript {
uint32 _nObj;
uint32 _nMoments;
struct Command _command[MAX_COMMANDS_PER_SCRIPT];
struct {
int32 _dwTime;
byte _nCmds;
uint32 _cmdNum[MAX_COMMANDS_PER_MOMENT];
} _moment[MAX_MOMENTS_PER_SCRIPT];
} PACKED_STRUCT;
typedef MpalScript *LpMpalScript;
#include "common/pack-end.h"
/****************************************************************************\
* Function prototypes
\****************************************************************************/
/**
* Returns the current value of a global variable
*
* @param lpszVarName Name of the variable
* @returns Current value
* @remarks Before using this method, you must call LockVar() to
* lock the global variablves for use. Then afterwards, you will
* need to remember to call UnlockVar()
*/
extern int32 varGetValue(const char *lpszVarName);
/**
* Sets the value of a MPAL global variable
* @param lpszVarName Name of the variable
* @param val Value to set
*/
extern void varSetValue(const char *lpszVarName, int32 val);
} // end of namespace MPAL
} // end of namespace Tony
#endif

View File

@@ -0,0 +1,115 @@
/* 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 "tony/mpal/mpalutils.h"
#include "tony/tony.h"
#include "common/memstream.h"
namespace Tony {
namespace MPAL {
/****************************************************************************\
* RMRes methods
\****************************************************************************/
/**
* Constructor
* @param resId MPAL resource to open
*/
RMRes::RMRes(uint32 resID) {
_buf = NULL;
_h = g_vm->_resUpdate.queryResource(resID);
if (_h == NULL)
_h = mpalQueryResource(resID);
if (_h != NULL)
_buf = (byte *)globalLock(_h);
}
/**
* Destructor
*/
RMRes::~RMRes() {
if (_h != NULL) {
globalUnlock(_h);
globalFree(_h);
}
}
/**
* Returns a pointer to the resource
*/
const byte *RMRes::dataPointer() {
return _buf;
}
/**
* Returns a pointer to the resource
*/
RMRes::operator const byte *() {
return dataPointer();
}
/**
* Returns the size of the resource
*/
unsigned int RMRes::size() {
return globalSize(_h);
}
Common::SeekableReadStream *RMRes::getReadStream() {
return new Common::MemoryReadStream(_buf, size());
}
bool RMRes::isValid() {
return _h != NULL;
}
/****************************************************************************\
* RMResRaw methods
\****************************************************************************/
RMResRaw::RMResRaw(uint32 resID) : RMRes(resID) {
}
RMResRaw::~RMResRaw() {
}
const byte *RMResRaw::dataPointer() {
return _buf + 8;
}
RMResRaw::operator const byte *() {
return dataPointer();
}
int RMResRaw::width() {
return READ_LE_UINT16(_buf + 4);
}
int RMResRaw::height() {
return READ_LE_UINT16(_buf + 6);
}
} // end of namespace MPAL
} // end of namespace Tony

View File

@@ -0,0 +1,72 @@
/* 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 TONY_MPAL_MPALUTILS
#define TONY_MPAL_MPALUTILS
#include "common/scummsys.h"
#include "tony/mpal/memory.h"
namespace Common {
class SeekableReadStream;
}
namespace Tony {
namespace MPAL {
class RMRes {
protected:
MpalHandle _h;
byte *_buf;
public:
RMRes(uint32 resID);
virtual ~RMRes();
// Attributes
unsigned int size();
const byte *dataPointer();
bool isValid();
// Casting for access to data
operator const byte*();
Common::SeekableReadStream *getReadStream();
};
class RMResRaw : public RMRes {
public:
RMResRaw(uint32 resID);
~RMResRaw() override;
const byte *dataPointer();
operator const byte*();
int width();
int height();
};
} // end of namespace MPAL
} // end of namespace Tony
#endif

69
engines/tony/resid.h Normal file
View File

@@ -0,0 +1,69 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
// From 10500 onwards there are .OGG for inventory and scrap
#ifndef TONY_RESID_H
#define TONY_RESID_H
#define RES_I_INTERFACE 10300
#define RES_I_INTERPAL 10301
#define RES_I_INTERPPAL 10302
#define RES_I_INTERP1 10303
#define RES_I_INTERP2 10304
#define RES_I_INTERP3 10305
#define RES_I_INTERP4 10306
#define RES_I_INTERP5 10307
#define RES_I_DLGTEXT 10350
#define RES_I_DLGTEXTLINE 10351
#define RES_I_DLGTEXTPAL 10352
#define RES_I_MINIINTER 10360
#define RES_P_PAL 10410
#define RES_P_GO 10400
#define RES_P_TAKE 10401
#define RES_P_USE 10402
#define RES_P_EXAM 10403
#define RES_P_TALK 10404
#define RES_P_PAP1 10420
#define RES_P_PAP2 10421
#define RES_P_PAP3 10422
#define RES_P_PAP4 10423
#define RES_P_FRMAP 10424
#define RES_F_PAL 10700
#define RES_F_PARL 10701
#define RES_F_OBJ 10702
#define RES_F_MACC 10703
#define RES_F_CREDITS 10704
#define RES_F_CPAL 10705
#define RES_W_CIRCLE 10800
#endif

778
engines/tony/sound.cpp Normal file
View File

@@ -0,0 +1,778 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#include "audio/audiostream.h"
#include "audio/decoders/adpcm.h"
#include "audio/decoders/flac.h"
#include "audio/decoders/mp3.h"
#include "audio/decoders/vorbis.h"
#include "audio/decoders/wave.h"
#include "common/textconsole.h"
#include "tony/game.h"
#include "tony/tony.h"
namespace Tony {
/*
* Tony uses a [0,63] volume scale (where 0 is silent and 63 is loudest).
* The original game engine linearly mapped this scale into DirectSound's
* [-10000, 0] scale (where -10000 is silent), which is a logarithmic scale.
*
* This means that Tony's scale is logarithmic as well, and must be converted
* to the linear scale used by the mixer.
*/
static int remapVolume(int volume) {
double dsvol = (double)(63 - volume) * -10000.0 / 63.0;
return (int)((double)Audio::Mixer::kMaxChannelVolume * pow(10.0, dsvol / 2000.0) + 0.5);
}
// Another obvious rip from gob engine. Hi DrMcCoy!
Common::Path setExtension(const Common::String &str, const Common::String &ext) {
if (str.empty())
return Common::Path();
const char *dot = strrchr(str.c_str(), '.');
if (dot)
return Common::Path(str.c_str(), dot - str.c_str()).appendInPlace(ext);
return Common::Path(str + ext);
}
/****************************************************************************\
* FPSOUND Methods
\****************************************************************************/
/**
* Default constructor. Initializes the attributes.
*
*/
FPSound::FPSound() {
_soundSupported = false;
}
/**
* Initializes the object, and prepare everything you need to create streams and sound effects.
*
* @returns True is everything is OK, False otherwise
*/
bool FPSound::init() {
_soundSupported = g_system->getMixer()->isReady();
return _soundSupported;
}
/**
* Destroy the object and free the memory
*
*/
FPSound::~FPSound() {
}
/**
* Allocates an object of type FPStream, and return its pointer
*
* @param streamPtr Will contain a pointer to the object you just created.
*
* @returns True is everything is OK, False otherwise
*/
bool FPSound::createStream(FPStream **streamPtr) {
(*streamPtr) = new FPStream(_soundSupported);
return true;
}
/**
* Allocates an object of type FpSfx, and return its pointer
*
* @param soundPtr Will contain a pointer to the object you just created.
*
* @returns True is everything is OK, False otherwise
*/
bool FPSound::createSfx(FPSfx **sfxPtr) {
(*sfxPtr) = new FPSfx(_soundSupported);
return (*sfxPtr != NULL);
}
/**
* Set the general volume
*
* @param volume Volume to set (0-63)
*/
void FPSound::setMasterVolume(int volume) {
if (!_soundSupported)
return;
// WORKAROUND: We don't use remapVolume() here, so that the main option screen exposes
// a linear scale to the user. This is an improvement over the original game
// where the user had to deal with a logarithmic volume scale.
g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, CLIP<int>(volume, 0, 63) * Audio::Mixer::kMaxChannelVolume / 63);
}
/**
* Get the general volume
*
* @param volumePtr Variable that will contain the volume (0-63)
*/
void FPSound::getMasterVolume(int *volumePtr) {
if (!_soundSupported)
return;
*volumePtr = g_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 63 / Audio::Mixer::kMaxChannelVolume;
}
/**
* Default constructor.
*
* @remarks Do *NOT* declare an object directly, but rather
* create it using FPSound::CreateSfx()
*
*/
FPSfx::FPSfx(bool soundOn) {
_soundSupported = soundOn;
_fileLoaded = false;
_lastVolume = 63;
_hEndOfBuffer = CoroScheduler.createEvent(true, false);
_isVoice = false;
_loopStream = 0;
_rewindableStream = 0;
_paused = false;
_loop = 0;
g_vm->_activeSfx.push_back(this);
}
/**
* Default Destructor.
*
* @remarks It is also stops the sound effect that may be
* currently played, and free the memory it uses.
*
*/
FPSfx::~FPSfx() {
if (!_soundSupported)
return;
g_system->getMixer()->stopHandle(_handle);
g_vm->_activeSfx.remove(this);
if (_loopStream)
delete _loopStream; // _rewindableStream is deleted by deleting _loopStream
else
delete _rewindableStream;
// Free the buffer end event
CoroScheduler.closeEvent(_hEndOfBuffer);
}
/**
* Releases the memory used by the object.
*
* @remarks Must be called when the object is no longer used and
* **ONLY** if the object was created by
* FPSound::CreateStream().
* Object pointers are no longer valid after this call.
*/
void FPSfx::release() {
delete this;
}
bool FPSfx::loadWave(Common::SeekableReadStream *stream) {
if (!stream)
return false;
_rewindableStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
if (!_rewindableStream)
return false;
_fileLoaded = true;
setVolume(_lastVolume);
return true;
}
bool FPSfx::loadVoiceFromVDB(Common::File &vdbFP) {
if (!_soundSupported)
return true;
switch (g_vm->_vdbCodec) {
case FPCODEC_ADPCM: {
uint32 size = vdbFP.readUint32LE();
uint32 rate = vdbFP.readUint32LE();
_rewindableStream = Audio::makeADPCMStream(vdbFP.readStream(size), DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, 1);
}
break;
case FPCODEC_MP3 : {
#ifdef USE_MAD
uint32 size = vdbFP.readUint32LE();
_rewindableStream = Audio::makeMP3Stream(vdbFP.readStream(size), DisposeAfterUse::YES);
#else
return false;
#endif
}
break;
case FPCODEC_OGG : {
#ifdef USE_VORBIS
uint32 size = vdbFP.readUint32LE();
_rewindableStream = Audio::makeVorbisStream(vdbFP.readStream(size), DisposeAfterUse::YES);
#else
return false;
#endif
}
break;
case FPCODEC_FLAC : {
#ifdef USE_FLAC
uint32 size = vdbFP.readUint32LE();
_rewindableStream = Audio::makeFLACStream(vdbFP.readStream(size), DisposeAfterUse::YES);
#else
return false;
#endif
}
break;
default:
return false;
}
_isVoice = true;
_fileLoaded = true;
setVolume(62);
return true;
}
/**
* Opens a file and loads a sound effect.
*
* @param fileName Sfx filename
*
* @returns True is everything is OK, False otherwise
*/
bool FPSfx::loadFile(const char *fileName) {
if (!_soundSupported)
return true;
SoundCodecs codec = FPCODEC_UNKNOWN;
Common::File file;
if (file.open(fileName))
codec = FPCODEC_ADPCM;
else if (file.open(setExtension(fileName, ".MP3")))
codec = FPCODEC_MP3;
else if (file.open(setExtension(fileName, ".OGG")))
codec = FPCODEC_OGG;
else if (file.open(setExtension(fileName, ".FLA")))
codec = FPCODEC_FLAC;
else {
warning("FPSfx::LoadFile(): Cannot open sfx file!");
return false;
}
Common::SeekableReadStream *buffer;
switch (codec) {
case FPCODEC_ADPCM: {
if (file.readUint32BE() != MKTAG('A', 'D', 'P', 0x10)) {
warning("FPSfx::LoadFile(): Invalid ADP header!");
return false;
}
uint32 rate = file.readUint32LE();
uint32 channels = file.readUint32LE();
buffer = file.readStream(file.size() - file.pos());
_rewindableStream = Audio::makeADPCMStream(buffer, DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, channels);
}
break;
case FPCODEC_MP3:
#ifdef USE_MAD
buffer = file.readStream(file.size());
_rewindableStream = Audio::makeMP3Stream(buffer, DisposeAfterUse::YES);
#endif
break;
case FPCODEC_OGG:
#ifdef USE_VORBIS
buffer = file.readStream(file.size());
_rewindableStream = Audio::makeVorbisStream(buffer, DisposeAfterUse::YES);
#endif
break;
case FPCODEC_FLAC:
buffer = file.readStream(file.size());
#ifdef USE_FLAC
_rewindableStream = Audio::makeFLACStream(buffer, DisposeAfterUse::YES);
#endif
break;
default:
return false;
}
_fileLoaded = true;
return true;
}
/**
* Play the Sfx in memory.
*
* @returns True is everything is OK, False otherwise
*/
bool FPSfx::play() {
stop(); // sanity check
if (_fileLoaded) {
CoroScheduler.resetEvent(_hEndOfBuffer);
_rewindableStream->rewind();
Audio::AudioStream *stream = _rewindableStream;
if (_loop) {
if (!_loopStream)
_loopStream = Audio::makeLoopingAudioStream(_rewindableStream, 0);
stream = _loopStream;
}
g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, stream, -1,
Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
setVolume(_lastVolume);
if (_paused)
g_system->getMixer()->pauseHandle(_handle, true);
}
return true;
}
/**
* Stops a Sfx.
*
* @returns True is everything is OK, False otherwise
*/
bool FPSfx::stop() {
if (_fileLoaded) {
g_system->getMixer()->stopHandle(_handle);
_paused = false;
}
return true;
}
/**
* Enables or disables the Sfx loop.
*
* @param loop True to enable the loop, False to disable
*
* @remarks The loop must be activated BEFORE the sfx starts
* playing. Any changes made during the play will have
* no effect until the sfx is stopped then played again.
*/
void FPSfx::setLoop(bool loop) {
_loop = loop;
}
/**
* Pauses a Sfx.
*
*/
void FPSfx::setPause(bool pause) {
if (_fileLoaded) {
if (g_system->getMixer()->isSoundHandleActive(_handle) && (pause ^ _paused))
g_system->getMixer()->pauseHandle(_handle, pause);
_paused = pause;
}
}
/**
* Change the volume of Sfx
*
* @param volume Volume to be set (0-63)
*
*/
void FPSfx::setVolume(int volume) {
volume = CLIP(volume, 0, 63);
_lastVolume = volume;
if (_isVoice) {
if (!GLOBALS._bCfgDubbing)
volume = 0;
else {
volume -= (10 - GLOBALS._nCfgDubbingVolume) * 2;
if (volume < 0)
volume = 0;
}
} else {
if (!GLOBALS._bCfgSFX)
volume = 0;
else {
volume -= (10 - GLOBALS._nCfgSFXVolume) * 2;
if (volume < 0)
volume = 0;
}
}
if (g_system->getMixer()->isSoundHandleActive(_handle))
g_system->getMixer()->setChannelVolume(_handle, remapVolume(volume));
}
/**
* Gets the Sfx volume
*
* @param volumePtr Will contain the current Sfx volume
*
*/
void FPSfx::getVolume(int *volumePtr) {
if (g_system->getMixer()->isSoundHandleActive(_handle))
*volumePtr = _lastVolume;
else
*volumePtr = 0;
}
/**
* Returns true if the underlying sound has ended
*/
bool FPSfx::endOfBuffer() const {
return !g_system->getMixer()->isSoundHandleActive(_handle) && (!_rewindableStream || _rewindableStream->endOfData());
}
/**
* Continually checks to see if active sounds have finished playing
* Sets the event signalling the sound has ended
*/
void FPSfx::soundCheckProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CONTEXT;
Common::List<FPSfx *>::iterator i;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
for (;;) {
// Check each active sound
for (_ctx->i = g_vm->_activeSfx.begin(); _ctx->i != g_vm->_activeSfx.end(); ++_ctx->i) {
FPSfx *sfx = *_ctx->i;
if (sfx->endOfBuffer())
CoroScheduler.setEvent(sfx->_hEndOfBuffer);
}
// Delay until the next check is done
CORO_INVOKE_1(CoroScheduler.sleep, 50);
}
CORO_END_CODE;
}
/**
* Default constructor.
*
* @remarks Do *NOT* declare an object directly, but rather
* create it using FPSound::CreateStream()
*/
FPStream::FPStream(bool soundOn) {
_soundSupported = soundOn;
_fileLoaded = false;
_paused = false;
_loop = false;
_doFadeOut = false;
_syncExit = false;
_bufferSize = _size = 0;
_lastVolume = 0;
_syncToPlay = NULL;
_loopStream = NULL;
_rewindableStream = NULL;
}
/**
* Default destructor.
*
* @remarks It calls CloseFile() if needed.
*/
FPStream::~FPStream() {
if (!_soundSupported)
return;
if (g_system->getMixer()->isSoundHandleActive(_handle))
stop();
if (_fileLoaded)
unloadFile();
_syncToPlay = NULL;
}
/**
* Releases the memory object.
*
* @remarks Must be called when the object is no longer used
* and **ONLY** if the object was created by
* FPSound::CreateStream().
* Object pointers are no longer valid after this call.
*/
void FPStream::release() {
delete this;
}
/**
* Opens a file stream
*
* @param fileName Filename to be opened
* @param bufSize Buffer size
*
* @returns True is everything is OK, False otherwise
*/
bool FPStream::loadFile(const Common::String &fileName, int bufSize) {
if (!_soundSupported)
return true;
if (_fileLoaded)
unloadFile();
SoundCodecs codec = FPCODEC_UNKNOWN;
// Open the file stream for reading
if (_file.open(Common::Path(fileName)))
codec = FPCODEC_ADPCM;
else if (_file.open(setExtension(fileName, ".MP3")))
codec = FPCODEC_MP3;
else if (_file.open(setExtension(fileName, ".OGG")))
codec = FPCODEC_OGG;
else if (_file.open(setExtension(fileName, ".FLA")))
codec = FPCODEC_FLAC;
// Fallback: try with an extra '0' prefix
else if (_file.open(Common::Path("0" + fileName))) {
codec = FPCODEC_ADPCM;
warning("FPStream::loadFile(): Fallback from %s to %s", fileName.c_str(), _file.getName());
} else if (_file.open(setExtension("0" + fileName, ".MP3"))) {
codec = FPCODEC_MP3;
warning("FPStream::loadFile(): Fallback from %s to %s", fileName.c_str(), _file.getName());
} else if (_file.open(setExtension("0" + fileName, ".OGG"))) {
codec = FPCODEC_OGG;
warning("FPStream::loadFile(): Fallback from %s to %s", fileName.c_str(), _file.getName());
} else if (_file.open(setExtension("0" + fileName, ".FLA"))) {
codec = FPCODEC_FLAC;
warning("FPStream::loadFile(): Fallback from %s to %s", fileName.c_str(), _file.getName());
} else
return false;
// Save the size of the stream
_size = _file.size();
switch (codec) {
case FPCODEC_ADPCM:
_rewindableStream = Audio::makeADPCMStream(&_file, DisposeAfterUse::NO, 0, Audio::kADPCMDVI, 44100, 2);
break;
case FPCODEC_MP3:
#ifdef USE_MAD
_rewindableStream = Audio::makeMP3Stream(&_file, DisposeAfterUse::NO);
#endif
break;
case FPCODEC_OGG:
#ifdef USE_VORBIS
_rewindableStream = Audio::makeVorbisStream(&_file, DisposeAfterUse::NO);
#endif
break;
case FPCODEC_FLAC:
#ifdef USE_FLAC
_rewindableStream = Audio::makeFLACStream(&_file, DisposeAfterUse::NO);
#endif
break;
default:
break;
}
// All done
_fileLoaded = true;
_paused = false;
setVolume(63);
return true;
}
/**
* Closes a file stream (opened or not).
*
* @returns For safety, the destructor calls unloadFile() if it has not
* been mentioned explicitly.
*
* @remarks It is necessary to call this function to free the
* memory used by the stream.
*/
bool FPStream::unloadFile() {
if (!_soundSupported || !_fileLoaded)
return true;
assert(!g_system->getMixer()->isSoundHandleActive(_handle));
// Closes the file handle stream
delete _loopStream;
delete _rewindableStream;
_loopStream = NULL;
_rewindableStream = NULL;
_file.close();
// Flag that the file is no longer in memory
_fileLoaded = false;
return true;
}
/**
* Play the stream.
*
* @returns True is everything is OK, False otherwise
*/
bool FPStream::play() {
if (!_soundSupported || !_fileLoaded)
return false;
stop();
_rewindableStream->rewind();
Audio::AudioStream *stream = _rewindableStream;
if (_loop) {
if (!_loopStream)
_loopStream = new Audio::LoopingAudioStream(_rewindableStream, 0, DisposeAfterUse::NO);
stream = _loopStream;
}
// FIXME: Should this be kMusicSoundType or KPlainSoundType?
g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_handle, stream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
setVolume(_lastVolume);
_paused = false;
return true;
}
/**
* Closes the stream.
*
* @returns True is everything is OK, False otherwise
*
*/
bool FPStream::stop() {
if (!_soundSupported)
return true;
if (!_fileLoaded)
return false;
if (!g_system->getMixer()->isSoundHandleActive(_handle))
return false;
g_system->getMixer()->stopHandle(_handle);
_paused = false;
return true;
}
void FPStream::waitForSync(FPStream *toPlay) {
// FIXME: The idea here is that you wait for this stream to reach
// a buffer which is a multiple of nBufSize/nSync, and then the
// thread stops it and immediately starts the 'toplay' stream.
stop();
toPlay->play();
}
/**
* Unables or disables stream loop.
*
* @param loop True enable loop, False disables it
*
* @remarks The loop must be activated BEFORE the stream starts
* playing. Any changes made during the play will have no
* effect until the stream is stopped then played again.
*/
void FPStream::setLoop(bool loop) {
_loop = loop;
}
/**
* Pause sound effect
*
* @param pause True enables pause, False disables it
*/
void FPStream::setPause(bool pause) {
if (!_fileLoaded)
return;
if (pause == _paused)
return;
if (g_system->getMixer()->isSoundHandleActive(_handle))
g_system->getMixer()->pauseHandle(_handle, pause);
_paused = pause;
// Trick to reset the volume after a possible new sound configuration
setVolume(_lastVolume);
}
/**
* Change the volume of the stream
*
* @param volume Volume to be set (0-63)
*
*/
void FPStream::setVolume(int volume) {
if (volume > 63)
volume = 63;
if (volume < 0)
volume = 0;
_lastVolume = volume;
if (!GLOBALS._bCfgMusic)
volume = 0;
else {
volume -= (10 - GLOBALS._nCfgMusicVolume) * 2;
if (volume < 0)
volume = 0;
}
if (g_system->getMixer()->isSoundHandleActive(_handle))
g_system->getMixer()->setChannelVolume(_handle, remapVolume(volume));
}
/**
* Gets the volume of the stream
*
* @param volumePtr Variable that will contain the current volume
*
*/
void FPStream::getVolume(int *volumePtr) {
if (g_system->getMixer()->isSoundHandleActive(_handle))
*volumePtr = _lastVolume;
else
*volumePtr = 0;
}
} // End of namespace Tony

377
engines/tony/sound.h Normal file
View File

@@ -0,0 +1,377 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_SOUND_H
#define TONY_SOUND_H
#include "audio/mixer.h"
#include "common/file.h"
#include "tony/gfxcore.h"
#include "tony/loc.h"
#include "tony/utils.h"
namespace Audio {
class RewindableAudioStream;
}
namespace Tony {
class FPStream;
class FPSfx;
enum SoundCodecs {
FPCODEC_UNKNOWN,
FPCODEC_ADPCM,
FPCODEC_MP3,
FPCODEC_OGG,
FPCODEC_FLAC
};
/**
* Sound driver For Tony Tough
*/
class FPSound {
private:
bool _soundSupported;
public:
/**
* Default constructor. Initializes the attributes.
*
*/
FPSound();
/**
* Destroy the object and free the memory
*
*/
~FPSound();
/**
* Initializes the object, and prepare everything you need to create streams and sound effects.
*
* @returns True is everything is OK, False otherwise
*/
bool init();
/**
* Allocates an object of type FPStream, and return its pointer
*
* @param streamPtr Will contain a pointer to the object you just created.
*
* @returns True is everything is OK, False otherwise
*/
bool createStream(FPStream **streamPtr);
/**
* Allocates an object of type FpSfx, and return its pointer
*
* @param sfxPtr Will contain a pointer to the object you just created.
*
* @returns True is everything is OK, False otherwise
*/
bool createSfx(FPSfx **sfxPtr);
/**
* Set the general volume
*
* @param volume Volume to set (0-63)
*/
void setMasterVolume(int volume);
/**
* Get the general volume
*
* @param volume Variable that will contain the volume (0-63)
*/
void getMasterVolume(int *volume);
};
class FPSfx {
private:
bool _soundSupported; // True if the sound is active
bool _fileLoaded; // True is a file is opened
bool _loop; // True is sound effect should loop
int _lastVolume;
bool _isVoice;
bool _paused;
Audio::AudioStream *_loopStream;
Audio::RewindableAudioStream *_rewindableStream;
Audio::SoundHandle _handle;
public:
uint32 _hEndOfBuffer;
/**
* Check process for whether sounds have finished playing
*/
static void soundCheckProcess(CORO_PARAM, const void *param);
/**
* Default constructor.
*
* @remarks Do *NOT* declare an object directly, but rather
* create it using FPSound::CreateSfx()
*
*/
FPSfx(bool soundOn);
/**
* Default Destructor.
*
* @remarks It is also stops the sound effect that may be
* currently played, and free the memory it uses.
*
*/
~FPSfx();
/**
* Releases the memory used by the object.
*
* @remarks Must be called when the object is no longer used and
* **ONLY** if the object was created by
* FPSound::CreateStream().
* Object pointers are no longer valid after this call.
*/
void release();
/**
* Opens a file and loads a sound effect.
*
* @param fileName Sfx filename
* @param codec CODEC used to uncompress the samples
*
* @returns True is everything is OK, False otherwise
*/
bool loadFile(const char *fileName);
bool loadWave(Common::SeekableReadStream *stream);
bool loadVoiceFromVDB(Common::File &vdbFP);
/**
* Play the Sfx in memory.
*
* @returns True is everything is OK, False otherwise
*/
bool play();
/**
* Stops a Sfx.
*
* @returns True is everything is OK, False otherwise
*/
bool stop();
/**
* Pauses a Sfx.
*
*/
void setPause(bool pause);
/**
* Enables or disables the Sfx loop.
*
* @param loop True to enable the loop, False to disable
*
* @remarks The loop must be activated BEFORE the sfx starts
* playing. Any changes made during the play will have
* no effect until the sfx is stopped then played again.
*/
void setLoop(bool loop);
/**
* Change the volume of Sfx
*
* @param volume Volume to be set (0-63)
*
*/
void setVolume(int volume);
/**
* Gets the Sfx volume
*
* @param volumePtr Will contain the current Sfx volume
*
*/
void getVolume(int *volumePtr);
/**
* Returns true if the underlying sound has ended
*/
bool endOfBuffer() const;
};
class FPStream {
private:
uint32 _bufferSize; // Buffer size (bytes)
uint32 _size; // Stream size (bytes)
Common::File _file; // File handle used for the stream
bool _soundSupported; // True if the sound is active
bool _fileLoaded; // True if the file is open
bool _loop; // True if the stream should loop
bool _doFadeOut; // True if fade out is required
bool _syncExit;
bool _paused;
int _lastVolume;
FPStream *_syncToPlay;
Audio::AudioStream *_loopStream;
Audio::RewindableAudioStream *_rewindableStream;
Audio::SoundHandle _handle;
public:
/**
* Default constructor.
*
* @remarks Do *NOT* declare an object directly, but rather
* create it using FPSound::CreateStream()
*/
FPStream(bool soundOn);
/**
* Default destructor.
*
* @remarks It calls CloseFile() if needed.
*/
~FPStream();
/**
* Releases the memory object.
*
* @remarks Must be called when the object is no longer used
* and **ONLY** if the object was created by
* FPSound::CreateStream().
* Object pointers are no longer valid after this call.
*/
void release();
/**
* Opens a file stream
*
* @param fileName Filename to be opened
*
* @returns True is everything is OK, False otherwise
*/
bool loadFile(const Common::String &fileName, int sync);
/**
* Closes a file stream (opened or not).
*
* @returns For safety, the destructor calls unloadFile() if it has not
* been mentioned explicitly.
*
* @remarks It is necessary to call this function to free the
* memory used by the stream.
*/
bool unloadFile();
/**
* Play the stream.
*
* @returns True is everything is OK, False otherwise
*/
bool play();
void playFast();
/**
* Closes the stream.
*
* @returns True is everything is OK, False otherwise
*/
bool stop();
void waitForSync(FPStream *toPlay);
/**
* Pause sound effect
*
* @param pause True enables pause, False disables it
*/
void setPause(bool pause);
/**
* Unables or disables stream loop.
*
* @param loop True enable loop, False disables it
*
* @remarks The loop must be activated BEFORE the stream starts
* playing. Any changes made during the play will have no
* effect until the stream is stopped then played again.
*/
void setLoop(bool loop);
/**
* Change the volume of the stream
*
* @param volume Volume to be set (0-63)
*/
void setVolume(int volume);
/**
* Gets the volume of the stream
*
* @param volumePtr Variable that will contain the current volume
*
*/
void getVolume(int *volumePtr);
};
} // End of namespace Tony
#endif

800
engines/tony/tony.cpp Normal file
View File

@@ -0,0 +1,800 @@
/* 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/scummsys.h"
#include "common/algorithm.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/file.h"
#include "common/compression/installshield_cab.h"
#include "common/translation.h"
#include "tony/tony.h"
#include "tony/custom.h"
#include "tony/debugger.h"
#include "tony/game.h"
#include "tony/mpal/mpal.h"
namespace Tony {
TonyEngine *g_vm;
TonyEngine::TonyEngine(OSystem *syst, const TonyGameDescription *gameDesc) : Engine(syst),
_gameDescription(gameDesc), _randomSource("tony") {
g_vm = this;
_loadSlotNumber = -1;
// Set the up the debugger
setDebugger(new Debugger());
// Add folders to the search directory list
const Common::FSNode gameDataDir(ConfMan.getPath("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "Voices");
SearchMan.addSubDirectoryMatching(gameDataDir, "Roasted");
SearchMan.addSubDirectoryMatching(gameDataDir, "Music");
SearchMan.addSubDirectoryMatching(gameDataDir, "Music/utilsfx");
SearchMan.addSubDirectoryMatching(gameDataDir, "Music/Layer");
// Set up load slot number
_initialLoadSlotNumber = -1;
if (ConfMan.hasKey("save_slot")) {
int slotNumber = ConfMan.getInt("save_slot");
if (slotNumber >= 0 && slotNumber <= 99)
_initialLoadSlotNumber = slotNumber;
}
// Load the ScummVM sound settings
syncSoundSettings();
_hEndOfFrame = 0;
for (int i = 0; i < 6; i++)
_stream[i] = NULL;
for (int i = 0; i < MAX_SFX_CHANNELS; i++) {
_sfx[i] = NULL;
_utilSfx[i] = NULL;
}
_bPaused = false;
_bDrawLocation = false;
_startTime = 0;
_curThumbnail = NULL;
_bQuitNow = false;
_bTimeFreezed = false;
_nTimeFreezed = 0;
_vdbCodec = FPCODEC_UNKNOWN;
memset(_funcList, 0, sizeof(_funcList));
}
TonyEngine::~TonyEngine() {
// Close the voice database
closeVoiceDatabase();
// Reset the coroutine scheduler
CoroScheduler.reset();
CoroScheduler.setResourceCallback(NULL);
}
/**
* Run the game
*/
Common::Error TonyEngine::run() {
Common::ErrorCode result = init();
if (result != Common::kNoError)
return result;
play();
close();
return Common::kNoError;
}
/**
* Initialize the game
*/
Common::ErrorCode TonyEngine::init() {
// Load DAT file (used by font manager)
if (!loadTonyDat())
return Common::kUnknownError;
if (isCompressed()) {
Common::Archive *cabinet = Common::makeInstallShieldArchive("data");
if (!cabinet)
error("Failed to open the InstallShield cabinet");
SearchMan.add("data1.cab", cabinet);
}
_hEndOfFrame = CoroScheduler.createEvent(false, false);
_bPaused = false;
_bDrawLocation = true;
_startTime = g_system->getMillis();
// Init static class fields
RMText::initStatics();
RMTony::initStatics();
// Reset the scheduler
CoroScheduler.reset();
// Initialize the graphics window
_window.init();
// Initialize the function list
Common::fill(_funcList, _funcList + 300, (LPCUSTOMFUNCTION)NULL);
initCustomFunctionMap();
// Initializes MPAL system, passing the custom functions list
Common::File f;
if (!f.open("ROASTED.MPC"))
return Common::kReadingFailed;
f.close();
if (!mpalInit("ROASTED.MPC", "ROASTED.MPR", _funcList, _funcListStrings))
return Common::kUnknownError;
// Initialize the update resources
_resUpdate.init("ROASTED.MPU");
// Initialize the music
initMusic();
// Initialize the voices database
if (!openVoiceDatabase())
return Common::kReadingFailed;
// Initialize the boxes
_theBoxes.init();
// Link to the custom graphics engine
_theEngine.initCustomDll();
_theEngine.init();
// Allocate space for thumbnails when saving the game
_curThumbnail = new uint16[160 * 120];
_bQuitNow = false;
return Common::kNoError;
}
bool TonyEngine::loadTonyDat() {
Common::U32String errorMessage;
Common::File in;
Common::String filename = "tony.dat";
in.open(filename.c_str());
if (!in.isOpen()) {
const char *msg = _s("Unable to locate the '%s' engine data file.");
errorMessage = Common::U32String::format(_(msg), filename.c_str());
GUIErrorMessage(errorMessage);
warning(msg, filename.c_str());
return false;
}
// Read header
char buf[4+1];
in.read(buf, 4);
buf[4] = '\0';
if (strcmp(buf, "TONY")) {
const char *msg = _s("The '%s' engine data file is corrupt.");
errorMessage = Common::U32String::format(_(msg), filename.c_str());
GUIErrorMessage(errorMessage);
warning(msg, filename.c_str());
return false;
}
int majVer = in.readByte();
int minVer = in.readByte();
if ((majVer != TONY_DAT_VER_MAJ) || (minVer != TONY_DAT_VER_MIN)) {
const char *msg = _s("Incorrect version of the '%s' engine data file found. Expected %d.%d but got %d.%d.");
errorMessage = Common::U32String::format(_(msg), filename.c_str(), TONY_DAT_VER_MAJ, TONY_DAT_VER_MIN, majVer, minVer);
GUIErrorMessage(errorMessage);
warning(msg, filename.c_str(), TONY_DAT_VER_MAJ, TONY_DAT_VER_MIN, majVer, minVer);
return false;
}
int expectedLangVariant = -1;
switch (g_vm->getLanguage()) {
case Common::IT_ITA:
case Common::EN_ANY:
expectedLangVariant = 0;
break;
case Common::PL_POL:
expectedLangVariant = 1;
break;
case Common::RU_RUS:
expectedLangVariant = 2;
break;
case Common::CS_CZE:
expectedLangVariant = 3;
break;
case Common::FR_FRA:
expectedLangVariant = 4;
break;
case Common::DE_DEU:
expectedLangVariant = 5;
break;
default:
warning("Unhandled language, falling back to English/Italian fonts.");
expectedLangVariant = 0;
break;
}
int numVariant = in.readUint16BE();
if (expectedLangVariant > numVariant - 1) {
const char *msg = _s("Font variant not present in '%s' engine data file.");
errorMessage = Common::U32String::format(_(msg), filename.c_str());
GUIErrorMessage(errorMessage);
warning(msg, filename.c_str());
return false;
}
in.seek(in.pos() + (2 * 256 * 8 * expectedLangVariant));
for (int i = 0; i < 256; i++) {
_cTableDialog[i] = in.readSint16BE();
_lTableDialog[i] = in.readSint16BE();
_cTableMacc[i] = in.readSint16BE();
_lTableMacc[i] = in.readSint16BE();
_cTableCred[i] = in.readSint16BE();
_lTableCred[i] = in.readSint16BE();
_cTableObj[i] = in.readSint16BE();
_lTableObj[i] = in.readSint16BE();
}
return true;
}
void TonyEngine::initCustomFunctionMap() {
INIT_CUSTOM_FUNCTION(_funcList, _funcListStrings);
}
void TonyEngine::playMusic(int nChannel, const Common::String &fname, int nFX, bool bLoop, int nSync) {
if (nChannel < 4) {
if (GLOBALS._flipflop)
nChannel = nChannel + 1;
}
switch (nFX) {
case 0:
case 1:
case 2:
_stream[nChannel]->stop();
_stream[nChannel]->unloadFile();
break;
case 22:
default:
break;
}
if (nFX == 22) { // Sync a tempo
GLOBALS._curChannel = nChannel;
GLOBALS._nextLoop = bLoop;
GLOBALS._nextSync = nSync;
GLOBALS._nextMusic = fname;
if (GLOBALS._flipflop)
GLOBALS._nextChannel = nChannel - 1;
else
GLOBALS._nextChannel = nChannel + 1;
uint32 hThread = CoroScheduler.createProcess(doNextMusic, NULL, 0);
assert(hThread != CORO_INVALID_PID_VALUE);
} else if (nFX == 44) { // Change the channel and let the first finish
if (GLOBALS._flipflop)
GLOBALS._nextChannel = nChannel - 1;
else
GLOBALS._nextChannel = nChannel + 1;
_stream[GLOBALS._nextChannel]->stop();
_stream[GLOBALS._nextChannel]->unloadFile();
if (!getIsDemo()) {
if (!_stream[GLOBALS._nextChannel]->loadFile(fname, nSync))
error("failed to open music file '%s'", fname.c_str());
} else {
_stream[GLOBALS._nextChannel]->loadFile(fname, nSync);
}
_stream[GLOBALS._nextChannel]->setLoop(bLoop);
_stream[GLOBALS._nextChannel]->play();
GLOBALS._flipflop = 1 - GLOBALS._flipflop;
} else {
if (!getIsDemo()) {
if (!_stream[nChannel]->loadFile(fname, nSync))
error("failed to open music file '%s'", fname.c_str());
} else {
_stream[nChannel]->loadFile(fname, nSync);
}
_stream[nChannel]->setLoop(bLoop);
_stream[nChannel]->play();
}
}
void TonyEngine::doNextMusic(CORO_PARAM, const void *param) {
CORO_BEGIN_CONTEXT;
Common::String fn;
CORO_END_CONTEXT(_ctx);
FPStream **streams = g_vm->_stream;
CORO_BEGIN_CODE(_ctx);
if (!g_vm->getIsDemo()) {
if (!streams[GLOBALS._nextChannel]->loadFile(GLOBALS._nextMusic, GLOBALS._nextSync))
error("failed to open next music file '%s'", GLOBALS._nextMusic.c_str());
} else {
streams[GLOBALS._nextChannel]->loadFile(GLOBALS._nextMusic, GLOBALS._nextSync);
}
streams[GLOBALS._nextChannel]->setLoop(GLOBALS._nextLoop);
//streams[GLOBALS._nextChannel]->prefetch();
streams[GLOBALS._curChannel]->waitForSync(streams[GLOBALS._nextChannel]);
streams[GLOBALS._curChannel]->unloadFile();
GLOBALS._flipflop = 1 - GLOBALS._flipflop;
CORO_END_CODE;
}
void TonyEngine::playSFX(int nChannel, int nFX) {
if (_sfx[nChannel] == NULL)
return;
switch (nFX) {
case 0:
_sfx[nChannel]->setLoop(false);
break;
case 1:
_sfx[nChannel]->setLoop(true);
break;
default:
break;
}
_sfx[nChannel]->play();
}
void TonyEngine::stopMusic(int nChannel) {
if (nChannel < 4)
_stream[nChannel + GLOBALS._flipflop]->stop();
else
_stream[nChannel]->stop();
}
void TonyEngine::stopSFX(int nChannel) {
_sfx[nChannel]->stop();
}
void TonyEngine::playUtilSFX(int nChannel, int nFX) {
if (_utilSfx[nChannel] == NULL)
return;
switch (nFX) {
case 0:
_utilSfx[nChannel]->setLoop(false);
break;
case 1:
_utilSfx[nChannel]->setLoop(true);
break;
default:
break;
}
_utilSfx[nChannel]->setVolume(52);
_utilSfx[nChannel]->play();
}
void TonyEngine::stopUtilSFX(int nChannel) {
_utilSfx[nChannel]->stop();
}
void TonyEngine::preloadSFX(int nChannel, const char *fn) {
if (_sfx[nChannel] != NULL) {
_sfx[nChannel]->stop();
_sfx[nChannel]->release();
_sfx[nChannel] = NULL;
}
_theSound.createSfx(&_sfx[nChannel]);
_sfx[nChannel]->loadFile(fn);
}
FPSfx *TonyEngine::createSFX(Common::SeekableReadStream *stream) {
FPSfx *sfx;
_theSound.createSfx(&sfx);
sfx->loadWave(stream);
return sfx;
}
void TonyEngine::preloadUtilSFX(int nChannel, const char *fn) {
if (_utilSfx[nChannel] != NULL) {
_utilSfx[nChannel]->stop();
_utilSfx[nChannel]->release();
_utilSfx[nChannel] = NULL;
}
_theSound.createSfx(&_utilSfx[nChannel]);
_utilSfx[nChannel]->loadFile(fn);
_utilSfx[nChannel]->setVolume(63);
}
void TonyEngine::unloadAllSFX() {
for (int i = 0; i < MAX_SFX_CHANNELS; i++) {
if (_sfx[i] != NULL) {
_sfx[i]->stop();
_sfx[i]->release();
_sfx[i] = NULL;
}
}
}
void TonyEngine::unloadAllUtilSFX() {
for (int i = 0; i < MAX_SFX_CHANNELS; i++) {
if (_utilSfx[i] != NULL) {
_utilSfx[i]->stop();
_utilSfx[i]->release();
_utilSfx[i] = NULL;
}
}
}
void TonyEngine::initMusic() {
int i;
_theSound.init();
_theSound.setMasterVolume(63);
for (i = 0; i < 6; i++)
_theSound.createStream(&_stream[i]);
for (i = 0; i < MAX_SFX_CHANNELS; i++) {
_sfx[i] = _utilSfx[i] = NULL;
}
// Preload sound effects
preloadUtilSFX(0, "U01.ADP"); // Reversed!!
preloadUtilSFX(1, "U02.ADP");
// Start check processes for sound
CoroScheduler.createProcess(FPSfx::soundCheckProcess, NULL);
}
void TonyEngine::closeMusic() {
for (int i = 0; i < 6; i++) {
_stream[i]->stop();
_stream[i]->unloadFile();
_stream[i]->release();
}
unloadAllSFX();
unloadAllUtilSFX();
}
void TonyEngine::pauseSound(bool bPause) {
_theEngine.pauseSound(bPause);
for (uint i = 0; i < 6; i++)
if (_stream[i])
_stream[i]->setPause(bPause);
for (uint i = 0; i < MAX_SFX_CHANNELS; i++) {
if (_sfx[i])
_sfx[i]->setPause(bPause);
if (_utilSfx[i])
_utilSfx[i]->setPause(bPause);
}
}
void TonyEngine::setMusicVolume(int nChannel, int volume) {
_stream[nChannel + GLOBALS._flipflop]->setVolume(volume);
}
int TonyEngine::getMusicVolume(int nChannel) {
int volume;
_stream[nChannel + GLOBALS._flipflop]->getVolume(&volume);
return volume;
}
Common::String TonyEngine::getSaveStateFileName(int n) {
return Common::String::format("tony.%03d", n);
}
Common::String TonyEngine::getSaveStateName(int slot) const {
return getSaveStateFileName(slot);
}
void TonyEngine::autoSave(CORO_PARAM) {
CORO_BEGIN_CONTEXT;
Common::String buf;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
grabThumbnail();
CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
_ctx->buf = getSaveStateFileName(0);
_theEngine.saveState(_ctx->buf, (byte *)_curThumbnail, "Autosave");
CORO_END_CODE;
}
void TonyEngine::saveState(int n, const char *name) {
Common::String buf = getSaveStateFileName(n);
_theEngine.saveState(buf.c_str(), (byte *)_curThumbnail, name);
}
void TonyEngine::loadState(CORO_PARAM, int n) {
CORO_BEGIN_CONTEXT;
Common::String buf;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->buf = getSaveStateFileName(n);
CORO_INVOKE_1(_theEngine.loadState, _ctx->buf.c_str());
CORO_END_CODE;
}
bool TonyEngine::openVoiceDatabase() {
// Open the voices database
if (!_vdbFP.open("voices.vdb"))
if (!_vdbFP.open("voices.mdb"))
if (!_vdbFP.open("voices.odb"))
if (!_vdbFP.open("voices.fdb"))
return false;
_vdbFP.seek(-8, SEEK_END);
uint32 numfiles = _vdbFP.readUint32LE();
int32 id = _vdbFP.readUint32BE();
if (id == MKTAG('V', 'D', 'B', '1'))
_vdbCodec = FPCODEC_ADPCM;
else if (id == MKTAG('M', 'D', 'B', '1'))
_vdbCodec = FPCODEC_MP3;
else if (id == MKTAG('O', 'D', 'B', '1'))
_vdbCodec = FPCODEC_OGG;
else if (id == MKTAG('F', 'D', 'B', '1'))
_vdbCodec = FPCODEC_FLAC;
else {
_vdbFP.close();
return false;
}
// Read in the index
_vdbFP.seek(-8 - (int64)(numfiles * VOICE_HEADER_SIZE), SEEK_END);
for (uint32 i = 0; i < numfiles; ++i) {
VoiceHeader vh;
vh._offset = _vdbFP.readUint32LE();
vh._code = _vdbFP.readUint32LE();
vh._parts = _vdbFP.readUint32LE();
_voices.push_back(vh);
}
return true;
}
void TonyEngine::closeVoiceDatabase() {
if (_vdbFP.isOpen())
_vdbFP.close();
if (_voices.size() > 0)
_voices.clear();
}
void TonyEngine::grabThumbnail() {
_window.grabThumbnail(_curThumbnail);
}
uint16 *TonyEngine::getThumbnail() {
return _curThumbnail;
}
void TonyEngine::quitGame() {
_bQuitNow = true;
}
void TonyEngine::openInitLoadMenu(CORO_PARAM) {
_theEngine.openOptionScreen(coroParam, 1);
}
void TonyEngine::openInitOptions(CORO_PARAM) {
_theEngine.openOptionScreen(coroParam, 2);
}
/**
* Main process for playing the game.
*
* @remarks This needs to be in a separate process, since there are some things that can briefly
* block the execution of process. For now, all ScummVm event handling is dispatched to within the context of this
* process. If it ever proves a problem, we may have to look into whether it's feasible to have it still remain
* in the outer 'main' process.
*/
void TonyEngine::playProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CONTEXT;
Common::String fn;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Game loop. We rely on the outer main process to detect if a shutdown is required,
// and kill the scheudler and all the processes, including this one
for (;;) {
// If a savegame needs to be loaded, then do so
if (g_vm->_loadSlotNumber != -1 && GLOBALS._gfxEngine != NULL) {
_ctx->fn = getSaveStateFileName(g_vm->_loadSlotNumber);
CORO_INVOKE_1(GLOBALS._gfxEngine->loadState, _ctx->fn);
g_vm->_loadSlotNumber = -1;
}
// Wait for the next frame
CORO_INVOKE_1(CoroScheduler.sleep, 50);
// Call the engine to handle the next frame
CORO_INVOKE_1(g_vm->_theEngine.doFrame, g_vm->_bDrawLocation);
// Warns that a frame is finished
CoroScheduler.pulseEvent(g_vm->_hEndOfFrame);
// Handle drawing the frame
if (!g_vm->_bPaused) {
if (!g_vm->_theEngine._bWiping)
g_vm->_window.getNewFrame(g_vm->_theEngine, NULL);
else
g_vm->_window.getNewFrame(g_vm->_theEngine, &g_vm->_theEngine._rcWipeEllipse);
}
// Paint the frame onto the screen
g_vm->_window.repaint();
}
CORO_END_CODE;
}
/**
* Play the game
*/
void TonyEngine::play() {
// Create the game player process
CoroScheduler.createProcess(playProcess, NULL);
// Loop through calling the scheduler until it's time for the game to quit
while (!shouldQuit() && !_bQuitNow) {
// Delay for a brief amount
g_system->delayMillis(10);
// Call any scheduled processes
CoroScheduler.schedule();
}
}
void TonyEngine::close() {
closeMusic();
CoroScheduler.closeEvent(_hEndOfFrame);
_theBoxes.close();
_theEngine.close();
_window.close();
mpalFree();
freeMpc();
delete[] _curThumbnail;
}
void TonyEngine::freezeTime() {
_bTimeFreezed = true;
_nTimeFreezed = getTime() - _startTime;
}
void TonyEngine::unfreezeTime() {
_bTimeFreezed = false;
}
/**
* Returns the millisecond timer
*/
uint32 TonyEngine::getTime() {
return g_system->getMillis();
}
bool TonyEngine::canLoadGameStateCurrently(Common::U32String *msg) {
return GLOBALS._gfxEngine != NULL && GLOBALS._gfxEngine->canLoadSave();
}
bool TonyEngine::canSaveGameStateCurrently(Common::U32String *msg) {
return GLOBALS._gfxEngine != NULL && GLOBALS._gfxEngine->canLoadSave();
}
Common::Error TonyEngine::loadGameState(int slot) {
_loadSlotNumber = slot;
return Common::kNoError;
}
Common::Error TonyEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
if (!GLOBALS._gfxEngine)
return Common::kUnknownError;
RMGfxTargetBuffer &bigBuf = *GLOBALS._gfxEngine;
RMSnapshot s;
s.grabScreenshot(bigBuf, 4, _curThumbnail);
GLOBALS._gfxEngine->saveState(getSaveStateFileName(slot), (byte *)_curThumbnail, desc);
return Common::kNoError;
}
void TonyEngine::syncSoundSettings() {
Engine::syncSoundSettings();
GLOBALS._bCfgDubbing = !ConfMan.getBool("mute") && !ConfMan.getBool("speech_mute");
GLOBALS._bCfgSFX = !ConfMan.getBool("mute") && !ConfMan.getBool("sfx_mute");
GLOBALS._bCfgMusic = !ConfMan.getBool("mute") && !ConfMan.getBool("music_mute");
GLOBALS._nCfgDubbingVolume = ConfMan.getInt("speech_volume") * 10 / 256;
GLOBALS._nCfgSFXVolume = ConfMan.getInt("sfx_volume") * 10 / 256;
GLOBALS._nCfgMusicVolume = ConfMan.getInt("music_volume") * 10 / 256;
GLOBALS._bShowSubtitles = ConfMan.getBool("subtitles");
GLOBALS._nCfgTextSpeed = ConfMan.getInt("talkspeed") * 10 / 256;
}
void TonyEngine::saveSoundSettings() {
ConfMan.setBool("speech_mute", !GLOBALS._bCfgDubbing);
ConfMan.setBool("sfx_mute", !GLOBALS._bCfgSFX);
ConfMan.setBool("music_mute", !GLOBALS._bCfgMusic);
ConfMan.setInt("speech_volume", GLOBALS._nCfgDubbingVolume * 256 / 10);
ConfMan.setInt("sfx_volume", GLOBALS._nCfgSFXVolume * 256 / 10);
ConfMan.setInt("music_volume", GLOBALS._nCfgMusicVolume * 256 / 10);
ConfMan.setBool("subtitles", GLOBALS._bShowSubtitles);
ConfMan.setInt("talkspeed", GLOBALS._nCfgTextSpeed * 256 / 10);
}
void TonyEngine::showLocation() {
_bDrawLocation = true;
}
void TonyEngine::hideLocation() {
_bDrawLocation = false;
}
} // End of namespace Tony

242
engines/tony/tony.h Normal file
View File

@@ -0,0 +1,242 @@
/* 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 TONY_TONY_H
#define TONY_TONY_H
#include "common/scummsys.h"
#include "common/system.h"
#include "common/array.h"
#include "common/coroutines.h"
#include "common/error.h"
#include "common/random.h"
#include "common/util.h"
#include "engines/engine.h"
#include "gui/debugger.h"
#include "tony/mpal/mpal.h"
#include "tony/mpal/memory.h"
#include "tony/debugger.h"
#include "tony/gfxengine.h"
#include "tony/loc.h"
#include "tony/utils.h"
#include "tony/window.h"
#include "tony/globals.h"
/**
* This is the namespace of the Tony engine.
*
* Status of this engine: In Development
*
* Games using this engine:
* - Tony Tough
*/
namespace Tony {
using namespace MPAL;
class Globals;
enum {
kTonyDebugAnimations = 1,
kTonyDebugActions,
kTonyDebugSound,
kTonyDebugMusic,
kTonyDebugMPAL,
};
#define DEBUG_BASIC 1
#define DEBUG_INTERMEDIATE 2
#define DEBUG_DETAILED 3
struct TonyGameDescription;
#define MAX_SFX_CHANNELS 32
#define TONY_DAT_VER_MAJ 0
#define TONY_DAT_VER_MIN 3
struct VoiceHeader {
int _offset;
int _code;
int _parts;
};
#define VOICE_HEADER_SIZE 12
class TonyEngine : public Engine {
private:
Common::ErrorCode init();
bool loadTonyDat();
void initMusic();
void closeMusic();
bool openVoiceDatabase();
void closeVoiceDatabase();
void initCustomFunctionMap();
static void playProcess(CORO_PARAM, const void *param);
static void doNextMusic(CORO_PARAM, const void *param);
protected:
// Engine APIs
Common::Error run() override;
bool hasFeature(EngineFeature f) const override;
public:
LPCUSTOMFUNCTION _funcList[300];
Common::String _funcListStrings[300];
Common::RandomSource _randomSource;
RMResUpdate _resUpdate;
uint32 _hEndOfFrame;
Common::File _vdbFP;
SoundCodecs _vdbCodec;
Common::Array<VoiceHeader> _voices;
FPSound _theSound;
Common::List<FPSfx *> _activeSfx;
Globals _globals;
int16 _cTableDialog[256];
int16 _lTableDialog[256];
int16 _cTableMacc[256];
int16 _lTableMacc[256];
int16 _cTableCred[256];
int16 _lTableCred[256];
int16 _cTableObj[256];
int16 _lTableObj[256];
enum DataDir {
DD_BASE = 1,
DD_SAVE,
DD_SHOTS,
DD_MUSIC,
DD_LAYER,
DD_UTILSFX,
DD_VOICES,
DD_BASE2
};
FPStream *_stream[6];
FPSfx *_sfx[MAX_SFX_CHANNELS];
FPSfx *_utilSfx[MAX_SFX_CHANNELS];
bool _bPaused;
bool _bDrawLocation;
int _startTime;
uint16 *_curThumbnail;
int _initialLoadSlotNumber;
int _loadSlotNumber;
// Bounding box list manager
RMGameBoxes _theBoxes;
RMWindow _window;
RMGfxEngine _theEngine;
bool _bQuitNow;
bool _bTimeFreezed;
int _nTimeFreezed;
public:
TonyEngine(OSystem *syst, const TonyGameDescription *gameDesc);
~TonyEngine() override;
const TonyGameDescription *_gameDescription;
uint32 getFeatures() const;
Common::Language getLanguage() const;
uint16 getVersion() const;
bool getIsDemo() const;
bool isCompressed() const;
RMGfxEngine *getEngine() {
return &_theEngine;
}
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
Common::Error loadGameState(int slot) override;
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
void play();
void close();
void getDataDirectory(DataDir dir, char *path);
void showLocation();
void hideLocation();
/**
* Reads the time
*/
uint32 getTime();
void freezeTime();
void unfreezeTime();
// Music
// ******
void playMusic(int nChannel, const Common::String &fn, int nFX, bool bLoop, int nSync);
void stopMusic(int nChannel);
void playSFX(int nSfx, int nFX = 0);
void stopSFX(int nSfx);
void playUtilSFX(int nSfx, int nFX = 0);
void stopUtilSFX(int nSfx);
FPSfx *createSFX(Common::SeekableReadStream *stream);
void preloadSFX(int nSfx, const char *fn);
void unloadAllSFX();
void preloadUtilSFX(int nSfx, const char *fn);
void unloadAllUtilSFX();
/**
* Stop all the audio
*/
void pauseSound(bool bPause);
void setMusicVolume(int nChannel, int volume);
int getMusicVolume(int nChannel);
/**
* Handle saving
*/
void autoSave(CORO_PARAM);
void saveState(int n, const char *name);
void loadState(CORO_PARAM, int n);
static Common::String getSaveStateFileName(int n);
Common::String getSaveStateName(int slot) const override;
/**
* Get a thumbnail
*/
void grabThumbnail();
uint16 *getThumbnail();
void quitGame();
void openInitLoadMenu(CORO_PARAM);
void openInitOptions(CORO_PARAM);
void syncSoundSettings() override;
void saveSoundSettings();
};
// Global reference to the TonyEngine object
extern TonyEngine *g_vm;
#define GLOBALS g_vm->_globals
} // End of namespace Tony
#endif /* TONY_TONY_H */

2098
engines/tony/tonychar.cpp Normal file

File diff suppressed because it is too large Load Diff

481
engines/tony/tonychar.h Normal file
View File

@@ -0,0 +1,481 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_TONYCHAR_H
#define TONY_TONYCHAR_H
#include "common/coroutines.h"
#include "tony/loc.h"
namespace Tony {
class RMTony : public RMCharacter {
private:
enum CharacterDirection {
UP, DOWN, LEFT, RIGHT
};
public:
enum CharacterTalkType {
TALK_NORMAL,
TALK_HIPS,
TALK_SING,
TALK_LAUGH,
TALK_INDICATE,
TALK_SCARED,
TALK_SCARED2,
TALK_WITHGLASSES,
TALK_WITHHAMMER,
TALK_WITHWORM,
TALK_WITHROPE,
TALK_WITHRABBIT,
TALK_WITHRECIPE,
TALK_WITHCARDS,
TALK_WITHSNOWMAN,
TALK_WITHSNOWMANSTATIC,
TALK_WITHRABBITSTATIC,
TALK_WITHRECIPESTATIC,
TALK_WITHCARDSSTATIC,
TALK_WITH_NOTEBOOK,
TALK_WITHMEGAPHONESTATIC,
TALK_WITHBEARDSTATIC,
TALK_LAUGH2,
TALK_DISGUSTED,
TALK_SARCASTIC,
TALK_MACBETH1,
TALK_MACBETH2,
TALK_MACBETH3,
TALK_MACBETH4,
TALK_MACBETH5,
TALK_MACBETH6,
TALK_MACBETH7,
TALK_MACBETH8,
TALK_MACBETH9,
TALK_SCAREDSTATIC,
TALK_WITHSECRETARY
};
private:
bool _bShow;
bool _bShowShadow;
bool _bBodyFront;
// Useless variable?
// RMGfxSourceBuffer8AB _shadow;
bool _bActionPending;
RMItem *_actionItem;
int _action;
int _actionParm;
static bool _bAction;
bool _bShepherdess;
bool _bIsStaticTalk;
bool _bIsTalking;
int _nPatB4Talking;
CharacterTalkType _nTalkType;
CharacterDirection _talkDirection;
RMPoint _nBodyOffset;
int _nTimeLastStep;
RMItem _body;
uint32 _hActionThread;
protected:
/**
* Overload of the allocation allocation of sprites
*/
RMGfxSourceBuffer *newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE) override;
/**
* Watch thread which waits for the end of an action
*/
static void waitEndOfAction(CORO_PARAM, const void *param);
public:
enum CharacterPatterns {
PAT_TAKEUP_UP1 = 9,
PAT_TAKEUP_UP2,
PAT_TAKEUP_MID1,
PAT_TAKEUP_MID2,
PAT_TAKEUP_DOWN1,
PAT_TAKEUP_DOWN2,
PAT_TAKELEFT_UP1,
PAT_TAKELEFT_UP2,
PAT_TAKELEFT_MID1,
PAT_TAKELEFT_MID2,
PAT_TAKELEFT_DOWN1,
PAT_TAKELEFT_DOWN2,
PAT_TAKERIGHT_UP1,
PAT_TAKERIGHT_UP2,
PAT_TAKERIGHT_MID1,
PAT_TAKERIGHT_MID2,
PAT_TAKERIGHT_DOWN1,
PAT_TAKERIGHT_DOWN2,
PAT_GETUPLEFT,
PAT_ONTHEFLOORLEFT,
PAT_GETUPRIGHT,
PAT_ONTHEFLOORRIGHT,
// Sheperdess!
PAT_PAST_WALKUP,
PAT_PAST_WALKDOWN,
PAT_PAST_WALKLEFT,
PAT_PAST_WALKRIGHT,
PAT_PAST_STANDUP,
PAT_PAST_STANDDOWN,
PAT_PAST_STANDLEFT,
PAT_PAST_STANDRIGHT,
// Speech
PAT_TALK_UP,
PAT_TALK_DOWN,
PAT_TALK_LEFT,
PAT_TALK_RIGHT,
// Static head
PAT_HEAD_UP,
PAT_HEAD_DOWN,
PAT_HEAD_LEFT,
PAT_HEAD_RIGHT,
// Laugh
PAT_LAUGHLEFT_START,
PAT_LAUGHLEFT_LOOP,
PAT_LAUGHLEFT_END,
PAT_LAUGHRIGHT_START,
PAT_LAUGHRIGHT_LOOP,
PAT_LAUGHRIGHT_END,
// Speaking as a shepherdess
PAT_PAST_TALKUP,
PAT_PAST_TALKDOWN,
PAT_PAST_TALKLEFT,
PAT_PAST_TALKRIGHT,
// Fear
PAT_SCAREDLEFT_START,
PAT_SCAREDLEFT_LOOP,
PAT_SCAREDLEFT_END,
PAT_SCAREDRIGHT_START,
PAT_SCAREDRIGHT_LOOP,
PAT_SCAREDRIGHT_END,
PAT_SCAREDDOWN_START,
PAT_SCAREDDOWN_LOOP,
PAT_SCAREDDOWN_END,
// With objects: full body
PAT_WITHGLASSES,
PAT_WITHROPE,
PAT_WITHWORM,
PAT_WITHHAMMER,
// Sound the whistle
PAT_WHISTLERIGHT,
// Head with beard
PAT_TALKBEARD_LEFT,
PAT_TALKBEARD_RIGHT,
// Sniff
PAT_SNIFF_LEFT,
PAT_SNIFF_RIGHT,
// Disgusted
PAT_DISGUSTEDLEFT_START,
PAT_DISGUSTEDLEFT_LOOP,
PAT_DISGUSTEDLEFT_END,
PAT_DISGUSTEDRIGHT_START,
PAT_DISGUSTEDRIGHT_LOOP,
PAT_DISGUSTEDRIGHT_END,
PAT_SARCASTICLEFT_START,
PAT_SARCASTICLEFT_LOOP,
PAT_SARCASTICLEFT_END,
PAT_SARCASTICRIGHT_START,
PAT_SARCASTICRIGHT_LOOP,
PAT_SARCASTICRIGHT_END,
// Stand scared
PAT_SCAREDLEFT_STAND,
PAT_SCAREDRIGHT_STAND,
PAT_SCAREDDOWN_STAND,
PAT_PUTLEFT_UP1,
PAT_PUTLEFT_UP2,
PAT_PUTRIGHT_UP1,
PAT_PUTRIGHT_UP2,
PAT_PUTLEFT_MID1,
PAT_PUTLEFT_MID2,
PAT_PUTRIGHT_MID1,
PAT_PUTRIGHT_MID2,
PAT_PUTLEFT_DOWN1,
PAT_PUTLEFT_DOWN2,
PAT_PUTRIGHT_DOWN1,
PAT_PUTRIGHT_DOWN2,
PAT_PUTUP_UP1,
PAT_PUTUP_UP2,
PAT_PUTUP_MID1,
PAT_PUTUP_MID2,
PAT_PUTUP_DOWN1,
PAT_PUTUP_DOWN2,
PAT_WITHSECRETARY
};
enum CharacterBodyPatterns {
BPAT_STANDUP = 1,
BPAT_STANDDOWN,
BPAT_STANDLEFT,
BPAT_STANDRIGHT,
BPAT_HAMMER,
BPAT_SNOWMAN,
BPAT_WORM,
BPAT_GLASS,
BPAT_SINGLEFT_START,
BPAT_SINGLEFT_LOOP,
BPAT_SINGLEFT_END,
BPAT_HIPSLEFT_START,
BPAT_HIPSLEFT_LOOP,
BPAT_HIPSLEFT_END,
BPAT_HIPSRIGHT_START,
BPAT_HIPSRIGHT_LOOP,
BPAT_HIPSRIGHT_END,
BPAT_HIPSUP_START,
BPAT_HIPSUP_LOOP,
BPAT_HIPSUP_END,
BPAT_HIPSDOWN_START,
BPAT_HIPSDOWN_LOOP,
BPAT_HIPSDOWN_END,
BPAT_LAUGHLEFT,
BPAT_LAUGHRIGHT,
BPAT_INDICATELEFT,
BPAT_INDICATERIGHT,
BPAT_SCAREDDOWN_START,
BPAT_SCAREDDOWN_LOOP,
BPAT_SCAREDDOWN_END,
BPAT_SCAREDLEFT_START,
BPAT_SCAREDLEFT_LOOP,
BPAT_SCAREDLEFT_END,
BPAT_SCAREDRIGHT_START,
BPAT_SCAREDRIGHT_LOOP,
BPAT_SCAREDRIGHT_END,
BPAT_SCAREDUP_START,
BPAT_SCAREDUP_LOOP,
BPAT_SCAREDUP_END,
BPAT_ROPE,
BPAT_WITHRABBITLEFT_START,
BPAT_WITHRABBITLEFT_LOOP,
BPAT_WITHRABBITLEFT_END,
BPAT_WITHRABBITRIGHT_START,
BPAT_WITHRABBITRIGHT_LOOP,
BPAT_WITHRABBITRIGHT_END,
BPAT_WITHRECIPELEFT_START,
BPAT_WITHRECIPELEFT_LOOP,
BPAT_WITHRECIPELEFT_END,
BPAT_WITHRECIPERIGHT_START,
BPAT_WITHRECIPERIGHT_LOOP,
BPAT_WITHRECIPERIGHT_END,
BPAT_WITHCARDSLEFT_START,
BPAT_WITHCARDSLEFT_LOOP,
BPAT_WITHCARDSLEFT_END,
BPAT_WITHCARDSRIGHT_START,
BPAT_WITHCARDSRIGHT_LOOP,
BPAT_WITHCARDSRIGHT_END,
BPAT_WITHSNOWMANLEFT_START,
BPAT_WITHSNOWMANLEFT_LOOP,
BPAT_WITHSNOWMANLEFT_END,
BPAT_WITHSNOWMANRIGHT_START,
BPAT_WITHSNOWMANRIGHT_LOOP,
BPAT_WITHSNOWMANRIGHT_END,
BPAT_WITHNOTEBOOKLEFT_START,
BPAT_WITHNOTEBOOKLEFT_LOOP,
BPAT_WITHNOTEBOOKLEFT_END,
BPAT_WITHNOTEBOOKRIGHT_START,
BPAT_WITHNOTEBOOKRIGHT_LOOP,
BPAT_WITHNOTEBOOKRIGHT_END,
BPAT_WITHMEGAPHONELEFT_START,
BPAT_WITHMEGAPHONELEFT_LOOP,
BPAT_WITHMEGAPHONELEFT_END,
BPAT_WITHMEGAPHONERIGHT_START,
BPAT_WITHMEGAPHONERIGHT_LOOP,
BPAT_WITHMEGAPHONERIGHT_END,
BPAT_WITHBEARDLEFT_START,
BPAT_WITHBEARDLEFT_END,
BPAT_WITHBEARDRIGHT_START,
BPAT_WITHBEARDRIGHT_END,
BPAT_WITHBEARDLEFT_STATIC,
BPAT_WITHBEARDRIGHT_STATIC,
BPAT_MACBETH1,
BPAT_MACBETH2,
BPAT_MACBETH3,
BPAT_MACBETH4,
BPAT_MACBETH5,
BPAT_MACBETH6,
BPAT_MACBETH7,
BPAT_MACBETH8,
BPAT_MACBETH9,
BPAT_WITHSECRETARY
};
public:
static void initStatics();
RMTony();
/**
* Initialize Tony
*/
void init();
/**
* Free all memory
*/
void close();
/**
* Tony makes a frame, updating the movement, etc.
*/
void doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int curLoc);
/**
* Draw method, which controls chararacter display
*/
void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) override;
/**
* Show or hide
*/
void show();
void hide(bool bShowShadow = false);
/**
* Move and make an action, if necessary
*/
void moveAndDoAction(CORO_PARAM, RMPoint dst, RMItem *item, int nAction, int nActionParm = 0);
/**
* Tony stops (on the right side with respect to any subject)
*/
void stop(CORO_PARAM) override;
void stopNoAction(CORO_PARAM);
/**
* Set a pattern
*/
void setPattern(int npatt, bool bPlayP0 = false) override;
/**
* Reads the current pattern
*/
int getCurPattern() override;
/**
* Waits until the end of a pattern
*/
void waitForEndPattern(CORO_PARAM, uint32 hCustomSkip = CORO_INVALID_PID_VALUE);
/**
* Check if currently in an action
*/
bool inAction();
/**
* Check if there needs to be an update for scrolling movement
*/
bool mustUpdateScrolling();
/**
* Returns Tony's position
*/
RMPoint position();
/**
* Set the scrolling position
*/
void setScrollPosition(const RMPoint &pt);
/**
* Set the take animation
*/
void take(int nWhere, int nPart);
void put(int nWhere, int nPart);
/**
* Start or End Talk
*/
bool startTalkCalculate(CharacterTalkType nTalkType, int &headStartPat, int &bodyStartPat,
int &headLoopPat, int &bodyLoopPat);
void startTalk(CORO_PARAM, CharacterTalkType nTalkType);
bool endTalkCalculate(int &headStandPat, int &headEndPat, int &bodyEndPat, int &finalPat, bool &bStatic);
void endTalk(CORO_PARAM);
/**
* Start or End Static
*/
void startStaticCalculate(CharacterTalkType nTalk, int &headPat, int &headLoopPat,
int &bodyStartPat, int &bodyLoopPat);
void startStatic(CORO_PARAM, CharacterTalkType nTalkType);
void endStaticCalculate(CharacterTalkType nTalk, int &bodyEndPat, int &finalPat, int &headEndPat);
void endStatic(CORO_PARAM, CharacterTalkType nTalkType);
/**
* Tony disguises himself!
*/
void setShepherdess(bool bIsPast);
int getShepherdess();
/**
* Perform an action
*/
void executeAction(int nAction, int nActionItem, int nParm);
void playSfx(int nSfx);
};
} // End of namespace Tony
#endif

447
engines/tony/utils.cpp Normal file
View File

@@ -0,0 +1,447 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#include "tony/utils.h"
#include "tony/tony.h"
#include "tony/mpal/lzo.h"
namespace Tony {
/**
* Extracts a string from a data stream
* @param df data stream
*/
Common::String readString(Common::ReadStream &df) {
Common::String var;
uint8 len = df.readByte();
for (int i = 0; i < len; i++) {
char c;
c = df.readByte();
var += c;
}
return var;
}
/****************************************************************************\
* RMPoint methods
\****************************************************************************/
/**
* Constructor
*/
RMPoint::RMPoint() {
_x = _y = 0;
}
/**
* Copy constructor
*/
RMPoint::RMPoint(const RMPoint &p) {
_x = p._x;
_y = p._y;
}
/**
* Constructor with integer parameters
*/
RMPoint::RMPoint(int x1, int y1) {
_x = x1;
_y = y1;
}
/**
* Copy operator
*/
RMPoint &RMPoint::operator=(RMPoint p) {
_x = p._x;
_y = p._y;
return *this;
}
/**
* Set a point
*/
void RMPoint::set(int x1, int y1) {
_x = x1;
_y = y1;
}
/**
* Offsets the point by another point
*/
void RMPoint::offset(const RMPoint &p) {
_x += p._x;
_y += p._y;
}
/**
* Offsets the point by a specified offset
*/
void RMPoint::offset(int xOff, int yOff) {
_x += xOff;
_y += yOff;
}
/**
* Sums together two points
*/
RMPoint operator+(RMPoint p1, RMPoint p2) {
RMPoint p(p1);
return (p += p2);
}
/**
* Subtracts two points
*/
RMPoint operator-(RMPoint p1, RMPoint p2) {
RMPoint p(p1);
return (p -= p2);
}
/**
* Sum (offset) of a point
*/
RMPoint &RMPoint::operator+=(RMPoint p) {
offset(p);
return *this;
}
/**
* Subtract (offset) of a point
*/
RMPoint &RMPoint::operator-=(RMPoint p) {
offset(-p);
return *this;
}
/**
* Inverts a point
*/
RMPoint RMPoint::operator-() {
RMPoint p;
p._x = -_x;
p._y = -_y;
return p;
}
/**
* Equality operator
*/
bool RMPoint::operator==(RMPoint p) {
return ((_x == p._x) && (_y == p._y));
}
/**
* Not equal operator
*/
bool RMPoint::operator!=(RMPoint p) {
return ((_x != p._x) || (_y != p._y));
}
/**
* Reads a point from a stream
*/
void RMPoint::readFromStream(Common::ReadStream &ds) {
_x = ds.readSint32LE();
_y = ds.readSint32LE();
}
/****************************************************************************\
* RMPointReference methods
\****************************************************************************/
RMPointReference::RMPointReference(int &x, int &y): _x(x), _y(y) {
}
RMPointReference &RMPointReference::operator=(const RMPoint &p) {
_x = p._x; _y = p._y;
return *this;
}
RMPointReference &RMPointReference::operator-=(const RMPoint &p) {
_x -= p._x; _y -= p._y;
return *this;
}
RMPointReference::operator RMPoint() const {
return RMPoint(_x, _y);
}
/****************************************************************************\
* RMRect methods
\****************************************************************************/
RMRect::RMRect(): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
setEmpty();
}
void RMRect::setEmpty() {
_x1 = _y1 = _x2 = _y2 = 0;
}
RMRect::RMRect(const RMPoint &p1, const RMPoint &p2): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
setRect(p1, p2);
}
RMRect::RMRect(int X1, int Y1, int X2, int Y2): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
setRect(X1, Y1, X2, Y2);
}
RMRect::RMRect(const RMRect &rc): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
copyRect(rc);
}
void RMRect::setRect(const RMPoint &p1, const RMPoint &p2) {
_x1 = p1._x;
_y1 = p1._y;
_x2 = p2._x;
_y2 = p2._y;
}
void RMRect::setRect(int X1, int Y1, int X2, int Y2) {
_x1 = X1;
_y1 = Y1;
_x2 = X2;
_y2 = Y2;
}
void RMRect::setRect(const RMRect &rc) {
copyRect(rc);
}
void RMRect::copyRect(const RMRect &rc) {
_x1 = rc._x1;
_y1 = rc._y1;
_x2 = rc._x2;
_y2 = rc._y2;
}
RMPointReference &RMRect::topLeft() {
return _topLeft;
}
RMPointReference &RMRect::bottomRight() {
return _bottomRight;
}
RMPoint RMRect::center() {
return RMPoint((_x2 - _x1) / 2, (_y2 - _y1) / 2);
}
int RMRect::width() const {
return _x2 - _x1;
}
int RMRect::height() const {
return _y2 - _y1;
}
int RMRect::size() const {
return width() * height();
}
RMRect::operator Common::Rect() const {
return Common::Rect(_x1, _y1, _x2, _y2);
}
bool RMRect::isEmpty() const {
return (_x1 == 0 && _y1 == 0 && _x2 == 0 && _y2 == 0);
}
const RMRect &RMRect::operator=(const RMRect &rc) {
copyRect(rc);
return *this;
}
void RMRect::offset(int xOff, int yOff) {
_x1 += xOff;
_y1 += yOff;
_x2 += xOff;
_y2 += yOff;
}
void RMRect::offset(const RMPoint &p) {
_x1 += p._x;
_y1 += p._y;
_x2 += p._x;
_y2 += p._y;
}
const RMRect &RMRect::operator+=(RMPoint p) {
offset(p);
return *this;
}
const RMRect &RMRect::operator-=(RMPoint p) {
offset(-p);
return *this;
}
RMRect operator+(const RMRect &rc, RMPoint p) {
RMRect r(rc);
return (r += p);
}
RMRect operator-(const RMRect &rc, RMPoint p) {
RMRect r(rc);
return (r -= p);
}
RMRect operator+(RMPoint p, const RMRect &rc) {
RMRect r(rc);
return (r += p);
}
RMRect operator-(RMPoint p, const RMRect &rc) {
RMRect r(rc);
return (r += p);
}
bool RMRect::operator==(const RMRect &rc) {
return ((_x1 == rc._x1) && (_y1 == rc._y1) && (_x2 == rc._x2) && (_y2 == rc._y2));
}
bool RMRect::operator!=(const RMRect &rc) {
return ((_x1 != rc._x1) || (_y1 != rc._y1) || (_x2 != rc._x2) || (_y2 != rc._y2));
}
void RMRect::normalizeRect() {
setRect(MIN(_x1, _x2), MIN(_y1, _y2), MAX(_x1, _x2), MAX(_y1, _y2));
}
void RMRect::readFromStream(Common::ReadStream &ds) {
_x1 = ds.readSint32LE();
_y1 = ds.readSint32LE();
_x2 = ds.readSint32LE();
_y2 = ds.readSint32LE();
}
/**
* Check if RMPoint is in RMRect
*/
bool RMRect::ptInRect(const RMPoint &pt) {
return (pt._x >= _x1 && pt._x <= _x2 && pt._y >= _y1 && pt._y <= _y2);
}
/****************************************************************************\
* Resource Update
\****************************************************************************/
RMResUpdate::RMResUpdate() {
_infos = NULL;
_numUpd = 0;
}
RMResUpdate::~RMResUpdate() {
if (_infos) {
delete[] _infos;
_infos = NULL;
}
if (_hFile.isOpen())
_hFile.close();
}
void RMResUpdate::init(const Common::Path &fileName) {
// Open the resource update file
if (!_hFile.open(fileName))
// It doesn't exist, so exit immediately
return;
_hFile.readByte(); // Version, unused
_numUpd = _hFile.readUint32LE();
_infos = new ResUpdInfo[_numUpd];
// Load the index of the resources in the file
for (uint32 i = 0; i < _numUpd; ++i) {
ResUpdInfo &info = _infos[i];
info._dwRes = _hFile.readUint32LE();
info._offset = _hFile.readUint32LE();
info._size = _hFile.readUint32LE();
info._cmpSize = _hFile.readUint32LE();
}
}
MpalHandle RMResUpdate::queryResource(uint32 dwRes) {
// If there isn't an update file, return NULL
if (!_hFile.isOpen())
return NULL;
uint32 i;
for (i = 0; i < _numUpd; ++i)
if (_infos[i]._dwRes == dwRes)
// Found the index
break;
if (i == _numUpd)
// Couldn't find a matching resource, so return NULL
return NULL;
const ResUpdInfo &info = _infos[i];
byte *cmpBuf = new byte[info._cmpSize];
uint32 dwRead;
// Move to the correct offset and read in the compressed data
_hFile.seek(info._offset);
dwRead = _hFile.read(cmpBuf, info._cmpSize);
if (info._cmpSize > dwRead) {
// Error occurred reading data, so return NULL
delete[] cmpBuf;
return NULL;
}
// Allocate space for the output resource
MpalHandle destBuf = globalAllocate(0, info._size);
byte *lpDestBuf = (byte *)globalLock(destBuf);
uint32 dwSize;
// Decompress the data
lzo1x_decompress(cmpBuf, info._cmpSize, lpDestBuf, &dwSize);
// Delete buffer for compressed data
delete [] cmpBuf;
// Return the resource
globalUnlock(destBuf);
return destBuf;
}
} // End of namespace Tony

175
engines/tony/utils.h Normal file
View File

@@ -0,0 +1,175 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_UTILS_H
#define TONY_UTILS_H
#include "common/scummsys.h"
#include "common/file.h"
#include "common/rect.h"
#include "common/str.h"
#include "tony/mpal/memory.h"
namespace Tony {
using namespace ::Tony::MPAL;
Common::String readString(Common::ReadStream &ds);
/**
* Point class
*/
class RMPoint {
public:
int _x, _y;
public:
// Constructor
RMPoint();
RMPoint(const RMPoint &p);
RMPoint(int x1, int y1);
// Copy
RMPoint &operator=(RMPoint p);
// Set
void set(int x1, int y1);
// Offset
void offset(int xOff, int yOff);
void offset(const RMPoint &p);
friend RMPoint operator+(RMPoint p1, RMPoint p2);
friend RMPoint operator-(RMPoint p1, RMPoint p2);
RMPoint &operator+=(RMPoint p);
RMPoint &operator-=(RMPoint p);
RMPoint operator-();
// Comparison
bool operator==(RMPoint p);
bool operator!=(RMPoint p);
// Casting a POINT
operator Common::Point() const;
// Extraction from data streams
void readFromStream(Common::ReadStream &ds);
};
class RMPointReference {
public:
int &_x;
int &_y;
RMPointReference(int &x, int &y);
RMPointReference &operator=(const RMPoint &p);
RMPointReference &operator-=(const RMPoint &p);
operator RMPoint() const;
};
class RMRect {
public:
int _x1, _y1;
int _x2, _y2;
RMPointReference _topLeft;
RMPointReference _bottomRight;
public:
RMRect();
RMRect(int x1, int y1, int x2, int y2);
RMRect(const RMPoint &p1, const RMPoint &p2);
RMRect(const RMRect &rc);
// Attributes
RMPointReference &topLeft();
RMPointReference &bottomRight();
RMPoint center();
int width() const;
int height() const;
bool isEmpty() const;
int size() const;
operator Common::Rect() const;
// Set
void setRect(int x1, int y1, int x2, int y2);
void setRect(const RMPoint &p1, const RMPoint &p2);
void setEmpty();
// Copiers
void setRect(const RMRect &rc);
void copyRect(const RMRect &rc);
const RMRect &operator=(const RMRect &rc);
// Offset
void offset(int xOff, int yOff);
void offset(const RMPoint &p);
friend RMRect operator+(const RMRect &rc, RMPoint p);
friend RMRect operator-(const RMRect &rc, RMPoint p);
friend RMRect operator+(RMPoint p, const RMRect &rc);
friend RMRect operator-(RMPoint p, const RMRect &rc);
const RMRect &operator+=(RMPoint p);
const RMRect &operator-=(RMPoint p);
// Comparison
bool operator==(const RMRect &rc);
bool operator!=(const RMRect &rc);
// Normalize
void normalizeRect();
// Point in rect
bool ptInRect(const RMPoint &pt);
// Extract from data stream
void readFromStream(Common::ReadStream &ds);
};
/**
* Resource update manager
*/
class RMResUpdate {
struct ResUpdInfo {
uint32 _dwRes;
uint32 _offset;
uint32 _size;
uint32 _cmpSize;
};
uint32 _numUpd;
ResUpdInfo *_infos;
Common::File _hFile;
public:
RMResUpdate();
~RMResUpdate();
void init(const Common::Path &fileName);
MpalHandle queryResource(uint32 dwRes);
};
} // End of namespace Tony
#endif /* TONY_H */

352
engines/tony/window.cpp Normal file
View File

@@ -0,0 +1,352 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#include "common/scummsys.h"
#include "graphics/surface.h"
#include "engines/util.h"
#include "tony/window.h"
#include "tony/game.h"
#include "tony/tony.h"
namespace Tony {
/****************************************************************************\
* RMWindow Methods
\****************************************************************************/
RMWindow::RMWindow() {
reset();
}
RMWindow::~RMWindow() {
close();
RMText::unload();
RMGfxTargetBuffer::freeBWPrecalcTable();
}
/**
* Initializes the graphics window
*/
void RMWindow::init() {
Graphics::PixelFormat pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
initGraphics(RM_SX, RM_SY, &pixelFormat);
reset();
}
/**
* Reset the variables
*/
void RMWindow::reset() {
_showDirtyRects = false;
_bGrabScreenshot = false;
_bGrabThumbnail = false;
_bGrabMovie = false;
_wiping = false;
_wThumbBuf = nullptr;
}
void RMWindow::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) {
if (GLOBALS._bCfgAnni30) {
if (!RMGfxTargetBuffer::_precalcTable) {
RMGfxTargetBuffer::createBWPrecalcTable();
g_vm->getEngine()->getPointer().updateCursor();
}
Graphics::Surface *screen = g_system->lockScreen();
const uint16 *src = (const uint16 *)buf;
for (int i = 0; i < h; i++) {
uint16 *dst = (uint16 *)screen->getBasePtr(x, y + i);
for (int j = 0; j < w; j++) {
dst[j] = RMGfxTargetBuffer::_precalcTable[src[j]];
}
src += (pitch / 2);
}
g_system->unlockScreen();
} else {
if (RMGfxTargetBuffer::_precalcTable) {
RMGfxTargetBuffer::freeBWPrecalcTable();
g_vm->getEngine()->getPointer().updateCursor();
}
g_system->copyRectToScreen(buf, pitch, x, y, w, h);
}
}
/**
* Close the window
*/
void RMWindow::close() {
}
void RMWindow::grabThumbnail(uint16 *thumbmem) {
_bGrabThumbnail = true;
_wThumbBuf = thumbmem;
}
/**
* Repaint the screen
*/
void RMWindow::repaint() {
g_system->updateScreen();
}
/**
* Wipes an area of the screen
*/
void RMWindow::wipeEffect(Common::Rect &rcBoundEllipse) {
if ((rcBoundEllipse.left == 0) && (rcBoundEllipse.top == 0) &&
(rcBoundEllipse.right == RM_SX) && (rcBoundEllipse.bottom == RM_SY)) {
// Full screen clear wanted, so use shortcut method
g_system->fillScreen(0);
} else {
// Clear the designated area a line at a time
uint16 line[RM_SX];
Common::fill(line, line + RM_SX, 0);
// Loop through each line
for (int yp = rcBoundEllipse.top; yp < rcBoundEllipse.bottom; ++yp) {
copyRectToScreen((const byte *)&line[0], RM_SX * 2, rcBoundEllipse.left, yp, rcBoundEllipse.width(), 1);
}
}
}
void RMWindow::getNewFrame(RMGfxTargetBuffer &bigBuf, Common::Rect *rcBoundEllipse) {
// Get a pointer to the bytes of the source buffer
byte *lpBuf = bigBuf;
if (rcBoundEllipse != NULL) {
// Circular wipe effect
getNewFrameWipe(lpBuf, *rcBoundEllipse);
_wiping = true;
} else if (_wiping) {
// Just finished a wiping effect, so copy the full screen
copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY);
_wiping = false;
} else {
// Standard screen copy - iterate through the dirty rects
Common::List<Common::Rect> dirtyRects = bigBuf.getDirtyRects();
Common::List<Common::Rect>::iterator i;
// If showing dirty rects, copy the entire screen background and set up a surface pointer
Graphics::Surface *s = NULL;
if (_showDirtyRects) {
copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY);
s = g_system->lockScreen();
}
for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) {
Common::Rect &r = *i;
const byte *lpSrc = lpBuf + (RM_SX * 2) * r.top + (r.left * 2);
copyRectToScreen(lpSrc, RM_SX * 2, r.left, r.top, r.width(), r.height());
}
if (_showDirtyRects) {
for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) {
// Frame the copied area with a rectangle
s->frameRect(*i, 0xffffff);
}
g_system->unlockScreen();
}
}
if (_bGrabThumbnail) {
// Need to generate a thumbnail
RMSnapshot s;
s.grabScreenshot(lpBuf, 4, _wThumbBuf);
_bGrabThumbnail = false;
}
// Clear the dirty rect list
bigBuf.clearDirtyRects();
}
/**
* Copies a section of the game frame in a circle bounded by the specified rectangle
*/
void RMWindow::getNewFrameWipe(byte *lpBuf, Common::Rect &rcBoundEllipse) {
// Clear the screen
g_system->fillScreen(0);
if (!rcBoundEllipse.isValidRect())
return;
Common::Point center(rcBoundEllipse.left + rcBoundEllipse.width() / 2,
rcBoundEllipse.top + rcBoundEllipse.height() / 2);
// The rectangle technically defines the area inside the ellipse, with the corners touching
// the ellipse boundary. Since we're currently simulating the ellipse using a plain circle,
// we need to calculate a necessary width using the hypotenuse of X/2 & Y/2
int x2y2 = (rcBoundEllipse.width() / 2) * (rcBoundEllipse.width() / 2) +
(rcBoundEllipse.height() / 2) * (rcBoundEllipse.height() / 2);
int radius = 0;
while ((radius * radius) < x2y2)
++radius;
// Proceed copying a circular area of the frame with the calculated radius onto the screen
int error = -radius;
int x = radius;
int y = 0;
while (x >= y) {
plotSplices(lpBuf, center, x, y);
error += y;
++y;
error += y;
if (error >= 0) {
error -= x;
--x;
error -= x;
}
}
}
/**
* Handles drawing the line splices for the circle of viewable area
*/
void RMWindow::plotSplices(const byte *lpBuf, const Common::Point &center, int x, int y) {
plotLines(lpBuf, center, x, y);
if (x != y)
plotLines(lpBuf, center, y, x);
}
/**
* Handles drawing the line splices for the circle of viewable area
*/
void RMWindow::plotLines(const byte *lpBuf, const Common::Point &center, int x, int y) {
// Skips lines that have no width (i.e. at the top of the circle)
if ((x == 0) || (y > center.y))
return;
const byte *pSrc;
int xs = MAX(center.x - x, 0);
int width = MIN(RM_SX - xs, x * 2);
if ((center.y - y) >= 0) {
// Draw line in top half of circle
pSrc = lpBuf + ((center.y - y) * RM_SX * 2) + xs * 2;
copyRectToScreen(pSrc, RM_SX * 2, xs, center.y - y, width, 1);
}
if ((center.y + y) < RM_SY) {
// Draw line in bottom half of circle
pSrc = lpBuf + ((center.y + y) * RM_SX * 2) + xs * 2;
copyRectToScreen(pSrc, RM_SX * 2, xs, center.y + y, width, 1);
}
}
void RMWindow::showDirtyRects(bool v) {
_showDirtyRects = v;
}
/****************************************************************************\
* RMSnapshot Methods
\****************************************************************************/
void RMSnapshot::grabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) {
uint16 *src = (uint16 *)lpBuf;
int dimx = RM_SX / dezoom;
int dimy = RM_SY / dezoom;
uint16 *cursrc;
if (lpDestBuf == NULL)
src += (RM_SY - 1) * RM_BBX;
if (dezoom == 1 && 0) {
byte *curOut = _rgb;
for (int y = 0; y < dimy; y++) {
for (int x = 0; x < dimx; x++) {
cursrc = &src[RM_SKIPX + x];
*curOut++ = ((*cursrc) & 0x1F) << 3;
*curOut++ = (((*cursrc) >> 5) & 0x3F) << 3;
*curOut++ = (((*cursrc) >> 11) & 0x1F) << 3;
if (lpDestBuf)
*lpDestBuf++ = *cursrc;
}
if (lpDestBuf == NULL)
src -= RM_BBX;
else
src += RM_BBX;
}
} else {
uint32 k = 0;
for (int y = 0; y < dimy; y++) {
for (int x = 0; x < dimx; x++) {
cursrc = &src[RM_SKIPX + x * dezoom];
int sommar, sommab, sommag, curv;
sommar = sommab = sommag = 0;
for (int v = 0; v < dezoom; v++) {
for (int u = 0; u < dezoom; u++) {
if (lpDestBuf == NULL)
curv = -v;
else
curv = v;
sommab += cursrc[curv * RM_BBX + u] & 0x1F;
sommag += (cursrc[curv * RM_BBX + u] >> 6) & 0x1F;
sommar += (cursrc[curv * RM_BBX + u] >> 11) & 0x1F;
}
}
_rgb[k + 0] = (byte)(sommab * 8 / (dezoom * dezoom));
_rgb[k + 1] = (byte)(sommag * 8 / (dezoom * dezoom));
_rgb[k + 2] = (byte)(sommar * 8 / (dezoom * dezoom));
if (lpDestBuf != NULL)
lpDestBuf[k / 3] = ((int)_rgb[k + 0] >> 3) | (((int)_rgb[k + 1] >> 3) << 5) |
(((int)_rgb[k + 2] >> 3) << 10);
k += 3;
}
if (lpDestBuf == NULL)
src -= RM_BBX * dezoom;
else
src += RM_BBX * dezoom;
}
}
#ifdef SCUMM_BIG_ENDIAN
if (lpDestBuf != NULL) {
for (int i = 0; i < dimx * dimy; i++) {
lpDestBuf[i] = SWAP_BYTES_16(lpDestBuf[i]);
}
}
#endif
}
} // End of namespace Tony

103
engines/tony/window.h Normal file
View File

@@ -0,0 +1,103 @@
/* 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/>.
*
*/
/*
* This code is based on original Tony Tough source code
*
* Copyright (c) 1997-2003 Nayma Software
*/
#ifndef TONY_WINDOW_H
#define TONY_WINDOW_H
#include "common/scummsys.h"
#include "common/rect.h"
#include "tony/game.h"
namespace Tony {
class RMSnapshot {
private:
static const int BUFFER_SIZE = RM_SX *RM_SY * 3;
// Buffer used to convert to RGB
byte *_rgb;
public:
RMSnapshot() : _rgb(new byte[BUFFER_SIZE]) {}
~RMSnapshot() {
delete[] _rgb;
}
/**
* Take a screenshot
*/
void grabScreenshot(byte *lpBuf, int dezoom = 1, uint16 *lpDestBuf = NULL);
};
class RMWindow {
private:
void plotSplices(const byte *lpBuf, const Common::Point &center, int x, int y);
void plotLines(const byte *lpBuf, const Common::Point &center, int x, int y);
protected:
bool _wiping;
bool _showDirtyRects;
bool _bGrabScreenshot;
bool _bGrabThumbnail;
bool _bGrabMovie;
uint16 *_wThumbBuf;
void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h);
void wipeEffect(Common::Rect &rcBoundEllipse);
void getNewFrameWipe(byte *lpBuf, Common::Rect &rcBoundEllipse);
public:
RMWindow();
~RMWindow();
/**
* Initialization
*/
void init();
void reset();
void close();
/**
* Drawing
*/
void repaint();
/**
* Reads the next frame
*/
void getNewFrame(RMGfxTargetBuffer &lpBuf, Common::Rect *rcBoundEllipse);
/**
* Request a thumbnail be grabbed during the next frame
*/
void grabThumbnail(uint16 *buf);
void showDirtyRects(bool v);
};
} // End of namespace Tony
#endif /* TONY_WINDOW_H */