mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-16 23:19:49 +00:00
Merge pull request #463 from somaen/mergemyst3
Add a WIP Myst III Exile engine
This commit is contained in:
commit
09a532d5d0
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
1
configure
vendored
@ -78,6 +78,7 @@ add_engine() {
|
||||
}
|
||||
|
||||
add_engine grim "Grim" yes
|
||||
add_engine myst3 "Myst 3" yes
|
||||
|
||||
|
||||
#
|
||||
|
@ -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
115
engines/myst3/archive.cpp
Normal 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
55
engines/myst3/archive.h
Normal 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
274
engines/myst3/console.cpp
Normal 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
57
engines/myst3/console.h
Normal 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
191
engines/myst3/cursor.cpp
Normal 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
68
engines/myst3/cursor.h
Normal 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
394
engines/myst3/database.cpp
Normal 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
142
engines/myst3/database.h
Normal 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_ */
|
97
engines/myst3/detection.cpp
Normal file
97
engines/myst3/detection.cpp
Normal 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
|
81
engines/myst3/directoryentry.cpp
Normal file
81
engines/myst3/directoryentry.cpp
Normal 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
|
55
engines/myst3/directoryentry.h
Normal file
55
engines/myst3/directoryentry.h
Normal 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
|
132
engines/myst3/directorysubentry.cpp
Normal file
132
engines/myst3/directorysubentry.cpp
Normal 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
|
95
engines/myst3/directorysubentry.h
Normal file
95
engines/myst3/directorysubentry.h
Normal 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
36
engines/myst3/gfx.h
Normal 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
58
engines/myst3/hotspot.cpp
Normal 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
61
engines/myst3/hotspot.h
Normal 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
279
engines/myst3/inventory.cpp
Normal 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
89
engines/myst3/inventory.h
Normal 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
29
engines/myst3/module.mk
Normal 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
292
engines/myst3/movie.cpp
Normal 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
125
engines/myst3/movie.h
Normal 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
633
engines/myst3/myst3.cpp
Normal 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
136
engines/myst3/myst3.h
Normal 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
382
engines/myst3/node.cpp
Normal 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
146
engines/myst3/node.h
Normal 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
122
engines/myst3/nodecube.cpp
Normal 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
39
engines/myst3/nodecube.h
Normal 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_ */
|
97
engines/myst3/nodeframe.cpp
Normal file
97
engines/myst3/nodeframe.cpp
Normal 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
39
engines/myst3/nodeframe.h
Normal 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
161
engines/myst3/puzzles.cpp
Normal 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
52
engines/myst3/puzzles.h
Normal 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
137
engines/myst3/scene.cpp
Normal 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
71
engines/myst3/scene.h
Normal 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
1564
engines/myst3/script.cpp
Normal file
File diff suppressed because it is too large
Load Diff
229
engines/myst3/script.h
Normal file
229
engines/myst3/script.h
Normal 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
183
engines/myst3/variables.cpp
Normal 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
115
engines/myst3/variables.h
Normal 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
50
graphics/conversion.h
Normal 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
|
@ -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
735
graphics/jpeg.cpp
Normal 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
127
graphics/jpeg.h
Normal 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
|
@ -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
271
graphics/yuva_to_rgba.cpp
Normal 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
57
graphics/yuva_to_rgba.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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
119
video/bink_decoder_seek.cpp
Normal 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
52
video/bink_decoder_seek.h
Normal 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
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user