Refactor resource management

- Move GameID into libgame
- Move resource provider registration into Game
- Move GUI background loading into GameGUI
This commit is contained in:
Vsevolod Kremianskii 2021-03-23 13:21:24 +07:00
parent 32bc207b14
commit 9c5be9b87a
69 changed files with 457 additions and 533 deletions

View file

@ -96,7 +96,6 @@ set_target_properties(libcommon PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINA
set(RESOURCE_HEADERS
src/resource/2da.h
src/resource/gameidutil.h
src/resource/gffstruct.h
src/resource/folder.h
src/resource/format/2dareader.h
@ -125,7 +124,6 @@ set(RESOURCE_HEADERS
set(RESOURCE_SOURCES
src/resource/2da.cpp
src/resource/gameidutil.cpp
src/resource/gffstruct.cpp
src/resource/folder.cpp
src/resource/format/2dareader.cpp
@ -181,7 +179,6 @@ set(RENDER_HEADERS
src/render/model/emitter.h
src/render/model/mdlreader.h
src/render/model/model.h
src/render/model/modelloader.h
src/render/model/modelmesh.h
src/render/model/modelnode.h
src/render/model/models.h
@ -442,6 +439,7 @@ set(GAME_HEADERS
src/game/enginetype/event.h
src/game/enginetype/location.h
src/game/game.h
src/game/gameidutil.h
src/game/gui/barkbubble.h
src/game/gui/chargen/abilities.h
src/game/gui/chargen/chargen.h
@ -557,6 +555,9 @@ set(GAME_SOURCES
src/game/cursors.cpp
src/game/dialog.cpp
src/game/game.cpp
src/game/game_kotor.cpp
src/game/game_tsl.cpp
src/game/gameidutil.cpp
src/game/gui/barkbubble.cpp
src/game/gui/chargen/abilities.cpp
src/game/gui/chargen/chargen.cpp

View file

@ -43,14 +43,14 @@ AudioFiles::AudioFiles() : MemoryCache(bind(&AudioFiles::doGet, this, _1)) {
shared_ptr<AudioStream> AudioFiles::doGet(string resRef) {
shared_ptr<AudioStream> result;
shared_ptr<ByteArray> mp3Data(Resources::instance().get(resRef, ResourceType::Mp3, false));
shared_ptr<ByteArray> mp3Data(Resources::instance().getRaw(resRef, ResourceType::Mp3, false));
if (mp3Data) {
Mp3Reader mp3;
mp3.load(wrap(mp3Data));
result = mp3.stream();
}
if (!result) {
shared_ptr<ByteArray> wavData(Resources::instance().get(resRef, ResourceType::Wav));
shared_ptr<ByteArray> wavData(Resources::instance().getRaw(resRef, ResourceType::Wav));
if (wavData) {
WavReader wav;
wav.load(wrap(wavData));

View file

@ -44,13 +44,13 @@ public:
~Cursors();
void init(resource::GameID gameId);
void init(GameID gameId);
void deinit();
std::shared_ptr<render::Cursor> get(CursorType type);
private:
resource::GameID _gameId { resource::GameID::KotOR };
GameID _gameId { GameID::KotOR };
std::unordered_map<CursorType, std::shared_ptr<render::Cursor>> _cache;
const std::pair<uint32_t, uint32_t> &getCursorNames(CursorType type);

View file

@ -17,6 +17,8 @@
#include "game.h"
#include <boost/algorithm/string.hpp>
#include "SDL2/SDL_timer.h"
#include "../audio/files.h"
@ -34,7 +36,6 @@
#include "../render/textures.h"
#include "../render/walkmesh/walkmeshes.h"
#include "../render/window.h"
#include "../resource/gameidutil.h"
#include "../resource/resources.h"
#include "../resource/strings.h"
#include "../script/scripts.h"
@ -43,6 +44,7 @@
#include "blueprint/blueprints.h"
#include "cursors.h"
#include "gameidutil.h"
#include "reputes.h"
#include "gui/sounds.h"
#include "script/routines.h"
@ -65,6 +67,9 @@ namespace reone {
namespace game {
static constexpr char kDataDirectoryName[] = "data";
static constexpr char kModulesDirectoryName[] = "modules";
static bool g_conversationsEnabled = true;
Game::Game(const fs::path &path, const Options &opts) :
@ -99,25 +104,23 @@ int Game::run() {
}
void Game::init() {
initResourceProviders();
loadModuleNames();
RenderWindow::instance().init(_options.graphics, this);
Strings::instance().init(_gameId, _path);
Resources::instance().init(_gameId, _path);
Strings::instance().init(_path);
Meshes::instance().init();
Textures::instance().init(_gameId);
Textures::instance().init();
Materials::instance().init();
PBRIBL::instance().init();
Shaders::instance().init();
AudioPlayer::instance().init(_options.audio);
GUISounds::instance().init();
Routines::instance().init(_gameId, this);
Routines::instance().init(this);
Reputes::instance().init();
Surfaces::instance().init();
Walkmeshes::instance().init(Surfaces::instance().getWalkableSurfaceIndices(), Surfaces::instance().getGrassSurfaceIndices());
Models::instance().init(_gameId);
registerModelLoaders();
Cursors::instance().init(_gameId);
setCursorType(CursorType::Default);
@ -126,8 +129,27 @@ void Game::init() {
_profileOverlay.init();
}
void Game::registerModelLoaders() {
Models::instance().registerLoader(ResourceType::Mdl, make_shared<MdlModelLoader>());
void Game::initResourceProviders() {
if (isTSL(_gameId)) {
initResourceProvidersForTSL();
} else {
initResourceProvidersForKotOR();
}
Resources::instance().indexDirectory(getPathIgnoreCase(fs::current_path(), kDataDirectoryName));
}
void Game::loadModuleNames() {
fs::path modules(getPathIgnoreCase(_path, kModulesDirectoryName));
for (auto &entry : fs::directory_iterator(modules)) {
string filename(boost::to_lower_copy(entry.path().filename().string()));
if (!boost::ends_with(filename, ".rim") || boost::ends_with(filename, "_s.rim")) continue;
string moduleName(boost::to_lower_copy(filename.substr(0, filename.size() - 4)));
_moduleNames.push_back(move(moduleName));
}
sort(_moduleNames.begin(), _moduleNames.end());
}
void Game::setCursorType(CursorType type) {
@ -232,7 +254,7 @@ void Game::loadModule(const string &name, string entry) {
SoundSets::instance().invalidate();
Lips::instance().invalidate();
Resources::instance().loadModule(name);
loadModuleResources(name);
if (_module) {
_module->area()->runOnExitScript();
@ -280,6 +302,22 @@ void Game::withLoadingScreen(const string &imageResRef, const function<void()> &
block();
}
void Game::loadModuleResources(const string &moduleName) {
Resources::instance().invalidateCache();
Resources::instance().clearTransientProviders();
fs::path modulesPath(getPathIgnoreCase(_path, kModulesDirectoryName));
Resources::instance().indexRimFile(getPathIgnoreCase(modulesPath, moduleName + ".rim"), true);
Resources::instance().indexRimFile(getPathIgnoreCase(modulesPath, moduleName + "_s.rim"), true);
fs::path lipsPath(getPathIgnoreCase(_path, kLipsDirectoryName));
Resources::instance().indexErfFile(getPathIgnoreCase(lipsPath, moduleName + "_loc.mod"), true);
if (isTSL(_gameId)) {
Resources::instance().indexErfFile(getPathIgnoreCase(modulesPath, moduleName + "_dlg.erf"), true);
}
}
void Game::drawAll() {
// Compute derived PBR IBL textures from queued environment maps
PBRIBL::instance().refresh();

View file

@ -26,7 +26,6 @@
#include "SDL2/SDL_events.h"
#include "../resource/types.h"
#include "../scene/pipeline/world.h"
#include "../scene/scenegraph.h"
@ -66,6 +65,15 @@ class Video;
namespace game {
constexpr char kKeyFilename[] = "chitin.key";
constexpr char kTexturePackDirectoryName[] = "texturepacks";
constexpr char kGUITexturePackFilename[] = "swpc_tex_gui.erf";
constexpr char kTexturePackFilename[] = "swpc_tex_tpa.erf";
constexpr char kMusicDirectoryName[] = "streammusic";
constexpr char kSoundsDirectoryName[] = "streamsounds";
constexpr char kLipsDirectoryName[] = "lips";
constexpr char kOverrideDirectoryName[] = "override";
/**
* Entry point for the game logic: contains the main game loop and delegates
* work to the instances of Module and GUI. Serves as a Service Locator.
@ -76,7 +84,6 @@ namespace game {
class Game : public render::IEventHandler, boost::noncopyable {
public:
Game(const boost::filesystem::path &path, const Options &opts);
virtual ~Game() = default;
/**
* Initialize the engine, run the main game loop and clean up on exit.
@ -100,7 +107,7 @@ public:
int getRunScriptVar() const;
std::shared_ptr<Object> getObjectById(uint32_t id) const;
resource::GameID gameId() const { return _gameId; }
GameID gameId() const { return _gameId; }
const Options &options() const { return _options; }
scene::SceneGraph &sceneGraph() { return _sceneGraph; }
ObjectFactory &objectFactory() { return *_objectFactory; }
@ -111,6 +118,7 @@ public:
CameraType cameraType() const { return _cameraType; }
ScriptRunner &scriptRunner() { return _scriptRunner; }
Conversation &conversation() { return *_conversation; }
const std::vector<std::string> &moduleNames() const { return _moduleNames; }
void setCursorType(CursorType type);
void setLoadFromSaveGame(bool load);
@ -205,7 +213,7 @@ private:
scene::SceneGraph _sceneGraph;
scene::WorldRenderPipeline _worldPipeline;
Console _console;
resource::GameID _gameId { resource::GameID::KotOR };
GameID _gameId { GameID::KotOR };
std::unique_ptr<ObjectFactory> _objectFactory;
GameScreen _screen { GameScreen::MainMenu };
Party _party;
@ -221,6 +229,7 @@ private:
bool _paused { false };
Conversation *_conversation { nullptr }; /**< pointer to either DialogGUI or ComputerGUI */
ProfileOverlay _profileOverlay;
std::vector<std::string> _moduleNames;
// Modules
@ -282,12 +291,22 @@ private:
void changeScreen(GameScreen screen);
void updateVideo(float dt);
void updateMusic();
void registerModelLoaders();
std::string getMainMenuMusic() const;
std::string getCharacterGenerationMusic() const;
gui::GUI *getScreenGUI() const;
// Resource management
void initResourceProviders();
void initResourceProvidersForKotOR();
void initResourceProvidersForTSL();
void loadModuleNames();
void loadModuleResources(const std::string &moduleName);
// END Resource management
// Loading
void loadCharacterGeneration();

68
src/game/game_kotor.cpp Normal file
View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2020-2021 The reone project contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
/** @file
* Game functions specific to KotOR.
*/
#include "game.h"
#include "../common/pathutil.h"
#include "../resource/keybifprovider.h"
#include "../resource/resources.h"
using namespace std;
using namespace reone::resource;
namespace fs = boost::filesystem;
namespace reone {
namespace game {
static constexpr char kPatchFilename[] = "patch.erf";
static constexpr char kWavesDirectoryName[] = "streamwaves";
static constexpr char kExeFilename[] = "swkotor.exe";
static vector<string> g_nonTransientLipFiles { "global.mod", "localization.mod" };
void Game::initResourceProvidersForKotOR() {
Resources::instance().indexKeyFile(getPathIgnoreCase(_path, kKeyFilename));
Resources::instance().indexErfFile(getPathIgnoreCase(_path, kPatchFilename));
fs::path texPacksPath(getPathIgnoreCase(_path, kTexturePackDirectoryName));
Resources::instance().indexErfFile(getPathIgnoreCase(texPacksPath, kGUITexturePackFilename));
Resources::instance().indexErfFile(getPathIgnoreCase(texPacksPath, kTexturePackFilename));
Resources::instance().indexDirectory(getPathIgnoreCase(_path, kMusicDirectoryName));
Resources::instance().indexDirectory(getPathIgnoreCase(_path, kSoundsDirectoryName));
Resources::instance().indexDirectory(getPathIgnoreCase(_path, kWavesDirectoryName));
fs::path lipsPath(getPathIgnoreCase(_path, kLipsDirectoryName));
for (auto &filename : g_nonTransientLipFiles) {
Resources::instance().indexErfFile(getPathIgnoreCase(lipsPath, filename));
}
Resources::instance().indexExeFile(getPathIgnoreCase(_path, kExeFilename));
Resources::instance().indexDirectory(getPathIgnoreCase(_path, kOverrideDirectoryName));
}
} // namespace game
} // namespace reone

63
src/game/game_tsl.cpp Normal file
View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2020-2021 The reone project contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
/** @file
* Game functions specific to TSL.
*/
#include "game.h"
#include "../common/pathutil.h"
#include "../resource/keybifprovider.h"
#include "../resource/resources.h"
using namespace std;
using namespace reone::resource;
namespace fs = boost::filesystem;
namespace reone {
namespace game {
static constexpr char kVoiceDirectoryName[] = "streamvoice";
static constexpr char kLocalizationLipFilename[] = "localization";
static constexpr char kExeFilename[] = "swkotor2.exe";
void Game::initResourceProvidersForTSL() {
Resources::instance().indexKeyFile(getPathIgnoreCase(_path, kKeyFilename));
fs::path texPacksPath(getPathIgnoreCase(_path, kTexturePackDirectoryName));
Resources::instance().indexErfFile(getPathIgnoreCase(texPacksPath, kGUITexturePackFilename));
Resources::instance().indexErfFile(getPathIgnoreCase(texPacksPath, kTexturePackFilename));
Resources::instance().indexDirectory(getPathIgnoreCase(_path, kMusicDirectoryName));
Resources::instance().indexDirectory(getPathIgnoreCase(_path, kSoundsDirectoryName));
Resources::instance().indexDirectory(getPathIgnoreCase(_path, kVoiceDirectoryName));
fs::path lipsPath(getPathIgnoreCase(_path, kLipsDirectoryName));
Resources::instance().indexErfFile(getPathIgnoreCase(lipsPath, kLocalizationLipFilename));
Resources::instance().indexExeFile(getPathIgnoreCase(_path, kExeFilename));
Resources::instance().indexDirectory(getPathIgnoreCase(_path, kOverrideDirectoryName));
}
} // namespace game
} // namespace reone

View file

@ -23,7 +23,7 @@ namespace fs = boost::filesystem;
namespace reone {
namespace resource {
namespace game {
GameID determineGameID(const fs::path &gameDir) {
// If there is no swkotor2 executable, then this is KotOR
@ -47,6 +47,6 @@ bool isTSL(GameID gameId) {
}
}
} // namespace resource
} // namespace game
} // namespace reone

View file

@ -23,7 +23,7 @@
namespace reone {
namespace resource {
namespace game {
/**
* @return GameID determined from the specified game directory
@ -32,6 +32,6 @@ GameID determineGameID(const boost::filesystem::path &gameDir);
bool isTSL(GameID gameId);
} // namespace resource
} // namespace game
} // namespace reone

View file

@ -29,7 +29,7 @@ class CharacterGeneration;
class CharGenAbilities : public GameGUI {
public:
CharGenAbilities(CharacterGeneration *charGen, resource::GameID gameId, const render::GraphicsOptions &opts);
CharGenAbilities(CharacterGeneration *charGen, GameID gameId, const render::GraphicsOptions &opts);
void load() override;

View file

@ -21,10 +21,10 @@
#include "../../../gui/scenebuilder.h"
#include "../../../render/model/models.h"
#include "../../../resource/gameidutil.h"
#include "../../../resource/resources.h"
#include "../../game.h"
#include "../../gameidutil.h"
#include "../../portraitutil.h"
#include "../../rp/classes.h"
@ -56,9 +56,9 @@ CharacterGeneration::CharacterGeneration(Game *game) :
_game(game) {
_resRef = getResRef("maincg");
_backgroundType = BackgroundType::Menu;
initForGame();
loadBackground(BackgroundType::Menu);
};
void CharacterGeneration::load() {

View file

@ -19,11 +19,11 @@
#include "../../../gui/scenebuilder.h"
#include "../../../render/model/models.h"
#include "../../../resource/gameidutil.h"
#include "../../../resource/strings.h"
#include "../../characterutil.h"
#include "../../game.h"
#include "../../gameidutil.h"
#include "../../object/creature.h"
#include "../../rp/classes.h"
@ -65,7 +65,7 @@ ClassSelection::ClassSelection(Game *game) :
_resRef = getResRef("classsel");
if (_gameId == GameID::KotOR) {
_backgroundType = BackgroundType::Menu;
loadBackground(BackgroundType::Menu);
}
initForGame();

View file

@ -27,7 +27,7 @@ class CharacterGeneration;
class CustomCharacterGeneration : public GameGUI {
public:
CustomCharacterGeneration(CharacterGeneration *charGen, resource::GameID gameId, const render::GraphicsOptions &opts);
CustomCharacterGeneration(CharacterGeneration *charGen, GameID gameId, const render::GraphicsOptions &opts);
void load() override;

View file

@ -27,7 +27,7 @@ class CharacterGeneration;
class CharGenFeats : public GameGUI {
public:
CharGenFeats(CharacterGeneration *charGen, resource::GameID gameId, const render::GraphicsOptions &opts);
CharGenFeats(CharacterGeneration *charGen, GameID gameId, const render::GraphicsOptions &opts);
void load() override;

View file

@ -27,7 +27,7 @@ class CharacterGeneration;
class LevelUpMenu : public GameGUI {
public:
LevelUpMenu(CharacterGeneration *charGen, resource::GameID gameId, const render::GraphicsOptions &options);
LevelUpMenu(CharacterGeneration *charGen, GameID gameId, const render::GraphicsOptions &options);
void load() override;

View file

@ -56,7 +56,7 @@ void NameEntry::load() {
}
void NameEntry::loadLtrFile(const string &resRef, LtrReader &ltr) {
shared_ptr<ByteArray> data(Resources::instance().get(resRef, ResourceType::Ltr));
shared_ptr<ByteArray> data(Resources::instance().getRaw(resRef, ResourceType::Ltr));
ltr.load(wrap(data));
}

View file

@ -30,7 +30,7 @@ class CharacterGeneration;
class NameEntry : public GameGUI {
public:
NameEntry(CharacterGeneration *charGen, resource::GameID gameId, const render::GraphicsOptions &opts);
NameEntry(CharacterGeneration *charGen, GameID gameId, const render::GraphicsOptions &opts);
void load() override;
bool handle(const SDL_Event &event) override;

View file

@ -53,11 +53,11 @@ PortraitSelection::PortraitSelection(Game *game, CharacterGeneration *charGen) :
_resRef = getResRef("portcust");
if (_gameId == GameID::KotOR) {
_backgroundType = BackgroundType::Menu;
}
initForGame();
if (_gameId == GameID::KotOR) {
loadBackground(BackgroundType::Menu);
}
}
void PortraitSelection::load() {

View file

@ -27,7 +27,7 @@ class CharacterGeneration;
class QuickCharacterGeneration : public GameGUI {
public:
QuickCharacterGeneration(CharacterGeneration *charGen, resource::GameID gameId, const render::GraphicsOptions &opts);
QuickCharacterGeneration(CharacterGeneration *charGen, GameID gameId, const render::GraphicsOptions &opts);
void load() override;

View file

@ -27,7 +27,7 @@ class CharacterGeneration;
class QuickOrCustom : public GameGUI {
public:
QuickOrCustom(CharacterGeneration *charGen, resource::GameID gameId, const render::GraphicsOptions &opts);
QuickOrCustom(CharacterGeneration *charGen, GameID gameId, const render::GraphicsOptions &opts);
void load() override;

View file

@ -29,7 +29,7 @@ class CharacterGeneration;
class CharGenSkills : public GameGUI {
public:
CharGenSkills(CharacterGeneration *charGen, resource::GameID gameId, const render::GraphicsOptions &opts);
CharGenSkills(CharacterGeneration *charGen, GameID gameId, const render::GraphicsOptions &opts);
void load() override;

View file

@ -17,9 +17,7 @@
#include "colorutil.h"
#include "../../resource/gameidutil.h"
using namespace reone::resource;
#include "../gameidutil.h"
namespace reone {

View file

@ -19,15 +19,15 @@
#include "glm/vec3.hpp"
#include "../../resource/types.h"
#include "../types.h"
namespace reone {
namespace game {
glm::vec3 getBaseColor(resource::GameID gameId);
glm::vec3 getHilightColor(resource::GameID gameId);
glm::vec3 getDisabledColor(resource::GameID gameId);
glm::vec3 getBaseColor(GameID gameId);
glm::vec3 getHilightColor(GameID gameId);
glm::vec3 getDisabledColor(GameID gameId);
} // namespace game

View file

@ -18,7 +18,8 @@
#include "computer.h"
#include "../../gui/control/listbox.h"
#include "../../resource/gameidutil.h"
#include "../gameidutil.h"
#include "colorutil.h"

View file

@ -20,10 +20,10 @@
#include "../../gui/control/imagebutton.h"
#include "../../gui/control/listbox.h"
#include "../../render/textures.h"
#include "../../resource/gameidutil.h"
#include "../../resource/strings.h"
#include "../game.h"
#include "../gameidutil.h"
#include "../object/item.h"
#include "../objectconverter.h"

View file

@ -17,8 +17,12 @@
#include "gui.h"
#include <boost/format.hpp>
#include "../../audio/player.h"
#include "../../resource/gameidutil.h"
#include "../../render/textures.h"
#include "../gameidutil.h"
#include "colorutil.h"
#include "sounds.h"
@ -34,7 +38,7 @@ namespace reone {
namespace game {
GameGUI::GameGUI(GameID gameId, const GraphicsOptions &options) : GUI(gameId, options) {
GameGUI::GameGUI(GameID gameId, const GraphicsOptions &options) : GUI(options), _gameId(gameId) {
}
void GameGUI::onClick(const string &control) {
@ -57,6 +61,53 @@ void GameGUI::initForGame() {
}
}
string GameGUI::getResRef(const std::string &base) const {
return isTSL(_gameId) ? base + "_p" : base;
}
void GameGUI::loadBackground(BackgroundType type) {
string resRef;
if (isTSL(_gameId)) {
switch (type) {
case BackgroundType::Computer0:
case BackgroundType::Computer1:
resRef = "pnl_computer_pc";
break;
default:
break;
}
} else {
if ((_gfxOpts.width == 1600 && _gfxOpts.height == 1200) ||
(_gfxOpts.width == 1280 && _gfxOpts.height == 960) ||
(_gfxOpts.width == 1024 && _gfxOpts.height == 768) ||
(_gfxOpts.width == 800 && _gfxOpts.height == 600)) {
resRef = str(boost::format("%dx%d") % _gfxOpts.width % _gfxOpts.height);
} else {
resRef = "1600x1200";
}
switch (type) {
case BackgroundType::Menu:
resRef += "back";
break;
case BackgroundType::Load:
resRef += "load";
break;
case BackgroundType::Computer0:
resRef += "comp0";
break;
case BackgroundType::Computer1:
resRef += "comp1";
break;
default:
return;
}
}
_background = Textures::instance().get(resRef, TextureUsage::Diffuse);
}
} // namespace game
} // namespace reone

View file

@ -19,6 +19,8 @@
#include "../../gui/gui.h"
#include "../types.h"
namespace reone {
namespace game {
@ -28,12 +30,19 @@ namespace game {
*/
class GameGUI : public gui::GUI {
protected:
GameGUI(resource::GameID gameId, const render::GraphicsOptions &options);
GameGUI(GameID gameId, const render::GraphicsOptions &options);
void onClick(const std::string &control) override;
void onFocusChanged(const std::string &control, bool focus) override;
void initForGame();
std::string getResRef(const std::string &base) const;
protected:
GameID _gameId;
void loadBackground(BackgroundType type);
};
} // namespace game

View file

@ -21,9 +21,9 @@
#include "../../gui/control/label.h"
#include "../../render/meshes.h"
#include "../../render/window.h"
#include "../../resource/gameidutil.h"
#include "../game.h"
#include "../gameidutil.h"
using namespace std;

View file

@ -19,11 +19,11 @@
#include "../../../gui/control/listbox.h"
#include "../../../render/textures.h"
#include "../../../resource/gameidutil.h"
#include "../../../resource/resources.h"
#include "../../../resource/strings.h"
#include "../../game.h"
#include "../../gameidutil.h"
#include "../colorutil.h"
@ -46,9 +46,9 @@ AbilitiesMenu::AbilitiesMenu(Game *game) :
_game(game) {
_resRef = getResRef("abilities");
_backgroundType = BackgroundType::Menu;
initForGame();
loadBackground(BackgroundType::Menu);
}
void AbilitiesMenu::load() {

View file

@ -39,9 +39,9 @@ CharacterMenu::CharacterMenu(Game *game) :
_game(game) {
_resRef = getResRef("character");
_backgroundType = BackgroundType::Menu;
initForGame();
loadBackground(BackgroundType::Menu);
}
void CharacterMenu::load() {

View file

@ -22,11 +22,11 @@
#include "../../../gui/control/imagebutton.h"
#include "../../../gui/control/listbox.h"
#include "../../../render/textures.h"
#include "../../../resource/gameidutil.h"
#include "../../../resource/strings.h"
#include "../../blueprint/blueprints.h"
#include "../../game.h"
#include "../../gameidutil.h"
#include "../../object/creature.h"
#include "../../object/item.h"
@ -78,9 +78,9 @@ Equipment::Equipment(Game *game) :
_game(game) {
_resRef = getResRef("equip");
_backgroundType = BackgroundType::Menu;
initForGame();
loadBackground(BackgroundType::Menu);
}
void Equipment::load() {

View file

@ -19,9 +19,8 @@
#include <unordered_map>
#include "../../../resource/gameidutil.h"
#include "../../game.h"
#include "../../gameidutil.h"
using namespace std;

View file

@ -36,9 +36,9 @@ InventoryMenu::InventoryMenu(Game *game) :
_game(game) {
_resRef = getResRef("inventory");
_backgroundType = BackgroundType::Menu;
initForGame();
loadBackground(BackgroundType::Menu);
}
void InventoryMenu::load() {

View file

@ -35,9 +35,9 @@ JournalMenu::JournalMenu(Game *game) :
_game(game) {
_resRef = getResRef("journal");
_backgroundType = BackgroundType::Menu;
initForGame();
loadBackground(BackgroundType::Menu);
}
void JournalMenu::load() {

View file

@ -42,9 +42,9 @@ MapMenu::MapMenu(Game *game) :
_game(game) {
_resRef = getResRef("map");
_backgroundType = BackgroundType::Menu;
initForGame();
loadBackground(BackgroundType::Menu);
}
void MapMenu::load() {

View file

@ -35,9 +35,9 @@ MessagesMenu::MessagesMenu(Game *game) :
_game(game) {
_resRef = getResRef("messages");
_backgroundType = BackgroundType::Menu;
initForGame();
loadBackground(BackgroundType::Menu);
}
void MessagesMenu::load() {

View file

@ -35,9 +35,9 @@ OptionsMenu::OptionsMenu(Game *game) :
_game(game) {
_resRef = getResRef("optionsingame");
_backgroundType = BackgroundType::Menu;
initForGame();
loadBackground(BackgroundType::Menu);
}
void OptionsMenu::load() {

View file

@ -18,10 +18,10 @@
#include "loadscreen.h"
#include "../../gui/control/progressbar.h"
#include "../../resource/gameidutil.h"
#include "../../resource/resources.h"
#include "../game.h"
#include "../gameidutil.h"
using namespace std;
@ -33,8 +33,7 @@ namespace reone {
namespace game {
LoadingScreen::LoadingScreen(Game *game) :
GUI(game->gameId(), game->options().graphics),
_game(game) {
GameGUI(game->gameId(), game->options().graphics) {
_resRef = getResRef("loadscreen");
@ -42,7 +41,7 @@ LoadingScreen::LoadingScreen(Game *game) :
_resolutionX = 800;
_resolutionY = 600;
} else {
_backgroundType = BackgroundType::Load;
loadBackground(BackgroundType::Load);
}
}

View file

@ -17,7 +17,7 @@
#pragma once
#include "../../gui/gui.h"
#include "gui.h"
namespace reone {
@ -25,7 +25,7 @@ namespace game {
class Game;
class LoadingScreen : public gui::GUI {
class LoadingScreen : public GameGUI {
public:
LoadingScreen(Game *game);

View file

@ -24,12 +24,12 @@
#include "../../gui/control/listbox.h"
#include "../../gui/scenebuilder.h"
#include "../../render/model/models.h"
#include "../../resource/gameidutil.h"
#include "../../resource/resources.h"
#include "../../scene/types.h"
#include "../blueprint/blueprints.h"
#include "../game.h"
#include "../gameidutil.h"
#include "colorutil.h"
@ -61,7 +61,7 @@ MainMenu::MainMenu(Game *game) :
_resRef = "mainmenu8x6_p";
} else {
_resRef = "mainmenu16x12";
_backgroundType = BackgroundType::Menu;
loadBackground(BackgroundType::Menu);
}
_resolutionX = 800;
@ -166,7 +166,7 @@ void MainMenu::startModuleSelection() {
void MainMenu::loadModuleNames() {
auto &modules = getControl<ListBox>("LB_MODULES");
for (auto &module : Resources::instance().moduleNames()) {
for (auto &module : _game->moduleNames()) {
ListBox::Item item;
item.tag = module;
item.text = module;

View file

@ -22,12 +22,12 @@
#include "../../gui/control/label.h"
#include "../../gui/control/togglebutton.h"
#include "../../render/textures.h"
#include "../../resource/gameidutil.h"
#include "../../resource/strings.h"
#include "../../script/types.h"
#include "../blueprint/blueprints.h"
#include "../game.h"
#include "../gameidutil.h"
#include "../portraitutil.h"
#include "colorutil.h"
@ -63,7 +63,7 @@ PartySelection::PartySelection(Game *game) :
_resRef = "partyselect_p";
} else {
_resRef = "partyselection";
_backgroundType = BackgroundType::Menu;
loadBackground(BackgroundType::Menu);
}
initForGame();

View file

@ -53,11 +53,11 @@ SaveLoad::SaveLoad(Game *game) :
_resRef = getResRef("saveload");
if (_gameId == GameID::KotOR) {
_backgroundType = BackgroundType::Menu;
}
initForGame();
if (_gameId == GameID::KotOR) {
loadBackground(BackgroundType::Menu);
}
}
void SaveLoad::load() {

View file

@ -30,10 +30,10 @@
#include "../render/stateutil.h"
#include "../render/textures.h"
#include "../render/window.h"
#include "../resource/gameidutil.h"
#include "../resource/types.h"
#include "game.h"
#include "gameidutil.h"
#include "gui/colorutil.h"
using namespace std;

View file

@ -115,7 +115,7 @@ void Area::load(const string &name, const GffStruct &are, const GffStruct &git)
void Area::loadLYT() {
LytReader lyt;
lyt.load(wrap(Resources::instance().get(_name, ResourceType::Lyt)));
lyt.load(wrap(Resources::instance().getRaw(_name, ResourceType::Lyt)));
for (auto &lytRoom : lyt.rooms()) {
shared_ptr<Model> model(Models::instance().get(lytRoom.name));
@ -140,7 +140,7 @@ void Area::loadLYT() {
void Area::loadVIS() {
VisReader vis;
vis.load(wrap(Resources::instance().get(_name, ResourceType::Vis)));
vis.load(wrap(Resources::instance().getRaw(_name, ResourceType::Vis)));
_visibility = fixVisibility(vis.visibility());
}

View file

@ -53,7 +53,7 @@ shared_ptr<ModelSceneNode> CreatureModelBuilder::build() {
string modelName(getBodyModelName());
if (modelName.empty()) return nullptr;
shared_ptr<Model> model(Models::instance().get(modelName, ResourceType::Mdl));
shared_ptr<Model> model(Models::instance().get(modelName));
if (!model) return nullptr;
auto modelSceneNode = make_unique<ModelSceneNode>(ModelSceneNode::Classification::Creature, model, &_creature->sceneGraph());

View file

@ -18,12 +18,12 @@
#include "routines.h"
#include "../../common/log.h"
#include "../../resource/gameidutil.h"
#include "../enginetype/event.h"
#include "../enginetype/location.h"
#include "../game.h"
#include "../gameidutil.h"
using namespace std;
@ -39,10 +39,10 @@ Routines &Routines::instance() {
return instance;
}
void Routines::init(GameID gameId, Game *game) {
void Routines::init(Game *game) {
_game = game;
if (isTSL(gameId)) {
if (isTSL(game->gameId())) {
addTslRoutines();
} else {
addKotorRoutines();

View file

@ -47,7 +47,7 @@ class Routines : public script::IRoutineProvider, boost::noncopyable {
public:
static Routines &instance();
void init(resource::GameID gameId, Game *game);
void init(Game *game);
void deinit();
const script::Routine &get(int index) override;

View file

@ -44,7 +44,7 @@ SoundSets::SoundSets() : MemoryCache(bind(&SoundSets::doGet, this, _1)) {
}
shared_ptr<SoundSet> SoundSets::doGet(string resRef) {
auto data = Resources::instance().get(resRef, ResourceType::Ssf);
auto data = Resources::instance().getRaw(resRef, ResourceType::Ssf);
if (!data) return nullptr;
auto result = make_shared<SoundSet>();

View file

@ -32,6 +32,20 @@ constexpr float kDefaultFollowDistance = 5.0f;
constexpr char kObjectTagPlayer[] = "player";
constexpr float kDefaultRaycastDistance = 8.0f;
enum class GameID {
KotOR,
TSL,
TSL_Steam
};
enum class BackgroundType {
None,
Menu,
Load,
Computer0,
Computer1
};
enum class CursorType {
None,
Default,

View file

@ -25,7 +25,6 @@
#include "../render/stateutil.h"
#include "../render/textures.h"
#include "../render/window.h"
#include "../resource/gameidutil.h"
#include "../resource/resources.h"
using namespace std;
@ -38,16 +37,12 @@ namespace reone {
namespace gui {
GUI::GUI(GameID gameId, const GraphicsOptions &opts) : _gameId(gameId), _gfxOpts(opts) {
GUI::GUI(const GraphicsOptions &opts) : _gfxOpts(opts) {
_aspect = _gfxOpts.width / static_cast<float>(_gfxOpts.height);
_screenCenter.x = _gfxOpts.width / 2;
_screenCenter.y = _gfxOpts.height / 2;
}
string GUI::getResRef(const string &base) const {
return isTSL(_gameId) ? base + "_p" : base;
}
void GUI::load() {
if (_resRef.empty()) {
throw logic_error("resRef must not be empty");
@ -55,8 +50,6 @@ void GUI::load() {
debug("GUI: load " + _resRef, 1, DebugChannels::gui);
shared_ptr<GffStruct> gui(Resources::instance().getGFF(_resRef, ResourceType::Gui));
loadBackground(_backgroundType);
ControlType type = Control::getType(*gui);
string tag(Control::getTag(*gui));
@ -84,49 +77,6 @@ void GUI::load() {
}
}
void GUI::loadBackground(BackgroundType type) {
string resRef;
if (isTSL(_gameId)) {
switch (type) {
case BackgroundType::Computer0:
case BackgroundType::Computer1:
resRef = "pnl_computer_pc";
break;
default:
break;
}
} else {
if ((_gfxOpts.width == 1600 && _gfxOpts.height == 1200) ||
(_gfxOpts.width == 1280 && _gfxOpts.height == 960) ||
(_gfxOpts.width == 1024 && _gfxOpts.height == 768) ||
(_gfxOpts.width == 800 && _gfxOpts.height == 600)) {
resRef = str(boost::format("%dx%d") % _gfxOpts.width % _gfxOpts.height);
} else {
resRef = "1600x1200";
}
switch (type) {
case BackgroundType::Menu:
resRef += "back";
break;
case BackgroundType::Load:
resRef += "load";
break;
case BackgroundType::Computer0:
resRef += "comp0";
break;
case BackgroundType::Computer1:
resRef += "comp1";
break;
default:
return;
}
}
_background = Textures::instance().get(resRef, TextureUsage::Diffuse);
}
void GUI::stretchControl(Control &control) {
float aspectX = _gfxOpts.width / static_cast<float>(_resolutionX);
float aspectY = _gfxOpts.height / static_cast<float>(_resolutionY);

View file

@ -72,10 +72,8 @@ protected:
Stretch
};
resource::GameID _gameId { resource::GameID::KotOR };
render::GraphicsOptions _gfxOpts;
std::string _resRef;
BackgroundType _backgroundType { BackgroundType::None };
int _resolutionX { kDefaultResolutionX };
int _resolutionY { kDefaultResolutionY };
ScalingMode _scaling { ScalingMode::Center };
@ -92,15 +90,13 @@ protected:
glm::vec3 _defaultHilightColor { 0.0f };
std::unordered_map<std::string, ScalingMode> _scalingByControlTag;
GUI(resource::GameID gameId, const render::GraphicsOptions &opts);
GUI(const render::GraphicsOptions &opts);
void loadBackground(BackgroundType type);
void loadControl(const resource::GffStruct &gffs);
virtual void onFocusChanged(const std::string &control, bool focus);
virtual void preloadControl(Control &control);
Control &getControl(const std::string &tag) const;
std::string getResRef(const std::string &base) const;
template <class T>
T &getControl(const std::string &tag) const {

View file

@ -33,14 +33,6 @@ enum class ControlType {
ListBox = 11
};
enum class BackgroundType {
None,
Menu,
Load,
Computer0,
Computer1
};
} // namespace gui
} // namespace reone

View file

@ -41,7 +41,7 @@ Lips::Lips() : MemoryCache(bind(&Lips::doGet, this, _1)) {
}
shared_ptr<LipAnimation> Lips::doGet(string resRef) {
shared_ptr<ByteArray> lipData(Resources::instance().get(resRef, ResourceType::Lip));
shared_ptr<ByteArray> lipData(Resources::instance().getRaw(resRef, ResourceType::Lip));
if (!lipData) return nullptr;
LipReader lip;

View file

@ -25,9 +25,7 @@
#include "../../common/collectionutil.h"
#include "../../common/log.h"
#include "../../common/streamutil.h"
#include "../../common/stringutil.h"
#include "../../resource/resources.h"
#include "../textures.h"
@ -573,7 +571,6 @@ unique_ptr<ModelNode> MdlReader::readNode(uint32_t offset, ModelNode *parent) {
transform = glm::translate(transform, position);
transform *= glm::mat4_cast(orientation);
auto node = make_unique<ModelNode>(_nodeIndex++, parent);
node->_flags = header.flags;
node->_nodeNumber = header.nodeNumber;
@ -996,20 +993,6 @@ unique_ptr<Animation> MdlReader::readAnimation(uint32_t offset) {
return make_unique<Animation>(name, header.length, header.transitionTime, move(events), move(rootNode));
}
shared_ptr<Model> MdlModelLoader::loadModel(GameID gameId, const string &resRef) {
shared_ptr<ByteArray> mdlData(Resources::instance().get(resRef, ResourceType::Mdl));
shared_ptr<ByteArray> mdxData(Resources::instance().get(resRef, ResourceType::Mdx));
shared_ptr<Model> model;
if (mdlData && mdxData) {
MdlReader mdl;
mdl.load(wrap(mdlData), wrap(mdxData));
model = mdl.model();
}
return move(model);
}
} // namespace render
} // namespace reone

View file

@ -18,11 +18,9 @@
#pragma once
#include "../../resource/format/binreader.h"
#include "../../resource/types.h"
#include "aabbnode.h"
#include "model.h"
#include "modelloader.h"
namespace reone {
@ -272,11 +270,6 @@ private:
std::shared_ptr<AABBNode> readAABBNode(uint32_t offset);
};
class MdlModelLoader : public IModelLoader {
public:
std::shared_ptr<Model> loadModel(resource::GameID gameId, const std::string &resRef) override;
};
} // namespace render
} // namespace reone

View file

@ -1,41 +0,0 @@
/*
* Copyright (c) 2020-2021 The reone project contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <memory>
#include "../../resource/types.h"
#include "model.h"
namespace reone {
namespace render {
/**
* An interface to abstract loading models from different formats.
*/
class IModelLoader {
public:
virtual std::shared_ptr<Model> loadModel(resource::GameID gameId, const std::string &resRef) = 0;
};
} // namespace render
} // namespace reone

View file

@ -36,39 +36,34 @@ Models &Models::instance() {
return instance;
}
void Models::init(GameID gameId) {
_gameId = gameId;
}
void Models::invalidateCache() {
_cache.clear();
}
void Models::registerLoader(ResourceType type, shared_ptr<IModelLoader> loader) {
_loaders.insert(make_pair(type, move(loader)));
}
shared_ptr<Model> Models::get(const string &resRef, ResourceType type) {
shared_ptr<Model> Models::get(const string &resRef) {
if (resRef.empty()) return nullptr;
auto maybeModel = _cache.find(resRef);
if (maybeModel != _cache.end()) return maybeModel->second;
auto inserted = _cache.insert(make_pair(resRef, doGet(resRef, type)));
auto inserted = _cache.insert(make_pair(resRef, doGet(resRef)));
return inserted.first->second;
}
shared_ptr<Model> Models::doGet(const string &resRef, ResourceType type) {
auto maybeLoader = _loaders.find(type);
if (maybeLoader == _loaders.end()) {
warn("Model loader not found by ResType: " + to_string(static_cast<int>(type)));
return nullptr;
}
shared_ptr<Model> Models::doGet(const string &resRef) {
debug("Load model " + resRef);
shared_ptr<Model> model(maybeLoader->second->loadModel(_gameId, resRef));
if (model) {
model->init();
shared_ptr<ByteArray> mdlData(Resources::instance().getRaw(resRef, ResourceType::Mdl));
shared_ptr<ByteArray> mdxData(Resources::instance().getRaw(resRef, ResourceType::Mdx));
shared_ptr<Model> model;
if (mdlData && mdxData) {
MdlReader mdl;
mdl.load(wrap(mdlData), wrap(mdxData));
model = mdl.model();
if (model) {
model->init();
}
}
return move(model);

View file

@ -23,12 +23,8 @@
#include <boost/noncopyable.hpp>
#include "../../resource/types.h"
#include "../types.h"
#include "modelloader.h"
namespace reone {
namespace render {
@ -39,22 +35,14 @@ class Models : boost::noncopyable {
public:
static Models &instance();
void init(resource::GameID gameId);
void invalidateCache();
/**
* Associates the specified model loader with the specified ResType.
*/
void registerLoader(resource::ResourceType type, std::shared_ptr<IModelLoader> loader);
std::shared_ptr<Model> get(const std::string &resRef, resource::ResourceType type = resource::ResourceType::Mdl);
std::shared_ptr<Model> get(const std::string &resRef);
private:
resource::GameID _gameId { resource::GameID::KotOR };
std::unordered_map<resource::ResourceType, std::shared_ptr<IModelLoader>> _loaders;
std::unordered_map<std::string, std::shared_ptr<Model>> _cache;
std::shared_ptr<Model> doGet(const std::string &resRef, resource::ResourceType type);
std::shared_ptr<Model> doGet(const std::string &resRef);
};
} // namespace render

View file

@ -44,9 +44,7 @@ Textures &Textures::instance() {
return instance;
}
void Textures::init(GameID gameId) {
_gameId = gameId;
void Textures::init() {
// Initialize default texture
_default = make_shared<Texture>("default", getTextureProperties(TextureUsage::Default));
_default->init();
@ -109,14 +107,14 @@ shared_ptr<Texture> Textures::get(const string &resRef, TextureUsage usage) {
shared_ptr<Texture> Textures::doGet(const string &resRef, TextureUsage usage) {
shared_ptr<Texture> texture;
shared_ptr<ByteArray> tgaData(Resources::instance().get(resRef, ResourceType::Tga, false));
shared_ptr<ByteArray> tgaData(Resources::instance().getRaw(resRef, ResourceType::Tga, false));
if (tgaData) {
TgaReader tga(resRef, usage);
tga.load(wrap(tgaData));
texture = tga.texture();
if (texture) {
shared_ptr<ByteArray> txiData(Resources::instance().get(resRef, ResourceType::Txi, false));
shared_ptr<ByteArray> txiData(Resources::instance().getRaw(resRef, ResourceType::Txi, false));
if (txiData) {
TxiReader txi;
txi.load(wrap(txiData));
@ -126,7 +124,7 @@ shared_ptr<Texture> Textures::doGet(const string &resRef, TextureUsage usage) {
}
if (!texture) {
shared_ptr<ByteArray> tpcData(Resources::instance().get(resRef, ResourceType::Tpc, false));
shared_ptr<ByteArray> tpcData(Resources::instance().getRaw(resRef, ResourceType::Tpc, false));
if (tpcData) {
TpcReader tpc(resRef, usage);
tpc.load(wrap(tpcData));

View file

@ -37,7 +37,7 @@ class Textures : boost::noncopyable {
public:
static Textures &instance();
void init(resource::GameID gameId);
void init();
void invalidateCache();
/**
@ -48,7 +48,6 @@ public:
std::shared_ptr<Texture> get(const std::string &resRef, TextureUsage usage = TextureUsage::Default);
private:
resource::GameID _gameId { resource::GameID::KotOR };
std::shared_ptr<render::Texture> _default;
std::shared_ptr<render::Texture> _defaultCubemap;
std::unordered_map<std::string, std::shared_ptr<Texture>> _cache;

View file

@ -55,7 +55,7 @@ shared_ptr<Walkmesh> Walkmeshes::get(const string &resRef, ResourceType type) {
}
shared_ptr<Walkmesh> Walkmeshes::doGet(const string &resRef, ResourceType type) {
shared_ptr<ByteArray> data(Resources::instance().get(resRef, type));
shared_ptr<ByteArray> data(Resources::instance().getRaw(resRef, type));
shared_ptr<Walkmesh> walkmesh;
if (data) {

View file

@ -29,7 +29,7 @@
#include "format/gffreader.h"
#include "format/rimreader.h"
#include "folder.h"
#include "gameidutil.h"
#include "keybifprovider.h"
#include "typeutil.h"
using namespace std;
@ -40,114 +40,55 @@ namespace reone {
namespace resource {
static const char kPatchFileName[] = "patch.erf";
static const char kExeFileNameKotor[] = "swkotor.exe";
static const char kExeFileNameTsl[] = "swkotor2.exe";
static const char kModulesDirectoryName[] = "modules";
static const char kOverrideDirectoryName[] = "override";
static const char kMusicDirectoryName[] = "streammusic";
static const char kSoundsDirectoryName[] = "streamsounds";
static const char kVoiceDirectoryName[] = "streamvoice";
static const char kWavesDirectoryName[] = "streamwaves";
static const char kTexturePackDirectoryName[] = "texturepacks";
static const char kLipsDirectoryName[] = "lips";
static const char kGUITexturePackFilename[] = "swpc_tex_gui.erf";
static const char kTexturePackFilename[] = "swpc_tex_tpa.erf";
Resources &Resources::instance() {
static Resources instance;
return instance;
}
void Resources::init(GameID gameId, const fs::path &gamePath) {
_gameId = gameId;
_gamePath = gamePath;
indexKeyBifFiles();
indexTexturePacks();
indexAudioFiles();
indexLipModFiles();
indexExeReader();
indexOverrideDirectory();
indexDataDirectory();
loadModuleNames();
}
void Resources::indexKeyBifFiles() {
fs::path keyPath(getPathIgnoreCase(_gamePath, "chitin.key"));
void Resources::indexKeyFile(const fs::path &path) {
if (!fs::exists(path)) return;
auto keyBif = make_unique<KeyBifResourceProvider>();
keyBif->init(keyPath);
keyBif->init(path);
_providers.push_back(move(keyBif));
debug("Indexed " + keyPath.string());
}
void Resources::indexTexturePacks() {
if (_gameId == GameID::KotOR) {
fs::path patchPath(getPathIgnoreCase(_gamePath, kPatchFileName));
indexErfFile(patchPath);
}
fs::path texPacksPath(getPathIgnoreCase(_gamePath, kTexturePackDirectoryName));
fs::path guiTexPackPath(getPathIgnoreCase(texPacksPath, kGUITexturePackFilename));
fs::path texPackPath(getPathIgnoreCase(texPacksPath, kTexturePackFilename));
indexErfFile(guiTexPackPath);
indexErfFile(texPackPath);
}
void Resources::indexErfFile(const fs::path &path) {
auto erf = make_unique<ErfReader>();
erf->load(path);
_providers.push_back(move(erf));
debug("Indexed " + path.string());
}
void Resources::indexAudioFiles() {
fs::path musicPath(getPathIgnoreCase(_gamePath, kMusicDirectoryName));
fs::path soundsPath(getPathIgnoreCase(_gamePath, kSoundsDirectoryName));
void Resources::indexErfFile(const fs::path &path, bool transient) {
if (!fs::exists(path)) return;
indexDirectory(musicPath);
indexDirectory(soundsPath);
auto erf = make_unique<ErfReader>();
erf->load(path);
if (isTSL(_gameId)) {
fs::path voicePath(getPathIgnoreCase(_gamePath, kVoiceDirectoryName));
indexDirectory(voicePath);
if (transient) {
_transientProviders.push_back(move(erf));
} else {
fs::path wavesPath(getPathIgnoreCase(_gamePath, kWavesDirectoryName));
indexDirectory(wavesPath);
_providers.push_back(move(erf));
}
debug("Indexed " + path.string());
}
void Resources::indexLipModFiles() {
static vector<string> kotorMods { "global", "localization" };
static vector<string> tslMods { "localization" };
void Resources::indexRimFile(const fs::path &path, bool transient) {
if (!fs::exists(path)) return;
const vector<string> &mods = _gameId == GameID::KotOR ? kotorMods : tslMods;
fs::path lipsPath(getPathIgnoreCase(_gamePath, "lips"));
for (auto &mod : mods) {
fs::path modPath(getPathIgnoreCase(lipsPath, mod + ".mod"));
indexErfFile(modPath);
}
}
void Resources::indexRimFile(const fs::path &path) {
auto rim = make_unique<RimReader>();
rim->load(path);
_providers.push_back(move(rim));
if (transient) {
_transientProviders.push_back(move(rim));
} else {
_providers.push_back(move(rim));
}
debug("Indexed " + path.string());
}
void Resources::indexDirectory(const fs::path &path) {
if (!fs::exists(path)) return;
auto folder = make_unique<Folder>();
folder->load(path);
@ -156,117 +97,59 @@ void Resources::indexDirectory(const fs::path &path) {
debug("Indexed " + path.string());
}
void Resources::indexOverrideDirectory() {
fs::path path(getPathIgnoreCase(_gamePath, kOverrideDirectoryName));
indexDirectory(path);
}
void Resources::indexDataDirectory() {
fs::path path(getPathIgnoreCase(fs::current_path(), "data", false));
if (!path.empty()) {
indexDirectory(path);
}
}
void Resources::indexExeReader() {
string filename(isTSL(_gameId) ? kExeFileNameTsl : kExeFileNameKotor);
fs::path path(getPathIgnoreCase(_gamePath, filename));
void Resources::indexExeFile(const fs::path &path) {
if (!fs::exists(path)) return;
_exeFile.load(path);
debug("Indexed " + path.string());
}
void Resources::loadModuleNames() {
fs::path modules(getPathIgnoreCase(_gamePath, kModulesDirectoryName));
for (auto &entry : fs::directory_iterator(modules)) {
string filename(entry.path().filename().string());
boost::to_lower(filename);
if (!boost::ends_with(filename, ".rim") || boost::ends_with(filename, "_s.rim")) continue;
string moduleName(filename.substr(0, filename.size() - 4));
boost::to_lower(moduleName);
_moduleNames.push_back(moduleName);
}
sort(_moduleNames.begin(), _moduleNames.end());
}
Resources::~Resources() {
deinit();
}
void Resources::deinit() {
invalidateCache();
_transientProviders.clear();
_providers.clear();
}
void Resources::invalidateCache() {
_rawCache.clear();
_2daCache.clear();
_gffCache.clear();
_resCache.clear();
}
void Resources::loadModule(const string &name) {
invalidateCache();
void Resources::clearTransientProviders() {
_transientProviders.clear();
fs::path modulesPath(getPathIgnoreCase(_gamePath, kModulesDirectoryName));
fs::path moduleRimPath(getPathIgnoreCase(modulesPath, name + ".rim"));
fs::path moduleRimSPath(getPathIgnoreCase(modulesPath, name + "_s.rim"));
fs::path lipsPath(getPathIgnoreCase(_gamePath, kLipsDirectoryName));
fs::path lipModPath(getPathIgnoreCase(lipsPath, name + "_loc.mod"));
indexTransientRimFile(moduleRimPath);
indexTransientRimFile(moduleRimSPath);
if (fs::exists(lipModPath)) {
indexTransientErfFile(lipModPath);
}
if (isTSL(_gameId)) {
fs::path dlgPath(getPathIgnoreCase(modulesPath, name + "_dlg.erf"));
indexTransientErfFile(dlgPath);
}
}
void Resources::indexTransientRimFile(const fs::path &path) {
auto rim = make_unique<RimReader>();
rim->load(path);
_transientProviders.push_back(move(rim));
debug("Indexed " + path.string());
}
void Resources::indexTransientErfFile(const fs::path &path) {
auto erf = make_unique<ErfReader>();
erf->load(path);
_transientProviders.push_back(move(erf));
debug("Indexed " + path.string());
}
template <class T>
static shared_ptr<T> findResource(const string &key, unordered_map<string, shared_ptr<T>> &cache, const function<shared_ptr<T>()> &getter) {
static shared_ptr<T> getResource(const string &key, unordered_map<string, shared_ptr<T>> &cache, const function<shared_ptr<T>()> &getter) {
auto maybeResource = cache.find(key);
if (maybeResource != cache.end()) {
return maybeResource->second;
};
auto inserted = cache.insert(make_pair(key, getter()));
if (maybeResource != cache.end()) return maybeResource->second;
auto inserted = cache.insert(make_pair(key, getter()));
return inserted.first->second;
}
shared_ptr<ByteArray> Resources::getRaw(const string &resRef, ResourceType type, bool logNotFound) {
if (resRef.empty()) return nullptr;
string cacheKey(getCacheKey(resRef, type));
auto res = _rawCache.find(cacheKey);
if (res != _rawCache.end()) return res->second;
shared_ptr<ByteArray> data = doGetRaw(_transientProviders, resRef, type);
if (!data) {
data = doGetRaw(_providers, resRef, type);
}
if (!data && logNotFound) {
warn("Resource not found: " + cacheKey);
}
auto pair = _rawCache.insert(make_pair(cacheKey, move(data)));
return pair.first->second;
}
string Resources::getCacheKey(const string &resRef, ResourceType type) const {
return str(boost::format("%s.%s") % resRef % getExtByResType(type));
}
shared_ptr<TwoDA> Resources::get2DA(const string &resRef, bool logNotFound) {
return findResource<TwoDA>(resRef, _2daCache, [&]() {
shared_ptr<ByteArray> data(get(resRef, ResourceType::TwoDa, logNotFound));
return getResource<TwoDA>(resRef, _2daCache, [&]() {
shared_ptr<ByteArray> data(getRaw(resRef, ResourceType::TwoDa, logNotFound));
shared_ptr<TwoDA> twoDa;
if (data) {
@ -279,37 +162,12 @@ shared_ptr<TwoDA> Resources::get2DA(const string &resRef, bool logNotFound) {
});
}
shared_ptr<ByteArray> Resources::get(const string &resRef, ResourceType type, bool logNotFound) {
if (resRef.empty()) return nullptr;
string cacheKey(getCacheKey(resRef, type));
auto res = _resCache.find(cacheKey);
if (res != _resCache.end()) return res->second;
shared_ptr<ByteArray> data = get(_transientProviders, resRef, type);
if (!data) {
data = get(_providers, resRef, type);
}
if (!data && logNotFound) {
warn("Resource not found: " + cacheKey);
}
auto pair = _resCache.insert(make_pair(cacheKey, move(data)));
return pair.first->second;
}
string Resources::getCacheKey(const string &resRef, resource::ResourceType type) const {
return str(boost::format("%s.%s") % resRef % getExtByResType(type));
}
shared_ptr<ByteArray> Resources::get(const vector<unique_ptr<IResourceProvider>> &providers, const string &resRef, ResourceType type) {
shared_ptr<ByteArray> Resources::doGetRaw(const vector<unique_ptr<IResourceProvider>> &providers, const string &resRef, ResourceType type) {
for (auto provider = providers.rbegin(); provider != providers.rend(); ++provider) {
if (!(*provider)->supports(type)) continue;
shared_ptr<ByteArray> data((*provider)->find(resRef, type));
if (data) {
return data;
}
if (data) return data;
}
return nullptr;
@ -318,8 +176,8 @@ shared_ptr<ByteArray> Resources::get(const vector<unique_ptr<IResourceProvider>>
shared_ptr<GffStruct> Resources::getGFF(const string &resRef, ResourceType type) {
string cacheKey(getCacheKey(resRef, type));
return findResource<GffStruct>(cacheKey, _gffCache, [this, &resRef, &type]() {
shared_ptr<ByteArray> data(get(resRef, type));
return getResource<GffStruct>(cacheKey, _gffCache, [this, &resRef, &type]() {
shared_ptr<ByteArray> data(getRaw(resRef, type));
shared_ptr<GffStruct> gffs;
if (data) {

View file

@ -27,10 +27,8 @@
#include <boost/noncopyable.hpp>
#include "2da.h"
#include "format/keyreader.h"
#include "format/pereader.h"
#include "gffstruct.h"
#include "keybifprovider.h"
#include "resourceprovider.h"
#include "types.h"
@ -47,67 +45,40 @@ class Resources : boost::noncopyable {
public:
static Resources &instance();
void init(GameID gameId, const boost::filesystem::path &gamePath);
void deinit();
void indexKeyFile(const boost::filesystem::path &path);
void indexErfFile(const boost::filesystem::path &path, bool transient = false);
void indexRimFile(const boost::filesystem::path &path, bool transient = false);
void indexDirectory(const boost::filesystem::path &path);
void indexExeFile(const boost::filesystem::path &path);
void loadModule(const std::string &name);
void invalidateCache();
void clearTransientProviders();
std::shared_ptr<ByteArray> getRaw(const std::string &resRef, ResourceType type, bool logNotFound = true);
std::shared_ptr<TwoDA> get2DA(const std::string &resRef, bool logNotFound = true);
std::shared_ptr<GffStruct> getGFF(const std::string &resRef, ResourceType type);
std::shared_ptr<ByteArray> getFromExe(uint32_t name, PEResourceType type);
/**
* Searches for the raw resource data by ResRef and ResType.
*/
std::shared_ptr<ByteArray> get(const std::string &resRef, ResourceType type, bool logNotFound = true);
/**
* @return list of available module names
*/
const std::vector<std::string> &moduleNames() const { return _moduleNames; }
private:
GameID _gameId { GameID::KotOR };
boost::filesystem::path _gamePath;
std::vector<std::string> _moduleNames;
// Resource providers
// Providers
PEReader _exeFile;
std::vector<std::unique_ptr<IResourceProvider>> _providers;
std::vector<std::unique_ptr<IResourceProvider>> _transientProviders; /**< transient providers are replaced when switching between modules */
// END Resource providers
// END Providers
// Resource caches
// Caches
std::unordered_map<std::string, std::shared_ptr<ByteArray>> _rawCache;
std::unordered_map<std::string, std::shared_ptr<TwoDA>> _2daCache;
std::unordered_map<std::string, std::shared_ptr<GffStruct>> _gffCache;
std::unordered_map<std::string, std::shared_ptr<ByteArray>> _resCache;
// END Resource caches
// END Caches
~Resources();
void indexKeyBifFiles();
void indexTexturePacks();
void indexAudioFiles();
void indexLipModFiles();
void indexExeReader();
void indexOverrideDirectory();
void indexDataDirectory();
void indexErfFile(const boost::filesystem::path &path);
void indexTransientErfFile(const boost::filesystem::path &path);
void indexRimFile(const boost::filesystem::path &path);
void indexTransientRimFile(const boost::filesystem::path &path);
void indexDirectory(const boost::filesystem::path &path);
void invalidateCache();
void loadModuleNames();
std::shared_ptr<ByteArray> get(const std::vector<std::unique_ptr<IResourceProvider>> &providers, const std::string &resRef, ResourceType type);
std::string getCacheKey(const std::string &resRef, ResourceType type) const;
std::shared_ptr<ByteArray> doGetRaw(const std::vector<std::unique_ptr<IResourceProvider>> &providers, const std::string &resRef, ResourceType type);
};
} // namespace resource

View file

@ -19,8 +19,6 @@
#include "../common/pathutil.h"
#include "gameidutil.h"
using namespace std;
namespace fs = boost::filesystem;
@ -34,9 +32,7 @@ Strings &Strings::instance() {
return instance;
}
void Strings::init(GameID gameId, const fs::path &gameDir) {
_gameId = gameId;
void Strings::init(const fs::path &gameDir) {
fs::path tlkPath(getPathIgnoreCase(gameDir, "dialog.tlk"));
_tlk.load(tlkPath);
}
@ -59,9 +55,7 @@ string Strings::getSound(int strRef) {
}
void Strings::process(string &str) {
if (isTSL(_gameId)) {
stripDeveloperNotes(str);
}
stripDeveloperNotes(str);
}
void Strings::stripDeveloperNotes(string &str) {

View file

@ -33,7 +33,7 @@ class Strings {
public:
static Strings &instance();
void init(GameID gameId, const boost::filesystem::path &gameDir);
void init(const boost::filesystem::path &gameDir);
/**
* Searches for a string in the global talktable by StrRef.
@ -50,7 +50,6 @@ public:
std::string getSound(int strRef);
private:
GameID _gameId { GameID::KotOR };
TlkReader _tlk;
void process(std::string &str);

View file

@ -26,12 +26,6 @@ namespace reone {
namespace resource {
enum class GameID {
KotOR,
TSL,
TSL_Steam
};
/**
* Used together with a ResRef to locate game resources.
*/

View file

@ -40,7 +40,7 @@ Scripts::Scripts() : MemoryCache(bind(&Scripts::doGet, this, _1)) {
}
shared_ptr<ScriptProgram> Scripts::doGet(string resRef) {
shared_ptr<ByteArray> data(Resources::instance().get(resRef, ResourceType::Ncs));
shared_ptr<ByteArray> data(Resources::instance().getRaw(resRef, ResourceType::Ncs));
if (!data) return nullptr;
NcsReader ncs(resRef);

View file

@ -25,7 +25,6 @@
#include <boost/program_options.hpp>
#include "../src/common/pathutil.h"
#include "../src/resource/gameidutil.h"
#include "tools.h"
#include "types.h"
@ -61,9 +60,6 @@ Program::Program(int argc, char **argv) : _argc(argc), _argv(argv) {
int Program::run() {
initOptions();
loadOptions();
_gameId = determineGameID(_gamePath);
loadTools();
switch (_operation) {

View file

@ -50,7 +50,6 @@ private:
boost::filesystem::path _destPath;
std::string _target;
Operation _operation { Operation::None };
resource::GameID _gameId { resource::GameID::KotOR };
std::vector<std::shared_ptr<ITool>> _tools;
void initOptions();