STARK: Update the resource provider to allow changing locations

This commit is contained in:
Bastien Bouclet 2014-12-28 11:50:12 +01:00
parent 642c5476b4
commit 7f2182279a
9 changed files with 284 additions and 113 deletions

View file

@ -23,6 +23,8 @@
#include "engines/stark/archiveloader.h"
#include "engines/stark/xrcreader.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/location.h"
namespace Stark {
@ -68,8 +70,8 @@ Resource *ArchiveLoader::LoadedArchive::importResources() {
}
ArchiveLoader::~ArchiveLoader() {
for (uint i = 0; i < _archives.size(); i++) {
delete _archives[i];
for (LoadedArchiveList::iterator it = _archives.begin(); it != _archives.end(); it++) {
delete *it;
}
}
@ -83,15 +85,13 @@ void ArchiveLoader::load(const Common::String &archiveName) {
_archives.push_back(archive);
}
void ArchiveLoader::unload(const Common::String &archiveName) {
for (uint i = 0; i < _archives.size(); i++) {
if (_archives[i]->getFilename() == archiveName) {
_archives.remove_at(i);
return;
void ArchiveLoader::unloadUnused() {
for (LoadedArchiveList::iterator it = _archives.begin(); it != _archives.end(); it++) {
if (!(*it)->isInUse()) {
delete *it;
_archives.erase(it);
}
}
error("The archive with name '%s' is not loaded.", archiveName.c_str());
}
Common::ReadStream *ArchiveLoader::getFile(const Common::String &fileName, const Common::String &archiveName) {
@ -100,14 +100,20 @@ Common::ReadStream *ArchiveLoader::getFile(const Common::String &fileName, const
return xarc.createReadStreamForMember(fileName);
}
Resource *ArchiveLoader::getRoot(const Common::String &archiveName) {
Resource *ArchiveLoader::useRoot(const Common::String &archiveName) {
LoadedArchive *archive = findArchive(archiveName);
archive->incUsage();
return archive->getRoot();
}
void ArchiveLoader::returnRoot(const Common::String &archiveName) {
LoadedArchive *archive = findArchive(archiveName);
archive->decUsage();
}
bool ArchiveLoader::hasArchive(const Common::String &archiveName) {
for (uint i = 0; i < _archives.size(); i++) {
if (_archives[i]->getFilename() == archiveName) {
for (LoadedArchiveList::iterator it = _archives.begin(); it != _archives.end(); it++) {
if ((*it)->getFilename() == archiveName) {
return true;
}
}
@ -116,13 +122,34 @@ bool ArchiveLoader::hasArchive(const Common::String &archiveName) {
}
ArchiveLoader::LoadedArchive *ArchiveLoader::findArchive(const Common::String &archiveName) {
for (uint i = 0; i < _archives.size(); i++) {
if (_archives[i]->getFilename() == archiveName) {
return _archives[i];
for (LoadedArchiveList::iterator it = _archives.begin(); it != _archives.end(); it++) {
if ((*it)->getFilename() == archiveName) {
return *it;
}
}
error("The archive with name '%s' is not loaded.", archiveName.c_str());
}
Common::String ArchiveLoader::buildArchiveName(Level *level, Location *location) {
Common::String archive;
if (!location) {
switch (level->getSubType()) {
case 1:
archive = Common::String::format("%s/%s.xarc", level->getName().c_str(), level->getName().c_str());
break;
case 2:
archive = Common::String::format("%02x/%02x.xarc", level->getIndex(), level->getIndex());
break;
default:
error("Unknown level archive type %d", level->getSubType());
}
} else {
archive = Common::String::format("%02x/%02x/%02x.xarc", level->getIndex(), location->getIndex(), location->getIndex());
}
return archive;
}
} // End of namespace Stark

View file

@ -23,14 +23,17 @@
#ifndef STARK_ARCHIVE_LOADER_H
#define STARK_ARCHIVE_LOADER_H
#include "common/array.h"
#include "common/list.h"
#include "common/str.h"
#include "common/util.h"
#include "engines/stark/archive.h"
namespace Stark {
class Resource;
class Level;
class Location;
/**
* XARC Archive loader.
@ -46,14 +49,20 @@ public:
/** Load a Xarc archive, and add it to the managed archives list */
void load(const Common::String &archiveName);
/** Unload a Xarc archive, and add it to the managed archives list */
void unload(const Common::String &archiveName);
/** Unload all the unused Xarc archives */
void unloadUnused();
/** Retrieve a file from a specified archive */
Common::ReadStream *getFile(const Common::String &fileName, const Common::String &archiveName);
/** Get the resource tree root for an archive */
Resource *getRoot(const Common::String &archiveName);
/** Get the resource tree root for an archive, and increment the archive use count */
Resource *useRoot(const Common::String &archiveName);
/** Decrement the root's archive use count */
void returnRoot(const Common::String &archiveName);
/** Build the archive filename for a level or a location */
Common::String buildArchiveName(Level *level, Location *location = nullptr);
private:
class LoadedArchive {
@ -65,18 +74,25 @@ private:
XARCArchive &getXArc() { return _xarc; }
Resource *getRoot() { return _root; }
bool isInUse() { return _useCount > 0; }
void incUsage() { _useCount++; }
void decUsage() { _useCount = MAX<int>(_useCount - 1, 0); }
private:
Resource *importResources();
uint _useCount;
Common::String _filename;
XARCArchive _xarc;
Resource *_root;
};
typedef Common::List<LoadedArchive *> LoadedArchiveList;
bool hasArchive(const Common::String &archiveName);
LoadedArchive *findArchive(const Common::String &archiveName);
Common::Array<LoadedArchive *> _archives;
LoadedArchiveList _archives;
};
} // End of namespace Stark

View file

@ -23,7 +23,7 @@
#include "engines/stark/console.h"
#include "engines/stark/archive.h"
#include "engines/stark/resources/resource.h"
#include "engines/stark/xrcreader.h"
#include "engines/stark/archiveloader.h"
#include "common/file.h"
@ -31,8 +31,8 @@ namespace Stark {
Console::Console(StarkEngine *vm) : GUI::Debugger(), _vm(vm) {
registerCmd("dumpArchive", WRAP_METHOD(Console, Cmd_DumpArchive));
registerCmd("dumpScript", WRAP_METHOD(Console, Cmd_DumpScript));
registerCmd("listRooms", WRAP_METHOD(Console, Cmd_ListRooms));
registerCmd("dumpResources", WRAP_METHOD(Console, Cmd_DumpResources));
registerCmd("listLocations", WRAP_METHOD(Console, Cmd_ListLocations));
}
Console::~Console() {
@ -83,7 +83,7 @@ bool Console::Cmd_DumpArchive(int argc, const char **argv) {
return true;
}
bool Console::Cmd_DumpScript(int argc, const char **argv) {
bool Console::Cmd_DumpResources(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Print the scripts from an archive.\n");
debugPrintf("Usage :\n");
@ -91,50 +91,27 @@ bool Console::Cmd_DumpScript(int argc, const char **argv) {
return true;
}
Resource *resource = loadXARCScripts(argv[1]);
ArchiveLoader *archiveLoader = new ArchiveLoader();
archiveLoader->load(argv[1]);
Resource *resource = archiveLoader->useRoot(argv[1]);
if (resource == nullptr) {
debugPrintf("Can't open archive with name '%s'\n", argv[1]);
return true;
}
resource->print();
delete resource;
delete archiveLoader;
return true;
}
Resource *Console::loadXARCScripts(Common::String archive) {
XARCArchive xarc;
if (!xarc.open(archive)) {
return nullptr;
}
bool Console::Cmd_ListLocations(int argc, const char **argv) {
ArchiveLoader *archiveLoader = new ArchiveLoader();
Common::ArchiveMemberList members;
xarc.listMatchingMembers(members, "*.xrc");
if (members.size() == 0) {
error("No scripts in archive '%s'", archive.c_str());
}
if (members.size() > 1) {
error("Too many scripts in archive '%s'", archive.c_str());
}
Common::SeekableReadStream *stream = xarc.createReadStreamForMember(members.front()->getName());
Resource *root = XRCReader::importTree(stream);
delete stream;
return root;
}
bool Console::Cmd_ListRooms(int argc, const char **argv) {
Resource *root = loadXARCScripts("x.xarc");
if (root == nullptr) {
debugPrintf("Can't open archive 'x.xarc'\n");
return true;
}
archiveLoader->load("x.xarc");
Resource *root = archiveLoader->useRoot("x.xarc");
// Loop over the levels
for (uint i = 0; i < root->getChildren().size(); i++) {
@ -143,30 +120,32 @@ bool Console::Cmd_ListRooms(int argc, const char **argv) {
// Only consider levels
if (level->getType() != ResourceType::kLevel) continue;
Common::String levelArchive = level->getArchive();
Common::String levelArchive = archiveLoader->buildArchiveName((Level *) level);
debugPrintf("%s - %s\n", levelArchive.c_str(), level->getName().c_str());
// Load the detailed level archive
level = loadXARCScripts(levelArchive);
archiveLoader->load(levelArchive);
level = archiveLoader->useRoot(levelArchive);
if (!level)
error("Unable to load archive '%s'", levelArchive.c_str());
// Loop over the rooms
// Loop over the locations
for (uint j = 0; j < level->getChildren().size(); j++) {
Resource *room = level->getChildren()[j];
Resource *location = level->getChildren()[j];
// Only consider rooms
if (room->getType() != ResourceType::kLocation) continue;
// Only consider locations
if (location->getType() != ResourceType::kLocation) continue;
Common::String roomArchive = room->getArchive();
debugPrintf("%s - %s\n", roomArchive.c_str(), room->getName().c_str());
Common::String roomArchive = archiveLoader->buildArchiveName((Level *) level, (Location *) location);
debugPrintf("%s - %s\n", roomArchive.c_str(), location->getName().c_str());
}
delete level;
archiveLoader->returnRoot(levelArchive);
archiveLoader->unloadUnused();
}
delete root;
delete archiveLoader;
return true;
}

View file

@ -42,10 +42,8 @@ private:
StarkEngine *_vm;
bool Cmd_DumpArchive(int argc, const char **argv);
bool Cmd_DumpScript(int argc, const char **argv);
bool Cmd_ListRooms(int argc, const char** argv);
Resource *loadXARCScripts(Common::String archive);
bool Cmd_DumpResources(int argc, const char **argv);
bool Cmd_ListLocations(int argc, const char** argv);
};
} // End of namespace Stark

View file

@ -25,42 +25,145 @@
#include "engines/stark/archiveloader.h"
#include "engines/stark/resources/root.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/location.h"
namespace Stark {
ResourceProvider::ResourceProvider(ArchiveLoader *archiveLoader, Global *global) :
_archiveLoader(archiveLoader),
_global(global) {
_global(global),
_locationChangeRequest(false) {
}
void ResourceProvider::initGlobal() {
// Load the root archive
_archiveLoader->load("x.xarc");
// Set the root tree
Root *root = static_cast<Root *>(_archiveLoader->getRoot("x.xarc"));
Root *root = static_cast<Root *>(_archiveLoader->useRoot("x.xarc"));
_global->setRoot(root);
// Find the global level node
Level *global = static_cast<Level *>(root->findChild(ResourceType::kLevel, 1));
// Load the global archive
Common::String globalArchiveName = global->getArchive();
Common::String globalArchiveName = _archiveLoader->buildArchiveName(global);
_archiveLoader->load(globalArchiveName);
// Set the global tree
global = static_cast<Level *>(_archiveLoader->getRoot(globalArchiveName));
global = static_cast<Level *>(_archiveLoader->useRoot(globalArchiveName));
_global->setLevel(global);
//TODO: Retrieve the inventory and April from the global tree
}
Current *ResourceProvider::findLevel(uint16 level) {
for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) {
if ((*it)->getLevel()->getIndex() == level) {
return *it;
}
}
return nullptr;
}
Current *ResourceProvider::findLocation(uint16 level, uint16 location) {
for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) {
if ((*it)->getLevel()->getIndex() == level
&& (*it)->getLocation()->getIndex() == location) {
return *it;
}
}
return nullptr;
}
void ResourceProvider::requestLocationChange(uint16 level, uint16 location) {
Current *currentLocation = new Current();
// Retrieve the level archive name
Root *root = _global->getRoot();
Level *rootLevelResource = (Level *) root->findChildWithIndex(ResourceType::kLevel, -1, level);
Common::String levelArchive = _archiveLoader->buildArchiveName(rootLevelResource);
// Load the archive, and get the resource sub-tree root
_archiveLoader->load(levelArchive);
currentLocation->setLevel(static_cast<Level *>(_archiveLoader->useRoot(levelArchive)));
// Retrieve the location archive name
Level *levelResource = currentLocation->getLevel();
Location *levelLocationResource = (Location *) levelResource->findChildWithIndex(ResourceType::kLocation, -1, location);
Common::String locationArchive = _archiveLoader->buildArchiveName(levelResource, levelLocationResource);
// Load the archive, and get the resource sub-tree root
_archiveLoader->load(locationArchive);
currentLocation->setLocation(static_cast<Location *>(_archiveLoader->useRoot(locationArchive)));
_locations.push_back(currentLocation);
_locationChangeRequest = true;
}
void ResourceProvider::performLocationChange() {
if (_global->getCurrent()) {
// Exit the previous location
Current *previous = _global->getCurrent();
// Resource lifecycle update
previous->getLocation()->onExitLocation();
previous->getLevel()->onExitLocation();
_global->getLevel()->onExitLocation();
}
// Set the new current location
Current *current = _locations.back();
_global->setCurrent(current);
// Resource lifecycle update
_global->getLevel()->onExitLocation();
current->getLevel()->onExitLocation();
current->getLocation()->onExitLocation();
purgeOldLocations();
_locationChangeRequest = false;
}
void ResourceProvider::purgeOldLocations() {
while (_locations.size() >= 2) {
Current *location = _locations.front();
_archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel(), location->getLocation()));
_archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel()));
delete location;
_locations.pop_front();
}
_archiveLoader->unloadUnused();
}
void ResourceProvider::shutdown() {
_archiveLoader->unload(_global->getLevel()->getArchive());
// Flush the locations list
for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) {
Current *location = *it;
_archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel(), location->getLocation()));
_archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel()));
delete location;
}
_locations.clear();
// Return the global resources
_archiveLoader->returnRoot(_archiveLoader->buildArchiveName(_global->getLevel()));
_archiveLoader->returnRoot("x.xarc");
_global->setLevel(nullptr);
_archiveLoader->unload("x.xarc");
_global->setRoot(nullptr);
_archiveLoader->unloadUnused();
}
} // End of namespace Stark

View file

@ -23,7 +23,7 @@
#ifndef STARK_RESOURCE_PROVIDER_H
#define STARK_RESOURCE_PROVIDER_H
#include "common/array.h"
#include "common/list.h"
namespace Stark {
@ -105,12 +105,39 @@ class ResourceProvider {
public:
ResourceProvider(ArchiveLoader *archiveLoader, Global *global);
/** Load the global archives and fill the global object */
void initGlobal();
/** Load the resources for the specified location */
void requestLocationChange(uint16 level, uint16 location);
/** Is a location change pending? */
bool hasLocationChangeRequest() { return _locationChangeRequest; }
/**
* Apply a location change request.
*
* Update the global object with the new location.
* Perform the necessary resource lifecycle updates
*/
void performLocationChange();
/** Release the global and current resources */
void shutdown();
private:
typedef Common::List<Current *> CurrentList;
Current *findLevel(uint16 level);
Current *findLocation(uint16 level, uint16 location);
void purgeOldLocations();
Global *_global;
ArchiveLoader *_archiveLoader;
bool _locationChangeRequest;
CurrentList _locations;
};
} // End of namespace Stark

View file

@ -124,6 +124,30 @@ void Resource::onAllLoaded() {
}
}
void Resource::onEnterLocation() {
Common::Array<Resource *>::iterator i = _children.begin();
while (i != _children.end()) {
(*i)->onEnterLocation();
i++;
}
}
void Resource::onGameLoop(uint msecs) {
Common::Array<Resource *>::iterator i = _children.begin();
while (i != _children.end()) {
(*i)->onGameLoop(msecs);
i++;
}
}
void Resource::onExitLocation() {
Common::Array<Resource *>::iterator i = _children.begin();
while (i != _children.end()) {
(*i)->onExitLocation();
i++;
}
}
void Resource::onPreDestroy() {
Common::Array<Resource *>::iterator i = _children.begin();
while (i != _children.end()) {
@ -162,33 +186,6 @@ void Resource::addChild(Resource *child) {
_children.push_back(child);
}
Common::String Resource::getArchive() {
Common::String archive;
switch (getType().get()) {
case ResourceType::kLevel:
switch (_subType) {
case 1:
archive = Common::String::format("%s/%s.xarc", _name.c_str(), _name.c_str());
break;
case 2:
archive = Common::String::format("%02x/%02x.xarc", _index, _index);
break;
default:
error("Unknown level archive type %d", _subType);
}
break;
case ResourceType::kLocation:
assert(_parent);
archive = Common::String::format("%02x/%02x/%02x.xarc", _parent->_index, _index, _index);
break;
default:
error("This type of resource cannot load children %s", _type.getName());
}
return archive;
}
Resource *Resource::findChild(ResourceType type, int subType, bool mustBeUnique) {
Resource *child = nullptr;

View file

@ -167,17 +167,26 @@ public:
*/
virtual void onAllLoaded();
/**
* Called when entering a location
*/
virtual void onEnterLocation();
/**
* Called once per game loop
*/
virtual void onGameLoop(uint msecs);
/**
* Called when exiting a location
*/
virtual void onExitLocation();
/**
* Called before a resource sub-tree is unloaded.
*/
virtual void onPreDestroy();
/**
* Get the archive file name containing the data for this resource.
* Only Levels and Locations have archives.
*/
Common::String getArchive();
Resource *findChild(ResourceType type, int subType, bool mustBeUnique = true);
Resource *findChildWithIndex(ResourceType type, int subType, uint16 index);

View file

@ -25,6 +25,8 @@
#include "engines/stark/console.h"
#include "engines/stark/debug.h"
#include "engines/stark/resourceprovider.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/scene.h"
#include "engines/stark/gfx/driver.h"
@ -76,8 +78,12 @@ Common::Error StarkEngine::run() {
_global = new Global();
_resourceProvider = new ResourceProvider(_archiveLoader, _global);
// Load global resources
_resourceProvider->initGlobal();
// Start us up at the house of all worlds
_resourceProvider->requestLocationChange(0x45, 0x00);
// Start running
mainLoop();
@ -91,6 +97,10 @@ void StarkEngine::mainLoop() {
_scene = new Scene(_gfx);
while (!shouldQuit()) {
if (_resourceProvider->hasLocationChangeRequest()) {
_resourceProvider->performLocationChange();
}
// Process events
Common::Event e;
while (g_system->getEventManager()->pollEvent(e)) {
@ -133,6 +143,11 @@ void StarkEngine::updateDisplayScene() {
// Clear the screen
_gfx->clearScreen();
// Update the game resources
_global->getLevel()->onGameLoop(delta);
_global->getCurrent()->getLevel()->onGameLoop(delta);
_global->getCurrent()->getLocation()->onGameLoop(delta);
// Render the current scene
_scene->render(delta);