Patch #2271425: Groovie engine

svn-id: r35060
This commit is contained in:
Max Horn 2008-11-14 21:32:20 +00:00
parent 3c5c774e76
commit bb87d39424
29 changed files with 5456 additions and 0 deletions

View file

@ -108,6 +108,9 @@ public:
#if PLUGIN_ENABLED_STATIC(GOB) #if PLUGIN_ENABLED_STATIC(GOB)
LINK_PLUGIN(GOB) LINK_PLUGIN(GOB)
#endif #endif
#if PLUGIN_ENABLED_STATIC(GROOVIE)
LINK_PLUGIN(GROOVIE)
#endif
#if PLUGIN_ENABLED_STATIC(IGOR) #if PLUGIN_ENABLED_STATIC(IGOR)
LINK_PLUGIN(IGOR) LINK_PLUGIN(IGOR)
#endif #endif

1
configure vendored
View file

@ -89,6 +89,7 @@ add_engine cine "Cinematique evo 1" yes
add_engine cruise "Cinematique evo 2" no add_engine cruise "Cinematique evo 2" no
add_engine drascula "Drascula: The Vampire Strikes Back" yes add_engine drascula "Drascula: The Vampire Strikes Back" yes
add_engine gob "Gobli*ns" yes add_engine gob "Gobli*ns" yes
add_engine groovie "Groovie" no
add_engine igor "Igor: Objective Uikokahonia" no add_engine igor "Igor: Objective Uikokahonia" no
add_engine kyra "Legend of Kyrandia" yes add_engine kyra "Legend of Kyrandia" yes
add_engine lure "Lure of the Temptress" yes add_engine lure "Lure of the Temptress" yes

View file

@ -42,6 +42,11 @@ DEFINES += -DENABLE_GOB=$(ENABLE_GOB)
MODULES += engines/gob MODULES += engines/gob
endif endif
ifdef ENABLE_GROOVIE
DEFINES += -DENABLE_GROOVIE=$(ENABLE_GROOVIE)
MODULES += engines/groovie
endif
ifdef ENABLE_IGOR ifdef ENABLE_IGOR
DEFINES += -DENABLE_IGOR=$(ENABLE_IGOR) DEFINES += -DENABLE_IGOR=$(ENABLE_IGOR)
MODULES += engines/igor MODULES += engines/igor

314
engines/groovie/cursor.cpp Normal file
View file

@ -0,0 +1,314 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "groovie/cursor.h"
#include "groovie/groovie.h"
namespace Groovie {
// Cursor Manager
CursorMan::CursorMan(OSystem *system) :
_syst(system), _lastTime(0), _cursor(NULL) {
}
CursorMan::~CursorMan() {
// Delete the cursors
for (uint cursor = 0; cursor < _cursors.size(); cursor++) {
delete _cursors[cursor];
}
}
uint8 CursorMan::getStyle() {
return _current;
}
void CursorMan::setStyle(uint8 newStyle) {
// Reset the animation
_lastFrame = 254;
_lastTime = 1;
// Save the new cursor
_current = newStyle;
_cursor = _cursors[newStyle];
// Show the first frame
_cursor->enable();
animate();
}
void CursorMan::animate() {
if (_lastTime) {
int newTime = _syst->getMillis();
if (_lastTime - newTime >= 75) {
_lastFrame++;
_lastFrame %= _cursor->getFrames();
_cursor->showFrame(_lastFrame);
_lastTime = _syst->getMillis();
}
}
}
// t7g Cursor
class Cursor_t7g : public Cursor {
public:
Cursor_t7g(OSystem *system, uint8 *img, uint8 *pal);
void enable();
void showFrame(uint16 frame);
private:
OSystem *_syst;
byte *_img;
byte *_pal;
};
Cursor_t7g::Cursor_t7g(OSystem *system, uint8 *img, uint8 *pal) :
_syst(system), _pal(pal) {
_width = img[0];
_height = img[1];
_numFrames = img[2];
uint8 elinor1 = img[3];
uint8 elinor2 = img[4];
_img = img + 5;
debugC(1, kGroovieDebugCursor | kGroovieDebugAll, "Groovie::Cursor: width: %d, height: %d, frames:%d", _width, _height, _numFrames);
debugC(1, kGroovieDebugCursor | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::Cursor: elinor: 0x%02X (%d), 0x%02X (%d)", elinor1, elinor1, elinor2, elinor2);
}
void Cursor_t7g::enable() {
// Apply the palette
_syst->setCursorPalette(_pal, 0, 32);
}
void Cursor_t7g::showFrame(uint16 frame) {
// Set the mouse cursor
int offset = _width * _height * frame;
_syst->setMouseCursor((const byte *)_img + offset, _width, _height, _width >> 1, _height >> 1, 0);
}
// t7g Cursor Manager
#define NUM_IMGS 9
static const uint16 cursorDataOffsets[NUM_IMGS] = {
0x0000, 0x182f, 0x3b6d, 0x50cc, 0x6e79, 0x825d, 0x96d7, 0xa455, 0xa776
};
#define NUM_PALS 7
//Pals: 0xb794, 0xb7f4, 0xb854, 0xb8b4, 0xb914, 0xb974, 0xb9d4
#define NUM_STYLES 11
// pyramid is cursor 8, eyes are 9 & 10
const uint CursorMan_t7g::_cursorImg[NUM_STYLES] = {3, 5, 4, 3, 1, 0, 2, 6, 7, 8, 8};
const uint CursorMan_t7g::_cursorPal[NUM_STYLES] = {0, 0, 0, 0, 2, 0, 1, 3, 5, 4, 6};
CursorMan_t7g::CursorMan_t7g(OSystem *system) :
CursorMan(system) {
// Open the cursors file
Common::File robgjd;
if (!robgjd.open("rob.gjd")) {
error("Groovie::Cursor: Couldn't open rob.gjd");
return;
}
// Load the images
for (uint imgnum = 0; imgnum < NUM_IMGS; imgnum++) {
robgjd.seek(cursorDataOffsets[imgnum]);
_images.push_back(loadImage(robgjd));
}
// Load the palettes
robgjd.seek(-0x60 * NUM_PALS, SEEK_END);
for (uint palnum = 0; palnum < NUM_PALS; palnum++) {
_palettes.push_back(loadPalette(robgjd));
}
// Build the cursors
for (uint cursor = 0; cursor < NUM_STYLES; cursor++) {
Cursor *s = new Cursor_t7g(_syst, _images[_cursorImg[cursor]], _palettes[_cursorPal[cursor]]);
_cursors.push_back(s);
}
robgjd.close();
}
CursorMan_t7g::~CursorMan_t7g() {
// Delete the images
for (uint img = 0; img < _images.size(); img++) {
delete[] _images[img];
}
// Delete the palettes
for (uint pal = 0; pal < _palettes.size(); pal++) {
delete[] _palettes[pal];
}
}
byte *CursorMan_t7g::loadImage(Common::File &file) {
uint16 decompbytes = 0, offset, i, length;
uint8 flagbyte, lengthmask = 0x0F, offsetlen, var_8;
byte *cursorStorage = new byte[65536];
uint8 *runningcursor = cursorStorage;
bool finished = false;
while (!(finished || file.eos())) {
flagbyte = file.readByte();
for (i = 1; i <= 8; i++) {
if (!file.eos()) {
if (flagbyte & 1) {
*(runningcursor++) = file.readByte();
decompbytes++;
} else {
var_8 = file.readByte();
offsetlen = file.readByte();
if (var_8 == 0 && offsetlen == 0) {
finished = true;
break;
}
length = (offsetlen & lengthmask) + 3;
offsetlen >>= 4;
offset = (offsetlen << 8) + var_8;
decompbytes += length;
for (; length > 0; length--, runningcursor++) {
*(runningcursor) = *(runningcursor - offset);
}
}
flagbyte = flagbyte >> 1;
}
}
}
return cursorStorage;
}
byte *CursorMan_t7g::loadPalette(Common::File &file) {
byte *palette = new byte[4 * 32];
for (uint8 colournum = 0; colournum < 32; colournum++) {
palette[colournum * 4 + 0] = file.readByte();
palette[colournum * 4 + 1] = file.readByte();
palette[colournum * 4 + 2] = file.readByte();
palette[colournum * 4 + 3] = 0;
}
return palette;
}
// v2 Cursor
class Cursor_v2 : public Cursor {
public:
Cursor_v2(Common::File &file);
void enable();
void showFrame(uint16 frame);
private:
//byte *_data;
};
Cursor_v2::Cursor_v2(Common::File &file) {
_numFrames = file.readUint16LE();
_width = file.readUint16LE();
_height = file.readUint16LE();
debugC(1, kGroovieDebugCursor | kGroovieDebugAll, "Groovie::Cursor: width: %d, height: %d, frames:%d", _width, _height, _numFrames);
uint16 tmp16 = file.readUint16LE();
debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "hotspot x?: %d\n", tmp16);
tmp16 = file.readUint16LE();
debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "hotspot y?: %d\n", tmp16);
int loop2count = file.readUint16LE();
debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "loop2count?: %d\n", loop2count);
for (int l = 0; l < loop2count; l++) {
tmp16 = file.readUint16LE();
debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "loop2a: %d\n", tmp16);
tmp16 = file.readUint16LE();
debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "loop2b: %d\n", tmp16);
}
file.seek(0x20 * 3, SEEK_CUR);
for (int f = 0; f < _numFrames; f++) {
uint32 tmp32 = file.readUint32LE();
debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "loop3: %d\n", tmp32);
//file.seek(tmp32, SEEK_CUR);
byte *data = new byte[tmp32];
file.read(data, tmp32);
//Common::hexdump(data, tmp32);
delete[] data;
}
}
void Cursor_v2::enable() {
}
void Cursor_v2::showFrame(uint16 frame) {
}
// v2 Cursor Manager
CursorMan_v2::CursorMan_v2(OSystem *system) :
CursorMan(system) {
// Open the icons file
Common::File iconsFile;
if (!iconsFile.open("icons.ph")) {
error("Groovie::Cursor: Couldn't open icons.ph");
return;
}
// Verify the signature
uint32 tmp32 = iconsFile.readUint32LE();
uint16 tmp16 = iconsFile.readUint16LE();
if (tmp32 != 0x6e6f6369 || tmp16 != 1) {
error("Groovie::Cursor: icons.ph signature failed: %04X %d", tmp32, tmp16);
return;
}
// Read the number of icons
uint16 nicons = iconsFile.readUint16LE();
// Read the icons
for (int i = 0; i < nicons; i++) {
Cursor *s = new Cursor_v2(iconsFile);
_cursors.push_back(s);
}
iconsFile.close();
}
CursorMan_v2::~CursorMan_v2() {
}
} // End of Groovie namespace

96
engines/groovie/cursor.h Normal file
View file

@ -0,0 +1,96 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef GROOVIE_CURSOR_H
#define GROOVIE_CURSOR_H
#include "common/system.h"
#include "common/file.h"
namespace Groovie {
class Cursor {
public:
virtual ~Cursor() {}
uint16 getFrames() { return _numFrames; }
virtual void enable() = 0;
virtual void showFrame(uint16 frame) = 0;
protected:
uint16 _width;
uint16 _height;
uint16 _numFrames;
};
class CursorMan {
public:
CursorMan(OSystem *system);
virtual ~CursorMan();
virtual void animate();
virtual void setStyle(uint8 newStyle);
virtual uint8 getStyle();
protected:
OSystem *_syst;
// Animation variables
uint8 _lastFrame;
uint32 _lastTime;
// Styles
Common::Array<Cursor *> _cursors;
uint8 _current;
Cursor *_cursor;
};
class CursorMan_t7g : public CursorMan {
public:
CursorMan_t7g(OSystem *system);
~CursorMan_t7g();
private:
// Styles data
static const uint _cursorImg[];
static const uint _cursorPal[];
// Cursors data
Common::Array<byte *> _images;
Common::Array<byte *> _palettes;
// Loading functions
byte *loadImage(Common::File &file);
byte *loadPalette(Common::File &file);
};
class CursorMan_v2 : public CursorMan {
public:
CursorMan_v2(OSystem *system);
~CursorMan_v2();
};
} // End of Groovie namespace
#endif // GROOVIE_CURSOR_H

145
engines/groovie/debug.cpp Normal file
View file

@ -0,0 +1,145 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "groovie/debug.h"
#include "groovie/script.h"
#include "groovie/groovie.h"
namespace Groovie {
Debugger::Debugger(GroovieEngine *vm) :
_vm (vm), _script(&_vm->_script), _syst(_vm->_system) {
// Register the debugger comands
DCmd_Register("step", WRAP_METHOD(Debugger, cmd_step));
DCmd_Register("go", WRAP_METHOD(Debugger, cmd_go));
DCmd_Register("pc", WRAP_METHOD(Debugger, cmd_pc));
DCmd_Register("fg", WRAP_METHOD(Debugger, cmd_fg));
DCmd_Register("bg", WRAP_METHOD(Debugger, cmd_bg));
DCmd_Register("mem", WRAP_METHOD(Debugger, cmd_mem));
DCmd_Register("load", WRAP_METHOD(Debugger, cmd_loadgame));
DCmd_Register("save", WRAP_METHOD(Debugger, cmd_savegame));
DCmd_Register("playref", WRAP_METHOD(Debugger, cmd_playref));
DCmd_Register("dumppal", WRAP_METHOD(Debugger, cmd_dumppal));
}
Debugger::~Debugger() {
Common::clearAllSpecialDebugLevels();
}
int Debugger::getNumber(const char *arg) {
return strtol(arg, (char **)NULL, 0);
}
bool Debugger::cmd_step(int argc, const char **argv) {
_script->step();
return true;
}
bool Debugger::cmd_go(int argc, const char **argv) {
_script->step();
return false;
}
bool Debugger::cmd_fg(int argc, const char **argv) {
_vm->_graphicsMan->updateScreen(&_vm->_graphicsMan->_foreground);
return false;
}
bool Debugger::cmd_bg(int argc, const char **argv) {
_vm->_graphicsMan->updateScreen(&_vm->_graphicsMan->_background);
return false;
}
bool Debugger::cmd_pc(int argc, const char **argv) {
if (argc == 2) {
int val = getNumber(argv[1]);
_script->_currentInstruction = val;
}
DebugPrintf("pc = 0x%04X\n", _script->_currentInstruction);
return true;
}
bool Debugger::cmd_mem(int argc, const char **argv) {
if (argc >= 2) {
int pos = getNumber(argv[1]);
uint8 val;
if (argc >= 3) {
// Set
val = getNumber(argv[2]);
_script->_variables[pos] = val;
} else {
// Get
val = _script->_variables[pos];
}
DebugPrintf("mem[0x%04X] = 0x%02X\n", pos, val);
} else {
DebugPrintf("Syntax: mem <addr> [<val>]\n");
}
return true;
}
bool Debugger::cmd_loadgame(int argc, const char **argv) {
if (argc == 2) {
int slot = getNumber(argv[1]);
_script->loadgame(slot);
} else {
DebugPrintf("Syntax: load <slot>\n");
}
return true;
}
bool Debugger::cmd_savegame(int argc, const char **argv) {
if (argc == 2) {
int slot = getNumber(argv[1]);
_script->savegame(slot);
} else {
DebugPrintf("Syntax: save <slot>\n");
}
return true;
}
bool Debugger::cmd_playref(int argc, const char **argv) {
if (argc == 2) {
int ref = getNumber(argv[1]);
_script->playvideofromref(ref);
} else {
DebugPrintf("Syntax: playref <videorefnum>\n");
}
return true;
}
bool Debugger::cmd_dumppal(int argc, const char **argv) {
uint16 i;
byte palettedump[256 * 4];
_syst->grabPalette(palettedump, 0, 256);
for (i = 0; i < 256; i++) {
DebugPrintf("%3d: %3d,%3d,%3d,%3d\n", i, palettedump[(i * 4)], palettedump[(i * 4) + 1], palettedump[(i * 4) + 2], palettedump[(i * 4) + 3]);
}
return true;
}
} // End of Groovie namespace

63
engines/groovie/debug.h Normal file
View file

@ -0,0 +1,63 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef GROOVIE_DEBUG_H
#define GROOVIE_DEBUG_H
#include "gui/debugger.h"
#include "engines/engine.h"
namespace Groovie {
class Script;
class GroovieEngine;
class Debugger : public GUI::Debugger {
public:
Debugger(GroovieEngine *vm);
~Debugger();
private:
GroovieEngine *_vm;
Script *_script;
OSystem *_syst;
int getNumber(const char *arg);
bool cmd_step(int argc, const char **argv);
bool cmd_go(int argc, const char **argv);
bool cmd_pc(int argc, const char **argv);
bool cmd_bg(int argc, const char **argv);
bool cmd_fg(int argc, const char **argv);
bool cmd_mem(int argc, const char **argv);
bool cmd_loadgame(int argc, const char **argv);
bool cmd_savegame(int argc, const char **argv);
bool cmd_playref(int argc, const char **argv);
bool cmd_dumppal(int argc, const char **argv);
};
} // End of Groovie namespace
#endif // GROOVIE_DEBUG_H

View file

@ -0,0 +1,265 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/savefile.h"
#include "groovie/groovie.h"
namespace Groovie {
//#define GROOVIE_EXPERIMENTAL
static const PlainGameDescriptor groovieGames[] = {
// Games
{"t7g", "The 7th Guest"},
#ifdef GROOVIE_EXPERIMENTAL
{"11h", "The 11th Hour: The sequel to The 7th Guest"},
{"clandestiny", "Clandestiny"},
{"unclehenry", "Uncle Henry's Playhouse"},
{"tlc", "Tender Loving Care"},
// Extras
{"making11h", "The Making of The 11th Hour"},
{"clantrailer", "Clandestiny Trailer"},
#endif
// Unknown
{"groovie", "Groovie engine game"},
{0, 0}
};
static const GroovieGameDescription gameDescriptions[] = {
// The 7th Guest DOS English
{
{
"t7g", "",
AD_ENTRY1s("script.grv", "d1b8033b40aa67c076039881eccce90d", 16659),
Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
},
kGroovieT7G, 0
},
// The 7th Guest Mac English
{
{
"t7g", "",
AD_ENTRY1s("script.grv", "6e30b54b1f3bc2262cdcf7961db2ae67", 17191),
Common::EN_ANY, Common::kPlatformMacintosh, Common::ADGF_NO_FLAGS
},
kGroovieT7G, 0
},
#ifdef GROOVIE_EXPERIMENTAL
// The 11th Hour DOS English
{
{
"11h", "",
AD_ENTRY1s("disk.1", "5c0428cd3659fc7bbcd0aa16485ed5da", 227),
Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
},
kGroovieV2, 1
},
// The Making of The 11th Hour DOS English
{
{
"making11h", "",
AD_ENTRY1s("disk.1", "5c0428cd3659fc7bbcd0aa16485ed5da", 227),
Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
},
kGroovieV2, 2
},
// Clandestiny Trailer DOS English
{
{
"clantrailer", "",
AD_ENTRY1s("disk.1", "5c0428cd3659fc7bbcd0aa16485ed5da", 227),
Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
},
kGroovieV2, 3
},
// Clandestiny DOS English
{
{
"clandestiny", "",
AD_ENTRY1s("disk.1", "f79fc1515174540fef6a34132efc4c53", 76),
Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
},
kGroovieV2, 1
},
// Uncle Henry's Playhouse PC English
{
{
"unclehenry", "",
AD_ENTRY1s("disk.1", "0e1b1d3cecc4fc7efa62a968844d1f7a", 72),
Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
},
kGroovieV2, 1
},
// Tender Loving Care PC English
{
{
"tlc", "",
AD_ENTRY1s("disk.1", "32a1afa68478f1f9d2b25eeea427f2e3", 84),
Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
},
kGroovieV2, 1
},
#endif
{AD_TABLE_END_MARKER, kGroovieT7G, 0}
};
static const Common::ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
(const byte *)gameDescriptions,
// Size of that superset structure
sizeof(GroovieGameDescription),
// Number of bytes to compute MD5 sum for
5000,
// List of all engine targets
groovieGames,
// Structure for autoupgrading obsolete targets
0,
// Name of single gameid (optional)
0,
// List of files for file-based fallback detection (optional)
0,
// Flags
0
};
class GroovieMetaEngine : public Common::AdvancedMetaEngine {
public:
GroovieMetaEngine() : Common::AdvancedMetaEngine(detectionParams) {}
const char *getName() const {
return "Groovie Engine";
}
const char *getCopyright() const {
return "Groovie Engine (C) 1990-1996 Trilobyte";
}
bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *gd) const;
bool hasFeature(MetaEngineFeature f) const;
SaveStateList listSaves(const char *target) const;
void removeSaveState(const char *target, int slot) const;
};
bool GroovieMetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *gd) const {
if (gd) {
*engine = new GroovieEngine(syst, (GroovieGameDescription *)gd);
}
return gd != 0;
}
bool GroovieMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave);
//(f == kSavesSupportCreationDate)
}
SaveStateList GroovieMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *sfm = g_system->getSavefileManager();
SaveStateList list;
// Get the list of savefiles
Common::String pattern = Common::String(target) + ".00?";
Common::StringList savefiles = sfm->listSavefiles(pattern.c_str());
// Sort the list of filenames
sort(savefiles.begin(), savefiles.end());
// Fill the information for the existing savegames
Common::StringList::iterator it = savefiles.begin();
while (it != savefiles.end()) {
int slot = it->lastChar() - '0';
if (slot >= 0 && slot <= 9) {
Common::InSaveFile *file = sfm->openForLoading(it->c_str());
// Read the savegame description
Common::String description;
unsigned char c = 1;
for (int i = 0; (c != 0) && (i < 15); i++) {
c = file->readByte();
switch (c) {
case 0:
break;
case 16: // @
c = ' ';
break;
case 244: // $
c = 0;
break;
default:
c += 0x30;
}
if (c != 0) {
description += c;
}
}
delete file;
list.push_back(SaveStateDescriptor(slot, description));
}
it++;
}
return list;
}
void GroovieMetaEngine::removeSaveState(const char *target, int slot) const {
if (slot < 0 || slot > 9) {
// Invalid slot, do nothing
return;
}
char extension[6];
snprintf(extension, sizeof(extension), ".00%01d", slot);
Common::String filename = target;
filename += extension;
g_system->getSavefileManager()->removeSavefile(filename.c_str());
}
} // End of namespace Groovie
#if PLUGIN_ENABLED_DYNAMIC(GROOVIE)
REGISTER_PLUGIN_DYNAMIC(GROOVIE, PLUGIN_TYPE_ENGINE, Groovie::GroovieMetaEngine);
#else
REGISTER_PLUGIN_STATIC(GROOVIE, PLUGIN_TYPE_ENGINE, Groovie::GroovieMetaEngine);
#endif

128
engines/groovie/font.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/file.h"
#include "graphics/surface.h"
#include "groovie/font.h"
namespace Groovie {
Font::Font(OSystem *syst) :
_syst(syst), _sphinxfnt(NULL) {
Common::File fontfile;
if (!fontfile.open("sphinx.fnt")) {
error("Groovie::Font: Couldn't open sphinx.fnt");
}
uint16 fontfilesize = fontfile.size();
_sphinxfnt = fontfile.readStream(fontfilesize);
fontfile.close();
}
Font::~Font() {
delete _sphinxfnt;
}
void Font::printstring(char *messagein) {
uint16 totalwidth = 0, currxoffset, i;
char message[15];
memset(message, 0, 15);
// Clear the top bar
Common::Rect topbar(640, 80);
Graphics::Surface *gamescreen;
gamescreen = _syst->lockScreen();
gamescreen->fillRect(topbar, 0);
_syst->unlockScreen();
for (i = 0; i < 14; i++) {
char chartocopy = messagein[i];
if (chartocopy <= 0x00 || chartocopy == 0x24) {
break;
}
message[i] = chartocopy;
}
Common::rtrim(message);
for (i = 0; i < strlen(message); i++) {
totalwidth += letterwidth(message[i]);
}
currxoffset = (640 - totalwidth) / 2;
char *currpos = message;
while (*(currpos) != 0) {
currxoffset += printletter(*(currpos++), currxoffset);
}
}
uint16 Font::letteroffset(char letter) {
uint16 offset;
offset = letter;
_sphinxfnt->seek(offset);
offset = _sphinxfnt->readByte() * 2 + 128;
_sphinxfnt->seek(offset);
offset = _sphinxfnt->readUint16LE();
return offset;
}
uint8 Font::letterwidth(char letter) {
uint16 offset = letteroffset(letter);
_sphinxfnt->seek(offset);
return _sphinxfnt->readByte();
}
uint8 Font::letterheight(char letter) {
uint16 offset, width, julia, data, counter = 0;
offset = letteroffset(letter);
_sphinxfnt->seek(offset);
width = _sphinxfnt->readByte();
julia = _sphinxfnt->readByte();
data = _sphinxfnt->readByte();
while (data != 0xFF) {
data = _sphinxfnt->readByte();
counter++;
}
if (counter % width != 0) assert("font file corrupt");
return counter / width;
}
uint8 Font::printletter(char letter, uint16 xoffset) {
uint16 offset, width, height, julia;
offset = letteroffset(letter);
height = letterheight(letter);
_sphinxfnt->seek(offset);
width = _sphinxfnt->readByte();
julia = _sphinxfnt->readByte();
byte *data = new byte[width * height];
_sphinxfnt->read(data, width * height);
_syst->copyRectToScreen(data, width, xoffset, 16, width, height);
delete data;
return width;
}
} // End of Groovie namespace

52
engines/groovie/font.h Normal file
View file

@ -0,0 +1,52 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef GROOVIE_FONT_H
#define GROOVIE_FONT_H
#include "common/stream.h"
#include "common/system.h"
namespace Groovie {
class Font {
public:
Font(OSystem *syst);
~Font();
void printstring(char *messagein);
private:
OSystem *_syst;
Common::MemoryReadStream *_sphinxfnt;
uint16 letteroffset(char letter);
uint8 letterwidth(char letter);
uint8 letterheight(char letter);
uint8 printletter(char letter, uint16 xoffset);
};
} // End of Groovie namespace
#endif // GROOVIE_FONT_H

View file

@ -0,0 +1,161 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "groovie/groovie.h"
#include "groovie/graphics.h"
namespace Groovie {
GraphicsMan::GraphicsMan(GroovieEngine *vm) :
_vm(vm), _changed(false), _fading(0) {
// Create the game surfaces
_foreground.create(640, 320, 1);
_background.create(640, 320, 1);
}
GraphicsMan::~GraphicsMan() {
// Free the game surfaces
_foreground.free();
_background.free();
}
void GraphicsMan::update() {
if (_fading) {
// Set the start time
uint32 time = _vm->_system->getMillis() - _fadeStartTime;
// Scale the time
int step = time / 4;
if (step > 256) {
step = 256;
}
if (_fading == 1) {
// Apply the fade in
applyFading(step);
} else if (_fading == 2) {
// Apply the fade out
applyFading(256 - step);
// Clear the buffer when ending the fade out
if (step == 256)
_foreground.fillRect(Common::Rect::Rect(640, 320), 0);
}
// Check for the end
if (step == 256) {
_fading = 0;
}
}
// Update the screen if needed and reset the status
if (_changed) {
_vm->_system->updateScreen();
_changed = false;
}
}
void GraphicsMan::change() {
_changed = true;
}
void GraphicsMan::mergeFgAndBg() {
uint32 i;
byte *countf, *countb;
countf = (byte *)_foreground.getBasePtr(0, 0);
countb = (byte *)_background.getBasePtr(0, 0);
for (i = 640 * 320; i; i--) {
if (255 == *(countf)) {
*(countf) = *(countb);
}
countf++;
countb++;
}
}
void GraphicsMan::updateScreen(Graphics::Surface *source) {
_vm->_system->copyRectToScreen((byte *)source->getBasePtr(0, 0), 640, 0, 80, 640, 320);
change();
}
bool GraphicsMan::isFading() {
return _fading;
}
void GraphicsMan::fadeIn(byte *pal) {
// Set the start time
_fadeStartTime = _vm->_system->getMillis();
// Copy the target palette
for (int i = 0; i < 256; i++) {
_paletteFull[(i * 4) + 0] = pal[(i * 3) + 0];
_paletteFull[(i * 4) + 1] = pal[(i * 3) + 1];
_paletteFull[(i * 4) + 2] = pal[(i * 3) + 2];
}
// Apply a black palette right now
applyFading(0);
// Set the current fading
_fading = 1;
}
void GraphicsMan::fadeOut() {
// Set the start time
_fadeStartTime = _vm->_system->getMillis();
// Get the current palette
_vm->_system->grabPalette(_paletteFull, 0, 256);
// Set the current fading
_fading = 2;
}
void GraphicsMan::applyFading(int step) {
// Calculate the fade factor for the given step
int factorR = 256 - (256 - step) * 1;
int factorGB = 256 - (256 - step) * 2;
if (factorR <= 0) factorR = 0;
if (factorGB <= 0) factorGB = 0;
// Calculate the new palette
byte newpal[256 * 4];
for (int i = 0; i < 256; i++) {
newpal[(i * 4) + 0] = (_paletteFull[(i * 4) + 0] * factorR) / 256;
newpal[(i * 4) + 1] = (_paletteFull[(i * 4) + 1] * factorGB) / 256;
newpal[(i * 4) + 2] = (_paletteFull[(i * 4) + 2] * factorGB) / 256;
}
// Set the screen palette
_vm->_system->setPalette(newpal, 0, 256);
// Request a screen update
change();
}
} // End of Groovie namespace

View file

@ -0,0 +1,65 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef GROOVIE_GRAPHICS_H
#define GROOVIE_GRAPHICS_H
namespace Groovie {
class GroovieEngine;
class GraphicsMan {
public:
GraphicsMan(GroovieEngine *vm);
~GraphicsMan();
// Buffers
void update();
void change();
void mergeFgAndBg();
void updateScreen(Graphics::Surface *source);
Graphics::Surface _foreground; // The main surface that most things are drawn to
Graphics::Surface _background; // Used occasionally, mostly (only?) in puzzles
// Palette fading
bool isFading();
void fadeIn(byte *pal);
void fadeOut();
private:
GroovieEngine *_vm;
bool _changed;
// Palette fading
void applyFading(int step);
int _fading;
byte _paletteFull[256 * 4];
uint32 _fadeStartTime;
};
} // End of Groovie namespace
#endif // GROOVIE_GRAPHICS_H

272
engines/groovie/groovie.cpp Normal file
View file

@ -0,0 +1,272 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/config-manager.h"
#include "common/events.h"
#include "sound/mixer.h"
#include "groovie/groovie.h"
#include "groovie/music.h"
#include "groovie/roq.h"
#include "groovie/vdx.h"
namespace Groovie {
GroovieEngine::GroovieEngine(OSystem *syst, GroovieGameDescription *gd) :
Engine(syst), _gameDescription(gd), _debugger(NULL), _script(this),
_resMan(NULL), _cursorMan(NULL), _videoPlayer(NULL), _musicPlayer(NULL),
_graphicsMan(NULL), _waitingForInput(false) {
// Adding the default directories
Common::File::addDefaultDirectory(_gameDataDir.getChild("groovie"));
Common::File::addDefaultDirectory(_gameDataDir.getChild("media"));
Common::File::addDefaultDirectory(_gameDataDir.getChild("system"));
// Initialize the custom debug levels
Common::addSpecialDebugLevel(kGroovieDebugAll, "All", "Debug everything");
Common::addSpecialDebugLevel(kGroovieDebugVideo, "Video", "Debug video and audio playback");
Common::addSpecialDebugLevel(kGroovieDebugResource, "Resource", "Debug resouce management");
Common::addSpecialDebugLevel(kGroovieDebugScript, "Script", "Debug the scripts");
Common::addSpecialDebugLevel(kGroovieDebugUnknown, "Unknown", "Report values of unknown data in files");
Common::addSpecialDebugLevel(kGroovieDebugHotspots, "Hotspots", "Show the hotspots");
Common::addSpecialDebugLevel(kGroovieDebugCursor, "Cursor", "Debug cursor decompression / switching");
}
GroovieEngine::~GroovieEngine() {
// Delete the remaining objects
delete _debugger;
delete _resMan;
delete _cursorMan;
delete _videoPlayer;
delete _musicPlayer;
delete _graphicsMan;
}
Common::Error GroovieEngine::init() {
// Initialize the graphics
_system->beginGFXTransaction();
initCommonGFX(true);
_system->initSize(640, 480);
_system->endGFXTransaction();
// Create debugger. It requires GFX to be initialized
_debugger = new Debugger(this);
_script.setDebugger(_debugger);
// Create the graphics manager
_graphicsMan = new GraphicsMan(this);
// Create the resource and cursor managers and the video player
switch (_gameDescription->version) {
case kGroovieT7G:
_resMan = new ResMan_t7g();
_cursorMan = new CursorMan_t7g(_system);
_videoPlayer = new VDXPlayer(this);
break;
case kGroovieV2:
_resMan = new ResMan_v2();
_cursorMan = new CursorMan_v2(_system);
_videoPlayer = new ROQPlayer(this);
break;
}
// Create the music player
_musicPlayer = new MusicPlayer(this);
// Load volume levels
syncSoundSettings();
// Get the name of the main script
Common::String filename = _gameDescription->desc.filesDescriptions[0].fileName;
if (_gameDescription->version == kGroovieT7G) {
// Run The 7th Guest's demo if requested
if (ConfMan.hasKey("demo_mode") && ConfMan.getBool("demo_mode")) {
filename = Common::String("demo.grv");
}
} else if (_gameDescription->version == kGroovieV2) {
// Open the disk index
Common::File disk;
if (!disk.open(filename)) {
error("Couldn't open %s", filename.c_str());
return Common::kNoGameDataFoundError;
}
// Search the entry
bool found = false;
int index = 0;
while (!found && !disk.eos()) {
Common::String line = disk.readLine();
if (line.hasPrefix("title: ")) {
// A new entry
index++;
} else if (line.hasPrefix("boot: ") && index == _gameDescription->indexEntry) {
// It's the boot of the entry were looking for,
// get the script filename
filename = line.c_str() + 6;
found = true;
}
}
// Couldn't find the entry
if (!found) {
error("Couldn't find entry %d in %s", _gameDescription->indexEntry, filename.c_str());
return Common::kUnknownError;
}
}
// Check the script file extension
if (!filename.hasSuffix(".grv")) {
error("%s isn't a valid script filename", filename.c_str());
return Common::kUnknownError;
}
// Load the script
if (!_script.loadScript(filename)) {
error("Couldn't load the script file %s", filename.c_str());
return Common::kUnknownError;
}
// Should I load a saved game?
if (ConfMan.hasKey("save_slot")) {
// Get the requested slot
int slot = ConfMan.getInt("save_slot");
_script.directGameLoad(slot);
}
return Common::kNoError;
}
Common::Error GroovieEngine::go() {
// Check that the game files and the audio tracks aren't together run from
// the same cd
checkCD();
// Initialize the CD
int cd_num = ConfMan.getInt("cdrom");
if (cd_num >= 0)
_system->openCD(cd_num);
while (!shouldQuit()) {
// Show the debugger if required
if (_debugger->isAttached()) {
_debugger->onFrame();
}
// If there's still a script error after debugging, end the execution
if (_script.haveError()) {
quitGame();
break;
}
// Handle input
Common::Event ev;
while (_eventMan->pollEvent(ev)) {
switch (ev.type) {
case Common::EVENT_KEYDOWN:
// CTRL-D: Attach the debugger
if ((ev.kbd.flags & Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d)
_debugger->attach();
// Send the event to the scripts
_script.setKbdChar(ev.kbd.ascii);
// Continue the script execution to handle the key
_waitingForInput = false;
break;
case Common::EVENT_MOUSEMOVE:
// Continue the script execution, the mouse
// pointer may fall inside a hotspot now
_waitingForInput = false;
break;
case Common::EVENT_LBUTTONDOWN:
// Send the event to the scripts
_script.setMouseClick();
// Continue the script execution to handle
// the click
_waitingForInput = false;
break;
case Common::EVENT_QUIT:
quitGame();
break;
default:
break;
}
}
if (_waitingForInput) {
// Still waiting for input, just update the mouse and wait a bit more
_cursorMan->animate();
_system->updateScreen();
_system->delayMillis(50);
} else if (_graphicsMan->isFading()) {
// We're waiting for a fading to end, let the CPU rest
// for a while and continue
_system->delayMillis(30);
} else {
// Everything's fine, execute another script step
_script.step();
}
// Update the screen if required
_graphicsMan->update();
}
return Common::kNoError;
}
bool GroovieEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsRTL) ||
(f == kSupportsLoadingDuringRuntime);
}
void GroovieEngine::syncSoundSettings() {
_musicPlayer->setUserVolume(ConfMan.getInt("music_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, ConfMan.getInt("speech_volume"));
}
bool GroovieEngine::canLoadGameStateCurrently() {
// TODO: verify the engine has been initialized
return true;
}
Common::Error GroovieEngine::loadGameState(int slot) {
_script.directGameLoad(slot);
// TODO: Use specific error codes
return Common::kNoError;
}
void GroovieEngine::waitForInput() {
_waitingForInput = true;
}
} // End of namespace Groovie

103
engines/groovie/groovie.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef GROOVIE_H
#define GROOVIE_H
#include "common/advancedDetector.h"
#include "engines/engine.h"
#include "graphics/surface.h"
#include "groovie/cursor.h"
#include "groovie/debug.h"
#include "groovie/graphics.h"
#include "groovie/player.h"
#include "groovie/resource.h"
#include "groovie/script.h"
namespace Groovie {
class MusicPlayer;
enum kDebugLevels {
kGroovieDebugAll = 1 << 0,
kGroovieDebugVideo = 1 << 1,
kGroovieDebugResource = 1 << 2,
kGroovieDebugScript = 1 << 3,
kGroovieDebugUnknown = 1 << 4,
kGroovieDebugHotspots = 1 << 5,
kGroovieDebugCursor = 1 << 6,
kGroovieDebugMIDI = 1 << 7
// the current limitation is 32 debug levels (1 << 31 is the last one)
};
enum kEngineVersion {
kGroovieT7G,
kGroovieV2
};
struct GroovieGameDescription {
Common::ADGameDescription desc;
kEngineVersion version; // Version of the engine
int indexEntry; // The index of the entry in disk.1 for V2 games
};
class GroovieEngine : public Engine {
public:
GroovieEngine(OSystem *syst, GroovieGameDescription *gd);
~GroovieEngine();
protected:
Common::Error init();
Common::Error go();
public:
bool hasFeature(EngineFeature f) const;
bool canLoadGameStateCurrently();
Common::Error loadGameState(int slot);
void syncSoundSettings();
Debugger *getDebugger() { return _debugger; }
void waitForInput();
Script _script;
ResMan *_resMan;
CursorMan *_cursorMan;
VideoPlayer *_videoPlayer;
MusicPlayer *_musicPlayer;
GraphicsMan *_graphicsMan;
private:
GroovieGameDescription *_gameDescription;
Debugger *_debugger;
bool _waitingForInput;
};
} // End of namespace Groovie
#endif // GROOVIE_H

99
engines/groovie/lzss.cpp Normal file
View file

@ -0,0 +1,99 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "groovie/lzss.h"
#define OUT_BUFF_SIZE 131072
#define COMP_THRESH 3 // Compression not attempted if string to be compressed is less than 3 long
LzssReadStream::LzssReadStream(Common::ReadStream *indata, uint8 lengthmask, uint8 lengthbits) {
/*
TODO: Nasty hack. Make a buffer bigger than I'll ever need... probably.
What should *really* happen is I should define a whole new type of stream
that gets lzss decompressed on the fly
*/
_outLzssBufData = (uint8 *)malloc(OUT_BUFF_SIZE);
_size = decodeLZSS(indata, lengthmask, lengthbits);
_pos = 0;
}
LzssReadStream::~LzssReadStream() {
free(_outLzssBufData);
}
uint32 LzssReadStream::decodeLZSS(Common::ReadStream *in, uint8 lengthmask, uint8 lengthbits) {
uint32 N = 1 << (16 - lengthbits); /* History buffer size */
byte *histbuff = new byte[N]; /* History buffer */
memset(histbuff, 0, N);
uint32 outstreampos = 0;
uint32 bufpos = 0;
while (!in->eos()) {
byte flagbyte = in->readByte();
for (uint32 i = 1; i <= 8; i++) {
if (!in->eos()) {
if ((flagbyte & 1) == 0) {
uint32 offsetlen = in->readUint16LE();
if (offsetlen == 0) {
break;
}
uint32 length = (offsetlen & lengthmask) + COMP_THRESH;
uint32 offset = (bufpos - (offsetlen >> lengthbits)) & (N - 1);
for (uint32 j = 0; j < length; j++) {
byte tempa = histbuff[(offset + j) & (N - 1)];
_outLzssBufData[outstreampos++] = tempa;
histbuff[bufpos] = tempa;
bufpos = (bufpos + 1) & (N - 1);
}
} else {
byte tempa = in->readByte();
if (in->eos()) {
break;
}
_outLzssBufData[outstreampos++] = tempa;
histbuff[bufpos] = tempa;
bufpos = (bufpos + 1) & (N - 1);
}
flagbyte = flagbyte >> 1;
}
}
}
delete[] histbuff;
return outstreampos;
}
bool LzssReadStream::eos() const {
return _pos >= _size;
}
uint32 LzssReadStream::read(void *buf, uint32 size) {
if (size > _size - _pos)
size = _size - _pos;
memcpy(buf, &_outLzssBufData[_pos], size);
_pos += size;
return size;
}

42
engines/groovie/lzss.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stream.h"
class LzssReadStream : public Common::ReadStream {
private:
uint8 *_outLzssBufData;
uint32 _size;
uint32 _pos;
uint32 decodeLZSS(Common::ReadStream *in, uint8 lengthmask, uint8 lengthbits);
public:
LzssReadStream(Common::ReadStream *indata, uint8 lengthmask, uint8 lengthbits);
~LzssReadStream();
bool eos() const;
uint32 read(void *buf, uint32 size);
};

24
engines/groovie/module.mk Normal file
View file

@ -0,0 +1,24 @@
MODULE := engines/groovie
MODULE_OBJS := \
cursor.o \
debug.o \
detection.o \
font.o \
graphics.o \
groovie.o \
lzss.o \
music.o \
player.o \
resource.o \
roq.o \
script.o \
vdx.o
# This module can be built as a plugin
ifeq ($(ENABLE_GROOVIE), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk

211
engines/groovie/music.cpp Normal file
View file

@ -0,0 +1,211 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "groovie/music.h"
#include "groovie/resource.h"
namespace Groovie {
MusicPlayer::MusicPlayer(GroovieEngine *vm) :
_vm(vm), _midiParser(NULL), _data(NULL), _driver(NULL),
_backgroundFileRef(0) {
// Create the parser
_midiParser = MidiParser::createParser_XMIDI();
// Create the driver
int driver = detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
_driver = createMidi(driver);
_driver->open();
// Initialize the channel volumes
for (int i = 0; i < 0x10; i++) {
_chanVolumes[i] = 0x7F;
}
// Set the parser's driver
_midiParser->setMidiDriver(this);
// Set the timer rate
_midiParser->setTimerRate(_driver->getBaseTempo());
}
MusicPlayer::~MusicPlayer() {
// Unload the parser
unload();
delete _midiParser;
// Unload the MIDI Driver
_driver->close();
delete _driver;
}
void MusicPlayer::playSong(uint16 fileref) {
// Play the referenced file once
play(fileref, false);
}
void MusicPlayer::setBackgroundSong(uint16 fileref) {
_backgroundFileRef = fileref;
}
void MusicPlayer::setUserVolume(uint16 volume) {
// Save the new user volume
_userVolume = volume;
if (_userVolume > 0x100) _userVolume = 0x100;
// Apply it to all the channels
for (int i = 0; i < 0x10; i++) {
updateChanVolume(i);
}
//FIXME: AdlibPercussionChannel::controlChange() is empty
//(can't set the volume for the percusion channel)
}
void MusicPlayer::setGameVolume(uint16 volume, uint16 time) {
//TODO: Implement volume fading
debugC(5, kGroovieDebugMIDI | kGroovieDebugAll, "setting game volume: %d, %d\n", volume, time);
// Save the new game volume
_gameVolume = volume;
if (_gameVolume > 100) _gameVolume = 100;
// Apply it to all the channels
for (int i = 0; i < 0x10; i++) {
updateChanVolume(i);
}
}
void MusicPlayer::updateChanVolume(byte channel) {
// Generate a MIDI Control change message for the volume
uint32 b = 0x7B0;
// Specify the channel
b |= (channel & 0xF);
// Scale by the user and game volumes
uint32 val = (_chanVolumes[channel] * _userVolume * _gameVolume) / 0x100 / 100;
val &= 0x7F;
// Send it to the driver
_driver->send(b | (val << 16));
}
bool MusicPlayer::play(uint16 fileref, bool loop) {
// Unload the previous song
unload();
// Set the looping option
_midiParser->property(MidiParser::mpAutoLoop, loop);
// Load the new file
return load(fileref);
}
bool MusicPlayer::load(uint16 fileref) {
// Open the song resource
Common::SeekableReadStream *xmidiFile = _vm->_resMan->open(fileref);
if (!xmidiFile) {
error("Groovie::Music: Couldn't resource 0x%04X", fileref);
return false;
}
// Read the whole file to memory
int length = xmidiFile->size();
_data = new byte[length];
xmidiFile->read(_data, length);
delete xmidiFile;
// Start parsing the data
if (!_midiParser->loadMusic(_data, length)) {
error("Groovie::Music: Invalid XMI file");
return false;
}
// Activate the timer source
_driver->setTimerCallback(_midiParser, MidiParser::timerCallback);
return true;
}
void MusicPlayer::unload() {
// Unload the parser
_midiParser->unloadMusic();
// Unload the xmi file
delete[] _data;
_data = NULL;
}
int MusicPlayer::open() {
return 0;
}
void MusicPlayer::close() {}
void MusicPlayer::send(uint32 b) {
if ((b & 0xFFF0) == 0x07B0) { // Volume change
// Save the specific channel volume
byte chan = b & 0xF;
_chanVolumes[chan] = (b >> 16) & 0x7F;
// Send the updated value
updateChanVolume(chan);
return;
}
_driver->send(b);
}
void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
switch (type) {
case 0x2F:
// End of Track, play the background song
if (_backgroundFileRef) {
play(_backgroundFileRef, true);
}
break;
default:
_driver->metaEvent(type, data, length);
break;
}
}
void MusicPlayer::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
_driver->setTimerCallback(timer_param, timer_proc);
}
uint32 MusicPlayer::getBaseTempo(void) {
return _driver->getBaseTempo();
}
MidiChannel *MusicPlayer::allocateChannel() {
return _driver->allocateChannel();
}
MidiChannel *MusicPlayer::getPercussionChannel() {
return _driver->getPercussionChannel();
}
} // End of Groovie namespace

78
engines/groovie/music.h Normal file
View file

@ -0,0 +1,78 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef GROOVIE_MUSIC_H
#define GROOVIE_MUSIC_H
#include "groovie/groovie.h"
#include "sound/mididrv.h"
#include "sound/midiparser.h"
namespace Groovie {
class MusicPlayer : public MidiDriver {
public:
MusicPlayer(GroovieEngine *vm);
~MusicPlayer();
void playSong(uint16 fileref);
void setBackgroundSong(uint16 fileref);
// Volume
void setUserVolume(uint16 volume);
void setGameVolume(uint16 volume, uint16 time);
private:
uint16 _userVolume;
uint16 _gameVolume;
byte _chanVolumes[0x10];
void updateChanVolume(byte channel);
public:
// MidiDriver interface
int open();
void close();
void send(uint32 b);
void metaEvent(byte type, byte *data, uint16 length);
void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc);
uint32 getBaseTempo(void);
MidiChannel *allocateChannel();
MidiChannel *getPercussionChannel();
private:
GroovieEngine *_vm;
byte *_data;
MidiParser *_midiParser;
MidiDriver *_driver;
uint16 _backgroundFileRef;
bool play(uint16 fileref, bool loop);
bool load(uint16 fileref);
void unload();
};
} // End of Groovie namespace
#endif // GROOVIE_MUSIC_H

View file

@ -0,0 +1,98 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "groovie/groovie.h"
#include "groovie/player.h"
namespace Groovie {
VideoPlayer::VideoPlayer(GroovieEngine *vm) :
_vm(vm), _syst(vm->_system), _file(NULL), _audioStream(NULL) {
}
bool VideoPlayer::load(Common::SeekableReadStream *file, uint16 flags) {
_file = file;
_flags = flags;
_audioStream = NULL;
uint16 fps = loadInternal();
if (fps != 0) {
_millisBetweenFrames = 1000 / fps;
_begunPlaying = false;
return true;
} else {
_file = NULL;
return false;
}
}
bool VideoPlayer::playFrame() {
bool end = true;
// Process the next frame while the file is open
if (_file) {
end = playFrameInternal();
}
// The file has been completely processed
if (end) {
_file = NULL;
// Wait for pending audio
if (_audioStream) {
if (_audioStream->endOfData()) {
// Mark the audio stream as finished (no more data will be appended)
_audioStream->finish();
} else {
// Don't end if there's still audio playing
end = false;
}
}
}
return end;
}
void VideoPlayer::waitFrame() {
uint32 currTime = _syst->getMillis();
if (!_begunPlaying) {
_begunPlaying = true;
_lastFrameTime = currTime;
} else {
uint32 millisDiff = currTime - _lastFrameTime;
if (millisDiff < _millisBetweenFrames) {
debugC(7, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::Player: Delaying %d (currTime=%d, _lastFrameTime=%d, millisDiff=%d, _millisBetweenFrame=%d)",
_millisBetweenFrames - millisDiff, currTime, _lastFrameTime, millisDiff, _millisBetweenFrames);
_syst->delayMillis(_millisBetweenFrames - millisDiff);
currTime = _syst->getMillis();
debugC(7, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::Player: Finished delay at %d", currTime);
}
debugC(6, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::Player: Frame displayed at %d (%f FPS)", currTime, 1000.0 / (currTime - _lastFrameTime));
_lastFrameTime = currTime;
}
}
} // End of Groovie namespace

68
engines/groovie/player.h Normal file
View file

@ -0,0 +1,68 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef GROOVIE_PLAYER_H
#define GROOVIE_PLAYER_H
#include "common/system.h"
#include "sound/audiostream.h"
namespace Groovie {
class GroovieEngine;
class VideoPlayer {
public:
VideoPlayer(GroovieEngine *vm);
virtual ~VideoPlayer() {}
bool load(Common::SeekableReadStream *file, uint16 flags);
bool playFrame();
virtual void setOrigin(int16 x, int16 y) {};
protected:
// To be implemented by subclasses
virtual uint16 loadInternal() = 0;
virtual bool playFrameInternal() = 0;
GroovieEngine *_vm;
OSystem *_syst;
Common::SeekableReadStream *_file;
uint16 _flags;
Audio::AppendableAudioStream *_audioStream;
private:
// Synchronization stuff
bool _begunPlaying;
uint16 _millisBetweenFrames;
uint32 _lastFrameTime;
protected:
void waitFrame();
};
} // End of Groovie namespace
#endif // GROOVIE_PLAYER_H

View file

@ -0,0 +1,240 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "groovie/groovie.h"
#include "groovie/resource.h"
namespace Groovie {
// ResMan
Common::SeekableReadStream *ResMan::open(uint16 fileRef) {
// Get the information about the resource
ResInfo resInfo;
if (!getResInfo(fileRef, resInfo)) {
return NULL;
}
// Do we know the name of the required GJD?
if (resInfo.gjd >= _gjds.size()) {
error("Groovie::Resource: Unknown GJD %d", resInfo.gjd);
return NULL;
}
debugC(1, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Opening resource 0x%04X (%s, %d, %d)", fileRef, _gjds[resInfo.gjd].c_str(), resInfo.offset, resInfo.size);
// Does it exist?
if (!Common::File::exists(_gjds[resInfo.gjd])) {
error("Groovie::Resource: %s not found", _gjds[resInfo.gjd].c_str());
return NULL;
}
// Open the pack file
Common::File *gjdFile = new Common::File();
if (!gjdFile->open(_gjds[resInfo.gjd].c_str())) {
delete gjdFile;
error("Groovie::Resource: Couldn't open %s", _gjds[resInfo.gjd].c_str());
return NULL;
}
// Save the used gjd file (except xmi and gamwav)
if (resInfo.gjd < 19) {
_lastGjd = resInfo.gjd;
}
// Returning the resource substream
return new Common::SeekableSubReadStream(gjdFile, resInfo.offset, resInfo.offset + resInfo.size, true);
}
// ResMan_t7g
static const char t7g_gjds[][0x15] = {"at", "b", "ch", "d", "dr", "fh", "ga", "hdisk", "htbd", "intro", "jhek", "k", "la", "li", "mb", "mc", "mu", "n", "p", "xmi", "gamwav"};
ResMan_t7g::ResMan_t7g() {
for (int i = 0; i < 0x15; i++) {
// Prepare the filename
Common::String filename = t7g_gjds[i];
filename += ".gjd";
// Append it to the list of GJD files
_gjds.push_back(filename);
}
}
uint16 ResMan_t7g::getRef(Common::String name, Common::String scriptname) {
// Get the name of the RL file
Common::String rlFileName(t7g_gjds[_lastGjd]);
rlFileName += ".rl";
// Open the RL file
Common::File rlFile;
if (!rlFile.open(rlFileName)) {
error("Groovie::Resource: Couldn't open %s", rlFileName.c_str());
return false;
}
uint16 resNum;
bool found = false;
for (resNum = 0; !found && !rlFile.ioFailed(); resNum++) {
// Read the resource name
char readname[12];
rlFile.read(readname, 12);
// Test whether it's the resource we're searching
Common::String resname(readname, 12);
if (resname.hasPrefix(name.c_str())) {
debugC(2, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Resource %12s matches %s", readname, name.c_str());
found = true;
}
// Skip the rest of resource information
rlFile.read(readname, 8);
}
// Close the RL file
rlFile.close();
// Verify we really found the resource
if (!found) {
error("Groovie::Resource: Couldn't find resource %s in %s", name.c_str(), rlFileName.c_str());
return (uint16)-1;
}
return (_lastGjd << 10) | (resNum - 1);
}
bool ResMan_t7g::getResInfo(uint16 fileRef, ResInfo &resInfo) {
// Calculate the GJD and the resource number
resInfo.gjd = fileRef >> 10;
uint16 resNum = fileRef & 0x3FF;
// Get the name of the RL file
Common::String rlFileName(t7g_gjds[resInfo.gjd]);
rlFileName += ".rl";
// Open the RL file
Common::File rlFile;
if (!rlFile.open(rlFileName)) {
error("Groovie::Resource: Couldn't open %s", rlFileName.c_str());
return false;
}
// Seek to the position of the desired resource
rlFile.seek(resNum * 20);
if (rlFile.eos()) {
rlFile.close();
error("Groovie::Resource: Invalid resource number: 0x%04X (%s)", resNum, rlFileName.c_str());
return false;
}
// Read the resource name (just for debugging purposes)
char resname[12];
rlFile.read(resname, 12);
debugC(2, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Resource name: %12s", resname);
// Read the resource information
resInfo.offset = rlFile.readUint32LE();
resInfo.size = rlFile.readUint32LE();
// Close the resource RL file
rlFile.close();
return true;
}
// ResMan_v2
ResMan_v2::ResMan_v2() {
Common::File indexfile;
// Open the GJD index file
if (!indexfile.open("gjd.gjd")) {
error("Groovie::Resource: Couldn't open gjd.gjd");
return;
}
Common::String line = indexfile.readLine();
while (!indexfile.eos() && !line.empty()) {
// Get the name before the space
Common::String filename;
for (const char *cur = line.c_str(); *cur != ' '; cur++) {
filename += *cur;
}
// Append it to the list of GJD files
if (!filename.empty()) {
_gjds.push_back(filename);
}
// Read the next line
line = indexfile.readLine();
}
// Close the GJD index file
indexfile.close();
}
uint16 ResMan_v2::getRef(Common::String name, Common::String scriptname) {
return 0;
}
bool ResMan_v2::getResInfo(uint16 fileRef, ResInfo &resInfo) {
// Open the RL file
Common::File rlFile;
if (!rlFile.open("dir.rl")) {
error("Groovie::Resource: Couldn't open dir.rl");
return false;
}
// Seek to the position of the desired resource
rlFile.seek(fileRef * 32);
if (rlFile.eos()) {
rlFile.close();
error("Groovie::Resource: Invalid resource number: 0x%04X", fileRef);
return false;
}
// Read the resource information
rlFile.readUint32LE(); // Unknown
resInfo.offset = rlFile.readUint32LE();
resInfo.size = rlFile.readUint32LE();
resInfo.gjd = rlFile.readUint16LE();
// Read the resource name (just for debugging purposes)
char resname[12];
rlFile.read(resname, 12);
debugC(2, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Resource name: %12s", resname);
// 6 padding bytes? (it looks like they're always 0)
// Close the resource RL file
rlFile.close();
return true;
}
} // End of Groovie namespace

View file

@ -0,0 +1,71 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef GROOVIE_RESOURCE_H
#define GROOVIE_RESOURCE_H
namespace Groovie {
struct ResInfo {
uint16 gjd;
uint32 offset;
uint32 size;
};
class ResMan {
public:
virtual ~ResMan() {};
Common::SeekableReadStream *open(uint16 fileRef);
virtual uint16 getRef(Common::String name, Common::String scriptname = "") = 0;
protected:
Common::Array<Common::String> _gjds;
virtual bool getResInfo(uint16 fileRef, ResInfo &resInfo) = 0;
uint16 _lastGjd;
};
class ResMan_t7g : public ResMan {
public:
ResMan_t7g();
~ResMan_t7g() {};
uint16 getRef(Common::String name, Common::String scriptname);
bool getResInfo(uint16 fileRef, ResInfo &resInfo);
};
class ResMan_v2 : public ResMan {
public:
ResMan_v2();
~ResMan_v2() {};
uint16 getRef(Common::String name, Common::String scriptname);
bool getResInfo(uint16 fileRef, ResInfo &resInfo);
};
} // End of Groovie namespace
#endif // GROOVIE_RESOURCE_H

403
engines/groovie/roq.cpp Normal file
View file

@ -0,0 +1,403 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "groovie/groovie.h"
#include "groovie/roq.h"
#include "sound/mixer.h"
namespace Groovie {
ROQPlayer::ROQPlayer(GroovieEngine *vm) :
VideoPlayer(vm) {
}
ROQPlayer::~ROQPlayer() {
}
uint16 ROQPlayer::loadInternal() {
// Begin reading the file
debugC(1, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Loading video");
// Read the file header
ROQBlockHeader blockHeader;
if (!readBlockHeader(blockHeader)) {
return 0;
}
if (blockHeader.type != 0x1084 || blockHeader.size != 0 || blockHeader.param != 0) {
return 0;
}
// Hardcoded FPS
return 25;
}
bool ROQPlayer::playFrameInternal() {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Playing frame");
// Process the needed blocks until the next video frame
bool endframe = false;
while (!endframe && !_file->eos()) {
endframe = processBlock();
}
// Wait until the current frame can be shown
waitFrame();
// Update the screen
_syst->updateScreen();
// Return whether the video has ended
return _file->eos();
}
bool ROQPlayer::readBlockHeader(ROQBlockHeader &blockHeader) {
if (_file->eos()) {
return false;
} else {
blockHeader.type = _file->readUint16LE();
blockHeader.size = _file->readUint32LE();
blockHeader.param = _file->readUint16LE();
debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block type = 0x%02X", blockHeader.type);
debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block size = 0x%08X", blockHeader.size);
debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block param = 0x%04X", blockHeader.param);
return true;
}
}
bool ROQPlayer::processBlock() {
// Read the header of the block
ROQBlockHeader blockHeader;
if (!readBlockHeader(blockHeader)) {
return true;
}
bool ok = true;
bool endframe = false;
switch (blockHeader.type) {
case 0x1001: // Video info
ok = processBlockInfo(blockHeader);
break;
case 0x1002: // Quad codebook definition
ok = processBlockQuadCodebook(blockHeader);
break;
case 0x1011: // Quad vector quantised video frame
ok = processBlockQuadVector(blockHeader);
endframe = true;
break;
case 0x1012: // Still image (JPEG)
ok = processBlockStill(blockHeader);
endframe = true;
break;
case 0x1013: // Hang
//warning("Groovie::ROQ: Hang block (skipped)");
break;
case 0x1020: // Mono sound samples
ok = processBlockSoundMono(blockHeader);
break;
case 0x1021: // Stereo sound samples
ok = processBlockSoundStereo(blockHeader);
break;
case 0x1030: // Audio container
ok = processBlockAudioContainer(blockHeader);
break;
default:
error("Groovie::ROQ: Unknown block type: 0x%04X", blockHeader.type);
ok = false;
}
// End the frame when the graphics have been modified or when there's an error
return endframe || !ok;
}
bool ROQPlayer::processBlockInfo(ROQBlockHeader &blockHeader) {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing info block");
// Verify the block header
if (blockHeader.type != 0x1001 || blockHeader.size != 8 || blockHeader.param != 0) {
return false;
}
uint16 tmp;
tmp = _file->readUint16LE();
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "w = %d\n", tmp);
if (tmp != 640) {
return false;
}
tmp = _file->readUint16LE();
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "h = %d\n", tmp);
if (tmp != 320) {
return false;
}
tmp = _file->readUint16LE();
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "unk1 = %d\n", tmp);
if (tmp != 8) {
return false;
}
tmp = _file->readUint16LE();
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "unk2 = %d\n", tmp);
if (tmp != 4) {
return false;
}
return true;
}
bool ROQPlayer::processBlockQuadCodebook(ROQBlockHeader &blockHeader) {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad codebook block");
// Get the number of 2x2 pixel blocks
_num2blocks = blockHeader.param >> 8;
if (_num2blocks == 0) {
_num2blocks = 256;
}
// Get the number of 4x4 pixel blocks
_num4blocks = blockHeader.param & 0xFF;
if (_num4blocks == 0 && (blockHeader.size > (uint32)_num2blocks * 6)) {
_num4blocks = 256;
}
_file->skip(_num2blocks * 6);
_file->skip(_num4blocks * 4);
return true;
}
bool ROQPlayer::processBlockQuadVector(ROQBlockHeader &blockHeader) {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector block");
_file->skip(blockHeader.size);
return true;
// Get the mean motion vectors
//byte Mx = blockHeader.param >> 8;
//byte My = blockHeader.param & 0xFF;
int32 ends =_file->pos() + blockHeader.size;
int numblocks = (640 / 8) * (320 / 8);
for (int j = 0; j < numblocks && ends > _file->pos(); j++) {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "doing block %d/%d\n", j, numblocks);
uint16 codingType = _file->readUint16LE();
for (int i = 0; i < 8; i++) {
switch (codingType >> 14) {
case 0: // MOT: Skip block
//printf("coding type 0\n");
break;
case 1: { // FCC: Copy an existing block
//printf("coding type 1\n");
byte argument;
argument = _file->readByte();
//byte Dx = Mx + (argument >> 4);
//byte Dy = My + (argument & 0x0F);
// Dx = X + 8 - (argument >> 4) - Mx
// Dy = Y + 8 - (argument & 0x0F) - My
break;
}
case 2: { // SLD: Quad vector quantisation
//printf("coding type 2\n");
byte argument = _file->readByte();
if (argument > _num4blocks) {
//error("invalid 4x4 block %d of %d", argument, _num4blocks);
}
// Upsample the 4x4 pixel block
break;
}
case 3: // CCC:
//printf("coding type 3:\n");
processBlockQuadVectorSub(blockHeader);
break;
}
codingType <<= 2;
}
}
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Should have ended at %d, and has ended at %d\n", ends, _file->pos());
return true;
}
bool ROQPlayer::processBlockQuadVectorSub(ROQBlockHeader &blockHeader) {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector sub block");
// Get the mean motion vectors
//byte Mx = blockHeader.param >> 8;
//byte My = blockHeader.param & 0xFF;
uint16 codingType = _file->readUint16LE();
for (int i = 0; i < 4; i++) {
switch (codingType >> 14) {
case 0: // MOT: Skip block
//printf("coding type 0\n");
break;
case 1: { // FCC: Copy an existing block
//printf("coding type 1\n");
byte argument;
argument = _file->readByte();
//byte Dx = Mx + (argument >> 4);
//byte Dy = My + (argument & 0x0F);
// Dx = X + 8 - (argument >> 4) - Mx
// Dy = Y + 8 - (argument & 0x0F) - My
break;
}
case 2: { // SLD: Quad vector quantisation
//printf("coding type 2\n");
byte argument = _file->readByte();
if (argument > _num2blocks) {
//error("invalid 2x2 block: %d of %d", argument, _num2blocks);
}
break;
}
case 3:
//printf("coding type 3\n");
_file->readByte();
_file->readByte();
_file->readByte();
_file->readByte();
break;
}
codingType <<= 2;
}
return true;
}
bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing still (JPEG) block");
//Common::ReadStream *jpegData = new Common::SubReadStream(_file, blockHeader.size);
//Graphics::JPEG jpegFrame;
//jpegFrame.read(jpegData);
/*
Common::File save;
save.open("dump.jpg", Common::File::kFileWriteMode);
save.write(data, blockHeader.size);
save.close();
*/
error("JPEG!");
return true;
}
bool ROQPlayer::processBlockSoundMono(ROQBlockHeader &blockHeader) {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing mono sound block");
// Verify the block header
if (blockHeader.type != 0x1020) {
return false;
}
// Initialize the audio stream if needed
if (!_audioStream) {
byte flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_AUTOFREE;
#ifdef SCUMM_LITTLE_ENDIAN
flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
#endif
_audioStream = Audio::makeAppendableAudioStream(22050, flags);
Audio::SoundHandle sound_handle;
::g_engine->_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
}
// Create the audio buffer
int16 *buffer = new int16[blockHeader.size];
// Initialize the prediction with the block parameter
int16 prediction = blockHeader.param ^ 0x8000;
// Process the data
for (uint16 i = 0; i < blockHeader.size; i++) {
int16 data = _file->readByte();
if (data < 0x80) {
prediction += data * data;
} else {
data -= 0x80;
prediction -= data * data;
}
buffer[i] = prediction;
}
// Queue the read buffer
_audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2);
return true;
}
bool ROQPlayer::processBlockSoundStereo(ROQBlockHeader &blockHeader) {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing stereo sound block");
// Verify the block header
if (blockHeader.type != 0x1021) {
return false;
}
// Initialize the audio stream if needed
if (!_audioStream) {
byte flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_STEREO;
#ifdef SCUMM_LITTLE_ENDIAN
flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
#endif
_audioStream = Audio::makeAppendableAudioStream(22050, flags);
Audio::SoundHandle sound_handle;
::g_engine->_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
}
// Create the audio buffer
int16 *buffer = new int16[blockHeader.size];
// Initialize the prediction with the block parameter
int16 predictionLeft = (blockHeader.param & 0xFF00) ^ 0x8000;
int16 predictionRight = (blockHeader.param << 8) ^ 0x8000;
bool left = true;
// Process the data
for (uint16 i = 0; i < blockHeader.size; i++) {
int16 data = _file->readByte();
if (left) {
if (data < 0x80) {
predictionLeft += data * data;
} else {
data -= 0x80;
predictionLeft -= data * data;
}
buffer[i] = predictionLeft;
} else {
if (data < 0x80) {
predictionRight += data * data;
} else {
data -= 0x80;
predictionRight -= data * data;
}
buffer[i] = predictionRight;
}
left = !left;
}
// Queue the read buffer
_audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2);
return true;
}
bool ROQPlayer::processBlockAudioContainer(ROQBlockHeader &blockHeader) {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing audio container block: 0x%04X", blockHeader.param);
return true;
}
} // End of Groovie namespace

69
engines/groovie/roq.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef GROOVIE_ROQ_H
#define GROOVIE_ROQ_H
#include "groovie/player.h"
namespace Groovie {
class GroovieEngine;
struct ROQBlockHeader {
uint16 type;
uint32 size;
uint16 param;
};
class ROQPlayer : public VideoPlayer {
public:
ROQPlayer(GroovieEngine *vm);
~ROQPlayer();
protected:
uint16 loadInternal();
bool playFrameInternal();
private:
bool readBlockHeader(ROQBlockHeader &blockHeader);
bool processBlock();
bool processBlockInfo(ROQBlockHeader &blockHeader);
bool processBlockQuadCodebook(ROQBlockHeader &blockHeader);
bool processBlockQuadVector(ROQBlockHeader &blockHeader);
bool processBlockQuadVectorSub(ROQBlockHeader &blockHeader);
bool processBlockStill(ROQBlockHeader &blockHeader);
bool processBlockSoundMono(ROQBlockHeader &blockHeader);
bool processBlockSoundStereo(ROQBlockHeader &blockHeader);
bool processBlockAudioContainer(ROQBlockHeader &blockHeader);
uint16 _num2blocks;
uint16 _num4blocks;
};
} // End of Groovie namespace
#endif // GROOVIE_ROQ_H

1566
engines/groovie/script.cpp Normal file

File diff suppressed because it is too large Load diff

211
engines/groovie/script.h Normal file
View file

@ -0,0 +1,211 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef GROOVIE_SCRIPT_H
#define GROOVIE_SCRIPT_H
#include "common/file.h"
#include "common/rect.h"
#include "groovie/font.h"
namespace Groovie {
class GroovieEngine;
class Script {
friend class Debugger;
public:
Script(GroovieEngine *vm);
~Script();
void setDebugger(Debugger *debugger);
bool loadScript(Common::String scriptfile);
void directGameLoad(int slot);
void step();
void setMouseClick();
void setKbdChar(uint8 c);
bool haveError();
private:
GroovieEngine *_vm;
Common::RandomSource _random;
bool _firstbit;
// Script filename (for debugging purposes)
Common::String _scriptFile;
Common::String _savedScriptFile;
// Code
byte *_code;
uint16 _currentInstruction;
byte *_savedCode;
uint16 _savedInstruction;
// Variables
byte _variables[0x400];
byte _savedVariables[0x180];
// Stack
uint16 _stack[0x20];
uint8 _stacktop;
uint8 _savedStacktop;
// Input
bool _mouseClicked;
bool _eventMouseClicked;
uint8 _kbdChar;
uint8 _eventKbdChar;
uint16 _inputLoopAddress;
int16 _inputAction;
uint8 _newCursorStyle;
uint16 _hotspotTopAction;
uint16 _hotspotTopCursor;
uint16 _hotspotBottomAction;
uint16 _hotspotBottomCursor;
uint16 _hotspotRightAction;
uint16 _hotspotLeftAction;
uint16 _hotspotCursorOldX;
uint16 _hotspotCursorOldY;
// Video
Font *_font;
Common::SeekableReadStream *_videoFile;
uint16 _videoRef;
uint16 _bitflags;
// Debugging
Debugger *_debugger;
Common::String _debugString;
void error(const char *msg);
bool _error;
// Helper functions
uint8 readScript8bits();
uint16 readScript16bits();
uint32 readScript32bits();
uint16 readScript8or16bits();
uint8 readScriptChar(bool allow7C, bool limitVal, bool limitVar);
uint8 readScriptVar();
uint16 getVideoRefString();
bool hotspot(Common::Rect rect, uint16 addr, uint8 cursor);
void loadgame(uint slot);
void savegame(uint slot);
bool playvideofromref(uint16 fileref);
// Opcodes
typedef void (Script::*OpcodeFunc)();
static OpcodeFunc _opcodes[];
void o_invalid();
void o_nop();
void o_nop8();
void o_nop16();
void o_nop32();
void o_nop8or16();
void o_playsong();
void o_bf9on();
void o_palfadeout();
void o_bf8on();
void o_bf6on();
void o_bf7on();
void o_setbackgroundsong();
void o_videofromref();
void o_bf5on();
void o_inputloopstart();
void o_keyboardaction();
void o_hotspot_rect();
void o_hotspot_left();
void o_hotspot_right();
void o_hotspot_center();
void o_hotspot_current();
void o_inputloopend();
void o_random();
void o_jmp();
void o_loadstring();
void o_ret();
void o_call();
void o_sleep();
void o_strcmpnejmp_var();
void o_copybgtofg();
void o_strcmpnejmp();
void o_xor_obfuscate();
void o_vdxtransition();
void o_swap();
void o_inc();
void o_dec();
void o_strcmpeqjmp();
void o_mov();
void o_add();
void o_videofromstring1();
void o_videofromstring2();
void o_stopmidi();
void o_endscript();
void o_sethotspottop();
void o_sethotspotbottom();
void o_loadgame();
void o_savegame();
void o_hotspotbottom_4();
void o_midivolume();
void o_jne();
void o_loadstringvar();
void o_chargreatjmp();
void o_bf7off();
void o_charlessjmp();
void o_copyrecttobg();
void o_restorestkpnt();
void o_obscureswap();
void o_printstring();
void o_hotspot_slot();
void o_checkvalidsaves();
void o_resetvars();
void o_mod();
void o_loadscript();
void o_setvideoorigin();
void o_sub();
void o_othello();
void o_returnscript();
void o_sethotspotright();
void o_sethotspotleft();
void o_getcd();
void o_opcode4D();
void o_hotspot_outrect();
void o_stub56();
void o_stub59();
};
} // End of Groovie namespace
#endif // GROOVIE_SCRIPT_H

522
engines/groovie/vdx.cpp Normal file
View file

@ -0,0 +1,522 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "groovie/groovie.h"
#include "groovie/lzss.h"
#include "groovie/vdx.h"
#include "sound/mixer.h"
#define TILE_SIZE 4 // Size of each tile on the image: only ever seen 4 so far
#define VDX_IDENT 0x9267 // 37479
namespace Groovie {
VDXPlayer::VDXPlayer(GroovieEngine *vm) :
VideoPlayer(vm), _origX(0), _origY(0), _flagOnePrev(false),
_fg(&_vm->_graphicsMan->_foreground), _bg(&_vm->_graphicsMan->_background) {
}
VDXPlayer::~VDXPlayer() {
//delete _audioStream;
}
void VDXPlayer::setOrigin(int16 x, int16 y) {
_origX = x;
_origY = y;
}
uint16 VDXPlayer::loadInternal() {
uint32 engine_level = kGroovieDebugVideo | kGroovieDebugAll;
if ((gDebugLevel == 11) || (Common::getEnabledSpecialDebugLevels() & engine_level)) {
int8 i;
debugN(1, "Groovie::VDX: New VDX: bitflags are ");
for (i = 11; i >= 0; i--) {
debugN(1, "%d", _flags & (1 << i)? 1 : 0);
if (i % 4 == 0) {
debugN(1, " ");
}
}
debug(1, " ");
}
// Flags:
// - 1 Puzzle piece? Skip palette, don't redraw full screen, draw still to b/ack buffer
// - 2 Transparent colour is 0xFF
// - 5 Skip still chunks
// - 7
// - 8 Just show the first frame
// - 9 Start a palette fade in
_flagZero = ((_flags & (1 << 0)) != 0);
_flagOne = ((_flags & (1 << 1)) != 0);
_flag2Byte = (_flags & (1 << 2)) ? 0xFF : 0x00;
_flagThree = ((_flags & (1 << 3)) != 0);
_flagFour = ((_flags & (1 << 4)) != 0);
_flagFive = ((_flags & (1 << 5)) != 0);
_flagSix = ((_flags & (1 << 6)) != 0);
_flagSeven = ((_flags & (1 << 7)) != 0);
_flagEight = ((_flags & (1 << 8)) != 0);
_flagNine = ((_flags & (1 << 9)) != 0);
if (_flagOnePrev && !_flagOne && !_flagEight) {
_flagSeven = true;
}
// Save _flagOne for the next video
_flagOnePrev = _flagOne;
//_flagTransparent = _flagOne;
_flagFirstFrame = _flagEight;
//_flagSkipPalette = _flagSeven;
_flagSkipPalette = false;
//_flagSkipStill = _flagFive || _flagSeven;
//_flagUpdateStill = _flagNine || _flagSix;
// Begin reading the file
debugC(1, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Playing video");
if (_file->readUint16LE() != VDX_IDENT) {
error("Groovie::VDX: This does not appear to be a 7th guest vxd file");
return 0;
} else {
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: VDX file identified correctly");
}
uint16 tmp;
// Skip unknown data: 6 bytes, ref Martine
tmp = _file->readUint16LE();
debugC(2, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Martine1 = 0x%04X", tmp);
tmp = _file->readUint16LE();
debugC(2, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Martine2 = 0x%04X", tmp);
tmp = _file->readUint16LE();
debugC(2, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Martine3 (FPS?) = %d", tmp);
return tmp;
}
bool VDXPlayer::playFrameInternal() {
byte currRes = 0x80;
while (!_file->eos() && currRes == 0x80) {
currRes = _file->readByte();
// Skip unknown data: 1 byte, ref Edward
byte tmp = _file->readByte();
debugC(5, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Edward = 0x%04X", tmp);
uint32 compSize = _file->readUint32LE();
uint8 lengthmask = _file->readByte();
uint8 lengthbits = _file->readByte();
// Read the chunk data and decompress if needed
Common::ReadStream *vdxData = new Common::SubReadStream(_file, compSize);
if (lengthmask && lengthbits) {
Common::ReadStream *decompData = new LzssReadStream(vdxData, lengthmask, lengthbits);
delete vdxData;
vdxData = decompData;
}
// Use the current chunk
switch (currRes) {
case 0x00:
debugC(6, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Replay frame");
break;
case 0x20:
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Still frame");
getStill(vdxData);
break;
case 0x25:
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Animation frame");
getDelta(vdxData);
break;
case 0x80:
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Sound resource");
chunkSound(vdxData);
break;
default:
error("Groovie::VDX: Invalid resource type: %d", currRes);
}
delete vdxData;
}
// Wait until the current frame can be shown
waitFrame();
// TODO: Move it to a better place
// Update the screen
if (currRes == 0x25) {
//if (_flagSeven) {
//_vm->_graphicsMan->mergeFgAndBg();
//}
_vm->_graphicsMan->updateScreen(_bg);
}
// Report the end of the video if we reached the end of the file or if we
// just wanted to play one frame.
return _file->eos() || _flagFirstFrame;
}
static const uint16 vdxBlockMapLookup[] = {
0xc800, 0xec80, 0xfec8, 0xffec, 0xfffe, 0x3100, 0x7310, 0xf731, 0xff73, 0xfff7, 0x6c80, 0x36c8, 0x136c, 0x6310, 0xc631, 0x8c63,
0xf000, 0xff00, 0xfff0, 0x1111, 0x3333, 0x7777, 0x6666, 0xcccc, 0x0ff0, 0x00ff, 0xffcc, 0x0076, 0xff33, 0x0ee6, 0xccff, 0x6770,
0x33ff, 0x6ee0, 0x4800, 0x2480, 0x1248, 0x0024, 0x0012, 0x2100, 0x4210, 0x8421, 0x0042, 0x0084, 0xf888, 0x0044, 0x0032, 0x111f,
0x22e0, 0x4c00, 0x888f, 0x4470, 0x2300, 0xf111, 0x0e22, 0x00c4, 0xf33f, 0xfccf, 0xff99, 0x99ff, 0x4444, 0x2222, 0xccee, 0x7733,
0x00f8, 0x00f1, 0x00bb, 0x0cdd, 0x0f0f, 0x0f88, 0x13f1, 0x19b3, 0x1f80, 0x226f, 0x27ec, 0x3077, 0x3267, 0x37e4, 0x38e3, 0x3f90,
0x44cf, 0x4cd9, 0x4c99, 0x5555, 0x603f, 0x6077, 0x6237, 0x64c9, 0x64cd, 0x6cd9, 0x70ef, 0x0f00, 0x00f0, 0x0000, 0x4444, 0x2222
};
void VDXPlayer::getDelta(Common::ReadStream *in) {
uint16 j, k, l;
uint32 offset;
uint8 currOpCode, param1, param2, param3;
// Get the size of the local palette
j = in->readUint16LE();
// Load the palette if it isn't empty
if (j) {
uint16 palBitField[16];
int flag = 1, palIndex;
// Load the bit field
for (l = 0; l < 16; l++) {
palBitField[l] = in->readUint16LE();
}
// Load the actual palette
for (l = 0; l < 16; l++) {
flag = 1 << 15;
for (j = 0; j < 16; j++) {
palIndex = (l * 16) + j;
if (flag & palBitField[l]) {
for (k = 0; k < 3; k++) {
_palBuf[(palIndex * 3) + k] = in->readByte();
}
}
flag = flag >> 1;
}
}
// Apply the palette
if (!_flagSix && !_flagSeven) {
setPalette(_palBuf);
}
}
currOpCode = in->readByte();
/* j now becomes the current block line we're dealing with */
j = 0;
offset = 0;
while (!in->eos()) {
byte colours[16];
if (currOpCode < 0x60) {
param1 = in->readByte();
param2 = in->readByte();
expandColourMap(colours, vdxBlockMapLookup[currOpCode], param1, param2);
decodeBlockDelta(offset, colours, 640);
offset += TILE_SIZE;
} else if (currOpCode > 0x7f) {
param1 = in->readByte();
param2 = in->readByte();
param3 = in->readByte();
expandColourMap(colours, (param1 << 8) + currOpCode, param2, param3);
decodeBlockDelta(offset, colours, 640);
offset += TILE_SIZE;
} else switch (currOpCode) {
case 0x60: /* Fill tile with the 16 colours given as parameters */
for (l = 0; l < 16; l++) {
colours[l] = in->readByte();
}
decodeBlockDelta(offset, colours, 640);
offset += TILE_SIZE;
break;
case 0x61: /* Skip to the end of this line, next block is start of next */
/* Note this is used at the end of EVERY line */
j++;
offset = j * TILE_SIZE * 640;
break;
case 0x62:
case 0x63:
case 0x64:
case 0x65:
case 0x66:
case 0x67:
case 0x68:
case 0x69:
case 0x6a:
case 0x6b: /* Skip next param1 blocks (within line) */
offset += (currOpCode - 0x62) * TILE_SIZE;
break;
case 0x6c:
case 0x6d:
case 0x6e:
case 0x6f:
case 0x70:
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75: /* Next param1 blocks are filled with colour param2 */
param1 = currOpCode - 0x6b;
param2 = in->readByte();
for (l = 0; l < 16; l++) {
colours[l] = param2;
}
for (k = 0; k < param1; k++) {
decodeBlockDelta(offset, colours, 640);
offset += TILE_SIZE;
}
break;
case 0x76:
case 0x77:
case 0x78:
case 0x79:
case 0x7a:
case 0x7b:
case 0x7c:
case 0x7d:
case 0x7e:
case 0x7f: /* Next bytes contain colours to fill the next param1 blocks in the current line*/
param1 = currOpCode - 0x75;
for (k = 0; k < param1; k++) {
param2 = in->readByte();
for (l = 0; l < 16; l++) {
colours[l] = param2;
}
decodeBlockDelta(offset, colours, 640);
offset += TILE_SIZE;
}
break;
default:
error("Groovie::VDX: Broken somehow");
}
currOpCode = in->readByte();
}
}
void VDXPlayer::getStill(Common::ReadStream *in) {
uint16 numXTiles = in->readUint16LE();
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: numXTiles=%d", numXTiles);
uint16 numYTiles = in->readUint16LE();
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: numYTiles=%d", numYTiles);
// It's skipped in the original:
uint16 colourDepth = in->readUint16LE();
debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: colourDepth=%d", colourDepth);
uint16 imageWidth = TILE_SIZE * numXTiles;
uint8 mask = 0;
byte *buf;
if (_flagOne) {
// Paint to the foreground
buf = (byte *)_fg->getBasePtr(0, 0);
if (_flag2Byte) {
mask = 0xff;
} else {
mask = 0;
}
// TODO: Verify this is the right procedure. Couldn't find it on the
// disassembly, but it's required to work properly
_flagFirstFrame = true;
} else {
// Paint to the background
buf = (byte *)_bg->getBasePtr(0, 0);
}
// Read the palette
in->read(_palBuf, 3 * 256);
if (_flagSeven) {
_flagFive = true;
}
// Skip the frame when flag 5 is set, unless flag 1 is set
if (!_flagFive || _flagOne) {
byte colours[16];
for (uint16 j = 0; j < numYTiles; j++) {
for (uint16 i = 0; i < numXTiles; i++) { /* Tile number */
uint8 colour1 = in->readByte();
uint8 colour0 = in->readByte();
uint16 colourMap = in->readUint16LE();
expandColourMap(colours, colourMap, colour1, colour0);
decodeBlockStill(buf + j * TILE_SIZE * imageWidth + i * TILE_SIZE, colours, 640, mask);
}
}
// Apply the palette
if (_flagNine) {
// Flag 9 starts a fade in
fadeIn(_palBuf);
} else {
if (!_flagOne && !_flagSeven) {
// Actually apply the palette
setPalette(_palBuf);
}
}
if (!_flagOne) {
_vm->_graphicsMan->updateScreen(_bg);
}
/*
if (_flagSix) {
if (_flagOne) {
_vm->_graphicsMan->updateScreen(_fg);
} else {
_vm->_graphicsMan->updateScreen(_bg);
}
_flagSix = 0;
}
*/
} else {
// Skip the remaining data
debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Skipping still frame");
while (!in->eos()) {
in->readByte();
}
}
}
void VDXPlayer::expandColourMap(byte *out, uint16 colourMap, uint8 colour1, uint8 colour0) {
int flag = 1 << 15;
for (int i = 0; i < 16; i++) {
// Set the corresponding colour
out[i] = (colourMap & flag) ? colour1 : colour0;
// Update the flag to test the next colour
flag >>= 1;
}
}
void VDXPlayer::decodeBlockStill(byte *buf, byte *colours, uint16 imageWidth, uint8 mask) {
for (int y = 0; y < TILE_SIZE; y++) {
for (int x = 0; x < TILE_SIZE; x++) {
if (_flagOne) {
// 0xff pixels don't modify the buffer
if (*colours != 0xff) {
// Write the colour
*buf = *colours | mask;
// Note: if the mask is 0, it paints the image
// else, it paints the image's mask using 0xff
}
} else {
*buf = *colours;
}
// Point to the next colour
colours++;
// Point to the next pixel
buf++;
}
// Point to the start of the next line
buf += imageWidth - TILE_SIZE;
}
}
void VDXPlayer::decodeBlockDelta(uint32 offset, byte *colours, uint16 imageWidth) {
byte *fgBuf = (byte *)_fg->getBasePtr(0, 0) + offset;
//byte *bgBuf = (byte *)_bg->getBasePtr(0, 0) + offset;
byte *dest;
// TODO: Verify just the else block is required
//if (_flagOne) {
// Paint to the foreground
//dest = (byte *)_fg->getBasePtr(0, 0) + offset;
//} else {
dest = (byte *)_bg->getBasePtr(0, 0) + offset;
//}
int32 off = _origX + _origY * imageWidth;
for (int y = 0; y < TILE_SIZE; y++) {
for (int x = 0; x < TILE_SIZE; x++) {
if (_flagSeven) {
if (fgBuf[off] != 0xff) {
if (*colours == 0xff) {
dest[off] = fgBuf[off];
} else {
dest[off] = *colours;
}
}
} else {
// Paint directly
dest[off] = *colours;
}
colours++;
off++;
}
// Prepare the offset of the next line
off += imageWidth - TILE_SIZE;
}
}
void VDXPlayer::chunkSound(Common::ReadStream *in) {
if (!_audioStream) {
_audioStream = Audio::makeAppendableAudioStream(22050, Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE);
Audio::SoundHandle sound_handle;
::g_engine->_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
}
byte *data = new byte[60000];
int chunksize = in->read(data, 60000);
_audioStream->queueBuffer(data, chunksize);
}
void VDXPlayer::fadeIn(uint8 *targetpal) {
// Don't do anything if we're asked to skip palette changes
if (_flagSkipPalette)
return;
// TODO: Is it required? If so, move to an appropiate place
// Copy the foreground to the background
memcpy((byte *)_vm->_graphicsMan->_foreground.getBasePtr(0, 0), (byte *)_vm->_graphicsMan->_background.getBasePtr(0, 0), 640 * 320);
// Start a fadein
_vm->_graphicsMan->fadeIn(targetpal);
// Show the background
_vm->_graphicsMan->updateScreen(_bg);
}
void VDXPlayer::setPalette(uint8 *palette) {
if (_flagSkipPalette)
return;
uint8 palBuf[4 * 256];
debugC(7, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Setting palette");
for (int i = 0; i < 256; i++) {
palBuf[(i * 4) + 0] = palette[(i * 3) + 0];
palBuf[(i * 4) + 1] = palette[(i * 3) + 1];
palBuf[(i * 4) + 2] = palette[(i * 3) + 2];
palBuf[(i * 4) + 3] = 0;
}
_syst->setPalette(palBuf, 0, 256);
}
} // End of Groovie namespace

81
engines/groovie/vdx.h Normal file
View file

@ -0,0 +1,81 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef GROOVIE_VDX_H
#define GROOVIE_VDX_H
#include "groovie/player.h"
namespace Groovie {
class VDXPlayer : public VideoPlayer {
public:
VDXPlayer(GroovieEngine *vm);
~VDXPlayer();
void setOrigin(int16 x, int16 y);
protected:
uint16 loadInternal();
bool playFrameInternal();
private:
Graphics::Surface *_fg, *_bg;
uint8 _palBuf[3 * 256];
// Origin
int16 _origX, _origY;
// Video flags
bool _flagZero;
bool _flagOne;
bool _flagOnePrev;
byte _flag2Byte;
bool _flagThree;
bool _flagFour;
bool _flagFive;
bool _flagSix;
bool _flagSeven;
bool _flagEight;
bool _flagNine;
bool _flagSkipStill;
bool _flagSkipPalette;
bool _flagFirstFrame;
bool _flagTransparent;
bool _flagUpdateStill;
void getStill(Common::ReadStream *in);
void getDelta(Common::ReadStream *in);
void expandColourMap(byte *out, uint16 colourMap, uint8 colour1, uint8 colour0);
void decodeBlockStill(byte *buf, byte *colours, uint16 imageWidth, uint8 mask);
void decodeBlockDelta(uint32 offset, byte *colours, uint16 imageWidth);
void chunkSound(Common::ReadStream *in);
void setPalette(uint8 *palette);
void fadeIn(uint8 *palette);
};
} // End of Groovie namespace
#endif // GROOVIE_VDX_H