Merge pull request #463 from somaen/mergemyst3

Add a WIP Myst III Exile engine
This commit is contained in:
Paweł Kołodziejski 2012-01-10 00:32:52 -08:00
commit 09a532d5d0
56 changed files with 8323 additions and 36 deletions

View File

@ -92,9 +92,11 @@ int SdlEventSource::mapKey(SDLKey key, SDLMod mod, Uint16 unicode) {
return key;
}
void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y) {
void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y, int relx, int rely) {
event.mouse.x = x;
event.mouse.y = y;
event.relMouse.x = relx;
event.relMouse.y = rely;
if (_graphicsManager) {
_graphicsManager->notifyMousePos(Common::Point(x, y));
@ -517,7 +519,7 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) {
bool SdlEventSource::handleMouseMotion(SDL_Event &ev, Common::Event &event) {
event.type = Common::EVENT_MOUSEMOVE;
processMouseEvent(event, ev.motion.x, ev.motion.y);
processMouseEvent(event, ev.motion.x, ev.motion.y, ev.motion.xrel, ev.motion.yrel);
return true;
}

View File

@ -118,8 +118,10 @@ protected:
/**
* Assigns the mouse coords to the mouse event. Furthermore notify the
* graphics manager about the position change.
*
* The parameters relx and rely for relative mouse movement are Residual specific
*/
virtual void processMouseEvent(Common::Event &event, int x, int y);
virtual void processMouseEvent(Common::Event &event, int x, int y, int relx = 0, int rely = 0);
/**
* Remaps key events. This allows platforms to configure

View File

@ -88,6 +88,9 @@ public:
#if PLUGIN_ENABLED_STATIC(GRIM)
LINK_PLUGIN(GRIM)
#endif
#if PLUGIN_ENABLED_STATIC(MYST3)
LINK_PLUGIN(MYST3)
#endif
// Music plugins
// TODO: Use defines to disable or enable each MIDI driver as a

View File

@ -161,7 +161,7 @@ void SearchSet::addSubDirectoriesMatching(const FSNode &directory, String origPa
}
if (nextPattern.empty())
addDirectory(name, *i, priority);
addDirectory(i->getPath(), *i, priority);
else
addSubDirectoriesMatching(*i, nextPattern, ignoreCase, priority);
}

View File

@ -99,6 +99,13 @@ struct Event {
*/
Point mouse;
/**
* Mouse movement since the last mouse movement event.
*
* This field is Residual specific
*/
Common::Point relMouse;
Event() : type(EVENT_INVALID), synthetic(false) {}
};

1
configure vendored
View File

@ -78,6 +78,7 @@ add_engine() {
}
add_engine grim "Grim" yes
add_engine myst3 "Myst 3" yes
#

View File

@ -2,3 +2,8 @@ ifdef ENABLE_GRIM
DEFINES += -DENABLE_GRIM=$(ENABLE_GRIM)
MODULES += engines/grim
endif
ifdef ENABLE_MYST3
DEFINES += -DENABLE_MYST3=$(ENABLE_MYST3)
MODULES += engines/myst3
endif

115
engines/myst3/archive.cpp Normal file
View File

@ -0,0 +1,115 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/archive.h"
#include "common/debug.h"
#include "common/memstream.h"
namespace Myst3 {
void Archive::_decryptHeader(Common::SeekableReadStream &inStream, Common::WriteStream &outStream) {
static const uint32 addKey = 0x3C6EF35F;
static const uint32 multKey = 0x0019660D;
inStream.seek(0);
uint32 encryptedSize = inStream.readUint32LE();
uint32 decryptedSize = encryptedSize ^ addKey;
inStream.seek(0);
uint32 currentKey = 0;
for (uint i = 0; i < decryptedSize; i++) {
currentKey += addKey;
outStream.writeUint32LE(inStream.readUint32LE() ^ currentKey);
currentKey *= multKey;
}
}
void Archive::_readDirectory() {
Common::MemoryWriteStreamDynamic buf(DisposeAfterUse::YES);
_decryptHeader(_file, buf);
Common::MemoryReadStream directory(buf.getData(), buf.size());
directory.skip(sizeof(uint32));
while (directory.pos() + 4 < directory.size()) {
DirectoryEntry entry(this);
if (_multipleRoom)
entry.readFromStream(directory, 0);
else
entry.readFromStream(directory, _roomName);
_directory.push_back(entry);
}
}
void Archive::dumpDirectory() {
for (uint i = 0; i < _directory.size(); i++) {
_directory[i].dump();
}
}
void Archive::dumpToFiles() {
for (uint i = 0; i < _directory.size(); i++) {
_directory[i].dumpToFiles(_file);
}
}
Common::MemoryReadStream *Archive::dumpToMemory(uint32 offset, uint32 size) {
_file.seek(offset);
return static_cast<Common::MemoryReadStream *>(_file.readStream(size));
}
const DirectorySubEntry *Archive::getDescription(const char *room, uint16 index, uint16 face, DirectorySubEntry::ResourceType type) {
for (uint i = 0; i < _directory.size(); i++) {
if (_directory[i].getIndex() == index
&& !strcmp(_directory[i].getRoom(), room)) {
return _directory[i].getItemDescription(face, type);
}
}
return 0;
}
bool Archive::open(const char *fileName, const char *room) {
// Copy the room name if provided
// If the room name is not provided, it is assumed that
// we are opening a multi-room archive
_multipleRoom = room == 0;
if (!_multipleRoom)
Common::strlcpy(_roomName, room, sizeof(_roomName));
if (_file.open(fileName)) {
_readDirectory();
return true;
}
return false;
}
void Archive::close() {
_directory.clear();
_file.close();
}
} // end of namespace Myst3

55
engines/myst3/archive.h Normal file
View File

@ -0,0 +1,55 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef MYST3_ARCHIVE_H
#define MYST3_ARCHIVE_H
#include "engines/myst3/directoryentry.h"
#include "common/stream.h"
#include "common/array.h"
#include "common/file.h"
namespace Myst3 {
class Archive {
private:
bool _multipleRoom;
char _roomName[5];
Common::File _file;
Common::Array<DirectoryEntry> _directory;
void _decryptHeader(Common::SeekableReadStream &inStream, Common::WriteStream &outStream);
void _readDirectory();
public:
const DirectorySubEntry *getDescription(const char *room, uint16 index, uint16 face, DirectorySubEntry::ResourceType type);
Common::MemoryReadStream *dumpToMemory(uint32 offset, uint32 size);
void dumpDirectory();
void dumpToFiles();
bool open(const char *fileName, const char *room);
void close();
};
} // end of namespace Myst3
#endif

274
engines/myst3/console.cpp Normal file
View File

@ -0,0 +1,274 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/console.h"
#include "engines/myst3/database.h"
#include "engines/myst3/inventory.h"
#include "engines/myst3/script.h"
#include "engines/myst3/variables.h"
namespace Myst3 {
Console::Console(Myst3Engine *vm) : GUI::Debugger(), _vm(vm) {
DCmd_Register("infos", WRAP_METHOD(Console, Cmd_Infos));
DCmd_Register("lookAt", WRAP_METHOD(Console, Cmd_LookAt));
DCmd_Register("initScript", WRAP_METHOD(Console, Cmd_InitScript));
DCmd_Register("var", WRAP_METHOD(Console, Cmd_Var));
DCmd_Register("listNodes", WRAP_METHOD(Console, Cmd_ListNodes));
DCmd_Register("run", WRAP_METHOD(Console, Cmd_Run));
DCmd_Register("go", WRAP_METHOD(Console, Cmd_Go));
DCmd_Register("extract", WRAP_METHOD(Console, Cmd_Extract));
DCmd_Register("fillInventory", WRAP_METHOD(Console, Cmd_FillInventory));
}
Console::~Console() {
}
void Console::describeScript(const Common::Array<Opcode> &script) {
for(uint j = 0; j < script.size(); j++) {
DebugPrintf("%s", _vm->_scriptEngine->describeOpcode(script[j]).c_str());
}
}
bool Console::Cmd_Infos(int argc, const char **argv) {
uint16 nodeId = _vm->_vars->getLocationNode();
uint32 roomId = _vm->_vars->getLocationRoom();
if (argc >= 2) {
nodeId = atoi(argv[1]);
}
if (argc >= 3) {
roomId = _vm->_db->getRoomId(argv[2]);
if (roomId == 0) {
DebugPrintf("Unknown room name %s\n", argv[2]);
return true;
}
}
NodePtr nodeData = _vm->_db->getNodeData(nodeId, roomId);
if (!nodeData) {
DebugPrintf("No node with id %d\n", nodeId);
return true;
}
char roomName[8];
_vm->_db->getRoomName(roomName, roomId);
DebugPrintf("node: %s %d ", roomName, nodeId);
for (uint i = 0; i < nodeData->hotspots.size(); i++) {
DebugPrintf("\nhotspot %d > %s (%s)\n", i,
_vm->_vars->describeCondition(nodeData->hotspots[i].condition).c_str(),
_vm->_vars->evaluate(nodeData->hotspots[i].condition) ? "true" : "false");
for(uint j = 0; j < nodeData->hotspots[i].rects.size(); j++) {
PolarRect &rect = nodeData->hotspots[i].rects[j];
DebugPrintf(" rect > pitch: %d heading: %d height: %d width: %d\n",
rect.centerPitch, rect.centerHeading, rect.width, rect.height);
}
describeScript(nodeData->hotspots[i].script);
}
for (uint i = 0; i < nodeData->scripts.size(); i++) {
DebugPrintf("\nscript %d > %s (%s)\n", i,
_vm->_vars->describeCondition(nodeData->scripts[i].condition).c_str(),
_vm->_vars->evaluate(nodeData->scripts[i].condition) ? "true" : "false");
describeScript(nodeData->scripts[i].script);
}
return true;
}
bool Console::Cmd_LookAt(int argc, const char **argv) {
if (argc != 1 && argc != 3) {
DebugPrintf("Usage :\n");
DebugPrintf("lookAt pitch heading\n");
return true;
}
Common::Point lookAt = _vm->_scene->getMousePos();
DebugPrintf("pitch: %d heading: %d\n", lookAt.y, lookAt.x);
if (argc >= 3){
_vm->_scene->lookAt(atof(argv[1]), atof(argv[2]));
return false;
}
return true;
}
bool Console::Cmd_InitScript(int argc, const char **argv) {
describeScript(_vm->_db->getNodeInitScript());
return true;
}
bool Console::Cmd_Var(int argc, const char **argv) {
if (argc != 2 && argc != 3) {
DebugPrintf("Usage :\n");
DebugPrintf("var variable : Display var value\n");
DebugPrintf("var variable value : Change var value\n");
return true;
}
uint16 var = atoi(argv[1]);
uint32 value = _vm->_vars->get(var);
if (argc == 3) {
value = atoi(argv[2]);
_vm->_vars->set(var, value);
}
DebugPrintf("%s: %d\n", _vm->_vars->describeVar(var).c_str(), value);
return true;
}
bool Console::Cmd_ListNodes(int argc, const char **argv) {
uint32 roomID = 0;
if (argc == 2) {
roomID = _vm->_db->getRoomId(argv[1]);
if (roomID == 0) {
DebugPrintf("Unknown room name %s\n", argv[1]);
return true;
}
}
DebugPrintf("Nodes:\n");
Common::Array<uint16> list = _vm->_db->listRoomNodes(roomID);
for (uint i = 0; i < list.size(); i++) {
DebugPrintf("%d\n", list[i]);
}
return true;
}
bool Console::Cmd_Run(int argc, const char **argv) {
uint16 nodeId = _vm->_vars->getLocationNode();
uint32 roomId = 0;
if (argc >= 2) {
nodeId = atoi(argv[1]);
}
if (argc >= 3) {
roomId = _vm->_db->getRoomId(argv[2]);
if (roomId == 0) {
DebugPrintf("Unknown room name %s\n", argv[2]);
return true;
}
}
_vm->runScriptsFromNode(nodeId, roomId);
return false;
}
bool Console::Cmd_Go(int argc, const char **argv) {
if (argc != 3) {
DebugPrintf("Usage :\n");
DebugPrintf("go [room name] [node id] : Go to node\n");
return true;
}
uint32 roomID = _vm->_db->getRoomId(argv[1]);
uint16 nodeId = atoi(argv[2]);
if (roomID == 0) {
DebugPrintf("Unknown room name %s\n", argv[1]);
return true;
}
_vm->_vars->setLocationNextRoom(roomID);
_vm->_vars->setLocationNextNode(nodeId);
_vm->goToNode(0, 1);
return false;
}
bool Console::Cmd_Extract(int argc, const char **argv) {
if (argc != 5) {
DebugPrintf("Extract a file from the game's archives\n");
DebugPrintf("Usage :\n");
DebugPrintf("extract [room] [node id] [face number] [object type]\n");
return true;
}
// Room names are uppercase
Common::String room = Common::String(argv[1]);
room.toUppercase();
uint16 id = atoi(argv[2]);
uint16 face = atoi(argv[3]);
DirectorySubEntry::ResourceType type = (DirectorySubEntry::ResourceType) atoi(argv[4]);
const DirectorySubEntry *desc = _vm->getFileDescription(room.c_str(), id, face, type);
if (!desc) {
DebugPrintf("File with room %s, id %d, face %d and type %d does not exist\n", room.c_str(), id, face, type);
return true;
}
Common::MemoryReadStream *s = desc->getData();
Common::String filename = Common::String::format("node%s_%d_face%d.%d", room.c_str(), id, face, type);
Common::DumpFile f;
f.open(filename);
uint8 *buf = new uint8[s->size()];
s->read(buf, s->size());
f.write(buf, s->size());
delete[] buf;
f.close();
delete s;
DebugPrintf("File '%s' successfully written\n", filename.c_str());
return true;
}
bool Console::Cmd_FillInventory(int argc, const char **argv) {
_vm->_inventory->addAll();
return false;
}
} /* namespace Myst3 */

57
engines/myst3/console.h Normal file
View File

@ -0,0 +1,57 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef CONSOLE_H_
#define CONSOLE_H_
#include "gui/debugger.h"
#include "engines/myst3/myst3.h"
namespace Myst3 {
class Myst3Engine;
struct Opcode;
class Console : public GUI::Debugger {
public:
Console(Myst3Engine *vm);
virtual ~Console();
private:
Myst3Engine *_vm;
void describeScript(const Common::Array<Opcode> &script);
bool Cmd_Infos(int argc, const char **argv);
bool Cmd_LookAt(int argc, const char **argv);
bool Cmd_InitScript(int argc, const char **argv);
bool Cmd_Var(int argc, const char **argv);
bool Cmd_ListNodes(int argc, const char **argv);
bool Cmd_Run(int argc, const char **argv);
bool Cmd_Go(int argc, const char **argv);
bool Cmd_Extract(int argc, const char **argv);
bool Cmd_FillInventory(int argc, const char **argv);
};
} /* namespace Myst3 */
#endif /* CONSOLE_H_ */

191
engines/myst3/cursor.cpp Normal file
View File

@ -0,0 +1,191 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/cursor.h"
#include "engines/myst3/directorysubentry.h"
#include "engines/myst3/myst3.h"
#include "engines/myst3/scene.h"
#include "graphics/surface.h"
#include "graphics/imagedec.h"
namespace Myst3 {
struct CursorData {
uint32 nodeID;
uint16 hotspotX;
uint16 hotspotY;
Graphics::Surface *surface;
double transparency;
};
static CursorData availableCursors[13] = {
{ 1000, 8, 8, 0, 0.25 },
{ 1001, 8, 8, 0, 0.5 },
{ 1002, 8, 8, 0, 0.5 },
{ 1003, 1, 5, 0, 0.5 },
{ 1004, 14, 5, 0, 0.5 },
{ 1005, 16, 14, 0, 0.5 },
{ 1006, 16, 14, 0, 0.5 },
{ 1007, 8, 8, 0, 0.55 },
{ 1000, 8, 8, 0, 0.25 },
{ 1001, 8, 8, 0, 0.5 },
{ 1011, 16, 16, 0, 0.5 },
{ 1000, 6, 1, 0, 0.5 },
{ 0, 0, 0, 0, 0 }
};
Cursor::Cursor(Myst3Engine *vm) :
_vm(vm),
_position(320, 210),
_lockedAtCenter(false) {
// Load available cursors
loadAvailableCursors();
// Generate texture
generateTexture();
// Set default cursor
changeCursor(8);
}
void Cursor::loadAvailableCursors() {
// Load available cursors
for (uint i = 0; availableCursors[i].nodeID; i++) {
const DirectorySubEntry *cursorDesc = _vm->getFileDescription("GLOB", availableCursors[i].nodeID, 0, DirectorySubEntry::kCursor);
if (!cursorDesc)
error("Cursor %d does not exist", availableCursors[i].nodeID);
Common::MemoryReadStream *bmpStream = cursorDesc->getData();
availableCursors[i].surface = Graphics::ImageDecoder::loadFile(*bmpStream, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
// Apply the colorkey for transparency
for (uint u = 0; u < availableCursors[i].surface->w; u++) {
for (uint v = 0; v < availableCursors[i].surface->h; v++) {
uint32 *pixel = (uint32*)(availableCursors[i].surface->getBasePtr(u, v));
if (*pixel == 0xFF00FF00)
*pixel = 0x0000FF00;
}
}
delete bmpStream;
}
}
void Cursor::generateTexture() {
glGenTextures(1, &_textureId);
glBindTexture(GL_TEXTURE_2D, _textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _textureSize, _textureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
Cursor::~Cursor() {
// Free available cursors
for (uint i = 0; availableCursors[i].nodeID; i++) {
if (availableCursors[i].surface) {
availableCursors[i].surface->free();
delete availableCursors[i].surface;
availableCursors[i].surface = 0;
}
}
// Delete texture
glDeleteTextures(1, &_textureId);
}
void Cursor::changeCursor(uint32 index) {
assert(index >= 0 && index <= 12);
if (_currentCursorID != index) {
_currentCursorID = index;
uploadTexture();
}
}
void Cursor::lockPosition(bool lock) {
_lockedAtCenter = lock;
if (_lockedAtCenter) {
_position.x = 320;
_position.y = 210;
}
}
void Cursor::updatePosition(Common::Point &mouse) {
if (!_lockedAtCenter) {
_position += mouse;
_position.x = CLIP<int16>(_position.x, 0, Scene::_originalWidth);
_position.y = CLIP<int16>(_position.y, 0, Scene::_originalHeight);
}
}
void Cursor::uploadTexture() {
Graphics::Surface *bitmap = availableCursors[_currentCursorID].surface;
glBindTexture(GL_TEXTURE_2D, _textureId);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap->w, bitmap->h, GL_RGBA, GL_UNSIGNED_BYTE, bitmap->pixels);
}
void Cursor::draw()
{
CursorData &cursor = availableCursors[_currentCursorID];
// Size of the cursor
const float w = cursor.surface->w;
const float h = cursor.surface->h;
// Used fragment of texture
const float u = w / (float)(_textureSize);
const float v = h / (float)(_textureSize);
const float left = _position.x - cursor.hotspotX;
const float top = _position.y - cursor.hotspotY;
float transparency;
if (_lockedAtCenter)
transparency = cursor.transparency;
else
transparency = 1.0f;
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glColor4f(1.0f, 1.0f, 1.0f, transparency);
glDepthMask(GL_FALSE);
glBindTexture(GL_TEXTURE_2D, _textureId);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0, v); glVertex3f( left + 0, top + h, 1.0f);
glTexCoord2f(u, v); glVertex3f( left + w, top + h, 1.0f);
glTexCoord2f(0, 0); glVertex3f( left + 0, top + 0, 1.0f);
glTexCoord2f(u, 0); glVertex3f( left + w, top + 0, 1.0f);
glEnd();
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
}
} /* namespace Myst3 */

68
engines/myst3/cursor.h Normal file
View File

@ -0,0 +1,68 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef CURSOR_H_
#define CURSOR_H_
#ifdef SDL_BACKEND
#include <SDL_opengl.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#include "common/rect.h"
namespace Myst3 {
class Myst3Engine;
class Cursor {
public:
Cursor(Myst3Engine *vm);
virtual ~Cursor();
void changeCursor(uint32 index);
void lockPosition(bool lock);
Common::Point getPosition() { return _position; }
void updatePosition(Common::Point &mouse);
void draw();
private:
Myst3Engine *_vm;
uint32 _currentCursorID;
GLuint _textureId;
static const uint _textureSize = 32;
/** Position of the cursor */
Common::Point _position;
bool _lockedAtCenter;
void loadAvailableCursors();
void generateTexture();
void uploadTexture();
};
} /* namespace Myst3 */
#endif /* CURSOR_H_ */

394
engines/myst3/database.cpp Normal file
View File

@ -0,0 +1,394 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/database.h"
#include "common/file.h"
#include "common/debug.h"
#include "common/md5.h"
namespace Myst3 {
Database::Database(const Common::String &executable) :
_exePath(executable),
_currentRoomID(0),
_gameVersion(0),
_currentRoomData(0) {
// Game versions database
static GameVersion versions[] = {
{ "1.22 English", "8f21c22a4ca4f383ab29cbba4df0b2b5", 0x486108, 0x486040 },
{ "1.22 French", "554612b239ff2d9a3364fa38e3f32b45", 0x486108, 0x486040 },
{ "1.27 French", "00e062994ddf98e0d5cf4aa78e738f47", 0x486110, 0x486040 },
{ "1.27 English", "a9e992323fa5048f0947d9ebd44088ac", 0x486110, 0x486040 },
{ "1.27 Dutch", "e9111bbae979d9c9c536aaf3601bd46f", 0x486110, 0x486040 },
{ "1.27 German", "e3ce37f0bb93dfc4df73de88a8c15e1d", 0x486110, 0x486040 },
{ "1.27 Italian", "6e7bda56f3f8542ba936d7556256d5eb", 0x486110, 0x486040 },
{ "1.27 Spanish", "67cb6a606f123b327fac0d16f82b0adb", 0x486110, 0x486040 }
};
Common::File file;
file.open(_exePath);
// Check whether the game version is known
Common::String md5 = Common::computeStreamMD5AsString(file, 0);
for (uint i = 0; i < sizeof(versions) / sizeof(GameVersion); i++) {
if (md5.equals(versions[i].md5)) {
_gameVersion = &versions[i];
break;
}
}
if (_gameVersion != 0) {
debug("Initializing database from %s (%s)", _exePath.c_str(), _gameVersion->description);
} else {
error("Unknown game version: %s (md5: %s)", _exePath.c_str(), md5.c_str());
}
// Load the ages and rooms description
file.seek(_gameVersion->ageTableOffset - _baseOffset);
_ages = loadAges(file);
for (uint i = 0; i < _ages.size(); i++) {
file.seek(_ages[i].roomsOffset);
// Read the room offset table
Common::Array<uint32> roomsOffsets;
for (uint j = 0; j < _ages[i].roomCount; j++) {
uint32 offset = file.readUint32LE() - _baseOffset;
roomsOffsets.push_back(offset);
}
// Load the rooms
for (uint j = 0; j < roomsOffsets.size(); j++) {
file.seek(roomsOffsets[j]);
_ages[i].rooms.push_back(loadRoomDescription(file));
}
}
file.seek(_gameVersion->nodeInitScriptOffset - _baseOffset);
_nodeInitScript = loadOpcodes(file);
file.close();
preloadCommonRooms();
}
void Database::preloadCommonRooms() {
// XXXX, MENU, JRNL
static const uint32 commonRooms[3] = { 101, 901, 902 };
for (uint i = 0; i < 3; i++) {
RoomData *data = findRoomData(commonRooms[i]);
_roomNodesCache.setVal(commonRooms[i], loadRoomScripts(data));
}
}
Common::Array<uint16> Database::listRoomNodes(uint32 roomID, uint32 ageID) {
Common::Array<NodePtr> nodes;
Common::Array<uint16> list;
if (roomID == 0)
roomID = _currentRoomID;
if (_roomNodesCache.contains(roomID)) {
nodes = _roomNodesCache.getVal(roomID);
} else {
RoomData *data = findRoomData(roomID);
nodes = loadRoomScripts(data);
}
for (uint i = 0; i < nodes.size(); i++) {
list.push_back(nodes[i]->id);
}
return list;
}
NodePtr Database::getNodeData(uint16 nodeID, uint32 roomID, uint32 ageID) {
Common::Array<NodePtr> nodes;
if (roomID == 0)
roomID = _currentRoomID;
if (_roomNodesCache.contains(roomID)) {
nodes = _roomNodesCache.getVal(roomID);
} else {
RoomData *data = findRoomData(roomID);
nodes = loadRoomScripts(data);
}
for (uint i = 0; i < nodes.size(); i++) {
if (nodes[i]->id == nodeID)
return nodes[i];
}
return NodePtr();
}
RoomData *Database::findRoomData(const uint32 & roomID)
{
for (uint i = 0; i < _ages.size(); i++)
for (uint j = 0; j < _ages[i].rooms.size(); j++) {
if (_ages[i].rooms[j].id == roomID) {
return &_ages[i].rooms[j];
}
}
return 0;
}
Common::Array<NodePtr> Database::loadRoomScripts(RoomData *room) {
Common::Array<NodePtr> nodes;
Common::File file;
file.open(_exePath);
file.seek(room->scriptsOffset);
while (1) {
int16 id = file.readUint16LE();
// End of list
if (id == 0)
break;
if (id <= -10)
error("Unimplemented node list command");
if (id > 0) {
// Normal node
NodePtr node = NodePtr(new NodeData());
node->id = id;
node->scripts = loadCondScripts(file);
node->hotspots = loadHotspots(file);
nodes.push_back(node);
} else {
// Several nodes sharing the same scripts
Common::Array<int16> nodeIds;
for (int i = 0; i < -id; i++) {
nodeIds.push_back(file.readUint16LE());
}
Common::Array<CondScript> scripts = loadCondScripts(file);
Common::Array<HotSpot> hotspots = loadHotspots(file);
for (int i = 0; i < -id; i++) {
NodePtr node = NodePtr(new NodeData());
node->id = nodeIds[i];
node->scripts = scripts;
node->hotspots = hotspots;
nodes.push_back(node);
}
}
}
file.close();
return nodes;
}
void Database::setCurrentRoom(const uint32 roomID) {
if (roomID == _currentRoomID)
return;
_currentRoomData = findRoomData(roomID);
if (!_currentRoomData || !_currentRoomData->scriptsOffset)
return;
// Remove old room from cache and add the new one
_roomNodesCache.erase(_currentRoomID);
_roomNodesCache.setVal(roomID, loadRoomScripts(_currentRoomData));
_currentRoomID = roomID;
}
Common::Array<CondScript> Database::loadCondScripts(Common::ReadStream &s) {
Common::Array<CondScript> scripts;
while (1) {
CondScript script = loadCondScript(s);
if (!script.condition)
break;
scripts.push_back(script);
}
return scripts;
}
Common::Array<HotSpot> Database::loadHotspots(Common::ReadStream &s) {
Common::Array<HotSpot> scripts;
while (1) {
HotSpot hotspot = loadHotspot(s);
if (!hotspot.condition)
break;
scripts.push_back(hotspot);
}
return scripts;
}
Common::Array<Opcode> Database::loadOpcodes(Common::ReadStream &s)
{
Common::Array<Opcode> script;
while(1){
Opcode opcode;
opcode.op = s.readByte();
uint8 count = s.readByte();
if(count == 0 && opcode.op == 0)
break;
for(int i = 0;i < count;i++){
uint16 value = s.readUint16LE();
opcode.args.push_back(value);
}
script.push_back(opcode);
}
return script;
}
CondScript Database::loadCondScript(Common::ReadStream &s)
{
CondScript script;
script.condition = s.readUint16LE();
if(!script.condition)
return script;
script.script = loadOpcodes(s);
return script;
}
HotSpot Database::loadHotspot(Common::ReadStream &s) {
HotSpot hotspot;
hotspot.condition = s.readUint16LE();
if (hotspot.condition == 0)
return hotspot;
if (hotspot.condition != -1) {
hotspot.rects = loadRects(s);
hotspot.cursor = s.readUint16LE();
}
hotspot.script = loadOpcodes(s);
return hotspot;
}
Common::Array<PolarRect> Database::loadRects(Common::ReadStream &s) {
Common::Array<PolarRect> rects;
bool lastRect = false;
do {
PolarRect rect;
rect.centerPitch = s.readUint16LE();
rect.centerHeading = s.readUint16LE();
rect.width = s.readUint16LE();
rect.height = s.readUint16LE();
if (rect.width < 0) {
rect.width = -rect.width;
} else {
lastRect = true;
}
rects.push_back(rect);
} while (!lastRect);
return rects;
}
Common::Array<AgeData> Database::loadAges(Common::ReadStream &s)
{
Common::Array<AgeData> ages;
for (uint i = 0; i < 10; i++) {
AgeData age;
age.id = s.readUint32LE();
age.disk = s.readUint32LE();
age.roomCount = s.readUint32LE();
age.roomsOffset = s.readUint32LE() - _baseOffset;
age.ageUnk1 = s.readUint32LE();
ages.push_back(age);
}
return ages;
}
RoomData Database::loadRoomDescription(Common::ReadStream &s) {
RoomData room;
room.id = s.readUint32LE();
s.read(&room.name, 8);
room.scriptsOffset = s.readUint32LE();
room.ambSoundsOffset = s.readUint32LE();
room.unkOffset = s.readUint32LE();
room.roomUnk4 = s.readUint32LE();
room.roomUnk5 = s.readUint32LE();
if (room.scriptsOffset != 0)
room.scriptsOffset -= _baseOffset;
if (room.ambSoundsOffset != 0)
room.ambSoundsOffset -= _baseOffset;
if (room.unkOffset != 0)
room.unkOffset -= _baseOffset;
return room;
}
void Database::getRoomName(char name[8], uint32 roomID) {
if (roomID != 0 && roomID != _currentRoomID) {
RoomData *data = findRoomData(roomID);
memcpy(&name[0], &data->name, 8);
} else if (_currentRoomData) {
memcpy(&name[0], &_currentRoomData->name, 8);
}
}
uint32 Database::getRoomId(const char *name) {
for (uint i = 0; i < _ages.size(); i++)
for (uint j = 0; j < _ages[i].rooms.size(); j++) {
if (!scumm_stricmp(_ages[i].rooms[j].name, name)) {
return _ages[i].rooms[j].id;
}
}
return 0;
}
} /* namespace Myst3 */

142
engines/myst3/database.h Normal file
View File

@ -0,0 +1,142 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef DATABASE_H_
#define DATABASE_H_
#include "common/scummsys.h"
#include "engines/myst3/hotspot.h"
#include "common/str.h"
#include "common/ptr.h"
#include "common/array.h"
#include "common/hashmap.h"
#include "common/stream.h"
namespace Myst3 {
struct NodeData
{
int16 id;
Common::Array<CondScript> scripts;
Common::Array<HotSpot> hotspots;
};
// Nodes are using ref counting pointers since they can be
// deleted by a script they own
typedef Common::SharedPtr<NodeData> NodePtr;
struct RoomData
{
uint32 id;
char name[8];
uint32 scriptsOffset;
uint32 ambSoundsOffset;
uint32 unkOffset;
uint32 roomUnk4;
uint32 roomUnk5;
};
struct AgeData
{
uint32 id;
uint32 disk;
uint32 roomCount;
uint32 roomsOffset;
uint32 ageUnk1;
Common::Array<RoomData> rooms;
};
class Database
{
public:
/**
* Initialize the database from an executable file
*/
Database(const Common::String & executable);
/**
* Loads a room's nodes into the database
*/
void setCurrentRoom(uint32 roomID);
/**
* Returns a node's hotspots and scripts from the currently loaded room
*/
NodePtr getNodeData(uint16 nodeID, uint32 roomID = 0, uint32 ageID = 0);
/**
* Returns the generic node init script
*/
const Common::Array<Opcode>& getNodeInitScript() { return _nodeInitScript; }
/**
* Returns the name of the currently loaded room
*/
void getRoomName(char name[8], uint32 roomID = 0);
/**
* Returns the id of a room from its name
*/
uint32 getRoomId(const char *name);
/**
* Returns the list of the nodes of a room
*/
Common::Array<uint16> listRoomNodes(uint32 roomID = 0, uint32 ageID = 0);
private:
struct GameVersion {
const char *description;
const char *md5;
const uint32 ageTableOffset;
const uint32 nodeInitScriptOffset;
};
static const uint32 _baseOffset = 0x400000;
Common::String _exePath;
GameVersion *_gameVersion;
Common::Array<AgeData> _ages;
uint32 _currentRoomID;
RoomData *_currentRoomData;
Common::HashMap< uint16, Common::Array<NodePtr> > _roomNodesCache;
Common::Array<Opcode> _nodeInitScript;
RoomData *findRoomData(const uint32 &roomID);
Common::Array<NodePtr> loadRoomScripts(RoomData *room);
void preloadCommonRooms();
Common::Array<AgeData> loadAges(Common::ReadStream &s);
RoomData loadRoomDescription(Common::ReadStream &s);
Common::Array<CondScript> loadCondScripts(Common::ReadStream & s);
Common::Array<Opcode> loadOpcodes(Common::ReadStream & s);
Common::Array<HotSpot> loadHotspots(Common::ReadStream & s);
Common::Array<PolarRect> loadRects(Common::ReadStream & s);
CondScript loadCondScript(Common::ReadStream & s);
HotSpot loadHotspot(Common::ReadStream & s);
};
} /* namespace Myst3 */
#endif /* DATABASE_H_ */

View File

@ -0,0 +1,97 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/advancedDetector.h"
#include "engines/myst3/myst3.h"
namespace Myst3 {
struct Myst3GameDescription {
ADGameDescription desc;
int flags;
};
static const PlainGameDescriptor myst3Games[] = {
{"myst3", "Myst III Exile"},
{0, 0}
};
static const char *directoryGlobs[] = {
"M3Data",
0
};
static const Myst3GameDescription gameDescriptions[] = {
{
// Myst 3 All PC / Mac Versions
{
"myst3",
0,
AD_ENTRY1s("RSRC.m3r", "a2c8ed69800f60bf5667e5c76a88e481", 1223862),
Common::UNK_LANG,
Common::kPlatformUnknown,
ADGF_NO_FLAGS,
GUIO_NONE
},
0,
},
{ AD_TABLE_END_MARKER, 0 }
};
class Myst3MetaEngine : public AdvancedMetaEngine {
public:
Myst3MetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(Myst3GameDescription), myst3Games) {
_singleid = "myst3";
_guioptions = GUIO_NOMIDI;
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
virtual const char *getName() const {
return "Myst III Engine";
}
virtual const char *getOriginalCopyright() const {
return "Myst III Game (C) Presto Studios";
}
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
};
bool Myst3MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
const Myst3GameDescription *gd = (const Myst3GameDescription *)desc;
if (gd) {
*engine = new Myst3Engine(syst, gd->flags);
}
return gd != 0;
}
} // End of namespace Myst3
#if PLUGIN_ENABLED_DYNAMIC(MYST3)
REGISTER_PLUGIN_DYNAMIC(MYST3, PLUGIN_TYPE_ENGINE, Myst3::Myst3MetaEngine);
#else
REGISTER_PLUGIN_STATIC(MYST3, PLUGIN_TYPE_ENGINE, Myst3::Myst3MetaEngine);
#endif

View File

@ -0,0 +1,81 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/directoryentry.h"
#include "common/debug.h"
namespace Myst3 {
DirectoryEntry::DirectoryEntry(Archive *archive) :
_archive(archive) {
memset(_roomName, 0, sizeof(_roomName));
}
void DirectoryEntry::readFromStream(Common::SeekableReadStream &inStream, const char *room) {
if (room == 0)
inStream.read(_roomName, 4);
else
Common::strlcpy(_roomName, room, sizeof(_roomName));
_index = inStream.readUint16LE();
_unk = inStream.readByte();
byte subItemCount = inStream.readByte();
// End of directory marker ?
if (_unk > 2) {
subItemCount = 0;
}
_subentries.clear();
for (uint i = 0; i < subItemCount ; i++) {
DirectorySubEntry subEntry(_archive);
subEntry.readFromStream(inStream);
_subentries.push_back(subEntry);
}
}
void DirectoryEntry::dump() {
debug("index : %d unk: %d subitems : %d", _index, _unk, _subentries.size());
for (uint i = 0; i < _subentries.size(); i++) {
_subentries[i].dump();
}
}
void DirectoryEntry::dumpToFiles(Common::SeekableReadStream &inStream) {
for (uint i = 0; i < _subentries.size(); i++) {
_subentries[i].dumpToFile(inStream, _index);
}
}
DirectorySubEntry *DirectoryEntry::getItemDescription(uint16 face, DirectorySubEntry::ResourceType type) {
for (uint i = 0; i < _subentries.size(); i++) {
if (_subentries[i].getFace() == face
&& _subentries[i].getType() == type) {
return &_subentries[i];
}
}
return 0;
}
} // end of namespace Myst3

View File

@ -0,0 +1,55 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef MYST3_DIRECTORYENTRY_H
#define MYST3_DIRECTORYENTRY_H
#include "engines/myst3/directorysubentry.h"
#include "common/stream.h"
#include "common/array.h"
namespace Myst3 {
class DirectoryEntry {
private:
char _roomName[5];
uint16 _index;
uint8 _unk;
Common::Array<DirectorySubEntry> _subentries;
Archive *_archive;
public:
DirectoryEntry() {}
DirectoryEntry(Archive *archive);
void readFromStream(Common::SeekableReadStream &inStream, const char *room);
void dump();
void dumpToFiles(Common::SeekableReadStream &inStream);
DirectorySubEntry *getItemDescription(uint16 face, DirectorySubEntry::ResourceType type);
uint16 getIndex() { return _index; }
const char *getRoom() { return _roomName; }
};
} // end of namespace Myst3
#endif // MYST3_DIRECTORYENTRY_H

View File

@ -0,0 +1,132 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/archive.h"
#include "engines/myst3/directorysubentry.h"
#include "common/str.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/memstream.h"
namespace Myst3 {
DirectorySubEntry::DirectorySubEntry(Archive *archive) :
_archive(archive) {
for (uint i = 0; i < 20; i++) {
_miscData[i] = 0;
}
}
void DirectorySubEntry::readFromStream(Common::SeekableReadStream &inStream) {
_offset = inStream.readUint32LE();
_size = inStream.readUint32LE();
_metadataSize = inStream.readUint16LE();
_face = inStream.readByte();
_type = static_cast<ResourceType>(inStream.readByte());
if (_metadataSize == 0) return;
if (_metadataSize == 2) {
_spotItemData.u = inStream.readUint32LE();
_spotItemData.v = inStream.readUint32LE();
} else if (_metadataSize == 10) {
_videoData.v1.setValue(0, inStream.readSint32LE() * 0.000001f);
_videoData.v1.setValue(1, inStream.readSint32LE() * 0.000001f);
_videoData.v1.setValue(2, inStream.readSint32LE() * 0.000001f);
_videoData.v2.setValue(0, inStream.readSint32LE() * 0.000001f);
_videoData.v2.setValue(1, inStream.readSint32LE() * 0.000001f);
_videoData.v2.setValue(2, inStream.readSint32LE() * 0.000001f);
_videoData.u = inStream.readSint32LE();
_videoData.v = inStream.readSint32LE();
_videoData.width = inStream.readSint32LE();
_videoData.height = inStream.readSint32LE();
} else {
if (_metadataSize > 20) {
warning("Too much metadata, skipping");
return;
}
for (uint i = 0; i < _metadataSize; i++)
_miscData[i] = inStream.readUint32LE();
}
}
void DirectorySubEntry::dump() {
debug("offset : %x size: %d padding : %d face : %d type : %d", _offset, _size, _metadataSize, _face, _type);
}
void DirectorySubEntry::dumpToFile(Common::SeekableReadStream &inStream, uint16 index) {
char fileName[255];
switch (_type) {
case kCubeFace:
case kSpotItem:
sprintf(fileName, "dump/%d-%d.jpg", index, _face);
break;
case kFaceMask:
sprintf(fileName, "dump/%d-%d.mask", index, _face);
break;
case kMovie:
sprintf(fileName, "dump/%d.bik", index);
break;
default:
sprintf(fileName, "dump/%d-%d.%d", index, _face, _type);
break;
}
debug("Extracted %s", fileName);
Common::DumpFile outFile;
outFile.open(fileName);
inStream.seek(_offset);
uint8 *buf = new uint8[_size];
inStream.read(buf, _size);
outFile.write(buf, _size);
delete[] buf;
outFile.close();
}
Common::MemoryReadStream *DirectorySubEntry::getData() const {
return _archive->dumpToMemory(_offset, _size);
}
uint32 DirectorySubEntry::getMiscData(uint index) const {
assert(index < 23);
if (index == 0)
return _offset;
else if (index == 1)
return _size;
else
return _miscData[index - 2];
}
} // end of namespace Myst3

View File

@ -0,0 +1,95 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef MYST3_DIRECTORYSUBENTRY_H
#define MYST3_DIRECTORYSUBENTRY_H
#include "common/memstream.h"
#include "math/vector3d.h"
namespace Myst3 {
struct SpotItemData {
uint32 u;
uint32 v;
};
struct VideoData {
Math::Vector3d v1;
Math::Vector3d v2;
int32 u;
int32 v;
int32 width;
int32 height;
};
typedef uint32 MiscData[20];
class Archive;
class DirectorySubEntry {
public:
enum ResourceType {
kCubeFace = 0,
kFaceMask = 1,
kSpotItem = 5,
kFrame = 6,
kCursor = 7,
kMovie = 8,
kStillMovie = 10,
kMetadata = 13,
kMenuSpotItem = 69,
kMenuFrame = 70,
kImagerMovie = 72
};
DirectorySubEntry() {}
DirectorySubEntry(Archive *archive);
void readFromStream(Common::SeekableReadStream &inStream);
void dump();
void dumpToFile(Common::SeekableReadStream &inStream, uint16 index);
Common::MemoryReadStream *getData() const;
uint16 getFace() const { return _face; }
ResourceType getType() const { return _type; }
const SpotItemData &getSpotItemData() const { return _spotItemData; }
const VideoData &getVideoData() const { return _videoData; }
uint32 getMiscData(uint index) const;
private:
uint32 _offset;
uint32 _size;
uint16 _metadataSize;
byte _face;
ResourceType _type;
// Metadata
SpotItemData _spotItemData;
VideoData _videoData;
MiscData _miscData;
Archive *_archive;
};
} // end of namespace Myst3
#endif // MYST3_DIRECTORYSUBENTRY_H

36
engines/myst3/gfx.h Normal file
View File

@ -0,0 +1,36 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef GFX_H_
#define GFX_H_
namespace Myst3 {
class Drawable {
public:
virtual void draw() = 0;
virtual ~Drawable() {};
};
} // end of namespace Myst3
#endif /* GFX_H_ */

58
engines/myst3/hotspot.cpp Normal file
View File

@ -0,0 +1,58 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/hotspot.h"
namespace Myst3 {
bool HotSpot::isPointInRectsCube(const Common::Point &p)
{
for(uint j = 0;j < rects.size();j++){
Common::Rect rect = Common::Rect(
rects[j].centerHeading - rects[j].width / 2,
rects[j].centerPitch - rects[j].height / 2,
rects[j].centerHeading + rects[j].width / 2,
rects[j].centerPitch + rects[j].height / 2);
if(rect.contains(p)){
return true;
}
}
return false;
}
bool HotSpot::isPointInRectsFrame(const Common::Point &p)
{
for(uint j = 0;j < rects.size();j++){
Common::Rect rect = Common::Rect(rects[j].width,
rects[j].height);
rect.translate(rects[j].centerPitch,
rects[j].centerHeading);
if(rect.contains(p)){
return true;
}
}
return false;
}
} /* namespace Myst3 */

61
engines/myst3/hotspot.h Normal file
View File

@ -0,0 +1,61 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef HOTSPOT_H_
#define HOTSPOT_H_
#include "common/rect.h"
#include "common/array.h"
namespace Myst3 {
struct Opcode {
uint8 op;
Common::Array<uint16> args;
};
struct CondScript {
uint16 condition;
Common::Array<Opcode> script;
};
struct PolarRect {
int16 centerPitch;
int16 centerHeading;
int16 height;
int16 width;
};
class HotSpot {
public:
int16 condition;
Common::Array<PolarRect> rects;
int16 cursor;
Common::Array<Opcode> script;
bool isPointInRectsCube(const Common::Point &p);
bool isPointInRectsFrame(const Common::Point & p);
};
} /* namespace Myst3 */
#endif /* HOTSPOT_H_ */

279
engines/myst3/inventory.cpp Normal file
View File

@ -0,0 +1,279 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/inventory.h"
#include "engines/myst3/cursor.h"
#include "engines/myst3/variables.h"
namespace Myst3 {
const Inventory::ItemData Inventory::_availableItems[8] = {
{ 0, 41, 47, 481 },
{ 41, 38, 50, 480 },
{ 79, 38, 49, 279 },
{ 117, 34, 48, 277 },
{ 151, 35, 44, 345 },
{ 186, 35, 44, 398 },
{ 221, 35, 44, 447 },
{ 0, 0, 0, 0 }
};
Inventory::Inventory(Myst3Engine *vm) :
_vm(vm) {
initializeTexture();
}
Inventory::~Inventory() {
glDeleteTextures(1, &_textureId);
}
void Inventory::initializeTexture() {
Graphics::Surface *s = _vm->loadTexture(1204);
glGenTextures(1, &_textureId);
glBindTexture(GL_TEXTURE_2D, _textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, s->w, s->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, s->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
s->free();
delete s;
}
void Inventory::draw() {
Common::Point mouse = _vm->_cursor->getPosition();
for (ItemList::const_iterator it = _inventory.begin(); it != _inventory.end(); it++) {
const ItemData &item = getData(it->var);
Common::Rect textureRect = Common::Rect(item.textureWidth,
item.textureHeight);
textureRect.translate(item.textureX, 0);
bool itemHighlighted = it->rect.contains(mouse)
|| (_vm->_vars->get(it->var) == 2);
drawItem(it->rect, textureRect, itemHighlighted);
}
}
void Inventory::drawItem(const Common::Rect &screenRect, const Common::Rect &textureRect, bool hovered) {
// Used fragment of texture
const float tleft = textureRect.left / (float)(256);
const float twidth = textureRect.width() / (float)(256);
float ttop = textureRect.top / (float)(128);
const float theight = textureRect.height() / (float)(128);
const float left = screenRect.left;
const float top = screenRect.top;
const float w = screenRect.width();
const float h = screenRect.height();
if (hovered)
ttop += 64.0 / 128.0;
glEnable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 1.0f);
glDepthMask(GL_FALSE);
glBindTexture(GL_TEXTURE_2D, _textureId);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(tleft, ttop + theight);
glVertex3f( left + 0, top + h, 1.0f);
glTexCoord2f(tleft + twidth, ttop + theight);
glVertex3f( left + w, top + h, 1.0f);
glTexCoord2f(tleft, ttop);
glVertex3f( left + 0, top + 0, 1.0f);
glTexCoord2f(tleft + twidth, ttop);
glVertex3f( left + w, top + 0, 1.0f);
glEnd();
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
}
void Inventory::reset() {
_inventory.clear();
reflow();
}
void Inventory::addItem(uint16 var, bool atEnd) {
// Only add objects once to the inventory
if (!hasItem(var)) {
_vm->_vars->set(var, 1);
InventoryItem i;
i.var = var;
if (atEnd) {
_inventory.push_back(i);
} else {
_inventory.push_front(i);
}
reflow();
}
}
void Inventory::removeItem(uint16 var) {
_vm->_vars->set(var, 0);
for (ItemList::iterator it = _inventory.begin(); it != _inventory.end(); it++) {
if (it->var == var) {
_inventory.erase(it);
break;
}
}
reflow();
}
void Inventory::addAll() {
for (uint i = 0; _availableItems[i].var; i++)
addItem(_availableItems[i].var, true);
}
bool Inventory::hasItem(uint16 var) {
for (ItemList::iterator it = _inventory.begin(); it != _inventory.end(); it++) {
if (it->var == var)
return true;
}
return false;
}
const Inventory::ItemData &Inventory::getData(uint16 var) {
for (uint i = 0; _availableItems[i].var; i++) {
if (_availableItems[i].var == var)
return _availableItems[i];
}
return _availableItems[7];
}
void Inventory::reflow() {
uint16 itemCount = 0;
uint16 totalWidth = 0;
for (uint i = 0; _availableItems[i].var; i++) {
if (hasItem(_availableItems[i].var)) {
totalWidth += _availableItems[i].textureWidth;
itemCount++;
}
}
if (itemCount >= 2)
totalWidth += 9 * (itemCount - 1);
uint16 left = (Scene::_originalWidth - totalWidth) / 2;
for (ItemList::iterator it = _inventory.begin(); it != _inventory.end(); it++) {
const ItemData &item = getData(it->var);
uint16 top = Scene::_topBorderHeight + Scene::_frameHeight
+ (Scene::_bottomBorderHeight - item.textureHeight) / 2;
it->rect = Common::Rect(item.textureWidth,
item.textureHeight);
it->rect.translate(left, top);
left += item.textureWidth;
if (itemCount >= 2)
left += 9;
}
}
uint16 Inventory::hoveredItem() {
Common::Point mouse = _vm->_cursor->getPosition();
for (ItemList::const_iterator it = _inventory.begin(); it != _inventory.end(); it++) {
if(it->rect.contains(mouse))
return it->var;
}
return 0;
}
void Inventory::useItem(uint16 var) {
switch (var) {
case 277: // Atrus
closeAllBooks();
_vm->_vars->setJournalAtrusState(2);
openBook(9, 902, 100);
break;
case 279: // Saavedro
closeAllBooks();
_vm->_vars->setJournalSaavedroState(2);
openBook(9, 902, 200);
break;
case 480: // Tomahna
closeAllBooks();
_vm->_vars->setBookStateTomahna(2);
openBook(8, 801, 220);
break;
case 481: // Releeshahn
closeAllBooks();
_vm->_vars->setBookStateReleeshahn(2);
openBook(9, 902, 300);
break;
default:
debug("Used inventory item %d which is not implemented", var);
}
}
void Inventory::closeAllBooks() {
if (_vm->_vars->getJournalAtrusState())
_vm->_vars->setJournalAtrusState(1);
if (_vm->_vars->getJournalSaavedroState())
_vm->_vars->setJournalSaavedroState(1);
if (_vm->_vars->getBookStateTomahna())
_vm->_vars->setBookStateTomahna(1);
if (_vm->_vars->getBookStateReleeshahn())
_vm->_vars->setBookStateReleeshahn(1);
}
void Inventory::openBook(uint16 age, uint16 room, uint16 node) {
if (!_vm->_vars->getBookSavedNode()) {
_vm->_vars->setBookSavedAge(_vm->_vars->getLocationAge());
_vm->_vars->setBookSavedRoom(_vm->_vars->getLocationRoom());
_vm->_vars->setBookSavedNode(_vm->_vars->getLocationNode());
}
_vm->_vars->setLocationNextAge(age);
_vm->_vars->setLocationNextRoom(room);
_vm->goToNode(node, 1);
}
void Inventory::addSaavedroChapter(uint16 var) {
_vm->_vars->set(var, 1);
_vm->_vars->setJournalSaavedroState(2);
_vm->_vars->setJournalSaavedroChapter(var - 285);
_vm->_vars->setJournalSaavedroPageInChapter(0);
openBook(9, 902, 200);
}
} /* namespace Myst3 */

89
engines/myst3/inventory.h Normal file
View File

@ -0,0 +1,89 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef INVENTORY_H_
#define INVENTORY_H_
#ifdef SDL_BACKEND
#include <SDL_opengl.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#include "common/list.h"
#include "common/rect.h"
namespace Myst3 {
class Myst3Engine;
class Inventory {
public:
Inventory(Myst3Engine *vm);
virtual ~Inventory();
void addItem(uint16 var, bool atEnd);
void addSaavedroChapter(uint16 var);
void addAll();
void removeItem(uint16 var);
void reset();
uint16 hoveredItem();
void useItem(uint16 var);
void draw();
private:
struct InventoryItem {
uint16 var;
Common::Rect rect;
};
typedef Common::List<InventoryItem> ItemList;
struct ItemData {
uint16 textureX;
uint16 textureWidth;
uint16 textureHeight;
uint16 var;
};
static const ItemData _availableItems[8];
const ItemData &getData(uint16 var);
Myst3Engine *_vm;
GLuint _textureId;
ItemList _inventory;
void initializeTexture();
bool hasItem(uint16 var);
void drawItem(const Common::Rect &screenRect, const Common::Rect &textureRect, bool hovered);
void reflow();
void openBook(uint16 age, uint16 room, uint16 node);
void closeAllBooks();
};
} /* namespace Myst3 */
#endif /* INVENTORY_H_ */

29
engines/myst3/module.mk Normal file
View File

@ -0,0 +1,29 @@
MODULE := engines/myst3
MODULE_OBJS := \
archive.o \
console.o \
cursor.o \
database.o \
detection.o \
directoryentry.o \
directorysubentry.o \
hotspot.o \
inventory.o \
movie.o \
myst3.o \
node.o \
nodecube.o \
nodeframe.o \
puzzles.o \
scene.o \
script.o \
variables.o
# This module can be built as a plugin
ifeq ($(ENABLE_MYST3), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk

292
engines/myst3/movie.cpp Normal file
View File

@ -0,0 +1,292 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/movie.h"
#include "engines/myst3/myst3.h"
#include "engines/myst3/variables.h"
namespace Myst3 {
Movie::Movie(Myst3Engine *vm, uint16 id) :
_vm(vm),
_startFrame(0),
_endFrame(0) {
const DirectorySubEntry *binkDesc = _vm->getFileDescription(0, id, 0, DirectorySubEntry::kMovie);
if (!binkDesc)
binkDesc = _vm->getFileDescription(0, id, 0, DirectorySubEntry::kStillMovie);
if (!binkDesc)
binkDesc = _vm->getFileDescription(0, id, 0, DirectorySubEntry::kImagerMovie);
if (!binkDesc)
error("Movie %d does not exist", id);
loadPosition(binkDesc->getVideoData());
initTexture();
Common::MemoryReadStream *binkStream = binkDesc->getData();
_bink.loadStream(binkStream, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
}
void Movie::loadPosition(const VideoData &videoData) {
static const float scale = 50.0f;
Math::Vector3d planeDirection = videoData.v1;
planeDirection.normalize();
Math::Vector3d u;
u.set(planeDirection.z(), 0.0f, -planeDirection.x());
u.normalize();
Math::Vector3d v = Math::Vector3d::crossProduct(planeDirection, u);
v.normalize();
Math::Vector3d planeOrigin = planeDirection * scale;
float left = (videoData.u - 320) * 0.003125f;
float right = (videoData.u + videoData.width - 320) * 0.003125f;
float top = (320 - videoData.v) * 0.003125f;
float bottom = (320 - videoData.v - videoData.height) * 0.003125f;
Math::Vector3d vLeft = scale * left * u;
Math::Vector3d vRight = scale * right * u;
Math::Vector3d vTop = scale * top * v;
Math::Vector3d vBottom = scale * bottom * v;
_pTopLeft = planeOrigin + vTop + vLeft;
_pBottomLeft = planeOrigin + vBottom + vLeft;
_pBottomRight = planeOrigin + vBottom + vRight;
_pTopRight = planeOrigin + vTop + vRight;
}
void Movie::initTexture() {
glGenTextures(1, &_texture);
glBindTexture(GL_TEXTURE_2D, _texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _movieTextureSize, _movieTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
void Movie::draw() {
const float w = _bink.getWidth() / (float)(_movieTextureSize);
const float h = _bink.getHeight() / (float)(_movieTextureSize);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, _texture);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0, 0);
glVertex3f(-_pTopLeft.x(), _pTopLeft.y(), _pTopLeft.z());
glTexCoord2f(0, h);
glVertex3f(-_pBottomLeft.x(), _pBottomLeft.y(), _pBottomLeft.z());
glTexCoord2f(w, 0);
glVertex3f(-_pTopRight.x(), _pTopRight.y(), _pTopRight.z());
glTexCoord2f(w, h);
glVertex3f(-_pBottomRight.x(), _pBottomRight.y(), _pBottomRight.z());
glEnd();
glDisable(GL_BLEND);
}
void Movie::drawNextFrameToTexture() {
const Graphics::Surface *frame = _bink.decodeNextFrame();
glBindTexture(GL_TEXTURE_2D, _texture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->w, frame->h, GL_RGBA, GL_UNSIGNED_BYTE, frame->pixels);
}
Movie::~Movie() {
glDeleteTextures(1, &_texture);
}
ScriptedMovie::ScriptedMovie(Myst3Engine *vm, uint16 id) :
Movie(vm, id),
_condition(0),
_conditionBit(0),
_startFrameVar(0),
_endFrameVar(0),
_posU(0),
_posUVar(0),
_posV(0),
_posVVar(0),
_nextFrameReadVar(0),
_nextFrameWriteVar(0),
_playingVar(0),
_enabled(false),
_disableWhenComplete(false),
_scriptDriven(false),
_isLastFrame(false) {
}
void ScriptedMovie::draw() {
if (!_enabled)
return;
Movie::draw();
}
void ScriptedMovie::update() {
if (_startFrameVar) {
_startFrame = _vm->_vars->get(_startFrameVar);
}
if (_endFrameVar) {
_endFrame = _vm->_vars->get(_endFrameVar);
}
if (!_endFrame) {
_endFrame = _bink.getFrameCount();
}
if (_posUVar) {
_posU = _vm->_vars->get(_posUVar);
}
if (_posVVar) {
_posV = _vm->_vars->get(_posVVar);
}
bool newEnabled;
if (_conditionBit) {
newEnabled = (_vm->_vars->get(_condition) & (1 << (_conditionBit - 1))) != 0;
} else {
newEnabled = _vm->_vars->evaluate(_condition);
}
if (newEnabled != _enabled) {
_enabled = newEnabled;
if (newEnabled) {
if (_disableWhenComplete
|| _bink.getCurFrame() < _startFrame
|| _bink.endOfVideo()) {
_bink.seekToFrame(_startFrame);
}
if (!_scriptDriven)
_bink.pauseVideo(false);
drawNextFrameToTexture();
} else {
_bink.pauseVideo(true);
}
}
if (_enabled) {
if (_nextFrameReadVar) {
int32 nextFrame = _vm->_vars->get(_nextFrameReadVar);
if (nextFrame > 0) {
if (_bink.getCurFrame() != nextFrame) {
_bink.seekToFrame(nextFrame - 1);
drawNextFrameToTexture();
}
_vm->_vars->set(_nextFrameReadVar, 0);
_isLastFrame = false;
}
}
if (!_scriptDriven && (_bink.needsUpdate() || _isLastFrame)) {
bool complete = false;
if (_isLastFrame) {
_isLastFrame = 0;
if (_loop) {
_bink.seekToFrame(_startFrame);
drawNextFrameToTexture();
} else {
complete = true;
}
} else {
drawNextFrameToTexture();
_isLastFrame = _bink.getCurFrame() == (_endFrame - 1);
}
if (_nextFrameWriteVar) {
_vm->_vars->set(_nextFrameWriteVar, _bink.getCurFrame() + 1);
}
if (_disableWhenComplete && complete) {
_bink.pauseVideo(true);
if (_playingVar) {
_vm->_vars->set(_playingVar, 0);
} else {
_enabled = 0;
_vm->_vars->set(_condition & 0x7FF, 0);
}
}
}
}
}
ScriptedMovie::~ScriptedMovie() {
}
SimpleMovie::SimpleMovie(Myst3Engine *vm, uint16 id) :
Movie(vm, id),
_synchronized(false) {
_startFrame = 1;
_endFrame = _bink.getFrameCount();
}
bool SimpleMovie::update() {
if (_bink.getCurFrame() < (_startFrame - 1)) {
_bink.seekToFrame(_startFrame - 1);
}
uint startEngineFrame = _vm->getFrameCount();
if (!_synchronized) {
// Play the movie according to the bink file framerate
if (_bink.needsUpdate()) {
drawNextFrameToTexture();
}
} else {
// Draw a movie frame each two engine frames
int targetFrame = (_vm->getFrameCount() - startEngineFrame) >> 2;
if (_bink.getCurFrame() < targetFrame) {
drawNextFrameToTexture();
}
}
return !_bink.endOfVideo() && _bink.getCurFrame() < _endFrame;
}
SimpleMovie::~SimpleMovie() {
}
} /* namespace Myst3 */

125
engines/myst3/movie.h Normal file
View File

@ -0,0 +1,125 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef MOVIE_H_
#define MOVIE_H_
#include "engines/myst3/gfx.h"
#include "engines/myst3/node.h"
#include "math/vector3d.h"
#include "video/bink_decoder_seek.h"
namespace Myst3 {
struct VideoData;
class Myst3Engine;
class Movie : public Drawable {
public:
Movie(Myst3Engine *vm, uint16 id);
virtual ~Movie();
virtual void draw();
void setStartFrame(int32 v) { _startFrame = v; }
void setEndFrame(int32 v) { _endFrame = v; }
protected:
static const int _movieTextureSize = 1024;
Myst3Engine *_vm;
Math::Vector3d _pTopLeft;
Math::Vector3d _pBottomLeft;
Math::Vector3d _pBottomRight;
Math::Vector3d _pTopRight;
Video::SeekableBinkDecoder _bink;
GLuint _texture;
int32 _startFrame;
int32 _endFrame;
void loadPosition(const VideoData &videoData);
void initTexture();
void drawNextFrameToTexture();
};
class ScriptedMovie : public Movie {
public:
ScriptedMovie(Myst3Engine *vm, uint16 id);
virtual ~ScriptedMovie();
void draw();
void update();
void setEndFrameVar(uint16 v) { _endFrameVar = v; }
void setNextFrameReadVar(uint16 v) { _nextFrameReadVar = v; }
void setNextFrameWriteVar(uint16 v) { _nextFrameWriteVar = v; }
void setPlayingVar(uint16 v) { _playingVar = v; }
void setPosU(int32 v) { _posU = v; }
void setPosUVar(uint16 v) { _posUVar = v; }
void setPosV(int32 v) { _posV = v; }
void setPosVVar(uint16 v) { _posVVar = v; }
void setStartFrameVar(uint16 v) { _startFrameVar = v; }
void setCondition(int16 condition) { _condition = condition; }
void setConditionBit(int16 cb) { _conditionBit = cb; }
void setDisableWhenComplete(bool upd) { _disableWhenComplete = upd; }
void setLoop(bool loop) { _loop = loop; }
void setScriptDriven(bool b) { _scriptDriven = b; }
private:
bool _enabled;
bool _loop;
bool _disableWhenComplete;
bool _scriptDriven;
bool _isLastFrame;
int16 _condition;
uint16 _conditionBit;
uint16 _startFrameVar;
uint16 _endFrameVar;
int32 _posU;
uint16 _posUVar;
int32 _posV;
uint16 _posVVar;
uint16 _nextFrameReadVar;
uint16 _nextFrameWriteVar;
uint16 _playingVar;
};
class SimpleMovie : public Movie {
public:
SimpleMovie(Myst3Engine *vm, uint16 id);
virtual ~SimpleMovie();
bool update();
void setSynchronized(bool b) { _synchronized = b; }
private:
bool _synchronized;
};
} /* namespace Myst3 */
#endif /* MOVIE_H_ */

633
engines/myst3/myst3.cpp Normal file
View File

@ -0,0 +1,633 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/error.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "common/util.h"
#include "common/textconsole.h"
#include "gui/debugger.h"
#include "engines/engine.h"
#include "engines/myst3/console.h"
#include "engines/myst3/database.h"
#include "engines/myst3/myst3.h"
#include "engines/myst3/nodecube.h"
#include "engines/myst3/nodeframe.h"
#include "engines/myst3/variables.h"
#include "engines/myst3/cursor.h"
#include "engines/myst3/inventory.h"
#include "engines/myst3/script.h"
#include "graphics/jpeg.h"
#include "graphics/conversion.h"
#ifdef SDL_BACKEND
#include <SDL_opengl.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
namespace Myst3 {
Myst3Engine::Myst3Engine(OSystem *syst, int gameFlags) :
Engine(syst), _system(syst),
_db(0), _console(0), _scriptEngine(0),
_vars(0), _node(0), _scene(0), _archive(0),
_archiveRSRC(0), _archiveOVER(0), _archiveLANG(0),
_cursor(0), _inventory(0),
_frameCount(0), _rnd(0), _shouldQuit(false) {
DebugMan.addDebugChannel(kDebugVariable, "Variable", "Track Variable Accesses");
DebugMan.addDebugChannel(kDebugSaveLoad, "SaveLoad", "Track Save/Load Function");
DebugMan.addDebugChannel(kDebugScript, "Script", "Track Script Execution");
DebugMan.addDebugChannel(kDebugNode, "Node", "Track Node Changes");
// Add subdirectories to the search path to allow running from a full HDD install
const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "bin");
SearchMan.addSubDirectoryMatching(gameDataDir, "M3Data");
SearchMan.addSubDirectoryMatching(gameDataDir, "M3Data/TEXT");
SearchMan.addSubDirectoriesMatching(gameDataDir, "EXILE Disc ?/Data", true);
}
Myst3Engine::~Myst3Engine() {
DebugMan.clearAllDebugChannels();
delete _inventory;
delete _cursor;
delete _scene;
delete _archive;
delete _archiveRSRC;
delete _archiveOVER;
delete _archiveLANG;
delete _db;
delete _scriptEngine;
delete _console;
delete _vars;
delete _rnd;
}
Common::Error Myst3Engine::run() {
const int w = 640;
const int h = 480;
_rnd = new Common::RandomSource("sprint");
_console = new Console(this);
_scriptEngine = new Script(this);
_db = new Database("M3.exe");
_vars = new Variables(this);
_scene = new Scene();
_archive = new Archive();
_archiveRSRC = new Archive();
_archiveOVER = new Archive();
_archiveLANG = new Archive();
_system->setupScreen(w, h, false, true);
_system->showMouse(false);
if (!_archiveLANG->open("ENGLISH.m3t", 0)) {
error("Unable to open archive ENGLISH.m3t");
}
if (!_archiveRSRC->open("RSRC.m3r", 0)) {
error("Unable to open archive RSRC.m3r");
}
if (!_archiveOVER->open("OVER101.m3o", 0)) {
// OVER101 is not required
delete _archiveOVER;
_archiveOVER = 0;
}
_cursor = new Cursor(this);
_inventory = new Inventory(this);
_scene->init(w, h);
// Var init script
runScriptsFromNode(1000, 101);
_vars->setLocationNextRoom(501); // LEIS
_vars->setLocationNextNode(3);
goToNode(0, 1);
while (!_shouldQuit) {
runNodeBackgroundScripts();
processInput(false);
drawFrame();
}
for (uint i = 0; i < _movies.size(); i++) {
delete _movies[i];
}
_movies.clear();
delete _node;
_archive->close();
return Common::kNoError;
}
Common::Array<HotSpot *> Myst3Engine::listHoveredHotspots(NodePtr nodeData) {
Common::Array<HotSpot *> hovered;
if (_viewType == kCube) {
Common::Point mouse = _scene->getMousePos();
for (uint j = 0; j < nodeData->hotspots.size(); j++) {
if (nodeData->hotspots[j].isPointInRectsCube(mouse)
&& _vars->evaluate(nodeData->hotspots[j].condition)) {
hovered.push_back(&nodeData->hotspots[j]);
}
}
} else {
Common::Point mouse = _cursor->getPosition();
Common::Point scaledMouse;
if (_viewType == kMenu) {
scaledMouse = Common::Point(
mouse.x * Scene::_originalWidth / _system->getWidth(),
CLIP<uint>(mouse.y * Scene::_originalHeight / _system->getHeight(),
0, Scene::_originalHeight));
} else {
scaledMouse = Common::Point(
mouse.x * Scene::_originalWidth / _system->getWidth(),
CLIP<uint>(mouse.y * Scene::_originalHeight / _system->getHeight()
- Scene::_topBorderHeight, 0, Scene::_frameHeight));
}
for (uint j = 0; j < nodeData->hotspots.size(); j++) {
if (nodeData->hotspots[j].isPointInRectsFrame(scaledMouse)
&& _vars->evaluate(nodeData->hotspots[j].condition)) {
hovered.push_back(&nodeData->hotspots[j]);
}
}
}
return hovered;
}
void Myst3Engine::updateCursor() {
NodePtr nodeData = _db->getNodeData(_vars->getLocationNode(), _vars->getLocationRoom());
Common::Array<HotSpot *> hovered = listHoveredHotspots(nodeData);
uint16 hoveredInventory = _inventory->hoveredItem();
if (hovered.size() > 0) {
HotSpot *h = hovered.front();
_cursor->changeCursor(h->cursor);
} else if (hoveredInventory > 0) {
_cursor->changeCursor(1);
} else {
_cursor->changeCursor(8);
}
}
void Myst3Engine::processInput(bool lookOnly) {
// Process events
Common::Event event;
while (_system->getEventManager()->pollEvent(event)) {
// Check for "Hard" quit"
if (event.type == Common::EVENT_QUIT) {
_shouldQuit = true;
} else if (event.type == Common::EVENT_MOUSEMOVE) {
if (_viewType == kCube) {
_scene->updateCamera(event.relMouse);
}
_cursor->updatePosition(event.relMouse);
updateCursor();
} else if (event.type == Common::EVENT_LBUTTONDOWN) {
// Skip the event when in look only mode
if (lookOnly) continue;
uint16 hoveredInventory = _inventory->hoveredItem();
if (hoveredInventory > 0) {
_inventory->useItem(hoveredInventory);
continue;
}
NodePtr nodeData = _db->getNodeData(_vars->getLocationNode(), _vars->getLocationRoom());
Common::Array<HotSpot *> hovered = listHoveredHotspots(nodeData);
if (hovered.size() > 0) {
_scriptEngine->run(&hovered.front()->script);
}
} else if (event.type == Common::EVENT_KEYDOWN) {
switch (event.kbd.keycode) {
case Common::KEYCODE_d:
if (event.kbd.flags & Common::KBD_CTRL) {
_console->attach();
_console->onFrame();
}
break;
default:
break;
}
}
}
}
void Myst3Engine::drawFrame() {
_scene->clear();
if (_viewType == kCube) {
_scene->setupCameraPerspective();
} else {
_scene->setupCameraOrtho2D();
}
_node->update();
_node->draw();
for (uint i = 0; i < _movies.size(); i++) {
_movies[i]->update();
_movies[i]->draw();
}
for (uint i = 0; i < _drawables.size(); i++) {
_drawables[i]->draw();
}
if (_viewType == kCube) {
_scene->setupCameraOrtho2D();
}
if (_viewType != kMenu) {
SunSpot flare = _node->computeSunspotsIntensity(_scene->getMousePos());
if (flare.intensity >= 0)
_scene->drawSunspotFlare(flare);
_scene->drawBlackBorders();
_inventory->draw();
}
_cursor->draw();
_system->updateScreen();
_system->delayMillis(10);
_frameCount++;
}
void Myst3Engine::goToNode(uint16 nodeID, uint transition) {
if (_node) {
for (uint i = 0; i < _movies.size(); i++) {
delete _movies[i];
}
_movies.clear();
delete _node;
_node = 0;
}
uint16 node = _vars->getLocationNextNode();
if (node == 0)
node = nodeID;
uint16 room = _vars->getLocationNextRoom();
uint16 age = _vars->getLocationNextAge();
loadNode(node, room, age);
_vars->setLocationNextNode(0);
_vars->setLocationNextRoom(0);
_vars->setLocationNextAge(0);
}
void Myst3Engine::loadNode(uint16 nodeID, uint32 roomID, uint32 ageID) {
_scriptEngine->run(&_db->getNodeInitScript());
if (nodeID)
_vars->setLocationNode(_vars->valueOrVarValue(nodeID));
if (roomID)
_vars->setLocationRoom(_vars->valueOrVarValue(roomID));
if (ageID)
_vars->setLocationAge(_vars->valueOrVarValue(ageID));
char oldRoomName[8];
char newRoomName[8];
_db->getRoomName(oldRoomName);
_db->getRoomName(newRoomName, roomID);
if (strcmp(newRoomName, "JRNL") && strcmp(newRoomName, "XXXX")
&& strcmp(newRoomName, "MENU") && strcmp(newRoomName, oldRoomName)) {
_db->setCurrentRoom(roomID);
Common::String nodeFile = Common::String::format("%snodes.m3a", newRoomName);
_archive->close();
if (!_archive->open(nodeFile.c_str(), newRoomName)) {
error("Unable to open archive %s", nodeFile.c_str());
}
}
runNodeInitScripts();
}
void Myst3Engine::runNodeInitScripts() {
NodePtr nodeData = _db->getNodeData(
_vars->getLocationNode(),
_vars->getLocationRoom(),
_vars->getLocationAge());
NodePtr nodeDataInit = _db->getNodeData(32765);
if (nodeDataInit)
runScriptsFromNode(32765);
if (!nodeData)
error("Node %d unknown in the database", _vars->getLocationNode());
for (uint j = 0; j < nodeData->scripts.size(); j++) {
if (_vars->evaluate(nodeData->scripts[j].condition)) {
_scriptEngine->run(&nodeData->scripts[j].script);
}
}
}
void Myst3Engine::runNodeBackgroundScripts() {
NodePtr nodeDataRoom = _db->getNodeData(32675);
if (nodeDataRoom) {
for (uint j = 0; j < nodeDataRoom->hotspots.size(); j++) {
if (nodeDataRoom->hotspots[j].condition == -1) {
if (!_scriptEngine->run(&nodeDataRoom->hotspots[j].script))
break;
}
}
}
NodePtr nodeData = _db->getNodeData(_vars->getLocationNode(), _vars->getLocationRoom());
for (uint j = 0; j < nodeData->hotspots.size(); j++) {
if (nodeData->hotspots[j].condition == -1) {
if (!_scriptEngine->run(&nodeData->hotspots[j].script))
break;
}
}
}
void Myst3Engine::loadNodeCubeFaces(uint16 nodeID) {
_viewType = kCube;
_cursor->lockPosition(true);
updateCursor();
_node = new NodeCube(this, nodeID);
}
void Myst3Engine::loadNodeFrame(uint16 nodeID) {
_viewType = kFrame;
_cursor->lockPosition(false);
updateCursor();
_node = new NodeFrame(this, nodeID);
}
void Myst3Engine::loadNodeMenu(uint16 nodeID) {
_viewType = kMenu;
_cursor->lockPosition(false);
updateCursor();
_node = new NodeFrame(this, nodeID);
}
void Myst3Engine::runScriptsFromNode(uint16 nodeID, uint32 roomID, uint32 ageID) {
NodePtr nodeData = _db->getNodeData(nodeID, roomID, ageID);
for (uint j = 0; j < nodeData->scripts.size(); j++) {
if (_vars->evaluate(nodeData->scripts[j].condition)) {
if (!_scriptEngine->run(&nodeData->scripts[j].script))
break;
}
}
}
void Myst3Engine::loadMovie(uint16 id, uint16 condition, bool resetCond, bool loop) {
ScriptedMovie *movie = new ScriptedMovie(this, id);
movie->setCondition(condition);
movie->setDisableWhenComplete(resetCond);
movie->setLoop(loop);
if (_vars->getMovieScriptDriven()) {
movie->setScriptDriven(_vars->getMovieScriptDriven());
_vars->setMovieScriptDriven(0);
}
if (_vars->getMovieStartFrameVar()) {
movie->setStartFrameVar(_vars->getMovieStartFrameVar());
_vars->setMovieStartFrameVar(0);
}
if (_vars->getMovieEndFrameVar()) {
movie->setEndFrameVar(_vars->getMovieEndFrameVar());
_vars->setMovieEndFrameVar(0);
}
if (_vars->getMovieStartFrame()) {
movie->setStartFrame(_vars->getMovieStartFrame());
_vars->setMovieStartFrame(0);
}
if (_vars->getMovieEndFrame()) {
movie->setEndFrame(_vars->getMovieEndFrame());
_vars->setMovieEndFrame(0);
}
if (_vars->getMovieNextFrameGetVar()) {
movie->setNextFrameReadVar(_vars->getMovieNextFrameGetVar());
_vars->setMovieNextFrameGetVar(0);
}
if (_vars->getMovieNextFrameSetVar()) {
movie->setNextFrameWriteVar(_vars->getMovieNextFrameSetVar());
_vars->setMovieNextFrameSetVar(0);
}
if (_vars->getMoviePlayingVar()) {
movie->setPlayingVar(_vars->getMoviePlayingVar());
_vars->setMoviePlayingVar(0);
}
if (_vars->getMovieOverridePosU()) {
movie->setPosU(_vars->getMovieOverridePosU());
_vars->setMovieOverridePosU(0);
}
if (_vars->getMovieOverridePosV()) {
movie->setPosV(_vars->getMovieOverridePosV());
_vars->setMovieOverridePosV(0);
}
if (_vars->getMovieUVar()) {
movie->setPosUVar(_vars->getMovieUVar());
_vars->setMovieUVar(0);
}
if (_vars->getMovieVVar()) {
movie->setPosVVar(_vars->getMovieVVar());
_vars->setMovieVVar(0);
}
if (_vars->getMovieOverrideCondition()) {
movie->setCondition(_vars->getMovieOverrideCondition());
_vars->setMovieOverrideCondition(0);
}
if (_vars->getMovieConditionBit()) {
movie->setConditionBit(_vars->getMovieConditionBit());
_vars->setMovieConditionBit(0);
}
_movies.push_back(movie);
}
void Myst3Engine::playSimpleMovie(uint16 id) {
SimpleMovie movie = SimpleMovie(this, id);
if (_vars->getMovieSynchronized()) {
movie.setSynchronized(_vars->getMovieSynchronized());
_vars->setMovieSynchronized(0);
}
if (_vars->getMovieStartFrame()) {
movie.setStartFrame(_vars->getMovieStartFrame());
_vars->setMovieStartFrame(0);
}
if (_vars->getMovieEndFrame()) {
movie.setEndFrame(_vars->getMovieEndFrame());
_vars->setMovieEndFrame(0);
}
_drawables.push_back(&movie);
while (movie.update()) {
processInput(true);
drawFrame();
}
_drawables.pop_back();
}
void Myst3Engine::addSpotItem(uint16 id, uint16 condition, bool fade) {
_node->loadSpotItem(id, condition, fade);
}
void Myst3Engine::addSunSpot(uint16 pitch, uint16 heading, uint16 intensity,
uint16 color, uint16 var, bool varControlledIntensity, uint16 radius) {
SunSpot s;
s.pitch = pitch;
s.heading = heading;
s.intensity = intensity * 2.55;
s.color = color & 0xF | 16
* (color & 0xF | 16
* ((color >> 4) & 0xF | 16
* ((color >> 4) & 0xF | 16
* ((color >> 8) & 0xF | 16
* ((color >> 8) & 0xF)))));
s.var = var;
s.variableIntensity = varControlledIntensity;
s.radius = radius;
_node->addSunSpot(s);
}
const DirectorySubEntry *Myst3Engine::getFileDescription(const char* room, uint16 index, uint16 face, DirectorySubEntry::ResourceType type) {
char currentRoom[8];
if (!room) {
_db->getRoomName(currentRoom, _vars->getLocationRoom());
room = currentRoom;
}
const DirectorySubEntry *desc = 0;
if (_archiveOVER)
desc = _archiveOVER->getDescription(room, index, face, type);
if (!desc && _archiveLANG)
desc = _archiveLANG->getDescription(room, index, face, type);
if (!desc && _archiveRSRC)
desc = _archiveRSRC->getDescription(room, index, face, type);
if (!desc && _archive)
desc = _archive->getDescription(room, index, face, type);
return desc;
}
Graphics::Surface *Myst3Engine::loadTexture(uint16 id) {
const DirectorySubEntry *desc = getFileDescription("GLOB", id, 0, DirectorySubEntry::kCursor);
if (!desc)
error("Texture %d does not exist", id);
Common::MemoryReadStream *data = desc->getData();
uint32 magic = data->readUint32LE();
if (magic != 0x2E544558)
error("Wrong texture format", id);
data->readUint32LE(); // unk 1
uint32 width = data->readUint32LE();
uint32 height = data->readUint32LE();
data->readUint32LE(); // unk 2
data->readUint32LE(); // unk 3
Graphics::Surface *s = new Graphics::Surface();
s->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
data->read(s->pixels, height * s->pitch);
delete data;
// ARGB => RGBA
uint32 *p = (uint32 *)s->pixels;
for (uint i = 0; i < width * height; i++) {
uint8 a = (*p >> 0) & 0xFF;
uint8 r = (*p >> 24) & 0xFF;
uint8 g = (*p >> 16) & 0xFF;
uint8 b = (*p >> 8) & 0xFF;
*p = (a << 24) | (r << 16) | (g << 8) | b;
p++;
}
return s;
}
} // end of namespace Myst3

136
engines/myst3/myst3.h Normal file
View File

@ -0,0 +1,136 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef MYST3_ENGINE_H
#define MYST3_ENGINE_H
#include "engines/engine.h"
#include "common/system.h"
#include "common/random.h"
#include "engines/myst3/archive.h"
#include "engines/myst3/movie.h"
#include "engines/myst3/node.h"
#include "engines/myst3/scene.h"
namespace Graphics {
struct Surface;
}
namespace Myst3 {
// Engine Debug Flags
enum {
kDebugVariable = (1 << 0),
kDebugSaveLoad = (1 << 1),
kDebugNode = (1 << 2),
kDebugScript = (1 << 3)
};
// View type
enum ViewType {
kCube = 1,
kFrame = 2,
kMenu = 3
};
class Console;
class Variables;
class HotSpot;
class Cursor;
class Inventory;
class Database;
class Script;
struct NodeData;
typedef Common::SharedPtr<NodeData> NodePtr;
class Myst3Engine : public Engine {
protected:
// Engine APIs
virtual Common::Error run();
virtual GUI::Debugger *getDebugger() { return (GUI::Debugger *)_console; }
public:
ViewType _viewType;
Variables *_vars;
Cursor *_cursor;
Inventory *_inventory;
Common::RandomSource *_rnd;
Myst3Engine(OSystem *syst, int gameFlags);
virtual ~Myst3Engine();
const DirectorySubEntry *getFileDescription(const char* room, uint16 index, uint16 face, DirectorySubEntry::ResourceType type);
Graphics::Surface *loadTexture(uint16 id);
void goToNode(uint16 nodeID, uint transition);
void loadNode(uint16 nodeID, uint32 roomID = 0, uint32 ageID = 0);
void loadNodeCubeFaces(uint16 nodeID);
void loadNodeFrame(uint16 nodeID);
void loadNodeMenu(uint16 nodeID);
void runNodeInitScripts();
void runNodeBackgroundScripts();
void runScriptsFromNode(uint16 nodeID, uint32 roomID = 0, uint32 ageID = 0);
void loadMovie(uint16 id, uint16 condition, bool resetCond, bool loop);
void playSimpleMovie(uint16 id);
void addSpotItem(uint16 id, uint16 condition, bool fade);
void addSunSpot(uint16 pitch, uint16 heading, uint16 intensity,
uint16 color, uint16 var, bool varControlledIntensity, uint16 radius);
void processInput(bool lookOnly);
void drawFrame();
uint getFrameCount() { return _frameCount; }
private:
OSystem *_system;
Console *_console;
Node *_node;
Scene *_scene;
Archive *_archive;
Archive *_archiveRSRC;
Archive *_archiveOVER;
Archive *_archiveLANG;
Script *_scriptEngine;
Database *_db;
Common::Array<ScriptedMovie *> _movies;
Common::Array<Drawable *> _drawables;
uint _frameCount;
bool _shouldQuit;
Common::Array<HotSpot *> listHoveredHotspots(NodePtr nodeData);
void updateCursor();
friend class Console;
};
} // end of namespace Myst3
#endif

382
engines/myst3/node.cpp Normal file
View File

@ -0,0 +1,382 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/node.h"
#include "engines/myst3/myst3.h"
#include "engines/myst3/variables.h"
#include "common/debug.h"
#include "common/rect.h"
namespace Myst3 {
void Face::setTextureFromJPEG(Graphics::JPEG *jpeg) {
_bitmap = new Graphics::Surface();
_bitmap->create(jpeg->getComponent(1)->w, jpeg->getComponent(1)->h, Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0));
byte *y = (byte *)jpeg->getComponent(1)->getBasePtr(0, 0);
byte *u = (byte *)jpeg->getComponent(2)->getBasePtr(0, 0);
byte *v = (byte *)jpeg->getComponent(3)->getBasePtr(0, 0);
byte *ptr = (byte *)_bitmap->getBasePtr(0, 0);
for (int i = 0; i < _bitmap->w * _bitmap->h; i++) {
byte r, g, b;
Graphics::YUV2RGB(*y++, *u++, *v++, r, g, b);
*ptr++ = r;
*ptr++ = g;
*ptr++ = b;
}
}
Face::Face() :
_textureDirty(true) {
glGenTextures(1, &_textureId);
glBindTexture(GL_TEXTURE_2D, _textureId);
glTexImage2D(GL_TEXTURE_2D, 0, 3, Node::_cubeTextureSize, Node::_cubeTextureSize, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
void Face::uploadTexture() {
if (_textureDirty) {
glBindTexture(GL_TEXTURE_2D, _textureId);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _bitmap->w, _bitmap->h, GL_RGB, GL_UNSIGNED_BYTE, _bitmap->pixels);
_textureDirty = false;
}
}
Face::~Face() {
_bitmap->free();
delete _bitmap;
_bitmap = 0;
glDeleteTextures(1, &_textureId);
}
Node::Node(Myst3Engine *vm, uint16 id) :
_vm(vm) {
for (uint i = 0; i < 6; i++)
_faces[i] = 0;
}
void Node::dumpFaceMask(uint16 index, int face) {
byte *mask = new byte[640 * 640];
memset(mask, 0, sizeof(mask));
uint32 headerOffset = 0;
uint32 dataOffset = 0;
const DirectorySubEntry *maskDesc = _vm->getFileDescription(0, index, face, DirectorySubEntry::kFaceMask);
Common::MemoryReadStream *maskStream = maskDesc->getData();
while (headerOffset < 400) {
int blockX = (headerOffset / sizeof(dataOffset)) % 10;
int blockY = (headerOffset / sizeof(dataOffset)) / 10;
maskStream->seek(headerOffset, SEEK_SET);
dataOffset = maskStream->readUint32LE();
headerOffset = maskStream->pos();
if (dataOffset != 0) {
maskStream->seek(dataOffset, SEEK_SET);
for(int i = 63; i >= 0; i--) {
int x = 0;
byte numValues = maskStream->readByte();
for (int j = 0; j < numValues; j++) {
byte repeat = maskStream->readByte();
byte value = maskStream->readByte();
for (int k = 0; k < repeat; k++) {
mask[((blockY * 64) + i) * 640 + blockX * 64 + x] = value;
x++;
}
}
}
}
}
delete maskStream;
Common::DumpFile outFile;
outFile.open("dump/1-1.masku");
outFile.write(mask, sizeof(mask));
outFile.close();
delete[] mask;
}
Node::~Node() {
for (uint i = 0; i < _spotItems.size(); i++) {
delete _spotItems[i];
}
_spotItems.clear();
for (int i = 0; i < 6; i++) {
delete _faces[i];
}
}
void Node::loadSpotItem(uint16 id, uint16 condition, bool fade) {
SpotItem *spotItem = new SpotItem(_vm);
spotItem->setCondition(condition);
spotItem->setFade(fade);
spotItem->setFadeVar(abs(condition));
for (int i = 0; i < 6; i++) {
const DirectorySubEntry *jpegDesc = _vm->getFileDescription(0, id, i + 1, DirectorySubEntry::kSpotItem);
if (!jpegDesc)
jpegDesc = _vm->getFileDescription(0, id, i + 1, DirectorySubEntry::kMenuSpotItem);
if (!jpegDesc) continue;
SpotItemFace *spotItemFace = new SpotItemFace(
_faces[i],
jpegDesc->getSpotItemData().u,
jpegDesc->getSpotItemData().v);
Common::MemoryReadStream *jpegStream = jpegDesc->getData();
Graphics::JPEG jpeg;
jpeg.read(jpegStream);
spotItemFace->loadData(&jpeg);
delete jpegStream;
spotItem->addFace(spotItemFace);
}
_spotItems.push_back(spotItem);
}
void Node::update() {
// First undraw ...
for (uint i = 0; i < _spotItems.size(); i++) {
_spotItems[i]->updateUndraw();
}
// ... then redraw
for (uint i = 0; i < _spotItems.size(); i++) {
_spotItems[i]->updateDraw();
}
}
SpotItem::SpotItem(Myst3Engine *vm) :
_vm(vm) {
}
SpotItem::~SpotItem() {
for (uint i = 0; i < _faces.size(); i++) {
delete _faces[i];
}
}
void SpotItem::updateUndraw() {
for (uint i = 0; i < _faces.size(); i++) {
if (!_vm->_vars->evaluate(_condition) && _faces[i]->isDrawn()) {
_faces[i]->undraw();
}
}
}
void SpotItem::updateDraw() {
for (uint i = 0; i < _faces.size(); i++) {
if (_enableFade) {
uint16 newFadeValue = _vm->_vars->get(_fadeVar);
if (_faces[i]->getFadeValue() != newFadeValue) {
_faces[i]->setFadeValue(newFadeValue);
_faces[i]->setDrawn(false);
}
}
if (_vm->_vars->evaluate(_condition) && !_faces[i]->isDrawn()) {
if (_enableFade)
_faces[i]->fadeDraw();
else
_faces[i]->draw();
}
}
}
SpotItemFace::SpotItemFace(Face *face, uint16 posX, uint16 posY):
_face(face),
_posX(posX),
_posY(posY),
_drawn(false),
_bitmap(0),
_notDrawnBitmap(0),
_fadeValue(0)
{
}
SpotItemFace::~SpotItemFace() {
if (_bitmap) {
_bitmap->free();
delete _bitmap;
_bitmap = 0;
}
if (_notDrawnBitmap) {
_notDrawnBitmap->free();
delete _notDrawnBitmap;
_notDrawnBitmap = 0;
}
}
void SpotItemFace::loadData(Graphics::JPEG *jpeg) {
// Convert active SpotItem image to raw data
_bitmap = new Graphics::Surface();
_bitmap->create(jpeg->getComponent(1)->w, jpeg->getComponent(1)->h, Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0));
for (int i = 0; i < _bitmap->h; i++) {
byte *y = (byte *)jpeg->getComponent(1)->getBasePtr(0, i);
byte *u = (byte *)jpeg->getComponent(2)->getBasePtr(0, i);
byte *v = (byte *)jpeg->getComponent(3)->getBasePtr(0, i);
byte *ptr = (byte *)_bitmap->getBasePtr(0, i);
for (int j = 0; j < _bitmap->w; j++) {
byte r, g, b;
Graphics::YUV2RGB(*y++, *u++, *v++, r, g, b);
*ptr++ = r;
*ptr++ = g;
*ptr++ = b;
}
}
// Copy not drawn SpotItem image from face
_notDrawnBitmap = new Graphics::Surface();
_notDrawnBitmap->create(jpeg->getComponent(1)->w, jpeg->getComponent(1)->h, Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0));
for (uint i = 0; i < _notDrawnBitmap->h; i++) {
memcpy(_notDrawnBitmap->getBasePtr(0, i),
_face->_bitmap->getBasePtr(_posX, _posY + i),
_notDrawnBitmap->w * 3);
}
}
void SpotItemFace::draw() {
for (uint i = 0; i < _bitmap->h; i++) {
memcpy(_face->_bitmap->getBasePtr(_posX, _posY + i),
_bitmap->getBasePtr(0, i),
_bitmap->w * 3);
}
_drawn = true;
_face->markTextureDirty();
}
void SpotItemFace::undraw() {
for (uint i = 0; i < _notDrawnBitmap->h; i++) {
memcpy(_face->_bitmap->getBasePtr(_posX, _posY + i),
_notDrawnBitmap->getBasePtr(0, i),
_notDrawnBitmap->w * 3);
}
_drawn = false;
_face->markTextureDirty();
}
void SpotItemFace::fadeDraw() {
for (int i = 0; i < _bitmap->h; i++) {
byte *ptrND = (byte *)_notDrawnBitmap->getBasePtr(0, i);
byte *ptrD = (byte *)_bitmap->getBasePtr(0, i);
byte *ptrDest = (byte *)_face->_bitmap->getBasePtr(_posX, _posY + i);
for (int j = 0; j < _bitmap->w; j++) {
byte rND = *ptrND++;
byte gND = *ptrND++;
byte bND = *ptrND++;
byte rD = *ptrD++;
byte gD = *ptrD++;
byte bD = *ptrD++;
// TODO: optimize ?
*ptrDest++ = rND * (100 - _fadeValue) / 100 + rD * _fadeValue / 100;
*ptrDest++ = gND * (100 - _fadeValue) / 100 + gD * _fadeValue / 100;
*ptrDest++ = bND * (100 - _fadeValue) / 100 + bD * _fadeValue / 100;
}
}
_drawn = true;
_face->markTextureDirty();
}
void Node::addSunSpot(const SunSpot &sunspot) {
SunSpot *sunSpot = new SunSpot(sunspot);
_sunspots.push_back(sunSpot);
}
static Math::Vector3d directionToVector(const Common::Point &lookAt) {
Math::Vector3d v;
float heading = Math::degreeToRadian(lookAt.x);
float pitch = Math::degreeToRadian(lookAt.y);
v.setValue(0, cos(pitch) * cos(heading));
v.setValue(1, sin(pitch));
v.setValue(2, cos(pitch) * sin(heading));
return v;
}
SunSpot Node::computeSunspotsIntensity(const Common::Point &lookAt) {
SunSpot result;
result.intensity = -1;
result.color = 0;
result.radius = 0;
for (uint i = 0; i < _sunspots.size(); i++) {
SunSpot *s = _sunspots[i];
uint32 value = _vm->_vars->get(s->var);
// Skip disabled items
if (value == 0) continue;
Math::Vector3d vLookAt = directionToVector(lookAt);
Math::Vector3d vSun = -directionToVector(Common::Point(s->heading, s->pitch));
float dotProduct = Math::Vector3d::dotProduct(vLookAt, vSun);
float distance = (0.05 * s->radius - (dotProduct + 1.0) * 90) / (0.05 * s->radius);
distance = CLIP<float>(distance, 0.0, 1.0);
if (distance > result.radius) {
result.radius = distance;
result.color = s->color;
result.intensity = s->intensity;
if (s->variableIntensity) {
result.radius = value * distance / 100;
}
}
}
return result;
}
} // end of namespace Myst3

146
engines/myst3/node.h Normal file
View File

@ -0,0 +1,146 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef MYST3_ROOM_H
#define MYST3_ROOM_H
#ifdef SDL_BACKEND
#include <SDL_opengl.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#include "common/array.h"
#include "common/rect.h"
#include "graphics/surface.h"
#include "graphics/jpeg.h"
#include "graphics/conversion.h"
namespace Myst3 {
class Myst3Engine;
class Face {
public:
Graphics::Surface *_bitmap;
GLuint _textureId;
Face();
~Face();
void setTextureFromJPEG(Graphics::JPEG *jpeg);
void markTextureDirty() { _textureDirty = true; }
void uploadTexture();
private:
bool _textureDirty;
};
class SpotItemFace {
public:
SpotItemFace(Face *face, uint16 posX, uint16 posY);
~SpotItemFace();
void loadData(Graphics::JPEG *jpeg);
void draw();
void undraw();
void fadeDraw();
bool isDrawn() { return _drawn; }
void setDrawn(bool drawn) { _drawn = drawn; }
uint16 getFadeValue() { return _fadeValue; }
void setFadeValue(uint16 value) { _fadeValue = value; }
private:
Face *_face;
bool _drawn;
uint16 _fadeValue;
uint16 _posX;
uint16 _posY;
Graphics::Surface *_bitmap;
Graphics::Surface *_notDrawnBitmap;
};
class SpotItem {
public:
SpotItem(Myst3Engine *vm);
~SpotItem();
void setCondition(uint16 condition) { _condition = condition; }
void setFade(bool fade) { _enableFade = fade; }
void setFadeVar(uint16 var) { _fadeVar = var; }
void addFace(SpotItemFace *face) { _faces.push_back(face); }
void updateUndraw();
void updateDraw();
private:
Myst3Engine *_vm;
uint16 _condition;
uint16 _fadeVar;
bool _enableFade;
Common::Array<SpotItemFace *> _faces;
};
class SunSpot {
public:
uint16 pitch;
uint16 heading;
float intensity;
uint32 color;
uint16 var;
bool variableIntensity;
float radius;
};
class Node {
protected:
Myst3Engine *_vm;
Face *_faces[6];
Common::Array<SpotItem *> _spotItems;
Common::Array<SunSpot *> _sunspots;
public:
Node(Myst3Engine *vm, uint16 id);
virtual ~Node();
void update();
virtual void draw() = 0;
void addSunSpot(const SunSpot &sunspot);
SunSpot computeSunspotsIntensity(const Common::Point &lookAt);
void loadSpotItem(uint16 id, uint16 condition, bool fade);
void dumpFaceMask(uint16 index, int face);
static const int _cubeTextureSize = 1024;
};
} // end of namespace Myst3
#endif

122
engines/myst3/nodecube.cpp Normal file
View File

@ -0,0 +1,122 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/nodecube.h"
#include "engines/myst3/directorysubentry.h"
#include "engines/myst3/myst3.h"
#include "common/debug.h"
namespace Myst3 {
NodeCube::NodeCube(Myst3Engine *vm, uint16 id) :
Node(vm, id) {
for (int i = 0; i < 6; i++) {
const DirectorySubEntry *jpegDesc = _vm->getFileDescription(0, id, i + 1, DirectorySubEntry::kCubeFace);
if (!jpegDesc)
error("Face %d does not exist", id);
Common::MemoryReadStream *jpegStream = jpegDesc->getData();
if (jpegStream) {
Graphics::JPEG jpeg;
jpeg.read(jpegStream);
_faces[i] = new Face();
_faces[i]->setTextureFromJPEG(&jpeg);
_faces[i]->markTextureDirty();
delete jpegStream;
}
}
}
NodeCube::~NodeCube() {
}
void NodeCube::draw() {
// Size of the cube
float t = 1.0f;
// Used fragment of the textures
float s = 640 / (float)_cubeTextureSize;
// Update the OpenGL textures if needed
for (uint i = 0; i < 6; i++)
_faces[i]->uploadTexture();
glEnable(GL_TEXTURE_2D);
glDepthMask(GL_FALSE);
glBindTexture(GL_TEXTURE_2D, _faces[4]->_textureId);
glBegin(GL_TRIANGLE_STRIP); // X-
glTexCoord2f(0, s); glVertex3f(-t,-t, t);
glTexCoord2f(s, s); glVertex3f(-t,-t,-t);
glTexCoord2f(0, 0); glVertex3f(-t, t, t);
glTexCoord2f(s, 0); glVertex3f(-t, t,-t);
glEnd();
glBindTexture(GL_TEXTURE_2D, _faces[3]->_textureId);
glBegin(GL_TRIANGLE_STRIP); // X+
glTexCoord2f(0, s); glVertex3f( t,-t,-t);
glTexCoord2f(s, s); glVertex3f( t,-t, t);
glTexCoord2f(0, 0); glVertex3f( t, t,-t);
glTexCoord2f(s, 0); glVertex3f( t, t, t);
glEnd();
glBindTexture(GL_TEXTURE_2D, _faces[1]->_textureId);
glBegin(GL_TRIANGLE_STRIP); // Y-
glTexCoord2f(0, s); glVertex3f( t,-t,-t);
glTexCoord2f(s, s); glVertex3f(-t,-t,-t);
glTexCoord2f(0, 0); glVertex3f( t,-t, t);
glTexCoord2f(s, 0); glVertex3f(-t,-t, t);
glEnd();
glBindTexture(GL_TEXTURE_2D, _faces[5]->_textureId);
glBegin(GL_TRIANGLE_STRIP); // Y+
glTexCoord2f(0, s); glVertex3f( t, t, t);
glTexCoord2f(s, s); glVertex3f(-t, t, t);
glTexCoord2f(0, 0); glVertex3f( t, t,-t);
glTexCoord2f(s, 0); glVertex3f(-t, t,-t);
glEnd();
glBindTexture(GL_TEXTURE_2D, _faces[0]->_textureId);
glBegin(GL_TRIANGLE_STRIP); // Z-
glTexCoord2f(0, s); glVertex3f(-t,-t,-t);
glTexCoord2f(s, s); glVertex3f( t,-t,-t);
glTexCoord2f(0, 0); glVertex3f(-t, t,-t);
glTexCoord2f(s, 0); glVertex3f( t, t,-t);
glEnd();
glBindTexture(GL_TEXTURE_2D, _faces[2]->_textureId);
glBegin(GL_TRIANGLE_STRIP); // Z+
glTexCoord2f(0, s); glVertex3f( t,-t, t);
glTexCoord2f(s, s); glVertex3f(-t,-t, t);
glTexCoord2f(0, 0); glVertex3f( t, t, t);
glTexCoord2f(s, 0); glVertex3f(-t, t, t);
glEnd();
glDepthMask(GL_TRUE);
}
} /* namespace Myst3 */

39
engines/myst3/nodecube.h Normal file
View File

@ -0,0 +1,39 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef NODECUBE_H_
#define NODECUBE_H_
#include "engines/myst3/node.h"
namespace Myst3 {
class NodeCube: public Myst3::Node {
public:
NodeCube(Myst3Engine *vm, uint16 id);
virtual ~NodeCube();
void draw();
};
} /* namespace Myst3 */
#endif /* NODECUBE_H_ */

View File

@ -0,0 +1,97 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/directorysubentry.h"
#include "engines/myst3/myst3.h"
#include "engines/myst3/nodeframe.h"
#include "engines/myst3/scene.h"
namespace Myst3 {
NodeFrame::NodeFrame(Myst3Engine *vm, uint16 id) :
Node(vm, id) {
const DirectorySubEntry *jpegDesc = _vm->getFileDescription(0, id, 1, DirectorySubEntry::kFrame);
if (!jpegDesc)
jpegDesc = _vm->getFileDescription(0, id, 0, DirectorySubEntry::kFrame);
if (!jpegDesc)
jpegDesc = _vm->getFileDescription(0, id, 1, DirectorySubEntry::kMenuFrame);
if (!jpegDesc)
error("Frame %d does not exist", id);
Common::MemoryReadStream *jpegStream = jpegDesc->getData();
if (jpegStream) {
Graphics::JPEG jpeg;
jpeg.read(jpegStream);
_faces[0] = new Face();
_faces[0]->setTextureFromJPEG(&jpeg);
_faces[0]->markTextureDirty();
delete jpegStream;
}
}
NodeFrame::~NodeFrame() {
}
void NodeFrame::draw() {
// Size and position of the frame
float w;
float h;
float top;
if (_vm->_viewType == kMenu) {
w = Scene::_originalWidth;
h = Scene::_originalHeight;
top = 0;
} else {
w = Scene::_originalWidth;
h = Scene::_frameHeight;
top = Scene::_topBorderHeight;
}
// Update the OpenGL texture if needed
_faces[0]->uploadTexture();
// Used fragment of texture
const float u = w / (float)_cubeTextureSize;
const float v = h / (float)_cubeTextureSize;
glEnable(GL_TEXTURE_2D);
glDepthMask(GL_FALSE);
glBindTexture(GL_TEXTURE_2D, _faces[0]->_textureId);
glBegin(GL_TRIANGLE_STRIP); // Z+
glTexCoord2f(0, v); glVertex3f( 0, top + h, 1.0f);
glTexCoord2f(u, v); glVertex3f( w, top + h, 1.0f);
glTexCoord2f(0, 0); glVertex3f( 0, top + 0, 1.0f);
glTexCoord2f(u, 0); glVertex3f( w, top + 0, 1.0f);
glEnd();
glDepthMask(GL_TRUE);
}
} /* namespace Myst3 */

39
engines/myst3/nodeframe.h Normal file
View File

@ -0,0 +1,39 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef NODEFRAME_H_
#define NODEFRAME_H_
#include "engines/myst3/node.h"
namespace Myst3 {
class NodeFrame : public Node {
public:
NodeFrame(Myst3Engine *vm, uint16 id);
virtual ~NodeFrame();
void draw();
};
} /* namespace Myst3 */
#endif /* NODEFRAME_H_ */

161
engines/myst3/puzzles.cpp Normal file
View File

@ -0,0 +1,161 @@
/* ResidualVM - A 3D game interpreter
*
* ResidualVM 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 library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
#include "engines/myst3/puzzles.h"
#include "engines/myst3/myst3.h"
#include "engines/myst3/variables.h"
namespace Myst3 {
Puzzles::Puzzles(Myst3Engine *vm) :
_vm(vm) {
}
Puzzles::~Puzzles() {
}
void Puzzles::run(uint16 id, uint16 arg0, uint16 arg1, uint16 arg3) {
switch (id) {
case 8:
journalSaavedro(arg0);
break;
case 9:
journalAtrus(arg0, arg1);
break;
default:
warning("Puzzle %d is not implemented", id);
}
}
void Puzzles::journalSaavedro(int16 move) {
uint16 chapter = _vm->_vars->getJournalSaavedroChapter();
int16 page = _vm->_vars->getJournalSaavedroPageInChapter();
if (!_journalSaavedroHasChapter(chapter))
chapter = _journalSaavedroNextChapter(chapter, true);
if (move > 0) {
// Go to the next available page
int16 pageCount = _journalSaavedroPageCount(chapter);
page++;
if (page == pageCount) {
chapter = _journalSaavedroNextChapter(chapter, true);
page = 0;
}
_vm->_vars->setJournalSaavedroChapter(chapter);
_vm->_vars->setJournalSaavedroPageInChapter(page);
} else if (move < 0) {
// Go to the previous available page
page--;
if (page < 0) {
chapter = _journalSaavedroNextChapter(chapter, false);
page = _journalSaavedroPageCount(chapter) - 1;
}
_vm->_vars->setJournalSaavedroChapter(chapter);
_vm->_vars->setJournalSaavedroPageInChapter(page);
} else {
// Display current page
int16 chapterStartNode = _journalSaavedroGetNode(chapter);
int16 closed = 0;
int16 opened = 0;
int16 lastPage = 0;
if (chapter > 0) {
opened = 1;
if (chapter == 21)
lastPage = 2;
else
lastPage = 1;
} else {
closed = 1;
}
uint16 nodeRight;
uint16 nodeLeft;
if (page || !chapter) {
nodeRight = chapterStartNode + page;
nodeLeft = chapterStartNode + page;
} else {
nodeRight = chapterStartNode + page;
uint16 chapterLeft = _journalSaavedroNextChapter(chapter, false);
if (chapterLeft > 0)
nodeLeft = _journalSaavedroGetNode(chapterLeft + 1);
else
nodeLeft = 2;
}
_vm->_vars->setJournalSaavedroClosed(closed);
_vm->_vars->setJournalSaavedroOpen(opened);
_vm->_vars->setJournalSaavedroLastPage(lastPage);
// TODO: Draw nodeLeft on the left part of the screen
_vm->loadNodeFrame(nodeRight);
}
}
uint16 Puzzles::_journalSaavedroGetNode(uint16 chapter) {
const DirectorySubEntry *desc = _vm->getFileDescription(0, 1200, 0, DirectorySubEntry::kMetadata);
if (!desc)
error("Node 1200 does not exist");
return desc->getMiscData(chapter) + 199;
}
uint16 Puzzles::_journalSaavedroPageCount(uint16 chapter) {
uint16 chapterStartNode = _journalSaavedroGetNode(chapter);
if (chapter != 21)
return _journalSaavedroGetNode(chapter + 1) - chapterStartNode;
else
return 1;
}
bool Puzzles::_journalSaavedroHasChapter(uint16 chapter) {
return _vm->_vars->get(285 + chapter) != 0;
}
uint16 Puzzles::_journalSaavedroNextChapter(uint16 chapter, bool forward) {
do {
if (forward)
chapter++;
else
chapter--;
} while (!_journalSaavedroHasChapter(chapter));
return chapter;
}
void Puzzles::journalAtrus(uint16 node, uint16 var) {
uint numPages = 0;
while (_vm->getFileDescription(0, node++, 0, DirectorySubEntry::kFrame))
numPages++;
_vm->_vars->set(var, numPages - 1);
}
} /* namespace Myst3 */

52
engines/myst3/puzzles.h Normal file
View File

@ -0,0 +1,52 @@
/* ResidualVM - A 3D game interpreter
*
* ResidualVM 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 library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
#ifndef PUZZLES_H_
#define PUZZLES_H_
#include "common/scummsys.h"
namespace Myst3 {
class Myst3Engine;
class Puzzles {
public:
Puzzles(Myst3Engine *vm);
virtual ~Puzzles();
void run(uint16 id, uint16 arg0 = 0, uint16 arg1 = 0, uint16 arg3 = 0);
private:
Myst3Engine *_vm;
void journalSaavedro(int16 move);
uint16 _journalSaavedroGetNode(uint16 chapter);
uint16 _journalSaavedroPageCount(uint16 chapter);
bool _journalSaavedroHasChapter(uint16 chapter);
uint16 _journalSaavedroNextChapter(uint16 chapter, bool forward);
void journalAtrus(uint16 node, uint16 var);
};
} /* namespace Myst3 */
#endif /* PUZZLES_H_ */

137
engines/myst3/scene.cpp Normal file
View File

@ -0,0 +1,137 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/scene.h"
#include "engines/myst3/node.h"
namespace Myst3 {
Scene::Scene():
_cameraPitch(0.0f), _cameraHeading(0.0f)
{
}
void Scene::init(int width, int height) {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
}
void Scene::clear() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1.0f, 1.0f, 1.0f);
}
void Scene::setupCameraOrtho2D() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, _originalWidth, _originalHeight, 0.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void Scene::setupCameraPerspective() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(65.0, (GLfloat)_originalWidth /(GLfloat)_originalHeight, 0.1, 100.0);
// Rotate the model to simulate the rotation of the camera
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(_cameraPitch, -1.0f, 0.0f, 0.0f);
glRotatef(_cameraHeading - 180.0f, 0.0f, 1.0f, 0.0f);
}
void Scene::updateCamera(Common::Point &mouse) {
_cameraPitch -= mouse.y / 3.0f;
_cameraHeading += mouse.x / 3.0f;
// Keep heading in 0..360 range
if (_cameraHeading > 360.0f)
_cameraHeading -= 360.0f;
else if (_cameraHeading < 0.0f)
_cameraHeading += 360.0f;
// Keep pitch within allowed values
_cameraPitch = CLIP(_cameraPitch, -60.0f, 80.0f);
}
void Scene::lookAt(float pitch, float heading) {
_cameraPitch = pitch;
_cameraHeading = heading;
}
void Scene::drawBlackRect(const Common::Rect &r) {
glDisable(GL_TEXTURE_2D);
glColor3f(0.0f, 0.0f, 0.0f);
glBegin(GL_TRIANGLE_STRIP);
glVertex3f( r.left, r.bottom, 0.0f);
glVertex3f( r.right, r.bottom, 0.0f);
glVertex3f( r.left, r.top, 0.0f);
glVertex3f( r.right, r.top, 0.0f);
glEnd();
}
void Scene::drawBlackBorders() {
Common::Rect top = Common::Rect(_originalWidth, _topBorderHeight);
Common::Rect bottom = Common::Rect(_originalWidth, _bottomBorderHeight);
bottom.translate(0, _topBorderHeight + _frameHeight);
drawBlackRect(top);
drawBlackRect(bottom);
}
void Scene::drawSunspotFlare(const SunSpot &s) {
Common::Rect frame = Common::Rect(_originalWidth, _frameHeight);
frame.translate(0, _topBorderHeight);
float r = ((s.color >> 16) & 0xFF) / 255.0;
float g = ((s.color >> 8) & 0xFF) / 255.0;
float b = ((s.color >> 0) & 0xFF) / 255.0;
float a = s.intensity * s.radius / 255.0;
glDisable(GL_TEXTURE_2D);
glColor4f(r, g, b, a);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBegin(GL_TRIANGLE_STRIP);
glVertex3f( frame.left, frame.bottom, 0.0f);
glVertex3f( frame.right, frame.bottom, 0.0f);
glVertex3f( frame.left, frame.top, 0.0f);
glVertex3f( frame.right, frame.top, 0.0f);
glEnd();
glDisable(GL_BLEND);
}
} // end of namespace Myst3

71
engines/myst3/scene.h Normal file
View File

@ -0,0 +1,71 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef MYST3_SCENE_H
#define MYST3_SCENE_H
#ifdef SDL_BACKEND
#include <SDL_opengl.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#include "common/rect.h"
namespace Myst3 {
class SunSpot;
class Scene {
private:
float _cameraPitch;
float _cameraHeading;
Common::Point _mouseOld;
void drawBlackRect(const Common::Rect &r);
public:
Scene();
void init(int width, int height);
void clear();
void setupCameraPerspective();
void setupCameraOrtho2D();
void updateCamera(Common::Point &mouse);
Common::Point getMousePos() { return Common::Point(_cameraHeading, _cameraPitch); }
void lookAt(float pitch, float heading);
void drawBlackBorders();
void drawSunspotFlare(const SunSpot &s);
static const int _originalWidth = 640;
static const int _originalHeight = 480;
static const int _topBorderHeight = 30;
static const int _bottomBorderHeight = 90;
static const int _frameHeight = 360;
};
} // end of namespace Myst3
#endif

1564
engines/myst3/script.cpp Normal file

File diff suppressed because it is too large Load Diff

229
engines/myst3/script.h Normal file
View File

@ -0,0 +1,229 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef SCRIPT_H_
#define SCRIPT_H_
#include "common/array.h"
namespace Myst3 {
class Myst3Engine;
class Puzzles;
struct Opcode;
#define DECLARE_OPCODE(x) void x(Context &c, const Opcode &cmd)
class Script {
public:
Script(Myst3Engine *vm);
virtual ~Script();
bool run(const Common::Array<Opcode> *script);
const Common::String describeOpcode(const Opcode &opcode);
private:
struct Context {
bool endScript;
bool result;
const Common::Array<Opcode> *script;
Common::Array<Opcode>::const_iterator op;
};
typedef void (Script::*CommandProc)(Context &c, const Opcode &cmd);
enum ArgumentType {
kUnknown,
kVar,
kValue,
kEvalValue,
kCondition
};
struct Command {
Command() {}
Command(uint16 o, CommandProc p, const char *d, uint8 argc, ...) : op(o), proc(p), desc(d) {
va_list types;
for(int j = 0; j < 5; j++)
argType[j] = kUnknown;
va_start(types, argc);
for(int j = 0; j < argc; j++)
argType[j] = (ArgumentType) va_arg(types, int);
va_end(types);
}
uint16 op;
CommandProc proc;
const char *desc;
ArgumentType argType[5];
};
Myst3Engine *_vm;
Puzzles *_puzzles;
Common::Array<Command> _commands;
const Command &findCommand(uint16 op);
const Common::String describeCommand(uint16 op);
const Common::String describeArgument(ArgumentType type, int16 value);
void runOp(Context &c, const Opcode &op);
void goToElse(Context &c);
void runScriptForVarDrawFramesHelper(uint16 var, int32 startValue, int32 endValue, uint16 script, int32 numFrames);
DECLARE_OPCODE(badOpcode);
DECLARE_OPCODE(nodeCubeInit);
DECLARE_OPCODE(nodeCubeInitIndex);
DECLARE_OPCODE(nodeFrameInit);
DECLARE_OPCODE(nodeFrameInitCond);
DECLARE_OPCODE(nodeFrameInitIndex);
DECLARE_OPCODE(nodeMenuInit);
DECLARE_OPCODE(stopWholeScript);
DECLARE_OPCODE(spotItemAdd);
DECLARE_OPCODE(spotItemAddCond);
DECLARE_OPCODE(spotItemAddCondFade);
DECLARE_OPCODE(movieInitLooping);
DECLARE_OPCODE(movieInitCondLooping);
DECLARE_OPCODE(movieInitCond);
DECLARE_OPCODE(movieInitPreloadLooping);
DECLARE_OPCODE(movieInitCondPreloadLooping);
DECLARE_OPCODE(movieInitCondPreload);
DECLARE_OPCODE(movieInitFrameVar);
DECLARE_OPCODE(movieInitFrameVarPreload);
DECLARE_OPCODE(movieInitOverrridePosition);
DECLARE_OPCODE(movieInitScriptedPosition);
DECLARE_OPCODE(sunspotAdd);
DECLARE_OPCODE(sunspotAddIntensity);
DECLARE_OPCODE(sunspotAddVarIntensity);
DECLARE_OPCODE(sunspotAddIntensityColor);
DECLARE_OPCODE(sunspotAddVarIntensityColor);
DECLARE_OPCODE(sunspotAddIntensityRadius);
DECLARE_OPCODE(sunspotAddVarIntensityRadius);
DECLARE_OPCODE(sunspotAddIntColorRadius);
DECLARE_OPCODE(sunspotAddVarIntColorRadius);
DECLARE_OPCODE(inventoryAddFront);
DECLARE_OPCODE(inventoryAddBack);
DECLARE_OPCODE(inventoryRemove);
DECLARE_OPCODE(inventoryReset);
DECLARE_OPCODE(inventoryAddSaavChapter);
DECLARE_OPCODE(varSetZero);
DECLARE_OPCODE(varSetOne);
DECLARE_OPCODE(varSetTwo);
DECLARE_OPCODE(varSetOneHundred);
DECLARE_OPCODE(varSetValue);
DECLARE_OPCODE(varToggle);
DECLARE_OPCODE(varSetOneIfZero);
DECLARE_OPCODE(varRandRange);
DECLARE_OPCODE(polarToRect);
DECLARE_OPCODE(varRemoveBits);
DECLARE_OPCODE(varToggleBits);
DECLARE_OPCODE(varCopy);
DECLARE_OPCODE(varSetBitsFromVar);
DECLARE_OPCODE(varSetBits);
DECLARE_OPCODE(varApplyMask);
DECLARE_OPCODE(varSwap);
DECLARE_OPCODE(varIncrement);
DECLARE_OPCODE(varIncrementMax);
DECLARE_OPCODE(varIncrementMaxLooping);
DECLARE_OPCODE(varAddValueMaxLooping);
DECLARE_OPCODE(varDecrement);
DECLARE_OPCODE(varDecrementMin);
DECLARE_OPCODE(varAddValueMax);
DECLARE_OPCODE(varSubValueMin);
DECLARE_OPCODE(varZeroRange);
DECLARE_OPCODE(varCopyRange);
DECLARE_OPCODE(varSetRange);
DECLARE_OPCODE(varIncrementMaxTen);
DECLARE_OPCODE(varAddValue);
DECLARE_OPCODE(varArrayAddValue);
DECLARE_OPCODE(varAddVarValue);
DECLARE_OPCODE(varSubValue);
DECLARE_OPCODE(varSubVarValue);
DECLARE_OPCODE(varModValue);
DECLARE_OPCODE(varMultValue);
DECLARE_OPCODE(varMultVarValue);
DECLARE_OPCODE(varDivValue);
DECLARE_OPCODE(varDivVarValue);
DECLARE_OPCODE(varMinValue);
DECLARE_OPCODE(varClipValue);
DECLARE_OPCODE(varClipChangeBound);
DECLARE_OPCODE(varAbsoluteSubValue);
DECLARE_OPCODE(varAbsoluteSubVar);
DECLARE_OPCODE(varRatioToPercents);
DECLARE_OPCODE(varRotateValue3);
DECLARE_OPCODE(ifElse);
DECLARE_OPCODE(ifCondition);
DECLARE_OPCODE(ifCond1AndCond2);
DECLARE_OPCODE(ifCond1OrCond2);
DECLARE_OPCODE(ifOneVarSetInRange);
DECLARE_OPCODE(ifVarEqualsValue);
DECLARE_OPCODE(ifVarNotEqualsValue);
DECLARE_OPCODE(ifVar1EqualsVar2);
DECLARE_OPCODE(ifVar1NotEqualsVar2);
DECLARE_OPCODE(ifVarSupEqValue);
DECLARE_OPCODE(ifVarInfEqValue);
DECLARE_OPCODE(ifVarInRange);
DECLARE_OPCODE(ifVarNotInRange);
DECLARE_OPCODE(ifVar1SupEqVar2);
DECLARE_OPCODE(ifVar1SupVar2);
DECLARE_OPCODE(ifVar1InfEqVar2);
DECLARE_OPCODE(ifVarHasAllBitsSet);
DECLARE_OPCODE(ifVarHasNoBitsSet);
DECLARE_OPCODE(ifVarHasSomeBitsSet);
DECLARE_OPCODE(ifMouseIsInRect);
DECLARE_OPCODE(chooseNextNode);
DECLARE_OPCODE(goToNodeTransition);
DECLARE_OPCODE(goToNodeTrans2);
DECLARE_OPCODE(goToNodeTrans1);
DECLARE_OPCODE(goToRoomNode);
DECLARE_OPCODE(zipToNode);
DECLARE_OPCODE(zipToRoomNode);
DECLARE_OPCODE(moviePlay);
DECLARE_OPCODE(moviePlaySynchronized);
DECLARE_OPCODE(runScriptWhileCond);
DECLARE_OPCODE(runScriptWhileCondEachXFrames);
DECLARE_OPCODE(runScriptForVar);
DECLARE_OPCODE(runScriptForVarEachXFrames);
DECLARE_OPCODE(runScriptForVarStartVar);
DECLARE_OPCODE(runScriptForVarStartVarEachXFrames);
DECLARE_OPCODE(runScriptForVarEndVar);
DECLARE_OPCODE(runScriptForVarEndVarEachXFrames);
DECLARE_OPCODE(runScriptForVarStartEndVar);
DECLARE_OPCODE(runScriptForVarStartEndVarEachXFrames);
DECLARE_OPCODE(drawFramesForVar);
DECLARE_OPCODE(drawFramesForVarEachTwoFrames);
DECLARE_OPCODE(drawFramesForVarStartEndVarEachTwoFrames);
DECLARE_OPCODE(runScript);
DECLARE_OPCODE(runPuzzle1);
DECLARE_OPCODE(runPuzzle2);
DECLARE_OPCODE(runPuzzle3);
DECLARE_OPCODE(runPuzzle4);
};
} /* namespace Myst3 */
#endif /* SCRIPT_H_ */

183
engines/myst3/variables.cpp Normal file
View File

@ -0,0 +1,183 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "engines/myst3/variables.h"
namespace Myst3 {
Variables::Variables(Myst3Engine *vm):
_vm(vm) {
#define VAR(var, x, unk) _descriptions.setVal(var, Description(var, #x, unk));
VAR(61, LocationAge, false)
VAR(62, LocationRoom, false)
VAR(63, LocationNode, false)
VAR(64, BookSavedAge, false)
VAR(65, BookSavedRoom, false)
VAR(66, BookSavedNode, false)
VAR(115, SunspotIntensity, false)
VAR(116, SunspotColor, false)
VAR(117, SunspotRadius, false)
VAR(142, MovieOverrideStartFrame, true)
VAR(143, MovieOverrideEndFrame, true)
VAR(144, MovieVolume1, true)
VAR(145, MovieVolume2, true)
VAR(146, MovieUnk146, true)
VAR(147, MovieUnk147, true)
VAR(148, MovieUnk148, true)
VAR(149, MovieConditionBit, true)
VAR(150, MoviePreloadToMemory, true)
VAR(151, MovieScriptDriven, true)
VAR(152, MovieNextFrameSetVar, true)
VAR(153, MovieNextFrameGetVar, true)
VAR(154, MovieStartFrameVar, true)
VAR(155, MovieEndFrameVar, true)
VAR(156, MovieForce2d, true)
VAR(157, MovieVolumeVar, true)
VAR(158, MovieSoundHeading, true)
VAR(159, MoviePanningStrenght, true)
VAR(160, MovieSynchronized, true)
VAR(161, MovieUnk161, true)
VAR(162, MovieUnk162, true)
VAR(163, MovieOverrideCondition, true)
VAR(164, MovieUVar, true)
VAR(165, MovieVVar, true)
VAR(166, MovieOverridePosition, true)
VAR(167, MovieOverridePosU, true)
VAR(168, MovieOverridePosV, true)
VAR(169, MovieScale, true)
VAR(170, MovieUnk170, true)
VAR(171, MovieUnk171, true)
VAR(172, MovieUnk172, true)
VAR(173, MoviePlayingVar, true)
VAR(178, MovieUnk178, true)
VAR(189, LocationNextNode, false)
VAR(190, LocationNextRoom, false)
VAR(191, LocationNextAge, false)
VAR(277, JournalAtrusState, false)
VAR(279, JournalSaavedroState, false)
VAR(280, JournalSaavedroClosed, false)
VAR(281, JournalSaavedroOpen, false)
VAR(282, JournalSaavedroLastPage, false)
VAR(283, JournalSaavedroChapter, false)
VAR(284, JournalSaavedroPageInChapter, false)
VAR(480, BookStateTomahna, false)
VAR(481, BookStateReleeshahn, false)
#undef VAR
memset(&_vars, 0, sizeof(_vars));
_vars[1] = 1;
}
Variables::~Variables() {
}
void Variables::checkRange(uint16 var) {
if (var < 1 || var > 2047)
error("Variable out of range %d", var);
}
uint32 Variables::get(uint16 var) {
checkRange(var);
return _vars[var];
}
void Variables::set(uint16 var, uint32 value) {
checkRange(var);
if (_descriptions.contains(var)) {
const Description &d = _descriptions.getVal(var);
if (d.unknown)
warning("A script is writing to the unimplemented engine-mapped var %d (%s)", var, d.name);
}
_vars[var] = value;
}
bool Variables::evaluate(int16 condition) {
uint16 unsignedCond = abs(condition);
uint16 var = unsignedCond & 2047;
int32 varValue = get(var);
int32 targetValue = (unsignedCond >> 11) - 1;
if (targetValue >= 0) {
if (condition >= 0)
return varValue == targetValue;
else
return varValue != targetValue;
} else {
if (condition >= 0)
return varValue != 0;
else
return varValue == 0;
}
}
uint32 Variables::valueOrVarValue(int16 value) {
if (value < 0)
return get(-value);
return value;
}
uint32 Variables::engineGet(uint16 var) {
if (!_descriptions.contains(var))
error("The engine is trying to access an undescribed var (%d)", var);
return _vars[var];
}
void Variables::engineSet(uint16 var, uint32 value) {
if (!_descriptions.contains(var))
error("The engine is trying to access an undescribed var (%d)", var);
_vars[var] = value;
}
const Common::String Variables::describeVar(uint16 var) {
if (_descriptions.contains(var)) {
const Description &d = _descriptions.getVal(var);
return Common::String::format("v%s", d.name);
} else {
return Common::String::format("v%d", var);
}
}
const Common::String Variables::describeCondition(int16 condition) {
uint16 unsignedCond = abs(condition);
uint16 var = unsignedCond & 2047;
int16 value = (unsignedCond >> 11) - 1;
return Common::String::format("c[%s %s %d]",
describeVar(var).c_str(),
(condition >= 0 && value >= 0) || (condition < 0 && value < 0) ? "==" : "!=",
value >= 0 ? value : 0);
}
} /* namespace Myst3 */

115
engines/myst3/variables.h Normal file
View File

@ -0,0 +1,115 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef VARIABLES_H_
#define VARIABLES_H_
#include "common/hashmap.h"
#include "engines/myst3/myst3.h"
namespace Myst3 {
#define DECLARE_VAR(num, name) \
void set##name(uint32 value) { engineSet(num, value); } \
uint32 get##name() { return engineGet(num); }
class Variables {
public:
Variables(Myst3Engine *vm);
virtual ~Variables();
uint32 get(uint16 var);
void set(uint16 var, uint32 value);
bool evaluate(int16 condition);
uint32 valueOrVarValue(int16 value);
const Common::String describeVar(uint16 var);
const Common::String describeCondition(int16 condition);
DECLARE_VAR(61, LocationAge)
DECLARE_VAR(62, LocationRoom)
DECLARE_VAR(63, LocationNode)
DECLARE_VAR(64, BookSavedAge)
DECLARE_VAR(65, BookSavedRoom)
DECLARE_VAR(66, BookSavedNode)
DECLARE_VAR(115, SunspotIntensity)
DECLARE_VAR(116, SunspotColor)
DECLARE_VAR(117, SunspotRadius)
DECLARE_VAR(142, MovieStartFrame)
DECLARE_VAR(143, MovieEndFrame)
DECLARE_VAR(149, MovieConditionBit)
DECLARE_VAR(150, MoviePreloadToMemory)
DECLARE_VAR(151, MovieScriptDriven)
DECLARE_VAR(152, MovieNextFrameSetVar)
DECLARE_VAR(153, MovieNextFrameGetVar)
DECLARE_VAR(154, MovieStartFrameVar)
DECLARE_VAR(155, MovieEndFrameVar)
DECLARE_VAR(160, MovieSynchronized)
DECLARE_VAR(163, MovieOverrideCondition)
DECLARE_VAR(164, MovieUVar)
DECLARE_VAR(165, MovieVVar)
DECLARE_VAR(166, MovieOverridePosition)
DECLARE_VAR(167, MovieOverridePosU)
DECLARE_VAR(168, MovieOverridePosV)
DECLARE_VAR(173, MoviePlayingVar)
DECLARE_VAR(189, LocationNextNode)
DECLARE_VAR(190, LocationNextRoom)
DECLARE_VAR(191, LocationNextAge)
DECLARE_VAR(277, JournalAtrusState)
DECLARE_VAR(279, JournalSaavedroState)
DECLARE_VAR(280, JournalSaavedroClosed)
DECLARE_VAR(281, JournalSaavedroOpen)
DECLARE_VAR(282, JournalSaavedroLastPage)
DECLARE_VAR(283, JournalSaavedroChapter)
DECLARE_VAR(284, JournalSaavedroPageInChapter)
DECLARE_VAR(480, BookStateTomahna)
DECLARE_VAR(481, BookStateReleeshahn)
private:
Myst3Engine *_vm;
uint32 _vars[2048];
struct Description {
Description() {}
Description(uint16 v, const char *n, bool u) : var(v), name(n), unknown(u) {}
uint16 var;
const char *name;
bool unknown;
};
Common::HashMap<uint16, Description> _descriptions;
void checkRange(uint16 var);
uint32 engineGet(uint16 var);
void engineSet(uint16 var, uint32 value);
};
} /* namespace Myst3 */
#endif /* VARIABLES_H_ */

50
graphics/conversion.h Normal file
View File

@ -0,0 +1,50 @@
/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/graphics/conversion.h $
* $Id: conversion.h 43626 2009-08-22 00:27:13Z dhewg $
*
*/
#ifndef GRAPHICS_CONVERSION_H
#define GRAPHICS_CONVERSION_H
#include "common/util.h"
#include "graphics/pixelformat.h"
namespace Graphics {
/** Converting a color from YUV to RGB colorspace. */
inline static void YUV2RGB(byte y, byte u, byte v, byte &r, byte &g, byte &b) {
r = CLIP<int>(y + ((1357 * (v - 128)) >> 10), 0, 255);
g = CLIP<int>(y - (( 691 * (v - 128)) >> 10) - ((333 * (u - 128)) >> 10), 0, 255);
b = CLIP<int>(y + ((1715 * (u - 128)) >> 10), 0, 255);
}
/** Converting a color from RGB to YUV colorspace. */
inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) {
y = CLIP<int>( ((r * 306) >> 10) + ((g * 601) >> 10) + ((b * 117) >> 10) , 0, 255);
u = CLIP<int>(-((r * 172) >> 10) - ((g * 340) >> 10) + ((b * 512) >> 10) + 128, 0, 255);
v = CLIP<int>( ((r * 512) >> 10) - ((g * 429) >> 10) - ((b * 83) >> 10) + 128, 0, 255);
}
} // end of namespace Graphics
#endif // GRAPHICS_CONVERSION_H

View File

@ -117,18 +117,23 @@ Surface *BMPDecoder::decodeImage(Common::SeekableReadStream &stream, const Pixel
assert(newSurf);
newSurf->create(info.width, info.height, format);
assert(newSurf->pixels);
OverlayColor *curPixel = (OverlayColor*)newSurf->pixels + (newSurf->h-1) * newSurf->w;
byte *curPixel = (byte*)newSurf->pixels + (newSurf->h-1) * newSurf->pitch;
int pitchAdd = info.width % 4;
for (int i = 0; i < newSurf->h; ++i) {
for (int i2 = 0; i2 < newSurf->w; ++i2) {
b = stream.readByte();
g = stream.readByte();
r = stream.readByte();
*curPixel = format.RGBToColor(r, g, b);
++curPixel;
if (format.bytesPerPixel == 2)
*((uint16 *)curPixel) = format.RGBToColor(r, g, b);
else
*((uint32 *)curPixel) = format.RGBToColor(r, g, b);
curPixel += format.bytesPerPixel;
}
stream.seek(pitchAdd, SEEK_CUR);
curPixel -= newSurf->w*2;
curPixel -= newSurf->pitch * 2;
}
stream.seek(0);

735
graphics/jpeg.cpp Normal file
View File

@ -0,0 +1,735 @@
/* 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 "graphics/conversion.h"
#include "graphics/jpeg.h"
#include "graphics/pixelformat.h"
#include "common/debug.h"
#include "common/endian.h"
#include "common/stream.h"
#include "common/textconsole.h"
namespace Graphics {
// Order used to traverse the quantization tables
static const uint8 _zigZagOrder[64] = {
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63
};
// IDCT table built with :
// _idct8x8[x][y] = cos(((2 * x + 1) * y) * (M_PI / 16.0)) * 0.5;
// _idct8x8[x][y] /= sqrt(2.0) if y == 0
static const double _idct8x8[8][8] = {
{ 0.353553390593274, 0.490392640201615, 0.461939766255643, 0.415734806151273, 0.353553390593274, 0.277785116509801, 0.191341716182545, 0.097545161008064 },
{ 0.353553390593274, 0.415734806151273, 0.191341716182545, -0.097545161008064, -0.353553390593274, -0.490392640201615, -0.461939766255643, -0.277785116509801 },
{ 0.353553390593274, 0.277785116509801, -0.191341716182545, -0.490392640201615, -0.353553390593274, 0.097545161008064, 0.461939766255643, 0.415734806151273 },
{ 0.353553390593274, 0.097545161008064, -0.461939766255643, -0.277785116509801, 0.353553390593274, 0.415734806151273, -0.191341716182545, -0.490392640201615 },
{ 0.353553390593274, -0.097545161008064, -0.461939766255643, 0.277785116509801, 0.353553390593274, -0.415734806151273, -0.191341716182545, 0.490392640201615 },
{ 0.353553390593274, -0.277785116509801, -0.191341716182545, 0.490392640201615, -0.353553390593273, -0.097545161008064, 0.461939766255643, -0.415734806151273 },
{ 0.353553390593274, -0.415734806151273, 0.191341716182545, 0.097545161008064, -0.353553390593274, 0.490392640201615, -0.461939766255643, 0.277785116509801 },
{ 0.353553390593274, -0.490392640201615, 0.461939766255643, -0.415734806151273, 0.353553390593273, -0.277785116509801, 0.191341716182545, -0.097545161008064 }
};
JPEG::JPEG() :
_stream(NULL), _w(0), _h(0), _numComp(0), _components(NULL), _numScanComp(0),
_scanComp(NULL), _currentComp(NULL) {
// Initialize the quantization tables
for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++)
_quant[i] = NULL;
// Initialize the Huffman tables
for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
_huff[i].count = 0;
_huff[i].values = NULL;
_huff[i].sizes = NULL;
_huff[i].codes = NULL;
}
}
JPEG::~JPEG() {
reset();
}
Surface *JPEG::getSurface(const PixelFormat &format) {
// Make sure we have loaded data
if (!isLoaded())
return 0;
// Only accept >8bpp surfaces
if (format.bytesPerPixel == 1)
return 0;
// Get our component surfaces
Graphics::Surface *yComponent = getComponent(1);
Graphics::Surface *uComponent = getComponent(2);
Graphics::Surface *vComponent = getComponent(3);
Graphics::Surface *output = new Graphics::Surface();
output->create(yComponent->w, yComponent->h, format);
for (uint16 i = 0; i < output->h; i++) {
for (uint16 j = 0; j < output->w; j++) {
byte r = 0, g = 0, b = 0;
YUV2RGB(*((byte *)yComponent->getBasePtr(j, i)), *((byte *)uComponent->getBasePtr(j, i)), *((byte *)vComponent->getBasePtr(j, i)), r, g, b);
if (format.bytesPerPixel == 2)
*((uint16 *)output->getBasePtr(j, i)) = format.RGBToColor(r, g, b);
else
*((uint32 *)output->getBasePtr(j, i)) = format.RGBToColor(r, g, b);
}
}
return output;
}
void JPEG::reset() {
// Reset member variables
_stream = NULL;
_w = _h = 0;
// Free the components
for (int c = 0; c < _numComp; c++)
_components[c].surface.free();
delete[] _components; _components = NULL;
_numComp = 0;
// Free the scan components
delete[] _scanComp; _scanComp = NULL;
_numScanComp = 0;
_currentComp = NULL;
// Free the quantization tables
for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++) {
delete[] _quant[i];
_quant[i] = NULL;
}
// Free the Huffman tables
for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
_huff[i].count = 0;
delete[] _huff[i].values; _huff[i].values = NULL;
delete[] _huff[i].sizes; _huff[i].sizes = NULL;
delete[] _huff[i].codes; _huff[i].codes = NULL;
}
}
bool JPEG::read(Common::SeekableReadStream *stream) {
// Reset member variables and tables from previous reads
reset();
// Save the input stream
_stream = stream;
bool ok = true;
bool done = false;
while (!_stream->eos() && ok && !done) {
// Read the marker
// WORKAROUND: While each and every JPEG file should end with
// an EOI (end of image) tag, in reality this may not be the
// case. For instance, at least one image in the Masterpiece
// edition of Myst doesn't, yet other programs are able to read
// the image without complaining.
//
// Apparently, the customary workaround is to insert a fake
// EOI tag.
uint16 marker = _stream->readByte();
bool fakeEOI = false;
if (_stream->eos()) {
fakeEOI = true;
marker = 0xFF;
}
if (marker != 0xFF) {
error("JPEG: Invalid marker[0]: 0x%02X", marker);
ok = false;
break;
}
while (marker == 0xFF && !_stream->eos())
marker = _stream->readByte();
if (_stream->eos()) {
fakeEOI = true;
marker = 0xD9;
}
if (fakeEOI)
warning("JPEG: Inserted fake EOI");
// Process the marker data
switch (marker) {
case 0xC0: // Start Of Frame
ok = readSOF0();
break;
case 0xC4: // Define Huffman Tables
ok = readDHT();
break;
case 0xD8: // Start Of Image
break;
case 0xD9: // End Of Image
done = true;
break;
case 0xDA: // Start Of Scan
ok = readSOS();
break;
case 0xDB: // Define Quantization Tables
ok = readDQT();
break;
case 0xE0: // JFIF/JFXX segment
ok = readJFIF();
break;
case 0xFE: // Comment
_stream->seek(_stream->readUint16BE() - 2, SEEK_CUR);
break;
default: { // Unknown marker
uint16 size = _stream->readUint16BE();
warning("JPEG: Unknown marker %02X, skipping %d bytes", marker, size - 2);
_stream->seek(size - 2, SEEK_CUR);
}
}
}
return ok;
}
bool JPEG::readJFIF() {
uint16 length = _stream->readUint16BE();
uint32 tag = _stream->readUint32BE();
if (tag != MKTAG('J','F','I','F')) {
warning("JPEG::readJFIF() tag mismatch");
return false;
}
if (_stream->readByte() != 0) { // NULL
warning("JPEG::readJFIF() NULL mismatch");
return false;
}
byte majorVersion = _stream->readByte();
byte minorVersion = _stream->readByte();
if(majorVersion != 1 || minorVersion != 1)
warning("JPEG::readJFIF() Non-v1.1 JPEGs may not be handled correctly");
/* byte densityUnits = */ _stream->readByte();
/* uint16 xDensity = */ _stream->readUint16BE();
/* uint16 yDensity = */ _stream->readUint16BE();
byte thumbW = _stream->readByte();
byte thumbH = _stream->readByte();
_stream->seek(thumbW * thumbH * 3, SEEK_CUR); // Ignore thumbnail
if (length != (thumbW * thumbH * 3) + 16) {
warning("JPEG::readJFIF() length mismatch");
return false;
}
return true;
}
// Marker 0xC0 (Start Of Frame, Baseline DCT)
bool JPEG::readSOF0() {
debug(5, "JPEG: readSOF0");
uint16 size = _stream->readUint16BE();
// Read the sample precision
uint8 precision = _stream->readByte();
if (precision != 8) {
warning("JPEG: Just 8 bit precision supported at the moment");
return false;
}
// Image size
_h = _stream->readUint16BE();
_w = _stream->readUint16BE();
// Number of components
_numComp = _stream->readByte();
if (size != 8 + 3 * _numComp) {
warning("JPEG: Invalid number of components");
return false;
}
// Allocate the new components
delete[] _components;
_components = new Component[_numComp];
// Read the components details
for (int c = 0; c < _numComp; c++) {
_components[c].id = _stream->readByte();
_components[c].factorH = _stream->readByte();
_components[c].factorV = _components[c].factorH & 0xF;
_components[c].factorH >>= 4;
_components[c].quantTableSelector = _stream->readByte();
}
return true;
}
// Marker 0xC4 (Define Huffman Tables)
bool JPEG::readDHT() {
debug(5, "JPEG: readDHT");
uint16 size = _stream->readUint16BE() - 2;
uint32 pos = _stream->pos();
while ((uint32)_stream->pos() < (size + pos)) {
// Read the table type and id
uint8 tableId = _stream->readByte();
uint8 tableType = tableId >> 4; // type 0: DC, 1: AC
tableId &= 0xF;
uint8 tableNum = (tableId << 1) + tableType;
// Free the Huffman table
delete[] _huff[tableNum].values; _huff[tableNum].values = NULL;
delete[] _huff[tableNum].sizes; _huff[tableNum].sizes = NULL;
delete[] _huff[tableNum].codes; _huff[tableNum].codes = NULL;
// Read the number of values for each length
uint8 numValues[16];
_huff[tableNum].count = 0;
for (int len = 0; len < 16; len++) {
numValues[len] = _stream->readByte();
_huff[tableNum].count += numValues[len];
}
// Allocate memory for the current table
_huff[tableNum].values = new uint8[_huff[tableNum].count];
_huff[tableNum].sizes = new uint8[_huff[tableNum].count];
_huff[tableNum].codes = new uint16[_huff[tableNum].count];
// Read the table contents
int cur = 0;
for (int len = 0; len < 16; len++) {
for (int i = 0; i < numValues[len]; i++) {
_huff[tableNum].values[cur] = _stream->readByte();
_huff[tableNum].sizes[cur] = len + 1;
cur++;
}
}
// Fill the table of Huffman codes
cur = 0;
uint16 curCode = 0;
uint8 curCodeSize = _huff[tableNum].sizes[0];
while (cur < _huff[tableNum].count) {
// Increase the code size to fit the request
while (_huff[tableNum].sizes[cur] != curCodeSize) {
curCode <<= 1;
curCodeSize++;
}
// Assign the current code
_huff[tableNum].codes[cur] = curCode;
curCode++;
cur++;
}
}
return true;
}
// Marker 0xDA (Start Of Scan)
bool JPEG::readSOS() {
debug(5, "JPEG: readSOS");
uint16 size = _stream->readUint16BE();
// Number of scan components
_numScanComp = _stream->readByte();
if (size != 6 + 2 * _numScanComp) {
warning("JPEG: Invalid number of components");
return false;
}
// Allocate the new scan components
delete[] _scanComp;
_scanComp = new Component *[_numScanComp];
// Reset the maximum sampling factors
_maxFactorV = 0;
_maxFactorH = 0;
// Component-specification parameters
for (int c = 0; c < _numScanComp; c++) {
// Read the desired component id
uint8 id = _stream->readByte();
// Search the component with the specified id
bool found = false;
for (int i = 0; !found && i < _numComp; i++) {
if (_components[i].id == id) {
// We found the desired component
found = true;
// Assign the found component to the c'th scan component
_scanComp[c] = &_components[i];
}
}
if (!found) {
warning("JPEG: Invalid component");
return false;
}
// Read the entropy table selectors
_scanComp[c]->DCentropyTableSelector = _stream->readByte();
_scanComp[c]->ACentropyTableSelector = _scanComp[c]->DCentropyTableSelector & 0xF;
_scanComp[c]->DCentropyTableSelector >>= 4;
// Calculate the maximum sampling factors
if (_scanComp[c]->factorV > _maxFactorV)
_maxFactorV = _scanComp[c]->factorV;
if (_scanComp[c]->factorH > _maxFactorH)
_maxFactorH = _scanComp[c]->factorH;
// Initialize the DC predictor
_scanComp[c]->DCpredictor = 0;
}
// Start of spectral selection
if (_stream->readByte() != 0) {
warning("JPEG: Progressive scanning not supported");
return false;
}
// End of spectral selection
if (_stream->readByte() != 63) {
warning("JPEG: Progressive scanning not supported");
return false;
}
// Successive approximation parameters
if (_stream->readByte() != 0) {
warning("JPEG: Progressive scanning not supported");
return false;
}
// Entropy coded sequence starts, initialize Huffman decoder
_bitsNumber = 0;
// Read all the scan MCUs
uint16 xMCU = _w / (_maxFactorH * 8);
uint16 yMCU = _h / (_maxFactorV * 8);
// Check for non- multiple-of-8 dimensions
if (_w % (_maxFactorH * 8) != 0)
xMCU++;
if (_h % (_maxFactorV * 8) != 0)
yMCU++;
// Initialize the scan surfaces
for (uint16 c = 0; c < _numScanComp; c++) {
_scanComp[c]->surface.create(xMCU * _maxFactorH * 8, yMCU * _maxFactorV * 8, PixelFormat::createFormatCLUT8());
}
bool ok = true;
for (int y = 0; ok && (y < yMCU); y++)
for (int x = 0; ok && (x < xMCU); x++)
ok = readMCU(x, y);
// Trim Component surfaces back to image height and width
// Note: Code using jpeg must use surface.pitch correctly...
for (uint16 c = 0; c < _numScanComp; c++) {
_scanComp[c]->surface.w = _w;
_scanComp[c]->surface.h = _h;
}
return ok;
}
// Marker 0xDB (Define Quantization Tables)
bool JPEG::readDQT() {
debug(5, "JPEG: readDQT");
uint16 size = _stream->readUint16BE() - 2;
uint32 pos = _stream->pos();
while ((uint32)_stream->pos() < (pos + size)) {
// Read the table precision and id
uint8 tableId = _stream->readByte();
bool highPrecision = (tableId & 0xF0) != 0;
// Validate the table id
tableId &= 0xF;
if (tableId > JPEG_MAX_QUANT_TABLES) {
warning("JPEG: Invalid number of components");
return false;
}
// Create the new table if necessary
if (!_quant[tableId])
_quant[tableId] = new uint16[64];
// Read the table (stored in Zig-Zag order)
for (int i = 0; i < 64; i++)
_quant[tableId][i] = highPrecision ? _stream->readUint16BE() : _stream->readByte();
}
return true;
}
bool JPEG::readMCU(uint16 xMCU, uint16 yMCU) {
bool ok = true;
for (int c = 0; ok && (c < _numComp); c++) {
// Set the current component
_currentComp = _scanComp[c];
// Read the data units of the current component
for (int y = 0; ok && (y < _scanComp[c]->factorV); y++)
for (int x = 0; ok && (x < _scanComp[c]->factorH); x++)
ok = readDataUnit(xMCU * _scanComp[c]->factorH + x, yMCU * _scanComp[c]->factorV + y);
}
return ok;
}
void JPEG::idct8x8(float result[64], const int16 dct[64]) {
float tmp[64];
// Apply 1D IDCT to rows
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
tmp[y + x * 8] = dct[0] * _idct8x8[x][0]
+ dct[1] * _idct8x8[x][1]
+ dct[2] * _idct8x8[x][2]
+ dct[3] * _idct8x8[x][3]
+ dct[4] * _idct8x8[x][4]
+ dct[5] * _idct8x8[x][5]
+ dct[6] * _idct8x8[x][6]
+ dct[7] * _idct8x8[x][7];
}
dct += 8;
}
// Apply 1D IDCT to columns
for (int x = 0; x < 8; x++) {
const float *u = tmp + x * 8;
for (int y = 0; y < 8; y++) {
result[y * 8 + x] = u[0] * _idct8x8[y][0]
+ u[1] * _idct8x8[y][1]
+ u[2] * _idct8x8[y][2]
+ u[3] * _idct8x8[y][3]
+ u[4] * _idct8x8[y][4]
+ u[5] * _idct8x8[y][5]
+ u[6] * _idct8x8[y][6]
+ u[7] * _idct8x8[y][7];
}
}
}
bool JPEG::readDataUnit(uint16 x, uint16 y) {
// Prepare an empty data array
int16 readData[64];
for (int i = 1; i < 64; i++)
readData[i] = 0;
// Read the DC component
readData[0] = _currentComp->DCpredictor + readDC();
_currentComp->DCpredictor = readData[0];
// Read the AC components (stored in Zig-Zag)
readAC(readData);
// Calculate the DCT coefficients from the input sequence
int16 DCT[64];
for (uint8 i = 0; i < 64; i++) {
// Dequantize
int16 val = readData[i];
int16 quant = _quant[_currentComp->quantTableSelector][i];
val *= quant;
// Store the normalized coefficients, undoing the Zig-Zag
DCT[_zigZagOrder[i]] = val;
}
// Apply the IDCT
float result[64];
idct8x8(result, DCT);
// Level shift to make the values unsigned
for (int i = 0; i < 64; i++) {
result[i] = result[i] + 128;
if (result[i] < 0)
result[i] = 0;
if (result[i] > 255)
result[i] = 255;
}
// Paint the component surface
uint8 scalingV = _maxFactorV / _currentComp->factorV;
uint8 scalingH = _maxFactorH / _currentComp->factorH;
// Convert coordinates from MCU blocks to pixels
x <<= 3;
y <<= 3;
for (uint8 j = 0; j < 8; j++) {
for (uint16 sV = 0; sV < scalingV; sV++) {
// Get the beginning of the block line
byte *ptr = (byte *)_currentComp->surface.getBasePtr(x * scalingH, (y + j) * scalingV + sV);
for (uint8 i = 0; i < 8; i++) {
for (uint16 sH = 0; sH < scalingH; sH++) {
*ptr = (byte)(result[j * 8 + i]);
ptr++;
}
}
}
}
return true;
}
int16 JPEG::readDC() {
// DC is type 0
uint8 tableNum = _currentComp->DCentropyTableSelector << 1;
// Get the number of bits to read
uint8 numBits = readHuff(tableNum);
// Read the requested bits
return readSignedBits(numBits);
}
void JPEG::readAC(int16 *out) {
// AC is type 1
uint8 tableNum = (_currentComp->ACentropyTableSelector << 1) + 1;
// Start reading AC element 1
uint8 cur = 1;
while (cur < 64) {
uint8 s = readHuff(tableNum);
uint8 r = s >> 4;
s &= 0xF;
if (s == 0) {
if (r == 15) {
// Skip 16 values
cur += 16;
} else {
// EOB: end of block
cur = 64;
}
} else {
// Skip r values
cur += r;
// Read the next value
out[cur] = readSignedBits(s);
cur++;
}
}
}
int16 JPEG::readSignedBits(uint8 numBits) {
uint16 ret = 0;
if (numBits > 16) error("requested %d bits", numBits); //XXX
// MSB=0 for negatives, 1 for positives
for (int i = 0; i < numBits; i++)
ret = (ret << 1) + readBit();
// Extend sign bits (PAG109)
if (!(ret >> (numBits - 1)))
{
uint16 tmp = ((uint16)-1 << numBits) + 1;
ret = ret + tmp;
}
return ret;
}
// TODO: optimize?
uint8 JPEG::readHuff(uint8 table) {
bool foundCode = false;
uint8 val = 0;
uint8 cur = 0;
uint8 codeSize = 1;
uint16 code = readBit();
while (!foundCode) {
// Prepare a code of the current size
while (codeSize < _huff[table].sizes[cur]) {
code = (code << 1) + readBit();
codeSize++;
}
// Compare the codes of the current size
while (!foundCode && (codeSize == _huff[table].sizes[cur])) {
if (code == _huff[table].codes[cur]) {
// Found the code
val = _huff[table].values[cur];
foundCode = true;
} else {
// Continue reading
cur++;
}
}
}
return val;
}
uint8 JPEG::readBit() {
// Read a whole byte if necessary
if (_bitsNumber == 0) {
_bitsData = _stream->readByte();
_bitsNumber = 8;
// Detect markers
if (_bitsData == 0xFF) {
uint8 byte2 = _stream->readByte();
// A stuffed 0 validates the previous byte
if (byte2 != 0) {
if (byte2 == 0xDC) {
// DNL marker: Define Number of Lines
// TODO: terminate scan
warning("DNL marker detected: terminate scan");
} else {
warning("Error: marker 0x%02X read in entropy data", byte2);
}
}
}
}
_bitsNumber--;
return (_bitsData & (1 << _bitsNumber)) ? 1 : 0;
}
Surface *JPEG::getComponent(uint c) {
for (int i = 0; i < _numComp; i++)
if (_components[i].id == c) // We found the desired component
return &_components[i].surface;
error("JPEG::getComponent: No component %d present", c);
return NULL;
}
} // End of Graphics namespace

127
graphics/jpeg.h Normal file
View File

@ -0,0 +1,127 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 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 GRAPHICS_JPEG_H
#define GRAPHICS_JPEG_H
#include "graphics/surface.h"
namespace Common {
class SeekableReadStream;
}
namespace Graphics {
struct PixelFormat;
#define JPEG_MAX_QUANT_TABLES 4
#define JPEG_MAX_HUFF_TABLES 2
class JPEG {
public:
JPEG();
~JPEG();
bool read(Common::SeekableReadStream *str);
bool isLoaded() const { return _numComp && _w && _h; }
uint16 getWidth() const { return _w; }
uint16 getHeight() const { return _h; }
Surface *getComponent(uint c);
Surface *getSurface(const PixelFormat &format);
private:
void reset();
Common::SeekableReadStream *_stream;
uint16 _w, _h;
// Image components
uint8 _numComp;
struct Component {
// Global values
uint8 id;
uint8 factorH;
uint8 factorV;
uint8 quantTableSelector;
// Scan specific values
uint8 DCentropyTableSelector;
uint8 ACentropyTableSelector;
int16 DCpredictor;
// Result image for this component
Surface surface;
};
Component *_components;
// Scan components
uint8 _numScanComp;
Component **_scanComp;
Component *_currentComp;
// Maximum sampling factors, used to calculate the interleaving of the MCU
uint8 _maxFactorV;
uint8 _maxFactorH;
// Quantization tables
uint16 *_quant[JPEG_MAX_QUANT_TABLES];
// Huffman tables
struct HuffmanTable {
uint8 count;
uint8 *values;
uint8 *sizes;
uint16 *codes;
} _huff[2 * JPEG_MAX_HUFF_TABLES];
// Marker read functions
bool readJFIF();
bool readSOF0();
bool readDHT();
bool readSOS();
bool readDQT();
// Helper functions
bool readMCU(uint16 xMCU, uint16 yMCU);
bool readDataUnit(uint16 x, uint16 y);
int16 readDC();
void readAC(int16 *out);
int16 readSignedBits(uint8 numBits);
// Huffman decoding
uint8 readHuff(uint8 table);
uint8 readBit();
uint8 _bitsData;
uint8 _bitsNumber;
// Inverse Discrete Cosine Transformation
void idct8x8(float dst[64], const int16 src[64]);
};
} // End of Graphics namespace
#endif // GRAPHICS_JPEG_H

View File

@ -15,6 +15,8 @@ MODULE_OBJS := \
VectorRenderer.o \
VectorRendererSpec.o \
yuv_to_rgb.o \
yuva_to_rgba.o \
jpeg.o \
tinygl/api.o \
tinygl/arrays.o \
tinygl/clear.o \

271
graphics/yuva_to_rgba.cpp Normal file
View File

@ -0,0 +1,271 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
// The YUV to RGB conversion code is derived from SDL's YUV overlay code, which
// in turn appears to be derived from mpeg_play. The following copyright
// notices have been included in accordance with the original license. Please
// note that the term "software" in this context only applies to the
// buildLookup() and plotYUV*() functions below.
// Copyright (c) 1995 The Regents of the University of California.
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without written agreement is
// hereby granted, provided that the above copyright notice and the following
// two paragraphs appear in all copies of this software.
//
// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
// OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
// CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
// ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
// PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
// Copyright (c) 1995 Erik Corry
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without written agreement is
// hereby granted, provided that the above copyright notice and the following
// two paragraphs appear in all copies of this software.
//
// IN NO EVENT SHALL ERIK CORRY BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
// THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF ERIK CORRY HAS BEEN ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//
// ERIK CORRY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
// BASIS, AND ERIK CORRY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
// Portions of this software Copyright (c) 1995 Brown University.
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without written agreement
// is hereby granted, provided that the above copyright notice and the
// following two paragraphs appear in all copies of this software.
//
// IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR
// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
// OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN
// UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
// BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#include "common/scummsys.h"
#include "common/singleton.h"
#include "graphics/surface.h"
namespace Graphics {
class YUVAToRGBALookup {
public:
YUVAToRGBALookup(Graphics::PixelFormat format);
~YUVAToRGBALookup();
int16 *_colorTab;
uint32 *_rgbToPix;
uint32 *_alphaToPix;
};
YUVAToRGBALookup::YUVAToRGBALookup(Graphics::PixelFormat format) {
_colorTab = new int16[4 * 256]; // 2048 bytes
int16 *Cr_r_tab = &_colorTab[0 * 256];
int16 *Cr_g_tab = &_colorTab[1 * 256];
int16 *Cb_g_tab = &_colorTab[2 * 256];
int16 *Cb_b_tab = &_colorTab[3 * 256];
_rgbToPix = new uint32[3 * 768]; // 9216 bytes
uint32 *r_2_pix_alloc = &_rgbToPix[0 * 768];
uint32 *g_2_pix_alloc = &_rgbToPix[1 * 768];
uint32 *b_2_pix_alloc = &_rgbToPix[2 * 768];
_alphaToPix = new uint32[256]; // 958 bytes
int16 CR, CB;
int i;
// Generate the tables for the display surface
for (i = 0; i < 256; i++) {
// Gamma correction (luminescence table) and chroma correction
// would be done here. See the Berkeley mpeg_play sources.
CR = CB = (i - 128);
Cr_r_tab[i] = (int16) ( (0.419 / 0.299) * CR) + 0 * 768 + 256;
Cr_g_tab[i] = (int16) (-(0.299 / 0.419) * CR) + 1 * 768 + 256;
Cb_g_tab[i] = (int16) (-(0.114 / 0.331) * CB);
Cb_b_tab[i] = (int16) ( (0.587 / 0.331) * CB) + 2 * 768 + 256;
}
// Set up entries 0-255 in rgb-to-pixel value tables.
for (i = 0; i < 256; i++) {
r_2_pix_alloc[i + 256] = format.ARGBToColor(0, i, 0, 0);
g_2_pix_alloc[i + 256] = format.ARGBToColor(0, 0, i, 0);
b_2_pix_alloc[i + 256] = format.ARGBToColor(0, 0, 0, i);
}
// Set up entries 0-255 in alpha-to-pixel value table.
for (i = 0; i < 256; i++) {
_alphaToPix[i] = format.ARGBToColor(i, 0, 0, 0);
}
// Spread out the values we have to the rest of the array so that we do
// not need to check for overflow.
for (i = 0; i < 256; i++) {
r_2_pix_alloc[i] = r_2_pix_alloc[256];
r_2_pix_alloc[i + 512] = r_2_pix_alloc[511];
g_2_pix_alloc[i] = g_2_pix_alloc[256];
g_2_pix_alloc[i + 512] = g_2_pix_alloc[511];
b_2_pix_alloc[i] = b_2_pix_alloc[256];
b_2_pix_alloc[i + 512] = b_2_pix_alloc[511];
}
}
YUVAToRGBALookup::~YUVAToRGBALookup() {
delete[] _rgbToPix;
delete[] _colorTab;
delete[] _alphaToPix;
}
class YUVAToRGBAManager : public Common::Singleton<YUVAToRGBAManager> {
public:
const YUVAToRGBALookup *getLookup(Graphics::PixelFormat format);
private:
friend class Common::Singleton<SingletonBaseType>;
YUVAToRGBAManager();
~YUVAToRGBAManager();
Graphics::PixelFormat _lastFormat;
YUVAToRGBALookup *_lookup;
};
YUVAToRGBAManager::YUVAToRGBAManager() {
_lookup = 0;
}
YUVAToRGBAManager::~YUVAToRGBAManager() {
delete _lookup;
}
const YUVAToRGBALookup *YUVAToRGBAManager::getLookup(Graphics::PixelFormat format) {
if (_lastFormat == format)
return _lookup;
delete _lookup;
_lookup = new YUVAToRGBALookup(format);
_lastFormat = format;
return _lookup;
}
} // End of namespace Graphics
namespace Common {
DECLARE_SINGLETON(Graphics::YUVAToRGBAManager);
}
#define YUVAToRGBAMan (Graphics::YUVAToRGBAManager::instance())
namespace Graphics {
#define PUT_PIXELA(s, a, d) \
L = &rgbToPix[(s)]; \
*((PixelInt *)(d)) = (L[cr_r] | L[crb_g] | L[cb_b] | aToPix[a])
template<typename PixelInt>
void convertYUVA420ToRGBA(byte *dstPtr, int dstPitch, const YUVAToRGBALookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, const byte *aSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
int halfHeight = yHeight >> 1;
int halfWidth = yWidth >> 1;
// Keep the tables in pointers here to avoid a dereference on each pixel
const int16 *Cr_r_tab = lookup->_colorTab;
const int16 *Cr_g_tab = Cr_r_tab + 256;
const int16 *Cb_g_tab = Cr_g_tab + 256;
const int16 *Cb_b_tab = Cb_g_tab + 256;
const uint32 *rgbToPix = lookup->_rgbToPix;
const uint32 *aToPix = lookup->_alphaToPix;
for (int h = 0; h < halfHeight; h++) {
for (int w = 0; w < halfWidth; w++) {
register const uint32 *L;
int16 cr_r = Cr_r_tab[*vSrc];
int16 crb_g = Cr_g_tab[*vSrc] + Cb_g_tab[*uSrc];
int16 cb_b = Cb_b_tab[*uSrc];
++uSrc;
++vSrc;
PUT_PIXELA(*ySrc, *aSrc, dstPtr);
PUT_PIXELA(*(ySrc + yPitch), *(aSrc + yPitch), dstPtr + dstPitch);
ySrc++;
aSrc++;
dstPtr += sizeof(PixelInt);
PUT_PIXELA(*ySrc, *aSrc, dstPtr);
PUT_PIXELA(*(ySrc + yPitch), *(aSrc + yPitch), dstPtr + dstPitch);
ySrc++;
aSrc++;
dstPtr += sizeof(PixelInt);
}
dstPtr += dstPitch;
ySrc += (yPitch << 1) - yWidth;
aSrc += (yPitch << 1) - yWidth;
uSrc += uvPitch - halfWidth;
vSrc += uvPitch - halfWidth;
}
}
void convertYUVA420ToRGBA(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, const byte *aSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
assert(dst && dst->pixels);
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
assert(ySrc && uSrc && vSrc && aSrc);
assert((yWidth & 1) == 0);
assert((yHeight & 1) == 0);
const YUVAToRGBALookup *lookup = YUVAToRGBAMan.getLookup(dst->format);
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
convertYUVA420ToRGBA<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, aSrc, yWidth, yHeight, yPitch, uvPitch);
else
convertYUVA420ToRGBA<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, aSrc, yWidth, yHeight, yPitch, uvPitch);
}
} // End of namespace Graphics

57
graphics/yuva_to_rgba.h Normal file
View File

@ -0,0 +1,57 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
/**
* @file
* YUV to RGB conversion used in engines:
* - scumm (he)
* - sword25
*/
#ifndef GRAPHICS_YUVA_TO_RGBA_H
#define GRAPHICS_YUVA_TO_RGBA_H
#include "common/scummsys.h"
#include "graphics/surface.h"
namespace Graphics {
struct Surface;
/**
* Convert a YUVA420 image to an RGBA surface
*
* @param dst the destination surface
* @param ySrc the source of the y component
* @param uSrc the source of the u component
* @param vSrc the source of the v component
* @param aSrc the source of the a component
* @param yWidth the width of the y surface (must be divisible by 2)
* @param yHeight the height of the y surface (must be divisible by 2)
* @param yPitch the pitch of the y surface
* @param uvPitch the pitch of the u and v surfaces
*/
void convertYUVA420ToRGBA(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, const byte *aSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
} // End of namespace Graphics
#endif

View File

@ -113,7 +113,22 @@ BinkDecoder::BinkDecoder() {
}
_audioStream = 0;
_audioStarted = false;
}
void BinkDecoder::startAudio() {
if (_audioTrack < _audioTracks.size()) {
const AudioTrack &audio = _audioTracks[_audioTrack];
_audioStream = Audio::makeQueuingAudioStream(audio.outSampleRate, audio.outChannels == 2);
g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audioHandle, _audioStream);
} // else no audio
}
void BinkDecoder::stopAudio() {
if (_audioStream) {
g_system->getMixer()->stopHandle(_audioHandle);
_audioStream = 0;
}
}
BinkDecoder::~BinkDecoder() {
@ -123,13 +138,8 @@ BinkDecoder::~BinkDecoder() {
void BinkDecoder::close() {
reset();
if (_audioStream) {
// Stop audio
g_system->getMixer()->stopHandle(_audioHandle);
_audioStream = 0;
}
_audioStarted = false;
// Stop audio
stopAudio();
for (int i = 0; i < 4; i++) {
delete[] _curPlanes[i]; _curPlanes[i] = 0;
@ -173,7 +183,7 @@ void BinkDecoder::close() {
uint32 BinkDecoder::getElapsedTime() const {
if (_audioStream && g_system->getMixer()->isSoundHandleActive(_audioHandle))
return g_system->getMixer()->getSoundElapsedTime(_audioHandle);
return g_system->getMixer()->getSoundElapsedTime(_audioHandle) + _audioStartOffset;
return g_system->getMillis() - _startTime;
}
@ -241,11 +251,6 @@ const Graphics::Surface *BinkDecoder::decodeNextFrame() {
if (_curFrame == 0)
_startTime = g_system->getMillis();
if (!_audioStarted && _audioStream) {
_audioStarted = true;
g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audioHandle, _audioStream);
}
return &_surface;
}
@ -510,6 +515,11 @@ void BinkDecoder::mergeHuffmanSymbols(VideoFrame &video, byte *dst, const byte *
}
bool BinkDecoder::loadStream(Common::SeekableReadStream *stream) {
Graphics::PixelFormat format = g_system->getOverlayFormat(); // residual FIXME: getScreenFormat();
return loadStream(stream, format);
}
bool BinkDecoder::loadStream(Common::SeekableReadStream *stream, const Graphics::PixelFormat &format) {
close();
_id = stream->readUint32BE();
@ -589,8 +599,6 @@ bool BinkDecoder::loadStream(Common::SeekableReadStream *stream) {
_hasAlpha = _videoFlags & kVideoFlagAlpha;
_swapPlanes = (_id == kBIKhID) || (_id == kBIKiID); // BIKh and BIKi swap the chroma planes
// Residual: replaced to getOverlayFormat()
Graphics::PixelFormat format = g_system->getOverlayFormat();
_surface.create(width, height, format);
// Give the planes a bit extra space
@ -619,11 +627,8 @@ bool BinkDecoder::loadStream(Common::SeekableReadStream *stream) {
initBundles();
initHuffman();
if (_audioTrack < _audioTracks.size()) {
const AudioTrack &audio = _audioTracks[_audioTrack];
_audioStream = Audio::makeQueuingAudioStream(audio.outSampleRate, audio.outChannels == 2);
}
startAudio();
_audioStartOffset = 0;
return true;
}

View File

@ -56,6 +56,8 @@ namespace Video {
*
* Video decoder used in engines:
* - scumm (he)
*
* This class is overriden in Residual to provide seek support
*/
class BinkDecoder : public FixedRateVideoDecoder {
public:
@ -64,6 +66,7 @@ public:
// VideoDecoder API
bool loadStream(Common::SeekableReadStream *stream);
bool loadStream(Common::SeekableReadStream *stream, const Graphics::PixelFormat &format);
void close();
bool isVideoLoaded() const { return _bink != 0; }
uint16 getWidth() const { return _surface.w; }
@ -76,7 +79,7 @@ public:
// FixedRateVideoDecoder
Common::Rational getFrameRate() const { return _frameRate; }
private:
protected:
static const int kAudioChannelsMax = 2;
static const int kAudioBlockSizeMax = (kAudioChannelsMax << 11);
@ -221,15 +224,13 @@ private:
Audio::SoundHandle _audioHandle;
Audio::QueuingAudioStream *_audioStream;
bool _audioStarted;
int32 _audioStartOffset;
uint32 _videoFlags; ///< Video frame features.
bool _hasAlpha; ///< Do video frames have alpha?
bool _swapPlanes; ///< Are the planes ordered (A)YVU instead of (A)YUV?
uint32 _audioFrame;
Common::Array<AudioTrack> _audioTracks; ///< All audio tracks.
Common::Array<VideoFrame> _frames; ///< All video frames.
@ -258,8 +259,14 @@ private:
/** Decode an audio packet. */
void audioPacket(AudioTrack &audio);
/** Decode a video packet. */
void videoPacket(VideoFrame &video);
/**
* Decode a video packet.
*
* This method is virtual because it is overriden in Residual
* to export the alpha channel of the video
*/
virtual void videoPacket(VideoFrame &video);
/** Decode a plane. */
void decodePlane(VideoFrame &video, int planeIdx, bool isChroma);
@ -327,6 +334,11 @@ private:
void IDCT(int16 *block);
void IDCTPut(DecodeContext &ctx, int16 *block);
void IDCTAdd(DecodeContext &ctx, int16 *block);
/** Start playing the audio track */
void startAudio();
/** Stop playing the audio track */
void stopAudio();
};
} // End of namespace Video

119
video/bink_decoder_seek.cpp Normal file
View File

@ -0,0 +1,119 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "common/bitstream.h"
#include "common/stream.h"
#include "common/system.h"
#include "graphics/surface.h"
#include "graphics/yuva_to_rgba.h"
#include "video/bink_decoder_seek.h"
static const uint32 kBIKiID = MKTAG('B', 'I', 'K', 'i');
namespace Video {
void SeekableBinkDecoder::videoPacket(VideoFrame &video) {
assert(video.bits);
if (_hasAlpha) {
if (_id == kBIKiID)
video.bits->skip(32);
decodePlane(video, 3, false);
}
if (_id == kBIKiID)
video.bits->skip(32);
for (int i = 0; i < 3; i++) {
int planeIdx = ((i == 0) || !_swapPlanes) ? i : (i ^ 3);
decodePlane(video, planeIdx, i != 0);
if (video.bits->pos() >= video.bits->size())
break;
}
// Convert the YUV data we have to our format
assert(_curPlanes[0] && _curPlanes[1] && _curPlanes[2] && _curPlanes[3]);
Graphics::convertYUVA420ToRGBA(&_surface, _curPlanes[0], _curPlanes[1], _curPlanes[2], _curPlanes[3],
_surface.w, _surface.h, _surface.w, _surface.w >> 1);
// And swap the planes with the reference planes
for (int i = 0; i < 4; i++)
SWAP(_curPlanes[i], _oldPlanes[i]);
}
uint32 SeekableBinkDecoder::getDuration() const
{
Common::Rational duration = getFrameCount() * 1000 / getFrameRate();
return duration.toInt();
}
uint32 SeekableBinkDecoder::findKeyFrame(uint32 frame) const {
for (int i = frame; i >= 0; i--) {
if (_frames[i].keyFrame)
return i;
}
// If none found, we'll assume the requested frame is a key frame
return frame;
}
void SeekableBinkDecoder::seekToFrame(uint32 frame) {
assert(frame < _frames.size());
// Fast path
if ((int32)frame == _curFrame + 1)
return;
// Stop all audio (for now)
stopAudio();
// Track down the keyframe
_curFrame = findKeyFrame(frame) - 1;
while (_curFrame < (int32)frame - 1)
decodeNextFrame();
// Map out the starting point
Common::Rational startTime = frame * 1000 / getFrameRate();
_startTime = g_system->getMillis() - startTime.toInt();
resetPauseStartTime();
// Adjust the audio starting point
if (_audioTrack < _audioTracks.size()) {
_audioStartOffset = startTime.toInt();
}
// Restart the audio
startAudio();
}
void SeekableBinkDecoder::seekToTime(Audio::Timestamp time) {
// Try to find the last frame that should have been decoded
Common::Rational frame = time.msecs() * getFrameRate() / 1000;
seekToFrame(frame.toInt());
}
} /* namespace Video */

52
video/bink_decoder_seek.h Normal file
View File

@ -0,0 +1,52 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#include "video/bink_decoder.h"
#ifdef USE_BINK
#ifndef VIDEO_BINK_DECODER_SEEK_H
#define VIDEO_BINK_DECODER_SEEK_H
namespace Video {
class SeekableBinkDecoder: public Video::BinkDecoder,
public Video::SeekableVideoDecoder {
public:
// SeekableVideoDecoder API
void seekToFrame(uint32 frame);
void seekToTime(Audio::Timestamp time);
uint32 getDuration() const;
protected:
/** Decode a video packet. */
void videoPacket(VideoFrame &video);
/** Find the keyframe needed to decode a frame */
uint32 findKeyFrame(uint32 frame) const;
};
} /* namespace Video */
#endif // VIDEO_BINK_DECODER_SEEK_H
#endif // USE_BINK

View File

@ -5,7 +5,8 @@ MODULE_OBJS := \
video_decoder.o
ifdef USE_BINK
MODULE_OBJS += \
bink_decoder.o
bink_decoder.o \
bink_decoder_seek.o
endif
# Include common rules