Initial commit

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

View File

@@ -0,0 +1,241 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/shared/engine/data_archive.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "common/translation.h"
#include "common/compression/unzip.h"
namespace Ultima {
namespace Shared {
#define DATA_FILENAME "ultima.dat"
class UltimaDataArchiveMember : public Common::ArchiveMember {
private:
Common::SharedPtr<Common::ArchiveMember> _member;
Common::Path _publicFolder;
Common::Path _innerfolder;
public:
UltimaDataArchiveMember(Common::SharedPtr<Common::ArchiveMember> member,
const Common::Path &subfolder) :
_member(member), _publicFolder("data/"), _innerfolder(subfolder) {
}
~UltimaDataArchiveMember() override {}
Common::SeekableReadStream *createReadStream() const override {
return _member->createReadStream();
}
Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override {
return _member->createReadStreamForAltStream(altStreamType);
}
Common::Path getPathInArchive() const override {
Common::Path name = _member->getPathInArchive();
assert(name.isRelativeTo(_innerfolder));
return _publicFolder.join(name.relativeTo(_innerfolder));
}
Common::U32String getDisplayName() const override {
return _member->getDisplayName();
}
Common::String getFileName() const override { return _member->getFileName(); }
Common::String getName() const override { return getPathInArchive().toString('/'); }
bool isDirectory() const override { return _member->isDirectory(); }
};
/*-------------------------------------------------------------------*/
bool UltimaDataArchive::load(const Common::Path &subfolder,
int reqMajorVersion, int reqMinorVersion, Common::U32String &errorMsg) {
Common::Archive *dataArchive = nullptr;
Common::File f;
#ifndef RELEASE_BUILD
Common::FSNode folder;
if (ConfMan.hasKey("extrapath")) {
if ((folder = Common::FSNode(ConfMan.getPath("extrapath"))).exists()
&& (folder = folder.getChild("files")).exists()
&& (folder = folder.getChild(subfolder.baseName())).exists()) {
f.open(folder.getChild("version.txt"));
}
}
#endif
if (!f.isOpen()) {
if (!Common::File::exists(DATA_FILENAME) ||
(dataArchive = Common::makeZipArchive(DATA_FILENAME)) == 0 ||
!f.open(subfolder.join("version.txt"), *dataArchive)) {
delete dataArchive;
errorMsg = Common::U32String::format(_("Could not locate engine data %s"), DATA_FILENAME);
return false;
}
}
// Validate the version
char buffer[5];
f.read(buffer, 4);
buffer[4] = '\0';
int major = 0, minor = 0;
if (buffer[1] == '.') {
major = buffer[0] - '0';
minor = atoi(&buffer[2]);
}
if (major != reqMajorVersion || minor != reqMinorVersion) {
delete dataArchive;
errorMsg = Common::U32String::format(_("Out of date engine data. Expected %d.%d, but got version %d.%d"),
reqMajorVersion, reqMinorVersion, major, minor);
return false;
}
// It was all validated correctly
Common::Archive *archive;
#ifndef RELEASE_BUILD
if (!dataArchive)
archive = new UltimaDataArchiveProxy(folder);
else
#endif
archive = new UltimaDataArchive(dataArchive, subfolder);
SearchMan.add("data", archive);
return true;
}
/*-------------------------------------------------------------------*/
bool UltimaDataArchive::hasFile(const Common::Path &path) const {
if (!path.isRelativeTo(_publicFolder))
return false;
Common::Path realFilename = innerToPublic(path);
return _zip->hasFile(realFilename);
}
int UltimaDataArchive::listMatchingMembers(Common::ArchiveMemberList &list, const Common::Path &pattern, bool matchPathComponents) const {
Common::ArchiveMemberList innerList;
int numMatches = 0;
// Test whether we can skip filtering.
bool matchAll = matchPathComponents && pattern == "*";
// First, get all zip members relevant to the current game
_zip->listMatchingMembers(innerList, _innerfolder.appendComponent("*"), true);
// Modify the results to change the filename, then filter with pattern
for (const auto &innerMember : innerList) {
Common::ArchiveMemberPtr member = Common::ArchiveMemberPtr(
new UltimaDataArchiveMember(innerMember, _innerfolder));
if (matchAll || member->getPathInArchive().toString().matchString(pattern.toString(), true, matchPathComponents ? nullptr : "/")) {
list.push_back(member);
++numMatches;
}
}
return numMatches;
}
int UltimaDataArchive::listMembers(Common::ArchiveMemberList &list) const {
return listMatchingMembers(list, "*", true);
}
const Common::ArchiveMemberPtr UltimaDataArchive::getMember(const Common::Path &path) const {
if (!hasFile(path))
return Common::ArchiveMemberPtr();
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
}
Common::SeekableReadStream *UltimaDataArchive::createReadStreamForMember(const Common::Path &path) const {
if (hasFile(path)) {
Common::Path filename = innerToPublic(path);
return _zip->createReadStreamForMember(filename);
}
return nullptr;
}
bool UltimaDataArchive::isPathDirectory(const Common::Path &path) const {
return _zip->isPathDirectory(innerToPublic(path));
}
/*-------------------------------------------------------------------*/
#ifndef RELEASE_BUILD
const Common::ArchiveMemberPtr UltimaDataArchiveProxy::getMember(const Common::Path &path) const {
if (!hasFile(path))
return Common::ArchiveMemberPtr();
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
}
Common::SeekableReadStream *UltimaDataArchiveProxy::createReadStreamForMember(const Common::Path &path) const {
if (hasFile(path))
return getNode(path).createReadStream();
return nullptr;
}
Common::FSNode UltimaDataArchiveProxy::getNode(const Common::Path &name) const {
Common::Path remainingName = name.relativeTo(_publicFolder);
Common::FSNode node = _folder;
Common::StringArray components = remainingName.splitComponents();
if (components.empty()) {
return node;
}
for(Common::StringArray::const_iterator it = components.begin(); it != components.end() - 1; it++) {
node = node.getChild(*it);
if (!node.exists())
return node;
}
node = node.getChild(*(components.end() - 1));
return node;
}
int UltimaDataArchiveProxy::listMembers(Common::ArchiveMemberList &list) const {
return listMatchingMembers(list, "*", true);
}
int UltimaDataArchiveProxy::listMatchingMembers(Common::ArchiveMemberList &list,
const Common::Path &pattern, bool matchPathComponents) const {
// Let FSDirectory adjust the filenames for us by using its prefix feature.
// Note: dir is intentionally constructed again on each call to prevent stale entries due to caching:
// Since this proxy class is intended for use during development, picking up modifications while the
// game is running might be useful.
const int maxDepth = 255; // chosen arbitrarily
Common::FSDirectory dir(_publicFolder, _folder, maxDepth, false, false, true);
if (matchPathComponents && pattern == "*")
return dir.listMembers(list);
else
return dir.listMatchingMembers(list, pattern, matchPathComponents);
}
bool UltimaDataArchiveProxy::isPathDirectory(const Common::Path &path) const {
return getNode(path).isDirectory();
}
#endif
} // End of namespace Shared
} // End of namespace Ultima

View File

@@ -0,0 +1,183 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA_SHARED_ENGINE_DATA_ARCHIVE_H
#define ULTIMA_SHARED_ENGINE_DATA_ARCHIVE_H
#include "common/archive.h"
#include "common/fs.h"
#include "common/path.h"
namespace Ultima {
namespace Shared {
/**
* The data archive class encapsulates access to a specific subfolder within
* the ultima.dat data file for a game. It wraps up the subfolder so it can
* be accessed in each game as a generic "data" subfolder. This allows the
* individual games to simplify their data loading code.
*/
class UltimaDataArchive : public Common::Archive {
private:
Common::Archive *_zip;
Common::Path _publicFolder;
Common::Path _innerfolder;
UltimaDataArchive(Common::Archive *zip, const Common::Path &subfolder) :
_zip(zip), _publicFolder("data/"), _innerfolder(subfolder) {}
Common::Path innerToPublic(const Common::Path &filename) const {
assert(filename.isRelativeTo(_publicFolder));
return _innerfolder.join(filename.relativeTo(_publicFolder));
}
public:
/**
* Creates a data archive wrapper for the ultima.dat datafile.
* Firstly, for debugging purposes, if a "files" folder exists on any path that
* has the given subfolder, it will be used first. This will allow for setting
* the ScummVM Extra Path to the create_ultima folder, and it will give preference
* the files there. Otherwise, it checks for the presence of ultima.dat, and
* if the required data is found, it returns the new archive.
* Otherwise, returns an error message in the errorMsg field
*/
static bool load(const Common::Path &subfolder,
int reqMajorVersion, int reqMinorVersion, Common::U32String &errorMsg);
public:
~UltimaDataArchive() override {
delete _zip;
}
/**
* Check if a member with the given name is present in the Archive.
* Patterns are not allowed, as this is meant to be a quick File::exists()
* replacement.
*/
bool hasFile(const Common::Path &path) const override;
/**
* Add all members of the Archive matching the specified pattern to list.
* Must only append to list, and not remove elements from it.
*
* @return the number of members added to list
*/
int listMatchingMembers(Common::ArchiveMemberList &list,
const Common::Path &pattern, bool matchPathComponents = false) const override;
/**
* Add all members of the Archive to list.
* Must only append to list, and not remove elements from it.
*
* @return the number of names added to list
*/
int listMembers(Common::ArchiveMemberList &list) const override;
/**
* Returns a ArchiveMember representation of the given file.
*/
const Common::ArchiveMemberPtr getMember(const Common::Path &path)
const override;
/**
* Create a stream bound to a member with the specified name in the
* archive. If no member with this name exists, 0 is returned.
* @return the newly created input stream
*/
Common::SeekableReadStream *createReadStreamForMember(
const Common::Path &path) const override;
bool isPathDirectory(const Common::Path &path) const override;
};
#ifndef RELEASE_BUILD
/**
* The data archive proxy class is used for debugging purposes to access engine data
* files when the create_ultima folder is in the search path. It will allow for
* local mucking around with the data files and committing changes without having to
* recreate the ultima.dat file every time a change is made. ultima.dat then just has
* to be recreated prior to a release or when the changes are completed and stable
*/
class UltimaDataArchiveProxy : public Common::Archive {
friend class UltimaDataArchive;
private:
Common::FSNode _folder;
const Common::Path _publicFolder;
UltimaDataArchiveProxy(const Common::FSNode &folder) : _folder(folder), _publicFolder("data/") {}
/**
* Gets a file node from the passed filename
*/
Common::FSNode getNode(const Common::Path &name) const;
public:
~UltimaDataArchiveProxy() override {
}
/**
* Check if a member with the given name is present in the Archive.
* Patterns are not allowed, as this is meant to be a quick File::exists()
* replacement.
*/
bool hasFile(const Common::Path &path) const override {
return path.isRelativeTo(_publicFolder) && getNode(path).exists();
}
/**
* Add all members of the Archive matching the specified pattern to list.
* Must only append to list, and not remove elements from it.
*
* @return the number of members added to list
*/
int listMatchingMembers(Common::ArchiveMemberList &list,
const Common::Path &pattern, bool matchPathComponents = false) const override;
/**
* Add all members of the Archive to list.
* Must only append to list, and not remove elements from it.
*
* @return the number of names added to list
*/
int listMembers(Common::ArchiveMemberList &list) const override;
/**
* Returns a ArchiveMember representation of the given file.
*/
const Common::ArchiveMemberPtr getMember(const Common::Path &path)
const override;
/**
* Create a stream bound to a member with the specified name in the
* archive. If no member with this name exists, 0 is returned.
* @return the newly created input stream
*/
Common::SeekableReadStream *createReadStreamForMember(
const Common::Path &path) const override;
bool isPathDirectory(const Common::Path &path) const override;
};
#endif
} // End of namespace Shared
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,255 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/scummsys.h"
#include "graphics/cursorman.h"
#include "common/events.h"
#include "common/endian.h"
#include "common/system.h"
#include "engines/util.h"
#include "ultima/shared/engine/events.h"
namespace Ultima {
namespace Shared {
EventsManager::EventsManager(EventsCallback *callback) : _callback(callback), _playTime(0),
_gameCounter(0), _frameCounter(0), _priorFrameCounterTime(0), _buttonsDown(0),
_specialButtons(0), _priorFrameTime(0) {
}
void EventsManager::showCursor() {
CursorMan.showMouse(true);
}
void EventsManager::hideCursor() {
CursorMan.showMouse(false);
}
bool EventsManager::isCursorVisible() {
return CursorMan.isVisible();
}
bool EventsManager::pollEvent(Common::Event &event) {
uint32 timer = g_system->getMillis();
if (timer >= (_priorFrameCounterTime + GAME_FRAME_TIME)) {
// Time to build up next game frame
_priorFrameCounterTime = timer;
nextFrame();
}
// Event handling
if (g_system->getEventManager()->pollEvent(event)) {
if (isMouseDownEvent(event.type)) {
setButtonDown(whichButton(event.type), true);
_mousePos = event.mouse;
} else if (isMouseUpEvent(event.type)) {
setButtonDown(whichButton(event.type), false);
_mousePos = event.mouse;
} else if (event.type == Common::EVENT_MOUSEMOVE) {
_mousePos = event.mouse;
}
switch (event.type) {
case Common::EVENT_KEYDOWN: {
handleKbdSpecial(event.kbd);
break;
}
case Common::EVENT_KEYUP:
handleKbdSpecial(event.kbd);
break;
case Common::EVENT_MOUSEMOVE:
_mousePos = event.mouse;
break;
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_RBUTTONDOWN:
break;
case Common::EVENT_LBUTTONUP:
case Common::EVENT_RBUTTONUP:
break;
default:
break;
}
return true;
}
return false;
}
void EventsManager::pollEvents() {
Common::Event event;
while (pollEvent(event)) {
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
eventTarget()->mouseMove(_mousePos);
break;
case Common::EVENT_LBUTTONDOWN:
eventTarget()->leftButtonDown(_mousePos);
break;
case Common::EVENT_LBUTTONUP:
eventTarget()->leftButtonUp(_mousePos);
break;
case Common::EVENT_MBUTTONDOWN:
eventTarget()->middleButtonDown(_mousePos);
break;
case Common::EVENT_MBUTTONUP:
eventTarget()->middleButtonUp(_mousePos);
break;
case Common::EVENT_RBUTTONDOWN:
eventTarget()->rightButtonDown(_mousePos);
break;
case Common::EVENT_RBUTTONUP:
eventTarget()->rightButtonUp(_mousePos);
break;
case Common::EVENT_WHEELUP:
case Common::EVENT_WHEELDOWN:
eventTarget()->mouseWheel(_mousePos, event.type == Common::EVENT_WHEELUP);
break;
case Common::EVENT_KEYDOWN:
eventTarget()->keyDown(event.kbd);
break;
case Common::EVENT_KEYUP:
eventTarget()->keyUp(event.kbd);
break;
default:
break;
}
}
}
void EventsManager::pollEventsAndWait() {
pollEvents();
g_system->delayMillis(10);
}
void EventsManager::nextFrame() {
++_frameCounter;
++_playTime;
// Handle any idle updates
if (!_eventTargets.empty())
eventTarget()->onIdle();
// Render anything pending for the screen
Graphics::Screen *screen = _callback->getScreen();
if (screen)
screen->update();
}
void EventsManager::setButtonDown(MouseButton button, bool isDown) {
assert(button != BUTTON_NONE);
byte mask = 0;
switch (button) {
case BUTTON_LEFT:
mask = MK_LBUTTON;
break;
case BUTTON_RIGHT:
mask = MK_RBUTTON;
break;
case BUTTON_MIDDLE:
mask = MK_MBUTTON;
break;
default:
break;
}
if (isDown) {
_buttonsDown |= BUTTON_MASK(button);
_specialButtons |= mask;
} else {
_buttonsDown &= ~BUTTON_MASK(button);
_specialButtons &= ~mask;
}
}
uint32 EventsManager::getTicksCount() const {
return _frameCounter * GAME_FRAME_TIME;
}
void EventsManager::sleep(uint time) {
uint32 delayEnd = g_system->getMillis() + time;
while (!g_engine->shouldQuit() && g_system->getMillis() < delayEnd)
pollEventsAndWait();
}
bool EventsManager::waitForPress(uint expiry) {
uint32 delayEnd = g_system->getMillis() + expiry;
CPressTarget pressTarget;
addTarget(&pressTarget);
while (!g_engine->shouldQuit() && g_system->getMillis() < delayEnd && !pressTarget._pressed) {
pollEventsAndWait();
}
removeTarget();
return pressTarget._pressed;
}
void EventsManager::setMousePos(const Point &pt) {
g_system->warpMouse(pt.x, pt.y);
_mousePos = pt;
eventTarget()->mouseMove(_mousePos);
}
void EventsManager::handleKbdSpecial(Common::KeyState keyState) {
if (keyState.flags & Common::KBD_CTRL)
_specialButtons |= MK_CONTROL;
else
_specialButtons &= ~MK_CONTROL;
if (keyState.flags & Common::KBD_SHIFT)
_specialButtons |= MK_SHIFT;
else
_specialButtons &= ~MK_SHIFT;
}
bool shouldQuit() {
return g_engine->shouldQuit();
}
bool isMouseDownEvent(Common::EventType type) {
return type == Common::EVENT_LBUTTONDOWN || type == Common::EVENT_RBUTTONDOWN
|| type == Common::EVENT_MBUTTONDOWN;
}
bool isMouseUpEvent(Common::EventType type) {
return type == Common::EVENT_LBUTTONUP || type == Common::EVENT_RBUTTONUP
|| type == Common::EVENT_MBUTTONUP;
}
MouseButton whichButton(Common::EventType type) {
if (type == Common::EVENT_LBUTTONDOWN || type == Common::EVENT_LBUTTONUP)
return BUTTON_LEFT;
else if (type == Common::EVENT_RBUTTONDOWN || type == Common::EVENT_RBUTTONUP)
return BUTTON_RIGHT;
else if (type == Common::EVENT_MBUTTONDOWN || type == Common::EVENT_MBUTTONUP)
return BUTTON_MIDDLE;
else
return BUTTON_NONE;
}
} // End of namespace Shared
} // End of namespace Ultima

View File

@@ -0,0 +1,338 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA_SHARED_ENGINE_EVENTS_H
#define ULTIMA_SHARED_ENGINE_EVENTS_H
#include "common/scummsys.h"
#include "common/events.h"
#include "common/stack.h"
#include "gui/debugger.h"
#include "graphics/screen.h"
#include "ultima/shared/core/rect.h"
namespace Ultima {
namespace Shared {
#define GAME_FRAME_RATE (1000 / 50)
#define GAME_FRAME_TIME 50
#define SCREEN_UPDATE_TIME 10
#define BUTTON_MASK(MB) (1 << ((int)(MB) - 1))
#define DOUBLE_CLICK_TIME 100
enum MouseButton {
BUTTON_NONE = 0,
BUTTON_LEFT = 1,
BUTTON_RIGHT = 2,
BUTTON_MIDDLE = 3,
MOUSE_LAST
};
enum SpecialButtons {
MK_LBUTTON = 1, MK_RBUTTON = 2, MK_MBUTTON = 4, MK_SHIFT = 8, MK_CONTROL = 0x10
};
/**
* A base class for windows that can receive event messages
*/
class EventTarget {
public:
virtual ~EventTarget() {
}
/**
* Called to handle any regular updates the game requires
*/
virtual void onIdle() {
}
/**
* Mouse/key event handlers
*/
virtual void mouseMove(const Common::Point &mousePos) {
}
virtual void leftButtonDown(const Common::Point &mousePos) {
}
virtual void leftButtonUp(const Common::Point &mousePos) {
}
virtual void leftButtonDoubleClick(const Common::Point &mousePos) {
}
virtual void middleButtonDown(const Common::Point &mousePos) {
}
virtual void middleButtonUp(const Common::Point &mousePos) {
}
virtual void middleButtonDoubleClick(const Common::Point &mousePos) {
}
virtual void rightButtonDown(const Common::Point &mousePos) {
}
virtual void rightButtonUp(const Common::Point &mousePos) {
}
virtual void mouseWheel(const Common::Point &mousePos, bool wheelUp) {
}
virtual void keyDown(Common::KeyState keyState) {
}
virtual void keyUp(Common::KeyState keyState) {
}
};
/**
* An eent target used for waiting for a mouse or keypress
*/
class CPressTarget : public EventTarget {
public:
bool _pressed;
public:
CPressTarget() : _pressed(false) {
}
~CPressTarget() override {
}
void leftButtonDown(const Common::Point &mousePos) override {
_pressed = true;
}
void middleButtonDown(const Common::Point &mousePos) override {
_pressed = true;
}
void rightButtonDown(const Common::Point &mousePos) override {
_pressed = true;
}
void keyDown(Common::KeyState keyState) override {
_pressed = true;
}
};
/**
* Abstract interface for engine functionality the events manager needs to access
*/
class EventsCallback {
public:
/**
* Destructor
*/
virtual ~EventsCallback() {}
/**
* Get the screen
*/
virtual Graphics::Screen *getScreen() const {
return nullptr;
}
};
class EventsManager {
private:
EventsCallback *_callback;
Common::Stack<EventTarget *> _eventTargets;
uint32 _frameCounter;
uint32 _priorFrameTime;
uint32 _priorFrameCounterTime;
uint32 _gameCounter;
uint32 _playTime;
Point _mousePos;
uint _specialButtons;
uint8 _buttonsDown;
/**
* Check whether it's time to display the next screen frame
*/
bool checkForNextFrameCounter();
/**
* Return the currently active event target
*/
EventTarget *eventTarget() const {
return _eventTargets.top();
}
/**
* Handles setting/resettings special buttons on key up/down
*/
void handleKbdSpecial(Common::KeyState keyState);
/**
* Sets whether a given button is depressed
*/
void setButtonDown(MouseButton button, bool isDown);
protected:
/**
* Handles moving to the next game frame
*/
virtual void nextFrame();
public:
EventsManager(EventsCallback *callback);
virtual ~EventsManager() {}
/**
* Adds a new event target to the top of the list. It will get
* all events generated until such time as another is pushed on
* top of it, or the removeTarget method is called
*/
void addTarget(EventTarget *target) {
_eventTargets.push(target);
}
/**
* Removes the currently active event target
*/
void removeTarget() {
_eventTargets.pop();
}
/**
* Polls the ScummVM backend for any pending events, passing out the event, if any
*/
virtual bool pollEvent(Common::Event &event);
/**
* Checks for any pending events. This differs from pollEvent, in that the event manager will dispatch
* all pending events to the currently registered active event target, rather than simply returning a
* single event like pollEvent does
*/
void pollEvents();
/**
* Poll for events and introduce a small delay, to allow the system to
* yield to other running programs
*/
void pollEventsAndWait();
/**
* Return the current game frame number
*/
uint32 getFrameCounter() const {
return _frameCounter;
}
/**
* Get the elapsed playtime
*/
uint32 getTicksCount() const;
/**
* Sleep for a specified period of time
*/
void sleep(uint time);
/**
* Wait for a mouse or keypress
*/
bool waitForPress(uint expiry);
/**
* Sets the mouse position
*/
void setMousePos(const Point &pt);
/*
* Return whether a given special key is currently pressed
*/
bool isSpecialPressed(SpecialButtons btn) const {
return (_specialButtons & btn) != 0;
}
/**
* Returns the bitset of the currently pressed special buttons
*/
uint getSpecialButtons() const {
return _specialButtons;
}
/*
* Set the cursor
*/
virtual void setCursor(int cursorId) {
}
/**
* Show the mouse cursor
*/
void showCursor();
/**
* Hide the mouse cursor
*/
void hideCursor();
/**
* Returns if the mouse cursor is visible
*/
bool isCursorVisible();
/**
* Gets the current total ticks
*/
uint32 getTicks() {
return _frameCounter;
}
/**
* Gets the total overall play time
*/
uint32 playTime() const {
return _playTime;
}
/**
* Sets the current play time
*/
void setPlayTime(uint32 time) {
_playTime = time;
}
/**
* Returns true if a given mouse button is pressed
*/
inline bool isButtonDown(MouseButton button) const {
return (_buttonsDown & BUTTON_MASK(button)) != 0;
}
/**
* Returns true if any mouse button is pressed
*/
bool isButtonDown() const {
return isButtonDown(BUTTON_LEFT) || isButtonDown(BUTTON_RIGHT) || isButtonDown(BUTTON_MIDDLE);
}
/**
* Returns the mouse buttons states
*/
byte getButtonState() const {
return _buttonsDown;
}
/**
* Return the mouse position
*/
Common::Point getMousePos() const {
return _mousePos;
}
};
extern bool isMouseDownEvent(Common::EventType type);
extern bool isMouseUpEvent(Common::EventType type);
extern MouseButton whichButton(Common::EventType type);
} // End of namespace Shared
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,118 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/shared/engine/input_handler.h"
#include "ultima/shared/engine/events.h"
#include "ultima/shared/early/game_base.h"
#include "ultima/shared/engine/messages.h"
#include "ultima/shared/gfx/visual_item.h"
namespace Ultima {
namespace Shared {
InputHandler::InputHandler(GameBase *game) : _game(game), _inputTranslator(nullptr), _dragging(false),
_buttonDown(false), _lockCount(0), _abortMessage(false) {
}
InputHandler::~InputHandler() {
}
void InputHandler::setTranslator(InputTranslator *translator) {
_inputTranslator = translator;
}
void InputHandler::incLockCount() {
++_lockCount;
}
void InputHandler::decLockCount() {
--_lockCount;
assert(_lockCount >= 0);
if (_lockCount == 0 && _inputTranslator) {
if (_dragging && !_inputTranslator->isMousePressed()) {
CMouseButtonUpMsg upMsg(_mousePos, MK_LBUTTON);
handleMessage(upMsg);
}
_buttonDown = _inputTranslator->isMousePressed();
_abortMessage = true;
}
}
void InputHandler::handleMessage(CMessage &msg, bool respectLock) {
if (!respectLock || _lockCount <= 0) {
processMessage(&msg);
}
}
void InputHandler::processMessage(CMessage *msg) {
const CMouseMsg *mouseMsg = dynamic_cast<const CMouseMsg *>(msg);
_abortMessage = false;
dispatchMessage(msg);
if (_abortMessage) {
_abortMessage = false;
} else if (mouseMsg) {
// Keep the game state mouse position up to date
if (_mousePos != mouseMsg->_mousePos) {
_mousePos = mouseMsg->_mousePos;
}
// Set flag for whether a mouse button is currently being pressed
if (mouseMsg->isButtonDownMsg())
_buttonDown = true;
else if (mouseMsg->isButtonUpMsg())
_buttonDown = false;
// Drag events generation
if (_dragging) {
if (mouseMsg->isMouseMoveMsg()) {
CMouseDragMsg moveMsg(_mousePos, mouseMsg->_buttons);
dispatchMessage(&moveMsg);
} else if (mouseMsg->isButtonUpMsg()) {
_dragging = false;
}
} else if (_buttonDown) {
if (!mouseMsg->isMouseMoveMsg()) {
// Save where the drag movement started from
_dragStartPos = _mousePos;
} else {
Point delta = _mousePos - _dragStartPos;
int distance = (int)sqrt(double(delta.x * delta.x + delta.y * delta.y));
if (distance > 4) {
// A drag has started
_dragging = true;
}
}
}
}
}
void InputHandler::dispatchMessage(CMessage *msg) {
Gfx::VisualItem *view = _game->getView();
if (view)
msg->execute(view);
}
} // End of namespace Shared
} // End of namespace Ultima

View File

@@ -0,0 +1,78 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA_SHARED_ENGINE_INPUT_HANDLER_H
#define ULTIMA_SHARED_ENGINE_INPUT_HANDLER_H
#include "ultima/shared/engine/input_translator.h"
#include "ultima/shared/core/tree_item.h"
namespace Ultima {
namespace Shared {
class GameBase;
class InputHandler {
private:
/**
* Process and dispatch a passed message
*/
void processMessage(CMessage *msg);
/**
* Dispatches a message to the project
*/
void dispatchMessage(CMessage *msg);
public:
GameBase *_game;
InputTranslator *_inputTranslator;
bool _dragging;
bool _buttonDown;
Point _mousePos;
Point _dragStartPos;
int _lockCount;
bool _abortMessage;
public:
InputHandler(GameBase *game);
~InputHandler();
void setTranslator(InputTranslator *translator);
/**
* Increment the lock count
*/
void incLockCount();
/**
* Decrement the lock count on the input handler
*/
void decLockCount();
/**
* Handles a genereated mouse message
*/
void handleMessage(CMessage &msg, bool respectLock = true);
};
} // End of namespace Shared
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,123 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/shared/engine/input_handler.h"
#include "ultima/shared/engine/input_translator.h"
#include "ultima/shared/engine/events.h"
#include "ultima/shared/engine/messages.h"
#include "ultima/shared/early/ultima_early.h"
namespace Ultima {
namespace Shared {
InputTranslator::InputTranslator(InputHandler *inputHandler) :
_inputHandler(inputHandler) {
inputHandler->setTranslator(this);
}
int InputTranslator::getButtons(int special) const {
int buttons = 0;
if (special & MK_LBUTTON)
buttons |= MB_LEFT;
if (special & MK_MBUTTON)
buttons |= MB_MIDDLE;
if (special & MK_RBUTTON)
buttons |= MB_RIGHT;
return buttons;
}
void InputTranslator::mouseMove(int special, const Point &pt) {
CMouseMoveMsg msg(pt, getButtons(special));
_inputHandler->handleMessage(msg);
}
void InputTranslator::mouseDrag(int special, const Point &pt) {
CMouseDragMsg msg(pt, getButtons(special));
_inputHandler->handleMessage(msg);
}
void InputTranslator::leftButtonDown(int special, const Point &pt) {
CMouseButtonDownMsg msg(pt, MB_LEFT);
_inputHandler->handleMessage(msg);
}
void InputTranslator::leftButtonUp(int special, const Point &pt) {
CMouseButtonUpMsg msg(pt, MB_LEFT);
_inputHandler->handleMessage(msg);
}
void InputTranslator::leftButtonDoubleClick(int special, const Point &pt) {
CMouseDoubleClickMsg msg(pt, MB_LEFT);
_inputHandler->handleMessage(msg);
}
void InputTranslator::middleButtonDown(int special, const Point &pt) {
CMouseButtonDownMsg msg(pt, MB_MIDDLE);
_inputHandler->handleMessage(msg);
}
void InputTranslator::middleButtonUp(int special, const Point &pt) {
CMouseButtonUpMsg msg(pt, MB_MIDDLE);
_inputHandler->handleMessage(msg);
}
void InputTranslator::middleButtonDoubleClick(int special, const Point &pt) {
CMouseDoubleClickMsg msg(pt, MB_MIDDLE);
_inputHandler->handleMessage(msg);
}
void InputTranslator::rightButtonDown(int special, const Point &pt) {
CMouseButtonDownMsg msg(pt, MB_RIGHT);
_inputHandler->handleMessage(msg);
}
void InputTranslator::rightButtonUp(int special, const Point &pt) {
CMouseButtonUpMsg msg(pt, MB_RIGHT);
_inputHandler->handleMessage(msg);
}
void InputTranslator::mouseWheel(bool wheelUp, const Point &pt) {
CMouseWheelMsg msg(pt, wheelUp);
_inputHandler->handleMessage(msg);
}
void InputTranslator::rightButtonDoubleClick(int special, const Point &pt) {
CMouseDoubleClickMsg msg(pt, MB_RIGHT);
_inputHandler->handleMessage(msg);
}
void InputTranslator::keyDown(const Common::KeyState &keyState) {
CKeypressMsg pressMsg(keyState);
_inputHandler->handleMessage(pressMsg);
if (keyState.ascii >= 32 && keyState.ascii <= 127) {
CKeyCharMsg charMsg(keyState.ascii);
_inputHandler->handleMessage(charMsg);
}
}
bool InputTranslator::isMousePressed() const {
return g_vm->_events->getSpecialButtons() & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON);
}
} // End of namespace Shared
} // End of namespace Ultima

View File

@@ -0,0 +1,67 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA_SHARED_ENGINE_INPUT_TRANSLATOR_H
#define ULTIMA_SHARED_ENGINE_INPUT_TRANSLATOR_H
#include "common/keyboard.h"
#include "ultima/shared/engine/messages.h"
namespace Ultima {
namespace Shared {
class InputHandler;
class InputTranslator {
private:
/**
* Converts the special buttons bitset into a buttons bitset
*/
int getButtons(int special) const;
public:
InputHandler *_inputHandler;
public:
InputTranslator(InputHandler *inputHandler);
void mouseMove(int special, const Point &pt);
void mouseDrag(int special, const Point &pt);
void leftButtonDown(int special, const Point &pt);
void leftButtonUp(int special, const Point &pt);
void leftButtonDoubleClick(int special, const Point &pt);
void middleButtonDown(int special, const Point &pt);
void middleButtonUp(int special, const Point &pt);
void middleButtonDoubleClick(int special, const Point &pt);
void rightButtonDown(int special, const Point &pt);
void rightButtonUp(int special, const Point &pt);
void mouseWheel(bool wheelUp, const Point &pt);
void rightButtonDoubleClick(int special, const Point &pt);
void keyDown(const Common::KeyState &keyState);
/**
* Returns true if any mouse button is currently pressed
*/
bool isMousePressed() const;
};
} // End of namespace Shared
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,121 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/shared/engine/messages.h"
#include "ultima/shared/early/ultima_early.h"
#include "ultima/shared/early/game_base.h"
#include "ultima/shared/gfx/screen.h"
#include "ultima/shared/early/game.h"
namespace Ultima {
namespace Shared {
CMessage::CMessage() : BaseObject() {
}
bool CMessage::execute(TreeItem *target, const ClassDef *classDef, int flags) {
// If no target was specified, then there's nothing to do
if (!target)
return false;
bool result = false;
TreeItem *item = target;
TreeItem *nextItem = nullptr;
do {
if (flags & MSGFLAG_SCAN)
nextItem = item->scan(target);
if (!classDef || item->isInstanceOf(*classDef)) {
bool handled = perform(item);
if (handled) {
result = true;
if (flags & MSGFLAG_BREAK_IF_HANDLED)
return true;
}
}
item = nextItem;
} while (nextItem);
return result;
}
bool CMessage::execute(const Common::String &target, const ClassDef *classDef, int flags) {
// Scan for the target by name
GameBase *game = g_vm->_game;
for (TreeItem *treeItem = game; treeItem; treeItem = treeItem->scan(game)) {
if (!treeItem->getName().compareToIgnoreCase(target))
return execute(treeItem, classDef, flags);
}
return false;
}
const MSGMAP_ENTRY *CMessage::findMapEntry(const TreeItem *treeItem, const ClassDef &classDef) {
// Iterate through the class and any parent classes
for (const MSGMAP *msgMap = treeItem->getMessageMap(); msgMap->pFnGetBaseMap;
msgMap = msgMap->pFnGetBaseMap()) {
// Iterate through the map entries for this class
for (const MSGMAP_ENTRY *entry = msgMap->lpEntries;
entry->_classDef != nullptr; ++entry) {
// Check if the class or any of it's ancesotrs is handled by this entry
for (ClassDef def = (*entry->_classDef)(); def.hasParent(); def = def.parent()) {
if (def == classDef)
return entry;
}
}
}
return nullptr;
}
bool CMessage::perform(TreeItem *treeItem) {
const MSGMAP_ENTRY *entry = findMapEntry(treeItem, getType());
return entry && (*treeItem.*(entry->_fn))(this);
}
bool CMessage::supports(const TreeItem *treeItem, const ClassDef &classDef) {
return findMapEntry(treeItem, classDef) != nullptr;
}
bool CMessage::isMouseMsg() const {
return dynamic_cast<const CMouseMsg *>(this) != nullptr;
}
bool CMessage::isButtonDownMsg() const {
return dynamic_cast<const CMouseButtonDownMsg *>(this) != nullptr;
}
bool CMessage::isButtonUpMsg() const {
return dynamic_cast<const CMouseButtonUpMsg *>(this) != nullptr;
}
bool CMessage::isMouseMoveMsg() const {
return dynamic_cast<const CMouseMoveMsg *>(this) != nullptr;
}
bool CMessage::isDoubleClickMsg() const {
return dynamic_cast<const CMouseDoubleClickMsg *>(this) != nullptr;
}
} // End of namespace Shared
} // End of namespace Ultima

View File

@@ -0,0 +1,407 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA_SHARED_ENGINE_MESSAGES_H
#define ULTIMA_SHARED_ENGINE_MESSAGES_H
#include "common/keyboard.h"
#include "common/events.h"
#include "ultima/shared/core/rect.h"
#include "ultima/shared/core/base_object.h"
#include "ultima/shared/core/tree_item.h"
namespace Ultima {
namespace Shared {
enum MessageFlag {
MSGFLAG_SCAN = 1,
MSGFLAG_BREAK_IF_HANDLED = 2,
MSGFLAG_CLASS_DEF = 4
};
class CMessage;
namespace Gfx {
class VisualItem;
class TextInput;
class Popup;
}
#define MESSAGEDEF(theClass) \
static ClassDef type() { return ClassDef(#theClass, &CMessage::type); } \
virtual ClassDef getType() const { return type(); }
#define MESSAGEDEFP(theClass, baseClass) \
static ClassDef type() { return ClassDef(#theClass, &baseClass::type); } \
virtual ClassDef getType() const { return type(); }
#define MESSAGE0(NAME) \
class NAME: public CMessage { \
public: NAME() : CMessage() {} \
MESSAGEDEF(NAME); \
}
#define MESSAGE1(NAME, F1, N1, V1) \
class NAME: public CMessage { \
public: F1 _##N1; \
NAME(F1 N1 = V1) : CMessage(), _##N1(N1) {} \
MESSAGEDEF(NAME); \
}
#define MESSAGE2(NAME, F1, N1, V1, F2, N2, V2) \
class NAME: public CMessage { \
public: F1 _##N1; F2 _##N2; \
NAME(F1 N1 = V1, F2 N2 = V2) : CMessage(), _##N1(N1), _##N2(N2) {} \
MESSAGEDEF(NAME); \
}
#define MESSAGE3(NAME, F1, N1, V1, F2, N2, V2, F3, N3, V3) \
class NAME: public CMessage { \
public: F1 _##N1; F2 _##N2; F3 _##N3; \
NAME(F1 N1 = V1, F2 N2 = V2, F3 N3 = V3) : CMessage(), _##N1(N1), _##N2(N2), _##N3(N3) {} \
MESSAGEDEF(NAME); \
}
#define MESSAGE4(NAME, F1, N1, V1, F2, N2, V2, F3, N3, V3, F4, N4, V4) \
class NAME: public CMessage { \
public: F1 _##N1; F2 _##N2; F3 _##N3; F4 _##N4; \
NAME(F1 N1 = V1, F2 N2 = V2, F3 N3 = V3, F4 N4 = V4) : CMessage(), _##N1(N1), _##N2(N2), _##N3(N3), _##N4(N4) {} \
MESSAGEDEF(NAME); \
}
/**
* Base class for all messages
*/
class CMessage : public BaseObject {
private:
/**
* Find a map entry that supports the given class
*/
static const MSGMAP_ENTRY *findMapEntry(const TreeItem *treeItem, const ClassDef &classDef);
public:
MESSAGEDEFP(CMessage, BaseObject);
CMessage();
/**
* Executes the message, passing it on to the designated target,
* and optionally it's children
*/
bool execute(TreeItem *target, const ClassDef *classDef = nullptr,
int flags = MSGFLAG_SCAN | MSGFLAG_BREAK_IF_HANDLED);
/**
* Executes the message, passing it on to the designated target,
* and optionally it's children
*/
bool execute(const Common::String &target, const ClassDef *classDef = nullptr,
int flags = MSGFLAG_SCAN | MSGFLAG_BREAK_IF_HANDLED);
/**
* Makes the passed item execute the message
*/
virtual bool perform(TreeItem *treeItem);
/**
* Returns true if the passed item supports the specified message class
*/
static bool supports(const TreeItem *treeItem, const ClassDef &classDef);
virtual bool isMouseMsg() const;
virtual bool isButtonDownMsg() const;
virtual bool isButtonUpMsg() const;
virtual bool isMouseMoveMsg() const;
virtual bool isDoubleClickMsg() const;
};
enum CMouseButton {
MB_LEFT = 1, MB_MIDDLE = 2, MB_RIGHT = 4
};
/**
* Base class for the different mouse notifications
*/
class CMouseMsg : public CMessage {
public:
int _buttons;
Point _mousePos;
public:
MESSAGEDEF(CMouseMsg);
CMouseMsg() : _buttons(0) {}
CMouseMsg(const Point &pt, int buttons) :
_mousePos(pt), _buttons(buttons) {}
};
/**
* Notifies a mouse movement
*
*/
class CMouseMoveMsg : public CMouseMsg {
public:
MESSAGEDEFP(CMouseMoveMsg, CMouseMsg);
CMouseMoveMsg() : CMouseMsg() {}
CMouseMoveMsg(const Point &pt, int buttons) : CMouseMsg(pt, buttons) {}
};
/**
* Notifies of a mouse drag operation
*/
class CMouseDragMsg : public CMouseMoveMsg {
public:
MESSAGEDEFP(CMouseDragMsg, CMouseMoveMsg);
CMouseDragMsg() : CMouseMoveMsg() {}
CMouseDragMsg(const Point &pt, int buttons) : CMouseMoveMsg(pt, buttons) {}
};
/**
* Base class for mouse button notifications
*/
class CMouseButtonMsg : public CMouseMsg {
public:
MESSAGEDEFP(CMouseButtonMsg, CMouseMsg);
CMouseButtonMsg() : CMouseMsg() {}
CMouseButtonMsg(const Point &pt, int buttons) : CMouseMsg(pt, buttons) {}
};
/**
* Notifies a mouse button down
*/
class CMouseButtonDownMsg : public CMouseButtonMsg {
public:
MESSAGEDEFP(CMouseButtonDownMsg, CMouseButtonMsg);
CMouseButtonDownMsg() : CMouseButtonMsg() {}
CMouseButtonDownMsg(const Point &pt, int buttons) : CMouseButtonMsg(pt, buttons) {}
};
/**
* Notifies a mouse button release
*/
class CMouseButtonUpMsg : public CMouseButtonMsg {
public:
MESSAGEDEFP(CMouseButtonUpMsg, CMouseButtonMsg);
CMouseButtonUpMsg() : CMouseButtonMsg() {}
CMouseButtonUpMsg(const Point &pt, int buttons) : CMouseButtonMsg(pt, buttons) {}
};
/**
* Notifies a mouse wheel action
*/
class CMouseWheelMsg : public CMouseMsg {
public:
bool _wheelUp;
public:
MESSAGEDEFP(CMouseWheelMsg, CMouseMsg);
CMouseWheelMsg() : CMouseMsg(), _wheelUp(false) {}
CMouseWheelMsg(const Point &pt, bool wheelUp) :
CMouseMsg(pt, 0), _wheelUp(wheelUp) {}
};
/**
* Notifies a mouse double-click
*/
class CMouseDoubleClickMsg : public CMouseButtonMsg {
public:
MESSAGEDEFP(CMouseDuobleClickMsg, CMouseButtonMsg);
CMouseDoubleClickMsg() : CMouseButtonMsg() {}
CMouseDoubleClickMsg(const Point &pt, int buttons) : CMouseButtonMsg(pt, buttons) {}
};
/**
* Used to notify that a rendering frame has finished, occurring at GAME_FRAME_RATE times every second
*/
MESSAGE1(CFrameMsg, uint, ticks, 0);
/**
* Notifies a game view is being hidden
*/
MESSAGE2(CHideMsg, Gfx::VisualItem *, view, (Gfx::VisualItem *)nullptr, bool, fadeOut, false);
/**
* Show a prompt in the info area, and get a keypress for a command
*/
MESSAGE1(CInfoGetCommandKeypress, TreeItem *, responder, (TreeItem *)nullptr);
/**
* Get a keypress in the info area
*/
MESSAGE1(CInfoGetKeypress, TreeItem *, responder, (TreeItem *)nullptr);
/**
* Get a text input in the input area
*/
MESSAGE3(CInfoGetInput, TreeItem *, responder, (TreeItem *)nullptr, bool, isNumeric, false, size_t, maxCharacters, 10);
/**
* Adds text strings to the info area
*/
MESSAGE3(CInfoMsg, Common::String, text, "", bool, newLine, true, bool, replaceLine, false);
/**
* Signals an unknown/unhandled keypress
*/
MESSAGE0(CHuhMsg);
/**
* Signals a single standard ASCII keypress
*/
MESSAGE1(CKeyCharMsg, int, key, 32);
/**
* Signals a keypress
*/
MESSAGE1(CKeypressMsg, Common::KeyState, keyState, Common::KeyState());
/**
* Called when a popup is finally shown
*/
MESSAGE1(CPopupShownMsg, Gfx::Popup *, view, (Gfx::Popup *)nullptr);
/**
* Called when a popup is hidden
*/
MESSAGE1(CPopupHiddenMsg, Gfx::Popup *, view, (Gfx::Popup *)nullptr);
/**
* Called when a game view is shown
*/
MESSAGE2(CShowMsg, Gfx::VisualItem *, view, (Gfx::VisualItem *)nullptr, bool, fadeIn, false);
/**
* Used when text input is finished, to pass the text back to the owning view
*/
MESSAGE2(CTextInputMsg, Common::String, text, "", bool, escaped, false);
/**
* Used when character input is finished, to pass the text back to the owning view
*/
MESSAGE1(CCharacterInputMsg, Common::KeyState, keyState, Common::KeyState());
/*-------------------------------------------------------------------*/
/**
* Used to trigger a party movement
*/
MESSAGE1(CMoveMsg, int, direction, 0);
/**
* Used to trigger an attack
*/
MESSAGE1(CAttackMsg, int, direction, 0);
/**
* Used to trigger a board action
*/
MESSAGE0(CBoardMsg);
/**
* Used to trigger a cast action
*/
MESSAGE0(CCastMsg);
/**
* Climb up or down
*/
MESSAGE0(CClimbMsg);
/**
* Used to trigger a drop action
*/
MESSAGE0(CDropMsg);
/**
* Used to trigger an Enter action
*/
MESSAGE0(CEnterMsg);
/**
* Used to trigger an exit action
*/
MESSAGE0(CExitTransportMsg);
/**
* Used to trigger a fire action
*/
MESSAGE0(CFireMsg);
/**
* Used to trigger a Get action
*/
MESSAGE0(CGetMsg);
/**
* Used to trigger a HyperJump action
*/
MESSAGE0(CHyperJumpMsg);
/**
* Used to trigger an inform action
*/
MESSAGE0(CInformMsg);
/**
* Used to trigger an open action
*/
MESSAGE0(COpenMsg);
/**
* Pass a turn
*/
MESSAGE0(CPassMsg);
/**
* Used to trigger a quit (save) action
*/
MESSAGE0(CQuitMsg);
/**
* Used to trigger a ready item/weapon/spell action
*/
MESSAGE0(CReadyMsg);
/**
* Used to trigger a stats action
*/
MESSAGE0(CStatsMsg);
/**
* Used to trigger a steal action
*/
MESSAGE0(CStealMsg);
/**
* Used to trigger a Transact action
*/
MESSAGE0(CTransactMsg);
/**
* Used to trigger an Unlock action
*/
MESSAGE0(CUnlockMsg);
/**
* Used to trigger a view change action
*/
MESSAGE0(CViewChangeMsg);
/**
* Used to signal an end of turn
*/
MESSAGE0(CEndOfTurnMsg);
} // End of namespace Shared
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,265 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/shared/engine/resources.h"
#include "ultima/shared/early/font_resources.h"
#include "common/endian.h"
#ifndef RELEASE_BUILD
#include "ultima/ultima1/core/resources.h"
#endif
namespace Ultima {
namespace Shared {
/*-------------------------------------------------------------------*/
bool Resources::open() {
// Save locally constructred resources to the archive manager for access
Shared::FontResources sharedFonts(this);
sharedFonts.save();
#ifndef RELEASE_BUILD
Ultima1::GameResources u1Data(this);
u1Data.save();
#endif
return true;
}
void Resources::addResource(const Common::Path &name, const byte *data, size_t size) {
// Add a new entry to the local resources list for the passed data
_localResources.push_back(LocalResource());
LocalResource &lr = _localResources[_localResources.size() - 1];
lr._name = name;
lr._data.resize(size);
Common::copy(data, data + size, &lr._data[0]);
}
bool Resources::hasFile(const Common::Path &path) const {
for (uint idx = 0; idx < _localResources.size(); ++idx)
if (_localResources[idx]._name.equalsIgnoreCase(path))
return true;
return false;
}
int Resources::listMembers(Common::ArchiveMemberList &list) const {
for (uint idx = 0; idx < _localResources.size(); ++idx) {
list.push_back(Common::ArchiveMemberPtr(new Common::GenericArchiveMember(_localResources[idx]._name, *this)));
}
return _localResources.size();
}
const Common::ArchiveMemberPtr Resources::getMember(const Common::Path &path) const {
if (!hasFile(path))
return Common::ArchiveMemberPtr();
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
}
Common::SeekableReadStream *Resources::createReadStreamForMember(const Common::Path &path) const {
for (uint idx = 0; idx < _localResources.size(); ++idx) {
const LocalResource &lr = _localResources[idx];
if (lr._name.equalsIgnoreCase(path))
return new Common::MemoryReadStream(&lr._data[0], lr._data.size());
}
return nullptr;
}
/*-------------------------------------------------------------------*/
void Resources::FileResource::load(File &f) {
_name = f.readString();
_offset = f.readUint32LE();
_size = f.readUint16LE();
}
/*-------------------------------------------------------------------*/
ResourceFile::ResourceFile(const Common::Path &filename) : _filename(filename), _bufferP(_buffer) {
Common::fill(_buffer, _buffer + STRING_BUFFER_SIZE, 0);
}
void ResourceFile::load() {
_file.open(_filename);
synchronize();
_file.close();
}
void ResourceFile::syncString(const char *&str) {
str = _bufferP;
do {
*_bufferP = _file.readByte();
} while (*_bufferP++);
assert(_bufferP < (_buffer + STRING_BUFFER_SIZE));
}
void ResourceFile::syncStrings(const char **str, size_t count) {
uint tag = _file.readUint32LE();
assert(tag == MKTAG(count, 0, 0, 0));
for (size_t idx = 0; idx < count; ++idx)
syncString(str[idx]);
}
void ResourceFile::syncStrings2D(const char **str, size_t count1, size_t count2) {
uint tag = _file.readUint32LE();
assert(tag == MKTAG(count1, count2, 0, 0));
for (size_t idx = 0; idx < count1 * count2; ++idx)
syncString(str[idx]);
}
void ResourceFile::syncNumber(int &val) {
val = _file.readSint32LE();
}
void ResourceFile::syncNumbers(int *vals, size_t count) {
uint tag = _file.readUint32LE();
assert(tag == MKTAG(count, 0, 0, 0));
for (size_t idx = 0; idx < count; ++idx)
vals[idx] = _file.readSint32LE();
}
void ResourceFile::syncNumbers2D(int *vals, size_t count1, size_t count2) {
uint tag = _file.readUint32LE();
assert(tag == MKTAG(count1, count2, 0, 0));
for (size_t idx = 0; idx < count1 * count2; ++idx)
vals[idx] = _file.readSint32LE();
}
void ResourceFile::syncNumbers3D(int *vals, size_t count1, size_t count2, size_t count3) {
uint tag = _file.readUint32LE();
assert(tag == MKTAG(count1, count2, count3, 0));
for (size_t idx = 0; idx < count1 * count2 * count3; ++idx)
vals[idx] = _file.readSint32LE();
}
void ResourceFile::syncBytes(byte *vals, size_t count) {
uint tag = _file.readUint32LE();
assert(tag == MKTAG(count, 0, 0, 0));
_file.read(vals, count);
}
void ResourceFile::syncBytes2D(byte *vals, size_t count1, size_t count2) {
uint tag = _file.readUint32LE();
assert(tag == MKTAG(count1, count2, 0, 0));
_file.read(vals, count1 * count2);
}
/*-------------------------------------------------------------------*/
void LocalResourceFile::save() {
synchronize();
_file.finalize();
_owner->addResource(_filename, _file.getData(), _file.size());
}
void LocalResourceFile::syncString(const char *&str) {
if (!_owner) {
ResourceFile::syncString(str);
} else {
_file.writeString(str);
_file.writeByte('\0');
}
}
void LocalResourceFile::syncStrings(const char **str, size_t count) {
if (!_owner) {
ResourceFile::syncStrings(str, count);
} else {
_file.writeUint32LE(MKTAG(count, 0, 0, 0));
for (size_t idx = 0; idx < count; ++idx)
syncString(str[idx]);
}
}
void LocalResourceFile::syncStrings2D(const char **str, size_t count1, size_t count2) {
if (!_owner) {
ResourceFile::syncStrings2D(str, count1, count2);
} else {
_file.writeUint32LE(MKTAG(count1, count2, 0, 0));
for (size_t idx = 0; idx < count1 * count2; ++idx)
syncString(str[idx]);
}
}
void LocalResourceFile::syncNumber(int &val) {
if (!_owner)
ResourceFile::syncNumber(val);
else
_file.writeUint32LE(val);
}
void LocalResourceFile::syncNumbers(int *vals, size_t count) {
if (!_owner) {
ResourceFile::syncNumbers(vals, count);
} else {
_file.writeUint32LE(MKTAG(count, 0, 0, 0));
for (size_t idx = 0; idx < count; ++idx)
_file.writeUint32LE(vals[idx]);
}
}
void LocalResourceFile::syncNumbers2D(int *vals, size_t count1, size_t count2) {
if (!_owner) {
ResourceFile::syncNumbers2D(vals, count1, count2);
} else {
_file.writeUint32LE(MKTAG(count1, count2, 0, 0));
for (size_t idx = 0; idx < count1 * count2; ++idx)
_file.writeUint32LE(vals[idx]);
}
}
void LocalResourceFile::syncNumbers3D(int *vals, size_t count1, size_t count2, size_t count3) {
if (!_owner) {
ResourceFile::syncNumbers3D(vals, count1, count2, count3);
} else {
_file.writeUint32LE(MKTAG(count1, count2, count3, 0));
for (size_t idx = 0; idx < count1 * count2 * count3; ++idx)
_file.writeUint32LE(vals[idx]);
}
}
void LocalResourceFile::syncBytes(byte *vals, size_t count) {
if (!_owner) {
ResourceFile::syncBytes(vals, count);
} else {
_file.writeUint32LE(MKTAG(count, 0, 0, 0));
_file.write(vals, count);
}
}
void LocalResourceFile::syncBytes2D(byte *vals, size_t count1, size_t count2) {
if (!_owner) {
ResourceFile::syncBytes2D(vals, count1, count2);
} else {
_file.writeUint32LE(MKTAG(count1, count2, 0, 0));
_file.write(vals, count1 * count2);
}
}
} // End of namespace Shared
} // End of namespace Ultima

View File

@@ -0,0 +1,196 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA_SHARED_ENGINE_RESOURCES_H
#define ULTIMA_SHARED_ENGINE_RESOURCES_H
#include "common/algorithm.h"
#include "common/archive.h"
#include "common/array.h"
#include "common/memstream.h"
#include "common/str.h"
#include "common/serializer.h"
#include "ultima/shared/core/file.h"
#define STRING_BUFFER_SIZE 32768
namespace Ultima {
namespace Shared {
class Resources;
/**
* Base class for classes that exposes a set of strings, arrays, and other data from a resource
*/
class ResourceFile {
private:
File _file;
char _buffer[STRING_BUFFER_SIZE];
char *_bufferP;
protected:
Common::Path _filename;
protected:
/**
* Constructor
*/
ResourceFile(const Common::Path &filename);
/**
* Destructor
*/
virtual ~ResourceFile() {
}
/**
* Synchronizes the data for the resource
*/
virtual void synchronize() = 0;
virtual void syncString(const char *&str);
virtual void syncStrings(const char **str, size_t count);
virtual void syncStrings2D(const char **str, size_t count1, size_t count2);
virtual void syncNumber(int &val);
virtual void syncNumbers(int *vals, size_t count);
virtual void syncNumbers2D(int *vals, size_t count1, size_t count2);
virtual void syncNumbers3D(int *vals, size_t count1, size_t count2, size_t count3);
virtual void syncBytes(byte *vals, size_t count);
virtual void syncBytes2D(byte *vals, size_t count1, size_t count2);
public:
/**
* Loads in a resource
*/
void load();
};
/**
* Derived base class for resources that have their contents within the executable rather than a data file.
* This will allow the data for a particular Ultima game to be gradually built up without repeatedly
* regenerating a data file. Once a game has been properly tested, then it can be moved out.
*/
class LocalResourceFile : public ResourceFile {
private:
Common::MemoryWriteStreamDynamic _file;
Resources *_owner;
protected:
/**
* Constructor
*/
LocalResourceFile(const Common::Path &filename) : ResourceFile(filename), _owner(nullptr), _file(DisposeAfterUse::YES) {}
/**
* Constructor
*/
LocalResourceFile(Resources *owner, const Common::Path &filename) : ResourceFile(filename),
_owner(owner), _file(DisposeAfterUse::YES) {}
/**
* Return true if in saving mode
*/
bool isSaving() const { return _owner != nullptr; }
void syncString(const char *&str) override;
void syncStrings(const char **str, size_t count) override;
void syncStrings2D(const char **str, size_t count1, size_t count2) override;
void syncNumber(int &val) override;
void syncNumbers(int *vals, size_t count) override;
void syncNumbers2D(int *vals, size_t count1, size_t count2) override;
void syncNumbers3D(int *vals, size_t count1, size_t count2, size_t count3) override;
void syncBytes(byte *vals, size_t count) override;
void syncBytes2D(byte *vals, size_t count1, size_t count2) override;
public:
/**
* Write out the resource to the in-memory resource store
*/
void save();
};
/**
* Resources manager
*/
class Resources : public Common::Archive {
struct LocalResource {
Common::Path _name;
Common::Array<byte> _data;
};
struct FileResource {
Common::Path _name;
size_t _offset, _size;
/**
* Load an index entry from the datafile
*/
void load(File &f);
};
private:
Common::Array<LocalResource> _localResources;
public:
/**
* Constructor
*/
Resources() {
}
/**
* Sets up the resources for the engine
* @returns False if setup failed
*/
bool open();
/**
* Adds a resource created in memory to the ScummVM archive manager, so that it can be
* later opened like a normal file. Just as it will when eventually shifted to the
* data file for the engine
*/
void addResource(const Common::Path &name, const byte *data, size_t size);
// Archive implementation
/**
* Check if a member with the given name is present in the Archive.
* Patterns are not allowed, as this is meant to be a quick File::exists()
* replacement.
*/
bool hasFile(const Common::Path &path) const override;
/**
* Add all members of the Archive to list.
* Must only append to list, and not remove elements from it.
*
* @return the number of names added to list
*/
int listMembers(Common::ArchiveMemberList &list) const override;
/**
* Returns a ArchiveMember representation of the given file.
*/
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
/**
* Create a stream bound to a member with the specified name in the
* archive. If no member with this name exists, 0 is returned.
* @return the newly created input stream
*/
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
};
} // End of namespace Shared
} // End of namespace Ultima
#endif