forked from teamnwah/openmw-tes3coop
Merge remote-tracking branch 'mark76/multiple_esm_esp' into next
This commit is contained in:
commit
f19fbaa293
25 changed files with 782 additions and 238 deletions
|
@ -209,7 +209,10 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
|
||||||
bool save = (info.mode == "clone");
|
bool save = (info.mode == "clone");
|
||||||
|
|
||||||
// Skip back to the beginning of the reference list
|
// Skip back to the beginning of the reference list
|
||||||
cell.restore(esm);
|
// FIXME: Changes to the references backend required to support multiple plugins have
|
||||||
|
// almost certainly broken this following line. I'll leave it as is for now, so that
|
||||||
|
// the compiler does not complain.
|
||||||
|
cell.restore(esm, 0);
|
||||||
|
|
||||||
// Loop through all the references
|
// Loop through all the references
|
||||||
ESM::CellRef ref;
|
ESM::CellRef ref;
|
||||||
|
|
|
@ -210,18 +210,31 @@ void OMW::Engine::setCell (const std::string& cellName)
|
||||||
|
|
||||||
// Set master file (esm)
|
// Set master file (esm)
|
||||||
// - If the given name does not have an extension, ".esm" is added automatically
|
// - If the given name does not have an extension, ".esm" is added automatically
|
||||||
// - Currently OpenMW only supports one master at the same time.
|
|
||||||
|
|
||||||
void OMW::Engine::addMaster (const std::string& master)
|
void OMW::Engine::addMaster (const std::string& master)
|
||||||
{
|
{
|
||||||
assert (mMaster.empty());
|
mMaster.push_back(master);
|
||||||
mMaster = master;
|
std::string &str = mMaster.back();
|
||||||
|
|
||||||
// Append .esm if not already there
|
// Append .esm if not already there
|
||||||
std::string::size_type sep = mMaster.find_last_of (".");
|
std::string::size_type sep = str.find_last_of (".");
|
||||||
if (sep == std::string::npos)
|
if (sep == std::string::npos)
|
||||||
{
|
{
|
||||||
mMaster += ".esm";
|
str += ".esm";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add plugin file (esp)
|
||||||
|
void OMW::Engine::addPlugin (const std::string& plugin)
|
||||||
|
{
|
||||||
|
mPlugins.push_back(plugin);
|
||||||
|
std::string &str = mPlugins.back();
|
||||||
|
|
||||||
|
// Append .esp if not already there
|
||||||
|
std::string::size_type sep = str.find_last_of (".");
|
||||||
|
if (sep == std::string::npos)
|
||||||
|
{
|
||||||
|
str += ".esp";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,13 +339,13 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
MWGui::CursorReplace replacer;
|
MWGui::CursorReplace replacer;
|
||||||
|
|
||||||
// Create the world
|
// Create the world
|
||||||
mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster,
|
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins,
|
||||||
mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap,
|
mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap,
|
||||||
mActivationDistanceOverride));
|
mActivationDistanceOverride));
|
||||||
|
|
||||||
//Load translation data
|
//Load translation data
|
||||||
mTranslationDataStorage.setEncoder(mEncoder);
|
mTranslationDataStorage.setEncoder(mEncoder);
|
||||||
mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster);
|
mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster[0]);
|
||||||
|
|
||||||
// Create window manager - this manages all the MW-specific GUI windows
|
// Create window manager - this manages all the MW-specific GUI windows
|
||||||
MWScript::registerExtensions (mExtensions);
|
MWScript::registerExtensions (mExtensions);
|
||||||
|
|
|
@ -67,7 +67,8 @@ namespace OMW
|
||||||
boost::filesystem::path mResDir;
|
boost::filesystem::path mResDir;
|
||||||
OEngine::Render::OgreRenderer *mOgre;
|
OEngine::Render::OgreRenderer *mOgre;
|
||||||
std::string mCellName;
|
std::string mCellName;
|
||||||
std::string mMaster;
|
std::vector<std::string> mMaster;
|
||||||
|
std::vector<std::string> mPlugins;
|
||||||
int mFpsLevel;
|
int mFpsLevel;
|
||||||
bool mDebug;
|
bool mDebug;
|
||||||
bool mVerboseScripts;
|
bool mVerboseScripts;
|
||||||
|
@ -132,9 +133,12 @@ namespace OMW
|
||||||
|
|
||||||
/// Set master file (esm)
|
/// Set master file (esm)
|
||||||
/// - If the given name does not have an extension, ".esm" is added automatically
|
/// - If the given name does not have an extension, ".esm" is added automatically
|
||||||
/// - Currently OpenMW only supports one master at the same time.
|
|
||||||
void addMaster(const std::string& master);
|
void addMaster(const std::string& master);
|
||||||
|
|
||||||
|
/// Same as "addMaster", but for plugin files (esp)
|
||||||
|
/// - If the given name does not have an extension, ".esp" is added automatically
|
||||||
|
void addPlugin(const std::string& plugin);
|
||||||
|
|
||||||
/// Enable fps counter
|
/// Enable fps counter
|
||||||
void showFPS(int level);
|
void showFPS(int level);
|
||||||
|
|
||||||
|
|
|
@ -211,18 +211,21 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||||
master.push_back("Morrowind");
|
master.push_back("Morrowind");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (master.size() > 1)
|
|
||||||
{
|
|
||||||
std::cout
|
|
||||||
<< "Ignoring all but the first master file (multiple master files not yet supported)."
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
engine.addMaster(master[0]);
|
|
||||||
|
|
||||||
StringsVector plugin = variables["plugin"].as<StringsVector>();
|
StringsVector plugin = variables["plugin"].as<StringsVector>();
|
||||||
if (!plugin.empty())
|
// Removed check for 255 files, which would be the hard-coded limit in Morrowind.
|
||||||
|
// I'll keep the following variable in, maybe we can use it for something different.
|
||||||
|
// Say, a feedback like "loading file x/cnt".
|
||||||
|
// Commenting this out for now to silence compiler warning.
|
||||||
|
//int cnt = master.size() + plugin.size();
|
||||||
|
|
||||||
|
// Prepare loading master/plugin files (i.e. send filenames to engine)
|
||||||
|
for (std::vector<std::string>::size_type i = 0; i < master.size(); i++)
|
||||||
{
|
{
|
||||||
std::cout << "Ignoring plugin files (plugins not yet supported)." << std::endl;
|
engine.addMaster(master[i]);
|
||||||
|
}
|
||||||
|
for (std::vector<std::string>::size_type i = 0; i < plugin.size(); i++)
|
||||||
|
{
|
||||||
|
engine.addPlugin(plugin[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// startup-settings
|
// startup-settings
|
||||||
|
|
|
@ -105,7 +105,7 @@ namespace MWBase
|
||||||
|
|
||||||
virtual const MWWorld::ESMStore& getStore() const = 0;
|
virtual const MWWorld::ESMStore& getStore() const = 0;
|
||||||
|
|
||||||
virtual ESM::ESMReader& getEsmReader() = 0;
|
virtual std::vector<ESM::ESMReader>& getEsmReader() = 0;
|
||||||
|
|
||||||
virtual MWWorld::LocalScripts& getLocalScripts() = 0;
|
virtual MWWorld::LocalScripts& getLocalScripts() = 0;
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,7 @@ namespace MWRender
|
||||||
std::map<uint16_t, int> indexes;
|
std::map<uint16_t, int> indexes;
|
||||||
initTerrainTextures(&terrainData, cellX, cellY,
|
initTerrainTextures(&terrainData, cellX, cellY,
|
||||||
x * numTextures, y * numTextures,
|
x * numTextures, y * numTextures,
|
||||||
numTextures, indexes);
|
numTextures, indexes, land->mPlugin);
|
||||||
|
|
||||||
if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL)
|
if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL)
|
||||||
{
|
{
|
||||||
|
@ -213,8 +213,13 @@ namespace MWRender
|
||||||
void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData,
|
void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData,
|
||||||
int cellX, int cellY,
|
int cellX, int cellY,
|
||||||
int fromX, int fromY, int size,
|
int fromX, int fromY, int size,
|
||||||
std::map<uint16_t, int>& indexes)
|
std::map<uint16_t, int>& indexes, size_t plugin)
|
||||||
{
|
{
|
||||||
|
// FIXME: In a multiple esm configuration, we have multiple palettes. Since this code
|
||||||
|
// crosses cell boundaries, we no longer have a unique terrain palette. Instead, we need
|
||||||
|
// to adopt the following code for a dynamic palette. And this is evil - the current design
|
||||||
|
// does not work well for this task...
|
||||||
|
|
||||||
assert(terrainData != NULL && "Must have valid terrain data");
|
assert(terrainData != NULL && "Must have valid terrain data");
|
||||||
assert(fromX >= 0 && fromY >= 0 &&
|
assert(fromX >= 0 && fromY >= 0 &&
|
||||||
"Can't get a terrain texture on terrain outside the current cell");
|
"Can't get a terrain texture on terrain outside the current cell");
|
||||||
|
@ -227,12 +232,20 @@ namespace MWRender
|
||||||
//
|
//
|
||||||
//If we don't sort the ltex indexes, the splatting order may differ between
|
//If we don't sort the ltex indexes, the splatting order may differ between
|
||||||
//cells which may lead to inconsistent results when shading between cells
|
//cells which may lead to inconsistent results when shading between cells
|
||||||
|
int num = MWBase::Environment::get().getWorld()->getStore().get<ESM::LandTexture>().getSize(plugin);
|
||||||
std::set<uint16_t> ltexIndexes;
|
std::set<uint16_t> ltexIndexes;
|
||||||
for ( int y = fromY - 1; y < fromY + size + 1; y++ )
|
for ( int y = fromY - 1; y < fromY + size + 1; y++ )
|
||||||
{
|
{
|
||||||
for ( int x = fromX - 1; x < fromX + size + 1; x++ )
|
for ( int x = fromX - 1; x < fromX + size + 1; x++ )
|
||||||
{
|
{
|
||||||
ltexIndexes.insert(getLtexIndexAt(cellX, cellY, x, y));
|
int idx = getLtexIndexAt(cellX, cellY, x, y);
|
||||||
|
// This is a quick hack to prevent the program from trying to fetch textures
|
||||||
|
// from a neighboring cell, which might originate from a different plugin,
|
||||||
|
// and use a separate texture palette. Right now, we simply cast it to the
|
||||||
|
// default texture (i.e. 0).
|
||||||
|
if (idx > num)
|
||||||
|
idx = 0;
|
||||||
|
ltexIndexes.insert(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +257,7 @@ namespace MWRender
|
||||||
iter != ltexIndexes.end();
|
iter != ltexIndexes.end();
|
||||||
++iter )
|
++iter )
|
||||||
{
|
{
|
||||||
const uint16_t ltexIndex = *iter;
|
uint16_t ltexIndex = *iter;
|
||||||
//this is the base texture, so we can ignore this at present
|
//this is the base texture, so we can ignore this at present
|
||||||
if ( ltexIndex == baseTexture )
|
if ( ltexIndex == baseTexture )
|
||||||
{
|
{
|
||||||
|
@ -260,8 +273,11 @@ namespace MWRender
|
||||||
const MWWorld::Store<ESM::LandTexture> <exStore =
|
const MWWorld::Store<ESM::LandTexture> <exStore =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::LandTexture>();
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::LandTexture>();
|
||||||
|
|
||||||
assert( (int)ltexStore.getSize() >= (int)ltexIndex - 1 &&
|
// NOTE: using the quick hack above, we should no longer end up with textures indices
|
||||||
"LAND.VTEX must be within the bounds of the LTEX array");
|
// that are out of bounds. However, I haven't updated the test to a multi-palette
|
||||||
|
// system yet. We probably need more work here, so we skip it for now.
|
||||||
|
//assert( (int)ltexStore.getSize() >= (int)ltexIndex - 1 &&
|
||||||
|
//"LAND.VTEX must be within the bounds of the LTEX array");
|
||||||
|
|
||||||
std::string texture;
|
std::string texture;
|
||||||
if ( ltexIndex == 0 )
|
if ( ltexIndex == 0 )
|
||||||
|
@ -270,7 +286,7 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
texture = ltexStore.search(ltexIndex-1)->mTexture;
|
texture = ltexStore.search(ltexIndex-1, plugin)->mTexture;
|
||||||
//TODO this is needed due to MWs messed up texture handling
|
//TODO this is needed due to MWs messed up texture handling
|
||||||
texture = texture.substr(0, texture.rfind(".")) + ".dds";
|
texture = texture.substr(0, texture.rfind(".")) + ".dds";
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace MWRender{
|
||||||
void initTerrainTextures(Ogre::Terrain::ImportData* terrainData,
|
void initTerrainTextures(Ogre::Terrain::ImportData* terrainData,
|
||||||
int cellX, int cellY,
|
int cellX, int cellY,
|
||||||
int fromX, int fromY, int size,
|
int fromX, int fromY, int size,
|
||||||
std::map<uint16_t, int>& indexes);
|
std::map<uint16_t, int>& indexes, size_t plugin);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the blend (splatting maps) for the given terrain from the ltex data.
|
* Creates the blend (splatting maps) for the given terrain from the ltex data.
|
||||||
|
|
|
@ -84,7 +84,7 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellS
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, ESM::ESMReader& reader)
|
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader)
|
||||||
: mStore (store), mReader (reader),
|
: mStore (store), mReader (reader),
|
||||||
mIdCache (20, std::pair<std::string, Ptr::CellStore *> ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable
|
mIdCache (20, std::pair<std::string, Ptr::CellStore *> ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable
|
||||||
mIdCacheIndex (0)
|
mIdCacheIndex (0)
|
||||||
|
@ -119,6 +119,7 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y)
|
||||||
|
|
||||||
if (result->second.mState!=Ptr::CellStore::State_Loaded)
|
if (result->second.mState!=Ptr::CellStore::State_Loaded)
|
||||||
{
|
{
|
||||||
|
// Multiple plugin support for landscape data is much easier than for references. The last plugin wins.
|
||||||
result->second.load (mStore, mReader);
|
result->second.load (mStore, mReader);
|
||||||
fillContainers (result->second);
|
fillContainers (result->second);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define GAME_MWWORLD_CELLS_H
|
#define GAME_MWWORLD_CELLS_H
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ptr.hpp"
|
#include "ptr.hpp"
|
||||||
|
@ -19,7 +20,7 @@ namespace MWWorld
|
||||||
class Cells
|
class Cells
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore& mStore;
|
const MWWorld::ESMStore& mStore;
|
||||||
ESM::ESMReader& mReader;
|
std::vector<ESM::ESMReader>& mReader;
|
||||||
std::map<std::string, CellStore> mInteriors;
|
std::map<std::string, CellStore> mInteriors;
|
||||||
std::map<std::pair<int, int>, CellStore> mExteriors;
|
std::map<std::pair<int, int>, CellStore> mExteriors;
|
||||||
std::vector<std::pair<std::string, CellStore *> > mIdCache;
|
std::vector<std::pair<std::string, CellStore *> > mIdCache;
|
||||||
|
@ -36,7 +37,7 @@ namespace MWWorld
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Cells (const MWWorld::ESMStore& store, ESM::ESMReader& reader);
|
Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader);
|
||||||
///< \todo pass the dynamic part of the ESMStore isntead (once it is written) of the whole
|
///< \todo pass the dynamic part of the ESMStore isntead (once it is written) of the whole
|
||||||
/// world
|
/// world
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,48 @@
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
|
||||||
|
template <typename X>
|
||||||
|
void CellRefList<X>::load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore)
|
||||||
|
{
|
||||||
|
// Get existing reference, in case we need to overwrite it.
|
||||||
|
typename std::list<LiveRef>::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefnum);
|
||||||
|
|
||||||
|
// Skip this when reference was deleted.
|
||||||
|
// TODO: Support respawning references, in this case, we need to track it somehow.
|
||||||
|
if (ref.mDeleted) {
|
||||||
|
mList.erase(iter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for throwing exception on unhandled record type
|
||||||
|
const MWWorld::Store<X> &store = esmStore.get<X>();
|
||||||
|
const X *ptr = store.search(ref.mRefID);
|
||||||
|
|
||||||
|
/// \note no longer redundant - changed to Store<X>::search(), don't throw
|
||||||
|
/// an exception on miss, try to continue (that's how MW does it, anyway)
|
||||||
|
if (ptr == NULL) {
|
||||||
|
std::cout << "Warning: could not resolve cell reference " << ref.mRefID << ", trying to continue anyway" << std::endl;
|
||||||
|
} else {
|
||||||
|
if (iter != mList.end())
|
||||||
|
*iter = LiveRef(ref, ptr);
|
||||||
|
else
|
||||||
|
mList.push_back(LiveRef(ref, ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename X> bool operator==(const LiveCellRef<X>& ref, int pRefnum)
|
||||||
|
{
|
||||||
|
return (ref.mRef.mRefnum == pRefnum);
|
||||||
|
}
|
||||||
|
|
||||||
CellStore::CellStore (const ESM::Cell *cell)
|
CellStore::CellStore (const ESM::Cell *cell)
|
||||||
: mCell (cell), mState (State_Unloaded)
|
: mCell (cell), mState (State_Unloaded)
|
||||||
{
|
{
|
||||||
mWaterLevel = cell->mWater;
|
mWaterLevel = cell->mWater;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellStore::load (const MWWorld::ESMStore &store, ESM::ESMReader &esm)
|
void CellStore::load (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
|
||||||
{
|
{
|
||||||
if (mState!=State_Loaded)
|
if (mState!=State_Loaded)
|
||||||
{
|
{
|
||||||
|
@ -31,7 +66,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellStore::preload (const MWWorld::ESMStore &store, ESM::ESMReader &esm)
|
void CellStore::preload (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
|
||||||
{
|
{
|
||||||
if (mState==State_Unloaded)
|
if (mState==State_Unloaded)
|
||||||
{
|
{
|
||||||
|
@ -41,46 +76,64 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellStore::listRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm)
|
void CellStore::listRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
|
||||||
{
|
{
|
||||||
assert (mCell);
|
assert (mCell);
|
||||||
|
|
||||||
if (mCell->mContext.filename.empty())
|
if (mCell->mContextList.size() == 0)
|
||||||
return; // this is a dynamically generated cell -> skipping.
|
return; // this is a dynamically generated cell -> skipping.
|
||||||
|
|
||||||
|
// Load references from all plugins that do something with this cell.
|
||||||
|
for (size_t i = 0; i < mCell->mContextList.size(); i++)
|
||||||
|
{
|
||||||
// Reopen the ESM reader and seek to the right position.
|
// Reopen the ESM reader and seek to the right position.
|
||||||
mCell->restore (esm);
|
int index = mCell->mContextList.at(i).index;
|
||||||
|
mCell->restore (esm[index], i);
|
||||||
|
|
||||||
ESM::CellRef ref;
|
ESM::CellRef ref;
|
||||||
|
|
||||||
// Get each reference in turn
|
// Get each reference in turn
|
||||||
while (mCell->getNextRef (esm, ref))
|
while (mCell->getNextRef (esm[index], ref))
|
||||||
{
|
{
|
||||||
std::string lowerCase = Misc::StringUtils::lowerCase (ref.mRefID);
|
std::string lowerCase = Misc::StringUtils::lowerCase (ref.mRefID);
|
||||||
|
if (ref.mDeleted) {
|
||||||
|
// Right now, don't do anything. Where is "listRefs" actually used, anyway?
|
||||||
|
// Skipping for now...
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
mIds.push_back (lowerCase);
|
mIds.push_back (lowerCase);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::sort (mIds.begin(), mIds.end());
|
std::sort (mIds.begin(), mIds.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellStore::loadRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm)
|
void CellStore::loadRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
|
||||||
{
|
{
|
||||||
assert (mCell);
|
assert (mCell);
|
||||||
|
|
||||||
if (mCell->mContext.filename.empty())
|
if (mCell->mContextList.size() == 0)
|
||||||
return; // this is a dynamically generated cell -> skipping.
|
return; // this is a dynamically generated cell -> skipping.
|
||||||
|
|
||||||
|
// Load references from all plugins that do something with this cell.
|
||||||
|
for (size_t i = 0; i < mCell->mContextList.size(); i++)
|
||||||
|
{
|
||||||
// Reopen the ESM reader and seek to the right position.
|
// Reopen the ESM reader and seek to the right position.
|
||||||
mCell->restore(esm);
|
int index = mCell->mContextList.at(i).index;
|
||||||
|
mCell->restore (esm[index], i);
|
||||||
|
|
||||||
ESM::CellRef ref;
|
ESM::CellRef ref;
|
||||||
|
|
||||||
// Get each reference in turn
|
// Get each reference in turn
|
||||||
while(mCell->getNextRef(esm, ref))
|
while(mCell->getNextRef(esm[index], ref))
|
||||||
{
|
{
|
||||||
|
// Don't load reference if it was moved to a different cell.
|
||||||
std::string lowerCase = Misc::StringUtils::lowerCase(ref.mRefID);
|
std::string lowerCase = Misc::StringUtils::lowerCase(ref.mRefID);
|
||||||
|
ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefnum);
|
||||||
|
if (iter != mCell->mMovedRefs.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
int rec = store.find(ref.mRefID);
|
int rec = store.find(ref.mRefID);
|
||||||
|
|
||||||
ref.mRefID = lowerCase;
|
ref.mRefID = lowerCase;
|
||||||
|
@ -119,4 +172,56 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load moved references, from separately tracked list.
|
||||||
|
for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); it++)
|
||||||
|
{
|
||||||
|
// Doesn't seem to work in one line... huh? Too sleepy to check...
|
||||||
|
ESM::CellRef &ref = const_cast<ESM::CellRef&>(*it);
|
||||||
|
//ESM::CellRef &ref = const_cast<ESM::CellRef&>(it->second);
|
||||||
|
|
||||||
|
std::string lowerCase;
|
||||||
|
|
||||||
|
std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase),
|
||||||
|
(int(*)(int)) std::tolower);
|
||||||
|
|
||||||
|
int rec = store.find(ref.mRefID);
|
||||||
|
|
||||||
|
ref.mRefID = lowerCase;
|
||||||
|
|
||||||
|
/* We can optimize this further by storing the pointer to the
|
||||||
|
record itself in store.all, so that we don't need to look it
|
||||||
|
up again here. However, never optimize. There are infinite
|
||||||
|
opportunities to do that later.
|
||||||
|
*/
|
||||||
|
switch(rec)
|
||||||
|
{
|
||||||
|
case ESM::REC_ACTI: mActivators.load(ref, store); break;
|
||||||
|
case ESM::REC_ALCH: mPotions.load(ref, store); break;
|
||||||
|
case ESM::REC_APPA: mAppas.load(ref, store); break;
|
||||||
|
case ESM::REC_ARMO: mArmors.load(ref, store); break;
|
||||||
|
case ESM::REC_BOOK: mBooks.load(ref, store); break;
|
||||||
|
case ESM::REC_CLOT: mClothes.load(ref, store); break;
|
||||||
|
case ESM::REC_CONT: mContainers.load(ref, store); break;
|
||||||
|
case ESM::REC_CREA: mCreatures.load(ref, store); break;
|
||||||
|
case ESM::REC_DOOR: mDoors.load(ref, store); break;
|
||||||
|
case ESM::REC_INGR: mIngreds.load(ref, store); break;
|
||||||
|
case ESM::REC_LEVC: mCreatureLists.load(ref, store); break;
|
||||||
|
case ESM::REC_LEVI: mItemLists.load(ref, store); break;
|
||||||
|
case ESM::REC_LIGH: mLights.load(ref, store); break;
|
||||||
|
case ESM::REC_LOCK: mLockpicks.load(ref, store); break;
|
||||||
|
case ESM::REC_MISC: mMiscItems.load(ref, store); break;
|
||||||
|
case ESM::REC_NPC_: mNpcs.load(ref, store); break;
|
||||||
|
case ESM::REC_PROB: mProbes.load(ref, store); break;
|
||||||
|
case ESM::REC_REPA: mRepairs.load(ref, store); break;
|
||||||
|
case ESM::REC_STAT: mStatics.load(ref, store); break;
|
||||||
|
case ESM::REC_WEAP: mWeapons.load(ref, store); break;
|
||||||
|
|
||||||
|
case 0: std::cout << "Cell reference " + ref.mRefID + " not found!\n"; break;
|
||||||
|
default:
|
||||||
|
std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
|
|
||||||
#include <components/esm/records.hpp>
|
#include <components/esm/records.hpp>
|
||||||
|
|
||||||
#include <list>
|
#include <deque>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "refdata.hpp"
|
#include "refdata.hpp"
|
||||||
#include "esmstore.hpp"
|
#include "esmstore.hpp"
|
||||||
|
|
||||||
|
struct C;
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class Ptr;
|
class Ptr;
|
||||||
|
@ -43,6 +44,8 @@ namespace MWWorld
|
||||||
RefData mData;
|
RefData mData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename X> bool operator==(const LiveCellRef<X>& ref, int pRefnum);
|
||||||
|
|
||||||
/// A list of cell references
|
/// A list of cell references
|
||||||
template <typename X>
|
template <typename X>
|
||||||
struct CellRefList
|
struct CellRefList
|
||||||
|
@ -51,21 +54,14 @@ namespace MWWorld
|
||||||
typedef std::list<LiveRef> List;
|
typedef std::list<LiveRef> List;
|
||||||
List mList;
|
List mList;
|
||||||
|
|
||||||
/// Searches for reference of appropriate type in given ESMStore.
|
// Search for the given reference in the given reclist from
|
||||||
/// If reference exists, loads it into container, throws an exception
|
// ESMStore. Insert the reference into the list if a match is
|
||||||
/// on miss
|
// found. If not, throw an exception.
|
||||||
void load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore)
|
// Moved to cpp file, as we require a custom compare operator for it,
|
||||||
{
|
// and the build will fail with an ugly three-way cyclic header dependence
|
||||||
// for throwing exception on unhandled record type
|
// so we need to pass the instantiation of the method to the lnker, when
|
||||||
const MWWorld::Store<X> &store = esmStore.get<X>();
|
// all methods are known.
|
||||||
const X *ptr = store.find(ref.mRefID);
|
void load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore);
|
||||||
|
|
||||||
/// \note redundant because Store<X>::find() throws exception on miss
|
|
||||||
if (ptr == NULL) {
|
|
||||||
throw std::runtime_error("Error resolving cell reference " + ref.mRefID);
|
|
||||||
}
|
|
||||||
mList.push_back(LiveRef(ref, ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
LiveRef *find (const std::string& name)
|
LiveRef *find (const std::string& name)
|
||||||
{
|
{
|
||||||
|
@ -124,9 +120,9 @@ namespace MWWorld
|
||||||
CellRefList<ESM::Static> mStatics;
|
CellRefList<ESM::Static> mStatics;
|
||||||
CellRefList<ESM::Weapon> mWeapons;
|
CellRefList<ESM::Weapon> mWeapons;
|
||||||
|
|
||||||
void load (const MWWorld::ESMStore &store, ESM::ESMReader &esm);
|
void load (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
|
||||||
|
|
||||||
void preload (const MWWorld::ESMStore &store, ESM::ESMReader &esm);
|
void preload (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
|
||||||
|
|
||||||
/// Call functor (ref) for each reference. functor must return a bool. Returning
|
/// Call functor (ref) for each reference. functor must return a bool. Returning
|
||||||
/// false will abort the iteration.
|
/// false will abort the iteration.
|
||||||
|
@ -185,9 +181,9 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run through references and store IDs
|
/// Run through references and store IDs
|
||||||
void listRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm);
|
void listRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
|
||||||
|
|
||||||
void loadRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm);
|
void loadRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <boost/filesystem/v3/operations.hpp>
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -25,6 +27,33 @@ void ESMStore::load(ESM::ESMReader &esm)
|
||||||
|
|
||||||
ESM::Dialogue *dialogue = 0;
|
ESM::Dialogue *dialogue = 0;
|
||||||
|
|
||||||
|
// Cache parent esX files by tracking their indices in the global list of
|
||||||
|
// all files/readers used by the engine. This will greaty accelerate
|
||||||
|
// refnumber mangling, as required for handling moved references.
|
||||||
|
int index = ~0;
|
||||||
|
const ESM::ESMReader::MasterList &masters = esm.getMasters();
|
||||||
|
std::vector<ESM::ESMReader> *allPlugins = esm.getGlobalReaderList();
|
||||||
|
for (size_t j = 0; j < masters.size(); j++) {
|
||||||
|
ESM::MasterData &mast = const_cast<ESM::MasterData&>(masters[j]);
|
||||||
|
std::string fname = mast.name;
|
||||||
|
for (int i = 0; i < esm.getIndex(); i++) {
|
||||||
|
const std::string &candidate = allPlugins->at(i).getContext().filename;
|
||||||
|
std::string fnamecandidate = boost::filesystem::path(candidate).filename().string();
|
||||||
|
if (fname == fnamecandidate) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index == (int)~0) {
|
||||||
|
// Tried to load a parent file that has not been loaded yet. This is bad,
|
||||||
|
// the launcher should have taken care of this.
|
||||||
|
std::string fstring = "File " + fname + " asks for parent file " + masters[j].name
|
||||||
|
+ ", but it has not been loaded yet. Please check your load order.";
|
||||||
|
esm.fail(fstring);
|
||||||
|
}
|
||||||
|
mast.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
// Loop through all records
|
// Loop through all records
|
||||||
while(esm.hasMoreRecs())
|
while(esm.hasMoreRecs())
|
||||||
{
|
{
|
||||||
|
@ -55,12 +84,21 @@ void ESMStore::load(ESM::ESMReader &esm)
|
||||||
} else {
|
} else {
|
||||||
// Load it
|
// Load it
|
||||||
std::string id = esm.getHNOString("NAME");
|
std::string id = esm.getHNOString("NAME");
|
||||||
|
// ... unless it got deleted! This means that the following record
|
||||||
|
// has been deleted, and trying to load it using standard assumptions
|
||||||
|
// on the structure will (probably) fail.
|
||||||
|
if (esm.isNextSub("DELE")) {
|
||||||
|
esm.skipRecord();
|
||||||
|
it->second->eraseStatic(id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
it->second->load(esm, id);
|
it->second->load(esm, id);
|
||||||
|
|
||||||
if (n.val==ESM::REC_DIAL) {
|
if (n.val==ESM::REC_DIAL) {
|
||||||
// dirty hack, but it is better than non-const search()
|
// dirty hack, but it is better than non-const search()
|
||||||
// or friends
|
// or friends
|
||||||
dialogue = &mDialogs.mStatic.back();
|
//dialogue = &mDialogs.mStatic.back();
|
||||||
|
dialogue = const_cast<ESM::Dialogue*>(mDialogs.find(id));
|
||||||
assert (dialogue->mId == id);
|
assert (dialogue->mId == id);
|
||||||
} else {
|
} else {
|
||||||
dialogue = 0;
|
dialogue = 0;
|
||||||
|
@ -84,7 +122,6 @@ void ESMStore::load(ESM::ESMReader &esm)
|
||||||
cout << *it << " ";
|
cout << *it << " ";
|
||||||
cout << endl;
|
cout << endl;
|
||||||
*/
|
*/
|
||||||
setUp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMStore::setUp()
|
void ESMStore::setUp()
|
||||||
|
@ -100,12 +137,11 @@ void ESMStore::setUp()
|
||||||
ESM::NPC item;
|
ESM::NPC item;
|
||||||
item.mId = "player";
|
item.mId = "player";
|
||||||
|
|
||||||
std::vector<ESM::NPC>::iterator pIt =
|
const ESM::NPC *pIt = mNpcs.find("player");
|
||||||
std::lower_bound(mNpcs.mStatic.begin(), mNpcs.mStatic.end(), item, RecordCmp());
|
assert(pIt != NULL);
|
||||||
assert(pIt != mNpcs.mStatic.end() && pIt->mId == "player");
|
|
||||||
|
|
||||||
mNpcs.insert(*pIt);
|
mNpcs.insert(*pIt);
|
||||||
mNpcs.mStatic.erase(pIt);
|
mNpcs.eraseStatic(pIt->mId);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace
|
} // end namespace
|
||||||
|
|
|
@ -94,6 +94,9 @@ namespace MWWorld
|
||||||
ESMStore()
|
ESMStore()
|
||||||
: mDynamicCount(0)
|
: mDynamicCount(0)
|
||||||
{
|
{
|
||||||
|
// Cell store needs access to this for tracking moved references
|
||||||
|
mCells.mEsmStore = this;
|
||||||
|
|
||||||
mStores[ESM::REC_ACTI] = &mActivators;
|
mStores[ESM::REC_ACTI] = &mActivators;
|
||||||
mStores[ESM::REC_ALCH] = &mPotions;
|
mStores[ESM::REC_ALCH] = &mPotions;
|
||||||
mStores[ESM::REC_APPA] = &mAppas;
|
mStores[ESM::REC_APPA] = &mAppas;
|
||||||
|
@ -168,7 +171,8 @@ namespace MWWorld
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
// This method must be called once, after loading all master/plugin files. This can only be done
|
||||||
|
// from the outside, so it must be public.
|
||||||
void setUp();
|
void setUp();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
65
apps/openmw/mwworld/store.cpp
Normal file
65
apps/openmw/mwworld/store.cpp
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#include "store.hpp"
|
||||||
|
|
||||||
|
namespace MWWorld {
|
||||||
|
|
||||||
|
|
||||||
|
void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
|
||||||
|
{
|
||||||
|
// Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell,
|
||||||
|
// and we merge all this data into one Cell object. However, we can't simply search for the cell id,
|
||||||
|
// as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they
|
||||||
|
// are not available until both cells have been loaded! So first, proceed as usual.
|
||||||
|
|
||||||
|
// All cells have a name record, even nameless exterior cells.
|
||||||
|
std::string idLower = Misc::StringUtils::lowerCase(id);
|
||||||
|
ESM::Cell *cell = new ESM::Cell;
|
||||||
|
cell->mName = id;
|
||||||
|
|
||||||
|
// The cell itself takes care of some of the hairy details
|
||||||
|
cell->load(esm, *mEsmStore);
|
||||||
|
|
||||||
|
if(cell->mData.mFlags & ESM::Cell::Interior)
|
||||||
|
{
|
||||||
|
// Store interior cell by name, try to merge with existing parent data.
|
||||||
|
ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(idLower));
|
||||||
|
if (oldcell) {
|
||||||
|
// push the new references on the list of references to manage
|
||||||
|
oldcell->mContextList.push_back(cell->mContextList.at(0));
|
||||||
|
// copy list into new cell
|
||||||
|
cell->mContextList = oldcell->mContextList;
|
||||||
|
// have new cell replace old cell
|
||||||
|
*oldcell = *cell;
|
||||||
|
} else
|
||||||
|
mInt[idLower] = *cell;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Store exterior cells by grid position, try to merge with existing parent data.
|
||||||
|
ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(cell->getGridX(), cell->getGridY()));
|
||||||
|
if (oldcell) {
|
||||||
|
// push the new references on the list of references to manage
|
||||||
|
oldcell->mContextList.push_back(cell->mContextList.at(0));
|
||||||
|
// copy list into new cell
|
||||||
|
cell->mContextList = oldcell->mContextList;
|
||||||
|
// merge lists of leased references, use newer data in case of conflict
|
||||||
|
for (ESM::MovedCellRefTracker::const_iterator it = cell->mMovedRefs.begin(); it != cell->mMovedRefs.end(); it++) {
|
||||||
|
// remove reference from current leased ref tracker and add it to new cell
|
||||||
|
ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefnum);
|
||||||
|
if (itold != oldcell->mMovedRefs.end()) {
|
||||||
|
ESM::MovedCellRef target0 = *itold;
|
||||||
|
ESM::Cell *wipecell = const_cast<ESM::Cell*>(search(target0.mTarget[0], target0.mTarget[1]));
|
||||||
|
ESM::CellRefTracker::iterator it_lease = std::find(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), it->mRefnum);
|
||||||
|
wipecell->mLeasedRefs.erase(it_lease);
|
||||||
|
*itold = *it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cell->mMovedRefs = oldcell->mMovedRefs;
|
||||||
|
// have new cell replace old cell
|
||||||
|
*oldcell = *cell;
|
||||||
|
} else
|
||||||
|
mExt[std::make_pair(cell->mData.mX, cell->mData.mY)] = *cell;
|
||||||
|
}
|
||||||
|
delete cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,6 +19,8 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual int getSize() const = 0;
|
virtual int getSize() const = 0;
|
||||||
virtual void load(ESM::ESMReader &esm, const std::string &id) = 0;
|
virtual void load(ESM::ESMReader &esm, const std::string &id) = 0;
|
||||||
|
|
||||||
|
virtual bool eraseStatic(const std::string &id) {return false;}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
@ -85,7 +87,7 @@ namespace MWWorld
|
||||||
template <class T>
|
template <class T>
|
||||||
class Store : public StoreBase
|
class Store : public StoreBase
|
||||||
{
|
{
|
||||||
std::vector<T> mStatic;
|
std::map<std::string, T> mStatic;
|
||||||
std::vector<T *> mShared;
|
std::vector<T *> mShared;
|
||||||
std::map<std::string, T> mDynamic;
|
std::map<std::string, T> mDynamic;
|
||||||
|
|
||||||
|
@ -107,11 +109,10 @@ namespace MWWorld
|
||||||
T item;
|
T item;
|
||||||
item.mId = Misc::StringUtils::lowerCase(id);
|
item.mId = Misc::StringUtils::lowerCase(id);
|
||||||
|
|
||||||
typename std::vector<T>::const_iterator it =
|
typename std::map<std::string, T>::const_iterator it = mStatic.find(item.mId);
|
||||||
std::lower_bound(mStatic.begin(), mStatic.end(), item, RecordCmp());
|
|
||||||
|
|
||||||
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->mId, id)) {
|
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) {
|
||||||
return &(*it);
|
return &(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
typename Dynamic::const_iterator dit = mDynamic.find(item.mId);
|
typename Dynamic::const_iterator dit = mDynamic.find(item.mId);
|
||||||
|
@ -133,18 +134,19 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm, const std::string &id) {
|
void load(ESM::ESMReader &esm, const std::string &id) {
|
||||||
mStatic.push_back(T());
|
std::string idLower = Misc::StringUtils::lowerCase(id);
|
||||||
mStatic.back().mId = Misc::StringUtils::lowerCase(id);
|
mStatic[idLower] = T();
|
||||||
mStatic.back().load(esm);
|
mStatic[idLower].mId = idLower;
|
||||||
|
mStatic[idLower].load(esm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setUp() {
|
void setUp() {
|
||||||
std::sort(mStatic.begin(), mStatic.end(), RecordCmp());
|
//std::sort(mStatic.begin(), mStatic.end(), RecordCmp());
|
||||||
|
|
||||||
mShared.reserve(mStatic.size());
|
mShared.reserve(mStatic.size());
|
||||||
typename std::vector<T>::iterator it = mStatic.begin();
|
typename std::map<std::string, T>::iterator it = mStatic.begin();
|
||||||
for (; it != mStatic.end(); ++it) {
|
for (; it != mStatic.end(); ++it) {
|
||||||
mShared.push_back(&(*it));
|
mShared.push_back(&(it->second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +183,19 @@ namespace MWWorld
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool eraseStatic(const std::string &id) {
|
||||||
|
T item;
|
||||||
|
item.mId = Misc::StringUtils::lowerCase(id);
|
||||||
|
|
||||||
|
typename std::map<std::string, T>::iterator it = mStatic.find(item.mId);
|
||||||
|
|
||||||
|
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) {
|
||||||
|
mStatic.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool erase(const std::string &id) {
|
bool erase(const std::string &id) {
|
||||||
std::string key = Misc::StringUtils::lowerCase(id);
|
std::string key = Misc::StringUtils::lowerCase(id);
|
||||||
typename Dynamic::iterator it = mDynamic.find(key);
|
typename Dynamic::iterator it = mDynamic.find(key);
|
||||||
|
@ -204,39 +219,48 @@ namespace MWWorld
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void Store<ESM::Dialogue>::load(ESM::ESMReader &esm, const std::string &id) {
|
inline void Store<ESM::Dialogue>::load(ESM::ESMReader &esm, const std::string &id) {
|
||||||
mStatic.push_back(ESM::Dialogue());
|
std::string idLower = Misc::StringUtils::lowerCase(id);
|
||||||
mStatic.back().mId = id;
|
mStatic[idLower] = ESM::Dialogue();
|
||||||
mStatic.back().load(esm);
|
mStatic[idLower].mId = id; // don't smash case here, as this line is printed... I think
|
||||||
|
mStatic[idLower].load(esm);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void Store<ESM::Script>::load(ESM::ESMReader &esm, const std::string &id) {
|
inline void Store<ESM::Script>::load(ESM::ESMReader &esm, const std::string &id) {
|
||||||
mStatic.push_back(ESM::Script());
|
ESM::Script scpt;
|
||||||
mStatic.back().load(esm);
|
scpt.load(esm);
|
||||||
Misc::StringUtils::toLower(mStatic.back().mId);
|
Misc::StringUtils::toLower(scpt.mId);
|
||||||
|
mStatic[scpt.mId] = scpt;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class Store<ESM::LandTexture> : public StoreBase
|
class Store<ESM::LandTexture> : public StoreBase
|
||||||
{
|
{
|
||||||
std::vector<ESM::LandTexture> mStatic;
|
// For multiple ESM/ESP files we need one list per file.
|
||||||
|
typedef std::vector<ESM::LandTexture> LandTextureList;
|
||||||
|
std::vector<LandTextureList> mStatic;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Store<ESM::LandTexture>() {
|
Store<ESM::LandTexture>() {
|
||||||
mStatic.reserve(128);
|
mStatic.push_back(LandTextureList());
|
||||||
|
LandTextureList <exl = mStatic[0];
|
||||||
|
// More than enough to hold Morrowind.esm. Extra lists for plugins will we
|
||||||
|
// added on-the-fly in a different method.
|
||||||
|
ltexl.reserve(128);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef std::vector<ESM::LandTexture>::const_iterator iterator;
|
typedef std::vector<ESM::LandTexture>::const_iterator iterator;
|
||||||
|
|
||||||
const ESM::LandTexture *search(size_t index) const {
|
const ESM::LandTexture *search(size_t index, size_t plugin) const {
|
||||||
if (index < mStatic.size()) {
|
assert(plugin < mStatic.size());
|
||||||
return &mStatic.at(index);
|
const LandTextureList <exl = mStatic[plugin];
|
||||||
}
|
|
||||||
return 0;
|
assert(index < ltexl.size());
|
||||||
|
return <exl.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ESM::LandTexture *find(size_t index) const {
|
const ESM::LandTexture *find(size_t index, size_t plugin) const {
|
||||||
const ESM::LandTexture *ptr = search(index);
|
const ESM::LandTexture *ptr = search(index, plugin);
|
||||||
if (ptr == 0) {
|
if (ptr == 0) {
|
||||||
std::ostringstream msg;
|
std::ostringstream msg;
|
||||||
msg << "Land texture with index " << index << " not found";
|
msg << "Land texture with index " << index << " not found";
|
||||||
|
@ -249,23 +273,40 @@ namespace MWWorld
|
||||||
return mStatic.size();
|
return mStatic.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getSize(size_t plugin) const {
|
||||||
|
assert(plugin < mStatic.size());
|
||||||
|
return mStatic[plugin].size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void load(ESM::ESMReader &esm, const std::string &id, size_t plugin) {
|
||||||
|
ESM::LandTexture lt;
|
||||||
|
lt.load(esm);
|
||||||
|
lt.mId = id;
|
||||||
|
|
||||||
|
// Make sure we have room for the structure
|
||||||
|
if (plugin >= mStatic.size()) {
|
||||||
|
mStatic.resize(plugin+1);
|
||||||
|
}
|
||||||
|
LandTextureList <exl = mStatic[plugin];
|
||||||
|
if(lt.mIndex + 1 > (int)ltexl.size())
|
||||||
|
ltexl.resize(lt.mIndex+1);
|
||||||
|
|
||||||
|
// Store it
|
||||||
|
ltexl[lt.mIndex] = lt;
|
||||||
|
}
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm, const std::string &id) {
|
void load(ESM::ESMReader &esm, const std::string &id) {
|
||||||
ESM::LandTexture ltex;
|
load(esm, id, esm.getIndex());
|
||||||
ltex.load(esm);
|
|
||||||
|
|
||||||
if (ltex.mIndex >= (int) mStatic.size()) {
|
|
||||||
mStatic.resize(ltex.mIndex + 1);
|
|
||||||
}
|
|
||||||
mStatic[ltex.mIndex] = ltex;
|
|
||||||
mStatic[ltex.mIndex].mId = id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator begin() const {
|
iterator begin(size_t plugin) const {
|
||||||
return mStatic.begin();
|
assert(plugin < mStatic.size());
|
||||||
|
return mStatic[plugin].begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator end() const {
|
iterator end(size_t plugin) const {
|
||||||
return mStatic.end();
|
assert(plugin < mStatic.size());
|
||||||
|
return mStatic[plugin].end();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -357,19 +398,18 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<ESM::Cell> mInt;
|
typedef std::map<std::string, ESM::Cell> DynamicInt;
|
||||||
std::vector<ESM::Cell> mExt;
|
typedef std::map<std::pair<int, int>, ESM::Cell> DynamicExt;
|
||||||
|
|
||||||
|
DynamicInt mInt;
|
||||||
|
DynamicExt mExt;
|
||||||
|
|
||||||
std::vector<ESM::Cell *> mSharedInt;
|
std::vector<ESM::Cell *> mSharedInt;
|
||||||
std::vector<ESM::Cell *> mSharedExt;
|
std::vector<ESM::Cell *> mSharedExt;
|
||||||
|
|
||||||
typedef std::map<std::string, ESM::Cell> DynamicInt;
|
|
||||||
typedef std::map<std::pair<int, int>, ESM::Cell> DynamicExt;
|
|
||||||
|
|
||||||
DynamicInt mDynamicInt;
|
DynamicInt mDynamicInt;
|
||||||
DynamicExt mDynamicExt;
|
DynamicExt mDynamicExt;
|
||||||
|
|
||||||
|
|
||||||
const ESM::Cell *search(const ESM::Cell &cell) const {
|
const ESM::Cell *search(const ESM::Cell &cell) const {
|
||||||
if (cell.isExterior()) {
|
if (cell.isExterior()) {
|
||||||
return search(cell.getGridX(), cell.getGridY());
|
return search(cell.getGridX(), cell.getGridY());
|
||||||
|
@ -378,6 +418,8 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
ESMStore *mEsmStore;
|
||||||
|
|
||||||
typedef SharedIterator<ESM::Cell> iterator;
|
typedef SharedIterator<ESM::Cell> iterator;
|
||||||
|
|
||||||
Store<ESM::Cell>()
|
Store<ESM::Cell>()
|
||||||
|
@ -387,11 +429,10 @@ namespace MWWorld
|
||||||
ESM::Cell cell;
|
ESM::Cell cell;
|
||||||
cell.mName = Misc::StringUtils::lowerCase(id);
|
cell.mName = Misc::StringUtils::lowerCase(id);
|
||||||
|
|
||||||
std::vector<ESM::Cell>::const_iterator it =
|
std::map<std::string, ESM::Cell>::const_iterator it = mInt.find(cell.mName);
|
||||||
std::lower_bound(mInt.begin(), mInt.end(), cell, RecordCmp());
|
|
||||||
|
|
||||||
if (it != mInt.end() && Misc::StringUtils::ciEqual(it->mName, id)) {
|
if (it != mInt.end() && Misc::StringUtils::ciEqual(it->second.mName, id)) {
|
||||||
return &(*it);
|
return &(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicInt::const_iterator dit = mDynamicInt.find(cell.mName);
|
DynamicInt::const_iterator dit = mDynamicInt.find(cell.mName);
|
||||||
|
@ -406,14 +447,12 @@ namespace MWWorld
|
||||||
ESM::Cell cell;
|
ESM::Cell cell;
|
||||||
cell.mData.mX = x, cell.mData.mY = y;
|
cell.mData.mX = x, cell.mData.mY = y;
|
||||||
|
|
||||||
std::vector<ESM::Cell>::const_iterator it =
|
std::pair<int, int> key(x, y);
|
||||||
std::lower_bound(mExt.begin(), mExt.end(), cell, ExtCmp());
|
std::map<std::pair<int, int>, ESM::Cell>::const_iterator it = mExt.find(key);
|
||||||
|
if (it != mExt.end()) {
|
||||||
if (it != mExt.end() && it->mData.mX == x && it->mData.mY == y) {
|
return &(it->second);
|
||||||
return &(*it);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int, int> key(x, y);
|
|
||||||
DynamicExt::const_iterator dit = mDynamicExt.find(key);
|
DynamicExt::const_iterator dit = mDynamicExt.find(key);
|
||||||
if (dit != mDynamicExt.end()) {
|
if (dit != mDynamicExt.end()) {
|
||||||
return &dit->second;
|
return &dit->second;
|
||||||
|
@ -422,6 +461,30 @@ namespace MWWorld
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ESM::Cell *searchOrCreate(int x, int y) {
|
||||||
|
ESM::Cell cell;
|
||||||
|
cell.mData.mX = x, cell.mData.mY = y;
|
||||||
|
|
||||||
|
std::pair<int, int> key(x, y);
|
||||||
|
std::map<std::pair<int, int>, ESM::Cell>::const_iterator it = mExt.find(key);
|
||||||
|
if (it != mExt.end()) {
|
||||||
|
return &(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicExt::const_iterator dit = mDynamicExt.find(key);
|
||||||
|
if (dit != mDynamicExt.end()) {
|
||||||
|
return &dit->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESM::Cell *newCell = new ESM::Cell;
|
||||||
|
newCell->mData.mX = x;
|
||||||
|
newCell->mData.mY = y;
|
||||||
|
mExt[std::make_pair(x, y)] = *newCell;
|
||||||
|
delete newCell;
|
||||||
|
|
||||||
|
return &mExt[std::make_pair(x, y)];
|
||||||
|
}
|
||||||
|
|
||||||
const ESM::Cell *find(const std::string &id) const {
|
const ESM::Cell *find(const std::string &id) const {
|
||||||
const ESM::Cell *ptr = search(id);
|
const ESM::Cell *ptr = search(id);
|
||||||
if (ptr == 0) {
|
if (ptr == 0) {
|
||||||
|
@ -443,32 +506,28 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
void setUp() {
|
void setUp() {
|
||||||
typedef std::vector<ESM::Cell>::iterator Iterator;
|
//typedef std::vector<ESM::Cell>::iterator Iterator;
|
||||||
|
typedef std::map<std::pair<int, int>, ESM::Cell>::iterator ExtIterator;
|
||||||
|
typedef std::map<std::string, ESM::Cell>::iterator IntIterator;
|
||||||
|
|
||||||
std::sort(mInt.begin(), mInt.end(), RecordCmp());
|
//std::sort(mInt.begin(), mInt.end(), RecordCmp());
|
||||||
mSharedInt.reserve(mInt.size());
|
mSharedInt.reserve(mInt.size());
|
||||||
for (Iterator it = mInt.begin(); it != mInt.end(); ++it) {
|
for (IntIterator it = mInt.begin(); it != mInt.end(); ++it) {
|
||||||
mSharedInt.push_back(&(*it));
|
mSharedInt.push_back(&(it->second));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(mExt.begin(), mExt.end(), ExtCmp());
|
//std::sort(mExt.begin(), mExt.end(), ExtCmp());
|
||||||
mSharedExt.reserve(mExt.size());
|
mSharedExt.reserve(mExt.size());
|
||||||
for (Iterator it = mExt.begin(); it != mExt.end(); ++it) {
|
for (ExtIterator it = mExt.begin(); it != mExt.end(); ++it) {
|
||||||
mSharedExt.push_back(&(*it));
|
mSharedExt.push_back(&(it->second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm, const std::string &id) {
|
// HACK: Method implementation had to be moved to a separate cpp file, as we would otherwise get
|
||||||
ESM::Cell cell;
|
// errors related to the compare operator used in std::find for ESM::MovedCellRefTracker::find.
|
||||||
cell.mName = id;
|
// There some nasty three-way cyclic header dependency involved, which I could only fix by moving
|
||||||
cell.load(esm);
|
// this method.
|
||||||
|
void load(ESM::ESMReader &esm, const std::string &id);
|
||||||
if (cell.isExterior()) {
|
|
||||||
mExt.push_back(cell);
|
|
||||||
} else {
|
|
||||||
mInt.push_back(cell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator intBegin() const {
|
iterator intBegin() const {
|
||||||
return iterator(mSharedInt.begin());
|
return iterator(mSharedInt.begin());
|
||||||
|
|
|
@ -55,7 +55,7 @@ namespace
|
||||||
|
|
||||||
for (iterator iter (refList.mList.begin()); iter!=refList.mList.end(); ++iter)
|
for (iterator iter (refList.mList.begin()); iter!=refList.mList.end(); ++iter)
|
||||||
{
|
{
|
||||||
if(iter->mData.getCount() > 0 && iter->mData.getBaseNode()){
|
if (iter->mData.getCount() > 0 && iter->mData.getBaseNode()){
|
||||||
if (iter->mData.getHandle()==handle)
|
if (iter->mData.getHandle()==handle)
|
||||||
{
|
{
|
||||||
return &*iter;
|
return &*iter;
|
||||||
|
@ -171,7 +171,8 @@ namespace MWWorld
|
||||||
|
|
||||||
World::World (OEngine::Render::OgreRenderer& renderer,
|
World::World (OEngine::Render::OgreRenderer& renderer,
|
||||||
const Files::Collections& fileCollections,
|
const Files::Collections& fileCollections,
|
||||||
const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame,
|
const std::vector<std::string>& master, const std::vector<std::string>& plugins,
|
||||||
|
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame,
|
||||||
ToUTF8::Utf8Encoder* encoder, std::map<std::string,std::string> fallbackMap, int mActivationDistanceOverride)
|
ToUTF8::Utf8Encoder* encoder, std::map<std::string,std::string> fallbackMap, int mActivationDistanceOverride)
|
||||||
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
|
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
|
||||||
mSky (true), mCells (mStore, mEsm),
|
mSky (true), mCells (mStore, mEsm),
|
||||||
|
@ -184,14 +185,42 @@ namespace MWWorld
|
||||||
|
|
||||||
mWeatherManager = new MWWorld::WeatherManager(mRendering);
|
mWeatherManager = new MWWorld::WeatherManager(mRendering);
|
||||||
|
|
||||||
boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master));
|
int idx = 0;
|
||||||
|
// NOTE: We might need to reserve one more for the running game / save.
|
||||||
|
mEsm.resize(master.size() + plugins.size());
|
||||||
|
for (std::vector<std::string>::size_type i = 0; i < master.size(); i++, idx++)
|
||||||
|
{
|
||||||
|
boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master[i]));
|
||||||
|
|
||||||
std::cout << "Loading ESM " << masterPath.string() << "\n";
|
std::cout << "Loading ESM " << masterPath.string() << "\n";
|
||||||
|
|
||||||
// This parses the ESM file and loads a sample cell
|
// This parses the ESM file
|
||||||
mEsm.setEncoder(encoder);
|
ESM::ESMReader lEsm;
|
||||||
mEsm.open (masterPath.string());
|
lEsm.setEncoder(encoder);
|
||||||
mStore.load (mEsm);
|
lEsm.setIndex(idx);
|
||||||
|
lEsm.setGlobalReaderList(&mEsm);
|
||||||
|
lEsm.open (masterPath.string());
|
||||||
|
mEsm[idx] = lEsm;
|
||||||
|
mStore.load (mEsm[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<std::string>::size_type i = 0; i < plugins.size(); i++, idx++)
|
||||||
|
{
|
||||||
|
boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i]));
|
||||||
|
|
||||||
|
std::cout << "Loading ESP " << pluginPath.string() << "\n";
|
||||||
|
|
||||||
|
// This parses the ESP file
|
||||||
|
ESM::ESMReader lEsm;
|
||||||
|
lEsm.setEncoder(encoder);
|
||||||
|
lEsm.setIndex(idx);
|
||||||
|
lEsm.setGlobalReaderList(&mEsm);
|
||||||
|
lEsm.open (pluginPath.string());
|
||||||
|
mEsm[idx] = lEsm;
|
||||||
|
mStore.load (mEsm[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
mStore.setUp();
|
||||||
|
|
||||||
mPlayer = new MWWorld::Player (mStore.get<ESM::NPC>().find ("player"), *this);
|
mPlayer = new MWWorld::Player (mStore.get<ESM::NPC>().find ("player"), *this);
|
||||||
mRendering->attachCameraTo(mPlayer->getPlayer());
|
mRendering->attachCameraTo(mPlayer->getPlayer());
|
||||||
|
@ -270,7 +299,7 @@ namespace MWWorld
|
||||||
return mStore;
|
return mStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::ESMReader& World::getEsmReader()
|
std::vector<ESM::ESMReader>& World::getEsmReader()
|
||||||
{
|
{
|
||||||
return mEsm;
|
return mEsm;
|
||||||
}
|
}
|
||||||
|
@ -1233,8 +1262,8 @@ namespace MWWorld
|
||||||
std::vector<World::DoorMarker> result;
|
std::vector<World::DoorMarker> result;
|
||||||
|
|
||||||
MWWorld::CellRefList<ESM::Door>& doors = cell->mDoors;
|
MWWorld::CellRefList<ESM::Door>& doors = cell->mDoors;
|
||||||
std::list< MWWorld::LiveCellRef<ESM::Door> >& refList = doors.mList;
|
CellRefList<ESM::Door>::List& refList = doors.mList;
|
||||||
for (std::list< MWWorld::LiveCellRef<ESM::Door> >::iterator it = refList.begin(); it != refList.end(); ++it)
|
for (CellRefList<ESM::Door>::List::iterator it = refList.begin(); it != refList.end(); ++it)
|
||||||
{
|
{
|
||||||
MWWorld::LiveCellRef<ESM::Door>& ref = *it;
|
MWWorld::LiveCellRef<ESM::Door>& ref = *it;
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace MWWorld
|
||||||
|
|
||||||
MWWorld::Scene *mWorldScene;
|
MWWorld::Scene *mWorldScene;
|
||||||
MWWorld::Player *mPlayer;
|
MWWorld::Player *mPlayer;
|
||||||
ESM::ESMReader mEsm;
|
std::vector<ESM::ESMReader> mEsm;
|
||||||
MWWorld::ESMStore mStore;
|
MWWorld::ESMStore mStore;
|
||||||
LocalScripts mLocalScripts;
|
LocalScripts mLocalScripts;
|
||||||
MWWorld::Globals *mGlobalVariables;
|
MWWorld::Globals *mGlobalVariables;
|
||||||
|
@ -113,7 +113,8 @@ namespace MWWorld
|
||||||
|
|
||||||
World (OEngine::Render::OgreRenderer& renderer,
|
World (OEngine::Render::OgreRenderer& renderer,
|
||||||
const Files::Collections& fileCollections,
|
const Files::Collections& fileCollections,
|
||||||
const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame,
|
const std::vector<std::string>& master, const std::vector<std::string>& plugins,
|
||||||
|
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame,
|
||||||
ToUTF8::Utf8Encoder* encoder, std::map<std::string,std::string> fallbackMap, int mActivationDistanceOverride);
|
ToUTF8::Utf8Encoder* encoder, std::map<std::string,std::string> fallbackMap, int mActivationDistanceOverride);
|
||||||
|
|
||||||
virtual ~World();
|
virtual ~World();
|
||||||
|
@ -143,7 +144,7 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual const MWWorld::ESMStore& getStore() const;
|
virtual const MWWorld::ESMStore& getStore() const;
|
||||||
|
|
||||||
virtual ESM::ESMReader& getEsmReader();
|
virtual std::vector<ESM::ESMReader>& getEsmReader();
|
||||||
|
|
||||||
virtual LocalScripts& getLocalScripts();
|
virtual LocalScripts& getLocalScripts();
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,7 @@ struct MasterData
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
|
int index; // Position of the parent file in the global list of loaded files
|
||||||
};
|
};
|
||||||
|
|
||||||
// Data that is only present in save game files
|
// Data that is only present in save game files
|
||||||
|
@ -113,6 +114,10 @@ struct ESM_Context
|
||||||
size_t leftFile;
|
size_t leftFile;
|
||||||
NAME recName, subName;
|
NAME recName, subName;
|
||||||
HEDRstruct header;
|
HEDRstruct header;
|
||||||
|
// When working with multiple esX files, we will generate lists of all files that
|
||||||
|
// actually contribute to a specific cell. Therefore, we need to store the index
|
||||||
|
// of the file belonging to this contest. See CellStore::(list/load)refs for details.
|
||||||
|
int index;
|
||||||
|
|
||||||
// True if subName has been read but not used.
|
// True if subName has been read but not used.
|
||||||
bool subCached;
|
bool subCached;
|
||||||
|
|
|
@ -78,6 +78,17 @@ public:
|
||||||
|
|
||||||
void openRaw(const std::string &file);
|
void openRaw(const std::string &file);
|
||||||
|
|
||||||
|
// This is a quick hack for multiple esm/esp files. Each plugin introduces its own
|
||||||
|
// terrain palette, but ESMReader does not pass a reference to the correct plugin
|
||||||
|
// to the individual load() methods. This hack allows to pass this reference
|
||||||
|
// indirectly to the load() method.
|
||||||
|
int mIdx;
|
||||||
|
void setIndex(const int index) {mIdx = index; mCtx.index = index;}
|
||||||
|
const int getIndex() {return mIdx;}
|
||||||
|
|
||||||
|
void setGlobalReaderList(std::vector<ESMReader> *list) {mGlobalReaderList = list;}
|
||||||
|
std::vector<ESMReader> *getGlobalReaderList() {return mGlobalReaderList;}
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
*
|
*
|
||||||
* Medium-level reading shortcuts
|
* Medium-level reading shortcuts
|
||||||
|
@ -110,6 +121,14 @@ public:
|
||||||
getHT(x);
|
getHT(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename X>
|
||||||
|
void getHNOT(X &x, const char* name, int size)
|
||||||
|
{
|
||||||
|
assert(sizeof(X) == size);
|
||||||
|
if(isNextSub(name))
|
||||||
|
getHT(x);
|
||||||
|
}
|
||||||
|
|
||||||
int64_t getHNLong(const char *name);
|
int64_t getHNLong(const char *name);
|
||||||
|
|
||||||
// Get data of a given type/size, including subrecord header
|
// Get data of a given type/size, including subrecord header
|
||||||
|
@ -251,6 +270,7 @@ private:
|
||||||
|
|
||||||
SaveData mSaveData;
|
SaveData mSaveData;
|
||||||
MasterList mMasters;
|
MasterList mMasters;
|
||||||
|
std::vector<ESMReader> *mGlobalReaderList;
|
||||||
ToUTF8::Utf8Encoder* mEncoder;
|
ToUTF8::Utf8Encoder* mEncoder;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,29 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <list>
|
||||||
|
#include <boost/concept_check.hpp>
|
||||||
|
|
||||||
#include "esmreader.hpp"
|
#include "esmreader.hpp"
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
|
#include <apps/openmw/mwworld/store.hpp>
|
||||||
|
#include <apps/openmw/mwworld/cellstore.hpp>
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// Some overloaded copare operators.
|
||||||
|
bool operator==(const MovedCellRef& ref, int pRefnum)
|
||||||
|
{
|
||||||
|
return (ref.mRefnum == pRefnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const CellRef& ref, int pRefnum)
|
||||||
|
{
|
||||||
|
return (ref.mRefnum == pRefnum);
|
||||||
|
}
|
||||||
|
|
||||||
void CellRef::save(ESMWriter &esm)
|
void CellRef::save(ESMWriter &esm)
|
||||||
{
|
{
|
||||||
esm.writeHNT("FRMR", mRefnum);
|
esm.writeHNT("FRMR", mRefnum);
|
||||||
|
@ -63,10 +79,11 @@ void CellRef::save(ESMWriter &esm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::load(ESMReader &esm)
|
void Cell::load(ESMReader &esm, MWWorld::ESMStore &store)
|
||||||
{
|
{
|
||||||
// Ignore this for now, it might mean we should delete the entire
|
// Ignore this for now, it might mean we should delete the entire
|
||||||
// cell?
|
// cell?
|
||||||
|
// TODO: treat the special case "another plugin moved this ref, but we want to delete it"!
|
||||||
if (esm.isNextSub("DELE")) {
|
if (esm.isNextSub("DELE")) {
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
}
|
}
|
||||||
|
@ -110,8 +127,36 @@ void Cell::load(ESMReader &esm)
|
||||||
esm.getHT(mNAM0);
|
esm.getHT(mNAM0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// preload moved references
|
||||||
|
while (esm.isNextSub("MVRF")) {
|
||||||
|
CellRef ref;
|
||||||
|
MovedCellRef cMRef;
|
||||||
|
getNextMVRF(esm, cMRef);
|
||||||
|
|
||||||
|
MWWorld::Store<ESM::Cell> &cStore = const_cast<MWWorld::Store<ESM::Cell>&>(store.get<ESM::Cell>());
|
||||||
|
ESM::Cell *cellAlt = const_cast<ESM::Cell*>(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1]));
|
||||||
|
|
||||||
|
// Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following
|
||||||
|
// implementation when the oher implementation works as well.
|
||||||
|
getNextRef(esm, ref);
|
||||||
|
std::string lowerCase;
|
||||||
|
|
||||||
|
std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase),
|
||||||
|
(int(*)(int)) std::tolower);
|
||||||
|
|
||||||
|
// Add data required to make reference appear in the correct cell.
|
||||||
|
// We should not need to test for duplicates, as this part of the code is pre-cell merge.
|
||||||
|
mMovedRefs.push_back(cMRef);
|
||||||
|
// But there may be duplicates here!
|
||||||
|
ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum);
|
||||||
|
if (iter == cellAlt->mLeasedRefs.end())
|
||||||
|
cellAlt->mLeasedRefs.push_back(ref);
|
||||||
|
else
|
||||||
|
*iter = ref;
|
||||||
|
}
|
||||||
|
|
||||||
// Save position of the cell references and move on
|
// Save position of the cell references and move on
|
||||||
mContext = esm.getContext();
|
mContextList.push_back(esm.getContext());
|
||||||
esm.skipRecord();
|
esm.skipRecord();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,9 +191,9 @@ void Cell::save(ESMWriter &esm)
|
||||||
esm.writeHNT("NAM0", mNAM0);
|
esm.writeHNT("NAM0", mNAM0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::restore(ESMReader &esm) const
|
void Cell::restore(ESMReader &esm, int iCtx) const
|
||||||
{
|
{
|
||||||
esm.restoreContext(mContext);
|
esm.restoreContext(mContextList[iCtx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Cell::getDescription() const
|
std::string Cell::getDescription() const
|
||||||
|
@ -167,17 +212,61 @@ std::string Cell::getDescription() const
|
||||||
|
|
||||||
bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
||||||
{
|
{
|
||||||
|
// TODO: Try and document reference numbering, I don't think this has been done anywhere else.
|
||||||
if (!esm.hasMoreSubs())
|
if (!esm.hasMoreSubs())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// NOTE: We should not need this check. It is a safety check until we have checked
|
||||||
|
// more plugins, and how they treat these moved references.
|
||||||
|
if (esm.isNextSub("MVRF")) {
|
||||||
|
esm.skipRecord(); // skip MVRF
|
||||||
|
esm.skipRecord(); // skip CNDT
|
||||||
|
// That should be it, I haven't seen any other fields yet.
|
||||||
|
}
|
||||||
|
|
||||||
esm.getHNT(ref.mRefnum, "FRMR");
|
esm.getHNT(ref.mRefnum, "FRMR");
|
||||||
ref.mRefID = esm.getHNString("NAME");
|
ref.mRefID = esm.getHNString("NAME");
|
||||||
|
|
||||||
|
// Identify references belonging to a parent file and adapt the ID accordingly.
|
||||||
|
int local = (ref.mRefnum & 0xff000000) >> 24;
|
||||||
|
size_t global = esm.getIndex() + 1;
|
||||||
|
if (local)
|
||||||
|
{
|
||||||
|
// If the most significant 8 bits are used, then this reference already exists.
|
||||||
|
// In this case, do not spawn a new reference, but overwrite the old one.
|
||||||
|
ref.mRefnum &= 0x00ffffff; // delete old plugin ID
|
||||||
|
const ESM::ESMReader::MasterList &masters = esm.getMasters();
|
||||||
|
global = masters[local-1].index + 1;
|
||||||
|
ref.mRefnum |= global << 24; // insert global plugin ID
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This is an addition by the present plugin. Set the corresponding plugin index.
|
||||||
|
ref.mRefnum |= global << 24; // insert global plugin ID
|
||||||
|
}
|
||||||
|
|
||||||
// getHNOT will not change the existing value if the subrecord is
|
// getHNOT will not change the existing value if the subrecord is
|
||||||
// missing
|
// missing
|
||||||
ref.mScale = 1.0;
|
ref.mScale = 1.0;
|
||||||
esm.getHNOT(ref.mScale, "XSCL");
|
esm.getHNOT(ref.mScale, "XSCL");
|
||||||
|
|
||||||
|
// TODO: support loading references from saves, there are tons of keys not recognized yet.
|
||||||
|
// The following is just an incomplete list.
|
||||||
|
if (esm.isNextSub("ACTN"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("STPR"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("ACDT"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("ACSC"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("ACSL"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("CHRD"))
|
||||||
|
esm.skipHSub();
|
||||||
|
else if (esm.isNextSub("CRED")) // ???
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
ref.mOwner = esm.getHNOString("ANAM");
|
ref.mOwner = esm.getHNOString("ANAM");
|
||||||
ref.mGlob = esm.getHNOString("BNAM");
|
ref.mGlob = esm.getHNOString("BNAM");
|
||||||
ref.mSoul = esm.getHNOString("XSOL");
|
ref.mSoul = esm.getHNOString("XSOL");
|
||||||
|
@ -215,11 +304,14 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
||||||
esm.getHNOT(ref.mUnam, "UNAM");
|
esm.getHNOT(ref.mUnam, "UNAM");
|
||||||
esm.getHNOT(ref.mFltv, "FLTV");
|
esm.getHNOT(ref.mFltv, "FLTV");
|
||||||
|
|
||||||
esm.getHNT(ref.mPos, "DATA", 24);
|
esm.getHNOT(ref.mPos, "DATA", 24);
|
||||||
|
|
||||||
// Number of references in the cell? Maximum once in each cell,
|
// Number of references in the cell? Maximum once in each cell,
|
||||||
// but not always at the beginning, and not always right. In other
|
// but not always at the beginning, and not always right. In other
|
||||||
// words, completely useless.
|
// words, completely useless.
|
||||||
|
// Update: Well, maybe not completely useless. This might actually be
|
||||||
|
// number_of_references + number_of_references_moved_here_Across_boundaries,
|
||||||
|
// and could be helpful for collecting these weird moved references.
|
||||||
ref.mNam0 = 0;
|
ref.mNam0 = 0;
|
||||||
if (esm.isNextSub("NAM0"))
|
if (esm.isNextSub("NAM0"))
|
||||||
{
|
{
|
||||||
|
@ -227,6 +319,29 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
||||||
//esm.getHNOT(NAM0, "NAM0");
|
//esm.getHNOT(NAM0, "NAM0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (esm.isNextSub("DELE")) {
|
||||||
|
esm.skipHSub();
|
||||||
|
ref.mDeleted = 2; // Deleted, will not respawn.
|
||||||
|
// TODO: find out when references do respawn.
|
||||||
|
} else
|
||||||
|
ref.mDeleted = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref)
|
||||||
|
{
|
||||||
|
esm.getHT(mref.mRefnum);
|
||||||
|
esm.getHNOT(mref.mTarget, "CNDT");
|
||||||
|
|
||||||
|
// Identify references belonging to a parent file and adapt the ID accordingly.
|
||||||
|
int local = (mref.mRefnum & 0xff000000) >> 24;
|
||||||
|
size_t global = esm.getIndex() + 1;
|
||||||
|
mref.mRefnum &= 0x00ffffff; // delete old plugin ID
|
||||||
|
const ESM::ESMReader::MasterList &masters = esm.getMasters();
|
||||||
|
global = masters[local-1].index + 1;
|
||||||
|
mref.mRefnum |= global << 24; // insert global plugin ID
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,18 @@
|
||||||
#define OPENMW_ESM_CELL_H
|
#define OPENMW_ESM_CELL_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "esmcommon.hpp"
|
#include "esmcommon.hpp"
|
||||||
#include "defs.hpp"
|
#include "defs.hpp"
|
||||||
|
#include "apps/openmw/mwbase/world.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
namespace MWWorld {
|
||||||
|
class ESMStore;
|
||||||
|
class CellStore;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
@ -70,6 +79,9 @@ public:
|
||||||
// No idea - occurs ONCE in Morrowind.esm, for an activator
|
// No idea - occurs ONCE in Morrowind.esm, for an activator
|
||||||
char mUnam;
|
char mUnam;
|
||||||
|
|
||||||
|
// Track deleted references. 0 - not deleted, 1 - deleted, but respawns, 2 - deleted and does not respawn.
|
||||||
|
int mDeleted;
|
||||||
|
|
||||||
// Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza
|
// Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza
|
||||||
// Brindisi Dorom", where it has the value 100. Also only for
|
// Brindisi Dorom", where it has the value 100. Also only for
|
||||||
// activators.
|
// activators.
|
||||||
|
@ -82,6 +94,31 @@ public:
|
||||||
void save(ESMWriter &esm);
|
void save(ESMWriter &esm);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Moved cell reference tracking object. This mainly stores the target cell
|
||||||
|
of the reference, so we can easily know where it has been moved when another
|
||||||
|
plugin tries to move it independently.
|
||||||
|
Unfortunately, we need to implement this here.
|
||||||
|
*/
|
||||||
|
class MovedCellRef
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int mRefnum;
|
||||||
|
|
||||||
|
// Target cell (if exterior)
|
||||||
|
int mTarget[2];
|
||||||
|
|
||||||
|
// TODO: Support moving references between exterior and interior cells!
|
||||||
|
// This may happen in saves, when an NPC follows the player. Tribunal
|
||||||
|
// introduces a henchman (which no one uses), so we may need this as well.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Overloaded copare operator used to search inside a list of cell refs.
|
||||||
|
bool operator==(const MovedCellRef& ref, int pRefnum);
|
||||||
|
bool operator==(const CellRef& ref, int pRefnum);
|
||||||
|
|
||||||
|
typedef std::list<MovedCellRef> MovedCellRefTracker;
|
||||||
|
typedef std::list<CellRef> CellRefTracker;
|
||||||
|
|
||||||
/* Cells hold data about objects, creatures, statics (rocks, walls,
|
/* Cells hold data about objects, creatures, statics (rocks, walls,
|
||||||
buildings) and landscape (for exterior cells). Cells frequently
|
buildings) and landscape (for exterior cells). Cells frequently
|
||||||
also has other associated LAND and PGRD records. Combined, all this
|
also has other associated LAND and PGRD records. Combined, all this
|
||||||
|
@ -120,7 +157,7 @@ struct Cell
|
||||||
// Optional region name for exterior and quasi-exterior cells.
|
// Optional region name for exterior and quasi-exterior cells.
|
||||||
std::string mRegion;
|
std::string mRegion;
|
||||||
|
|
||||||
ESM_Context mContext; // File position
|
std::vector<ESM_Context> mContextList; // File position; multiple positions for multiple plugin support
|
||||||
DATAstruct mData;
|
DATAstruct mData;
|
||||||
AMBIstruct mAmbi;
|
AMBIstruct mAmbi;
|
||||||
float mWater; // Water level
|
float mWater; // Water level
|
||||||
|
@ -128,7 +165,16 @@ struct Cell
|
||||||
int mMapColor;
|
int mMapColor;
|
||||||
int mNAM0;
|
int mNAM0;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
// References "leased" from another cell (i.e. a different cell
|
||||||
|
// introduced this ref, and it has been moved here by a plugin)
|
||||||
|
CellRefTracker mLeasedRefs;
|
||||||
|
MovedCellRefTracker mMovedRefs;
|
||||||
|
|
||||||
|
void load(ESMReader &esm, MWWorld::ESMStore &store);
|
||||||
|
|
||||||
|
// This method is left in for compatibility with esmtool. Parsing moved references currently requires
|
||||||
|
// passing ESMStore, bit it does not know about this parameter, so we do it this way.
|
||||||
|
void load(ESMReader &esm) {};
|
||||||
void save(ESMWriter &esm);
|
void save(ESMWriter &esm);
|
||||||
|
|
||||||
bool isExterior() const
|
bool isExterior() const
|
||||||
|
@ -151,7 +197,7 @@ struct Cell
|
||||||
// somewhere other than the file system, you need to pre-open the
|
// somewhere other than the file system, you need to pre-open the
|
||||||
// ESMReader, and the filename must match the stored filename
|
// ESMReader, and the filename must match the stored filename
|
||||||
// exactly.
|
// exactly.
|
||||||
void restore(ESMReader &esm) const;
|
void restore(ESMReader &esm, int iCtx) const;
|
||||||
|
|
||||||
std::string getDescription() const;
|
std::string getDescription() const;
|
||||||
///< Return a short string describing the cell (mostly used for debugging/logging purpose)
|
///< Return a short string describing the cell (mostly used for debugging/logging purpose)
|
||||||
|
@ -163,6 +209,11 @@ struct Cell
|
||||||
reuse one memory location without blanking it between calls.
|
reuse one memory location without blanking it between calls.
|
||||||
*/
|
*/
|
||||||
static bool getNextRef(ESMReader &esm, CellRef &ref);
|
static bool getNextRef(ESMReader &esm, CellRef &ref);
|
||||||
|
|
||||||
|
/* This fetches an MVRF record, which is used to track moved references.
|
||||||
|
* Since they are comparably rare, we use a separate method for this.
|
||||||
|
*/
|
||||||
|
static bool getNextMVRF(ESMReader &esm, MovedCellRef &mref);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -81,6 +81,7 @@ Land::~Land()
|
||||||
void Land::load(ESMReader &esm)
|
void Land::load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
mEsm = &esm;
|
mEsm = &esm;
|
||||||
|
mPlugin = mEsm->getIndex();
|
||||||
|
|
||||||
// Get the grid location
|
// Get the grid location
|
||||||
esm.getSubNameIs("INTV");
|
esm.getSubNameIs("INTV");
|
||||||
|
|
|
@ -23,6 +23,7 @@ struct Land
|
||||||
int mFlags; // Only first four bits seem to be used, don't know what
|
int mFlags; // Only first four bits seem to be used, don't know what
|
||||||
// they mean.
|
// they mean.
|
||||||
int mX, mY; // Map coordinates.
|
int mX, mY; // Map coordinates.
|
||||||
|
int mPlugin; // Plugin index, used to reference the correct material palette.
|
||||||
|
|
||||||
// File context. This allows the ESM reader to be 'reset' to this
|
// File context. This allows the ESM reader to be 'reset' to this
|
||||||
// location later when we are ready to load the full data set.
|
// location later when we are ready to load the full data set.
|
||||||
|
|
97
extern/shiny/Main/Factory.cpp
vendored
97
extern/shiny/Main/Factory.cpp
vendored
|
@ -50,7 +50,7 @@ namespace sh
|
||||||
{
|
{
|
||||||
assert(mCurrentLanguage != Language_None);
|
assert(mCurrentLanguage != Language_None);
|
||||||
|
|
||||||
bool anyShaderDirty = false;
|
bool removeBinaryCache = false;
|
||||||
|
|
||||||
if (boost::filesystem::exists (mPlatform->getCacheFolder () + "/lastModified.txt"))
|
if (boost::filesystem::exists (mPlatform->getCacheFolder () + "/lastModified.txt"))
|
||||||
{
|
{
|
||||||
|
@ -182,57 +182,33 @@ namespace sh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string sourceFile = mPlatform->getBasePath() + "/" + it->second->findChild("source")->getValue();
|
std::string sourceAbsolute = mPlatform->getBasePath() + "/" + it->second->findChild("source")->getValue();
|
||||||
|
std::string sourceRelative = it->second->findChild("source")->getValue();
|
||||||
|
|
||||||
ShaderSet newSet (it->second->findChild("type")->getValue(), cg_profile, hlsl_profile,
|
ShaderSet newSet (it->second->findChild("type")->getValue(), cg_profile, hlsl_profile,
|
||||||
sourceFile,
|
sourceAbsolute,
|
||||||
mPlatform->getBasePath(),
|
mPlatform->getBasePath(),
|
||||||
it->first,
|
it->first,
|
||||||
&mGlobalSettings);
|
&mGlobalSettings);
|
||||||
|
|
||||||
int lastModified = boost::filesystem::last_write_time (boost::filesystem::path(sourceFile));
|
int lastModified = boost::filesystem::last_write_time (boost::filesystem::path(sourceAbsolute));
|
||||||
mShadersLastModifiedNew[sourceFile] = lastModified;
|
mShadersLastModifiedNew[sourceRelative] = lastModified;
|
||||||
if (mShadersLastModified.find(sourceFile) != mShadersLastModified.end()
|
if (mShadersLastModified.find(sourceRelative) != mShadersLastModified.end())
|
||||||
&& mShadersLastModified[sourceFile] != lastModified)
|
|
||||||
{
|
{
|
||||||
// delete any outdated shaders based on this shader set.
|
if (mShadersLastModified[sourceRelative] != lastModified)
|
||||||
if ( boost::filesystem::exists(mPlatform->getCacheFolder())
|
|
||||||
&& boost::filesystem::is_directory(mPlatform->getCacheFolder()))
|
|
||||||
{
|
{
|
||||||
boost::filesystem::directory_iterator end_iter;
|
// delete any outdated shaders based on this shader set
|
||||||
for( boost::filesystem::directory_iterator dir_iter(mPlatform->getCacheFolder()) ; dir_iter != end_iter ; ++dir_iter)
|
removeCache (it->first);
|
||||||
{
|
// remove the whole binary cache (removing only the individual shaders does not seem to be possible at this point with OGRE)
|
||||||
if (boost::filesystem::is_regular_file(dir_iter->status()) )
|
removeBinaryCache = true;
|
||||||
{
|
|
||||||
boost::filesystem::path file = dir_iter->path();
|
|
||||||
|
|
||||||
std::string pathname = file.filename().string();
|
|
||||||
|
|
||||||
// get first part of filename, e.g. main_fragment_546457654 -> main_fragment
|
|
||||||
// there is probably a better method for this...
|
|
||||||
std::vector<std::string> tokens;
|
|
||||||
boost::split(tokens, pathname, boost::is_any_of("_"));
|
|
||||||
tokens.erase(--tokens.end());
|
|
||||||
std::string shaderName;
|
|
||||||
for (std::vector<std::string>::const_iterator vector_iter = tokens.begin(); vector_iter != tokens.end();)
|
|
||||||
{
|
|
||||||
shaderName += *(vector_iter++);
|
|
||||||
if (vector_iter != tokens.end())
|
|
||||||
shaderName += "_";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shaderName == it->first)
|
|
||||||
{
|
|
||||||
boost::filesystem::remove(file);
|
|
||||||
std::cout << "Removing outdated file: " << file << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if we get here, this is either the first run or a new shader file was added
|
||||||
|
// in both cases we can safely delete
|
||||||
|
removeCache (it->first);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
anyShaderDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mShaderSets.insert(std::make_pair(it->first, newSet));
|
mShaderSets.insert(std::make_pair(it->first, newSet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,7 +302,7 @@ namespace sh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPlatform->supportsShaderSerialization () && mReadMicrocodeCache && !anyShaderDirty)
|
if (mPlatform->supportsShaderSerialization () && mReadMicrocodeCache && !removeBinaryCache)
|
||||||
{
|
{
|
||||||
std::string file = mPlatform->getCacheFolder () + "/shShaderCache.txt";
|
std::string file = mPlatform->getCacheFolder () + "/shShaderCache.txt";
|
||||||
if (boost::filesystem::exists(file))
|
if (boost::filesystem::exists(file))
|
||||||
|
@ -613,4 +589,41 @@ namespace sh
|
||||||
assert(m);
|
assert(m);
|
||||||
m->createForConfiguration (configuration, 0);
|
m->createForConfiguration (configuration, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Factory::removeCache(const std::string& pattern)
|
||||||
|
{
|
||||||
|
if ( boost::filesystem::exists(mPlatform->getCacheFolder())
|
||||||
|
&& boost::filesystem::is_directory(mPlatform->getCacheFolder()))
|
||||||
|
{
|
||||||
|
boost::filesystem::directory_iterator end_iter;
|
||||||
|
for( boost::filesystem::directory_iterator dir_iter(mPlatform->getCacheFolder()) ; dir_iter != end_iter ; ++dir_iter)
|
||||||
|
{
|
||||||
|
if (boost::filesystem::is_regular_file(dir_iter->status()) )
|
||||||
|
{
|
||||||
|
boost::filesystem::path file = dir_iter->path();
|
||||||
|
|
||||||
|
std::string pathname = file.filename().string();
|
||||||
|
|
||||||
|
// get first part of filename, e.g. main_fragment_546457654 -> main_fragment
|
||||||
|
// there is probably a better method for this...
|
||||||
|
std::vector<std::string> tokens;
|
||||||
|
boost::split(tokens, pathname, boost::is_any_of("_"));
|
||||||
|
tokens.erase(--tokens.end());
|
||||||
|
std::string shaderName;
|
||||||
|
for (std::vector<std::string>::const_iterator vector_iter = tokens.begin(); vector_iter != tokens.end();)
|
||||||
|
{
|
||||||
|
shaderName += *(vector_iter++);
|
||||||
|
if (vector_iter != tokens.end())
|
||||||
|
shaderName += "_";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shaderName == pattern)
|
||||||
|
{
|
||||||
|
boost::filesystem::remove(file);
|
||||||
|
std::cout << "Removing outdated shader: " << file << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
2
extern/shiny/Main/Factory.hpp
vendored
2
extern/shiny/Main/Factory.hpp
vendored
|
@ -202,6 +202,8 @@ namespace sh
|
||||||
|
|
||||||
MaterialInstance* findInstance (const std::string& name);
|
MaterialInstance* findInstance (const std::string& name);
|
||||||
MaterialInstance* searchInstance (const std::string& name);
|
MaterialInstance* searchInstance (const std::string& name);
|
||||||
|
|
||||||
|
void removeCache (const std::string& pattern);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue