mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-10 21:40:15 +00:00
Merge branch 'esm_readers_cache' into 'master'
Limit the number of simultaneously open not actively used content files (#6756) Closes #6756 See merge request OpenMW/openmw!1966
This commit is contained in:
commit
58fd560ce9
@ -15,6 +15,7 @@
|
||||
#include <components/version/version.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
@ -156,7 +157,7 @@ namespace
|
||||
Settings::Manager settings;
|
||||
settings.load(config);
|
||||
|
||||
std::vector<ESM::ESMReader> readers(contentFiles.size());
|
||||
ESM::ReadersCache readers;
|
||||
EsmLoader::Query query;
|
||||
query.mLoadActivators = true;
|
||||
query.mLoadCells = true;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <components/version/version.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
#include <osg/Vec3f>
|
||||
|
||||
@ -174,7 +175,7 @@ namespace NavMeshTool
|
||||
|
||||
DetourNavigator::NavMeshDb db(dbPath, maxDbFileSize);
|
||||
|
||||
std::vector<ESM::ESMReader> readers(contentFiles.size());
|
||||
ESM::ReadersCache readers;
|
||||
EsmLoader::Query query;
|
||||
query.mLoadActivators = true;
|
||||
query.mLoadCells = true;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/debug/debugging.hpp>
|
||||
#include <components/navmeshtool/protocol.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
#include <LinearMath/btVector3.h>
|
||||
|
||||
@ -68,17 +69,17 @@ namespace NavMeshTool
|
||||
}
|
||||
|
||||
std::vector<CellRef> loadCellRefs(const ESM::Cell& cell, const EsmLoader::EsmData& esmData,
|
||||
std::vector<ESM::ESMReader>& readers)
|
||||
ESM::ReadersCache& readers)
|
||||
{
|
||||
std::vector<EsmLoader::Record<CellRef>> cellRefs;
|
||||
|
||||
for (std::size_t i = 0; i < cell.mContextList.size(); i++)
|
||||
{
|
||||
ESM::ESMReader& reader = readers[static_cast<std::size_t>(cell.mContextList[i].index)];
|
||||
cell.restore(reader, static_cast<int>(i));
|
||||
ESM::ReadersCache::BusyItem reader = readers.get(static_cast<std::size_t>(cell.mContextList[i].index));
|
||||
cell.restore(*reader, static_cast<int>(i));
|
||||
ESM::CellRef cellRef;
|
||||
bool deleted = false;
|
||||
while (ESM::Cell::getNextRef(reader, cellRef, deleted))
|
||||
while (ESM::Cell::getNextRef(*reader, cellRef, deleted))
|
||||
{
|
||||
Misc::StringUtils::lowerCaseInPlace(cellRef.mRefID);
|
||||
const ESM::RecNameInts type = getType(esmData, cellRef.mRefID);
|
||||
@ -101,7 +102,7 @@ namespace NavMeshTool
|
||||
|
||||
template <class F>
|
||||
void forEachObject(const ESM::Cell& cell, const EsmLoader::EsmData& esmData, const VFS::Manager& vfs,
|
||||
Resource::BulletShapeManager& bulletShapeManager, std::vector<ESM::ESMReader>& readers,
|
||||
Resource::BulletShapeManager& bulletShapeManager, ESM::ReadersCache& readers,
|
||||
F&& f)
|
||||
{
|
||||
std::vector<CellRef> cellRefs = loadCellRefs(cell, esmData, readers);
|
||||
@ -233,7 +234,7 @@ namespace NavMeshTool
|
||||
mAabb.m_max = btVector3(0, 0, 0);
|
||||
}
|
||||
|
||||
WorldspaceData gatherWorldspaceData(const DetourNavigator::Settings& settings, std::vector<ESM::ESMReader>& readers,
|
||||
WorldspaceData gatherWorldspaceData(const DetourNavigator::Settings& settings, ESM::ReadersCache& readers,
|
||||
const VFS::Manager& vfs, Resource::BulletShapeManager& bulletShapeManager, const EsmLoader::EsmData& esmData,
|
||||
bool processInteriorCells, bool writeBinaryLog)
|
||||
{
|
||||
|
@ -18,6 +18,7 @@
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ReadersCache;
|
||||
}
|
||||
|
||||
namespace VFS
|
||||
@ -89,7 +90,7 @@ namespace NavMeshTool
|
||||
std::vector<std::vector<float>> mHeightfields;
|
||||
};
|
||||
|
||||
WorldspaceData gatherWorldspaceData(const DetourNavigator::Settings& settings, std::vector<ESM::ESMReader>& readers,
|
||||
WorldspaceData gatherWorldspaceData(const DetourNavigator::Settings& settings, ESM::ReadersCache& readers,
|
||||
const VFS::Manager& vfs, Resource::BulletShapeManager& bulletShapeManager, const EsmLoader::EsmData& esmData,
|
||||
bool processInteriorCells, bool writeBinaryLog);
|
||||
}
|
||||
|
@ -158,8 +158,6 @@ namespace MWBase
|
||||
|
||||
virtual const MWWorld::ESMStore& getStore() const = 0;
|
||||
|
||||
virtual std::vector<ESM::ESMReader>& getEsmReader() = 0;
|
||||
|
||||
virtual MWWorld::LocalScripts& getLocalScripts() = 0;
|
||||
|
||||
virtual bool hasCellChanged() const = 0;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <components/sceneutil/nodecallback.hpp>
|
||||
#include <components/terrain/quadtreenode.hpp>
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
#include "../mwworld/groundcoverstore.hpp"
|
||||
|
||||
@ -176,7 +177,7 @@ namespace MWRender
|
||||
osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f));
|
||||
osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f));
|
||||
DensityCalculator calculator(mDensity);
|
||||
std::vector<ESM::ESMReader> esm;
|
||||
ESM::ReadersCache readers;
|
||||
osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f));
|
||||
for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX)
|
||||
{
|
||||
@ -190,14 +191,13 @@ namespace MWRender
|
||||
std::map<ESM::RefNum, ESM::CellRef> refs;
|
||||
for (size_t i=0; i<cell.mContextList.size(); ++i)
|
||||
{
|
||||
unsigned int index = cell.mContextList[i].index;
|
||||
if (esm.size() <= index)
|
||||
esm.resize(index+1);
|
||||
cell.restore(esm[index], i);
|
||||
const std::size_t index = static_cast<std::size_t>(cell.mContextList[i].index);
|
||||
const ESM::ReadersCache::BusyItem reader = readers.get(index);
|
||||
cell.restore(*reader, i);
|
||||
ESM::CellRef ref;
|
||||
ref.mRefNum.unset();
|
||||
bool deleted = false;
|
||||
while(cell.getNextRef(esm[index], ref, deleted))
|
||||
while (cell.getNextRef(*reader, ref, deleted))
|
||||
{
|
||||
if (!deleted && refs.find(ref.mRefNum) == refs.end() && !calculator.isInstanceEnabled()) deleted = true;
|
||||
if (!deleted && !isInChunkBorders(ref, minBound, maxBound)) deleted = true;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <components/sceneutil/clone.hpp>
|
||||
#include <components/sceneutil/util.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
#include <osgParticle/ParticleProcessor>
|
||||
#include <osgParticle/ParticleSystemUpdater>
|
||||
@ -412,7 +413,7 @@ namespace MWRender
|
||||
osg::Vec3f relativeViewPoint = viewPoint - worldCenter;
|
||||
|
||||
std::map<ESM::RefNum, ESM::CellRef> refs;
|
||||
std::vector<ESM::ESMReader> esm;
|
||||
ESM::ReadersCache readers;
|
||||
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX)
|
||||
@ -425,17 +426,16 @@ namespace MWRender
|
||||
{
|
||||
try
|
||||
{
|
||||
unsigned int index = cell->mContextList[i].index;
|
||||
if (esm.size()<=index)
|
||||
esm.resize(index+1);
|
||||
cell->restore(esm[index], i);
|
||||
const std::size_t index = static_cast<std::size_t>(cell->mContextList[i].index);
|
||||
const ESM::ReadersCache::BusyItem reader = readers.get(index);
|
||||
cell->restore(*reader, i);
|
||||
ESM::CellRef ref;
|
||||
ref.mRefNum.unset();
|
||||
ESM::MovedCellRef cMRef;
|
||||
cMRef.mRefNum.mIndex = 0;
|
||||
bool deleted = false;
|
||||
bool moved = false;
|
||||
while (ESM::Cell::getNextRef(esm[index], ref, deleted, cMRef, moved, ESM::Cell::GetNextRefMode::LoadOnlyNotMoved))
|
||||
while (ESM::Cell::getNextRef(*reader, ref, deleted, cMRef, moved, ESM::Cell::GetNextRefMode::LoadOnlyNotMoved))
|
||||
{
|
||||
if (moved)
|
||||
continue;
|
||||
|
@ -68,9 +68,7 @@ MWWorld::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell)
|
||||
std::map<std::string, CellStore>::iterator result = mInteriors.find (lowerName);
|
||||
|
||||
if (result==mInteriors.end())
|
||||
{
|
||||
result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell, mStore, mReader))).first;
|
||||
}
|
||||
result = mInteriors.emplace(std::move(lowerName), CellStore(cell, mStore, mReaders)).first;
|
||||
|
||||
return &result->second;
|
||||
}
|
||||
@ -80,11 +78,8 @@ MWWorld::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell)
|
||||
mExteriors.find (std::make_pair (cell->getGridX(), cell->getGridY()));
|
||||
|
||||
if (result==mExteriors.end())
|
||||
{
|
||||
result = mExteriors.insert (std::make_pair (
|
||||
std::make_pair (cell->getGridX(), cell->getGridY()), CellStore (cell, mStore, mReader))).first;
|
||||
|
||||
}
|
||||
result = mExteriors.emplace(std::make_pair(cell->getGridX(), cell->getGridY()),
|
||||
CellStore(cell, mStore, mReaders)).first;
|
||||
|
||||
return &result->second;
|
||||
}
|
||||
@ -130,9 +125,10 @@ void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, CellStore& cell) const
|
||||
writer.endRecord (ESM::REC_CSTA);
|
||||
}
|
||||
|
||||
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader)
|
||||
: mStore (store), mReader (reader),
|
||||
mIdCacheIndex (0)
|
||||
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, ESM::ReadersCache& readers)
|
||||
: mStore(store)
|
||||
, mReaders(readers)
|
||||
, mIdCacheIndex(0)
|
||||
{
|
||||
int cacheSize = std::clamp(Settings::Manager::getInt("pointers cache size", "Cells"), 40, 1000);
|
||||
mIdCache = IdCache(cacheSize, std::pair<std::string, CellStore *> ("", (CellStore*)nullptr));
|
||||
@ -165,8 +161,7 @@ MWWorld::CellStore *MWWorld::Cells::getExterior (int x, int y)
|
||||
cell = MWBase::Environment::get().getWorld()->createRecord (record);
|
||||
}
|
||||
|
||||
result = mExteriors.insert (std::make_pair (
|
||||
std::make_pair (x, y), CellStore (cell, mStore, mReader))).first;
|
||||
result = mExteriors.emplace(std::make_pair(x, y), CellStore(cell, mStore, mReaders)).first;
|
||||
}
|
||||
|
||||
if (result->second.getState()!=CellStore::State_Loaded)
|
||||
@ -186,7 +181,7 @@ MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name)
|
||||
{
|
||||
const ESM::Cell *cell = mStore.get<ESM::Cell>().find(lowerName);
|
||||
|
||||
result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell, mStore, mReader))).first;
|
||||
result = mInteriors.emplace(std::move(lowerName), CellStore(cell, mStore, mReaders)).first;
|
||||
}
|
||||
|
||||
if (result->second.getState()!=CellStore::State_Loaded)
|
||||
|
@ -11,6 +11,7 @@ namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
class ReadersCache;
|
||||
struct CellId;
|
||||
struct Cell;
|
||||
struct RefNum;
|
||||
@ -30,7 +31,7 @@ namespace MWWorld
|
||||
{
|
||||
typedef std::vector<std::pair<std::string, CellStore *> > IdCache;
|
||||
const MWWorld::ESMStore& mStore;
|
||||
std::vector<ESM::ESMReader>& mReader;
|
||||
ESM::ReadersCache& mReaders;
|
||||
mutable std::map<std::string, CellStore> mInteriors;
|
||||
mutable std::map<std::pair<int, int>, CellStore> mExteriors;
|
||||
IdCache mIdCache;
|
||||
@ -51,7 +52,7 @@ namespace MWWorld
|
||||
|
||||
void clear();
|
||||
|
||||
Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader);
|
||||
explicit Cells(const MWWorld::ESMStore& store, ESM::ReadersCache& reader);
|
||||
|
||||
CellStore *getExterior (int x, int y);
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <components/esm3/fogstate.hpp>
|
||||
#include <components/esm3/creaturelevliststate.hpp>
|
||||
#include <components/esm3/doorstate.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
@ -387,8 +388,14 @@ namespace MWWorld
|
||||
return false;
|
||||
}
|
||||
|
||||
CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector<ESM::ESMReader>& readerList)
|
||||
: mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0), mRechargingItemsUpToDate(false)
|
||||
CellStore::CellStore(const ESM::Cell* cell, const MWWorld::ESMStore& esmStore, ESM::ReadersCache& readers)
|
||||
: mStore(esmStore)
|
||||
, mReaders(readers)
|
||||
, mCell(cell)
|
||||
, mState(State_Unloaded)
|
||||
, mHasState(false)
|
||||
, mLastRespawn(0, 0)
|
||||
, mRechargingItemsUpToDate(false)
|
||||
{
|
||||
mWaterLevel = cell->mWater;
|
||||
}
|
||||
@ -545,8 +552,6 @@ namespace MWWorld
|
||||
|
||||
void CellStore::listRefs()
|
||||
{
|
||||
std::vector<ESM::ESMReader>& esm = mReader;
|
||||
|
||||
assert (mCell);
|
||||
|
||||
if (mCell->mContextList.empty())
|
||||
@ -558,8 +563,9 @@ namespace MWWorld
|
||||
try
|
||||
{
|
||||
// Reopen the ESM reader and seek to the right position.
|
||||
int index = mCell->mContextList[i].index;
|
||||
mCell->restore (esm[index], i);
|
||||
const std::size_t index = static_cast<std::size_t>(mCell->mContextList[i].index);
|
||||
const ESM::ReadersCache::BusyItem reader = mReaders.get(index);
|
||||
mCell->restore(*reader, i);
|
||||
|
||||
ESM::CellRef ref;
|
||||
|
||||
@ -568,7 +574,7 @@ namespace MWWorld
|
||||
cMRef.mRefNum.mIndex = 0;
|
||||
bool deleted = false;
|
||||
bool moved = false;
|
||||
while (ESM::Cell::getNextRef(esm[index], ref, deleted, cMRef, moved, ESM::Cell::GetNextRefMode::LoadOnlyNotMoved))
|
||||
while (ESM::Cell::getNextRef(*reader, ref, deleted, cMRef, moved, ESM::Cell::GetNextRefMode::LoadOnlyNotMoved))
|
||||
{
|
||||
if (deleted || moved)
|
||||
continue;
|
||||
@ -602,8 +608,6 @@ namespace MWWorld
|
||||
|
||||
void CellStore::loadRefs()
|
||||
{
|
||||
std::vector<ESM::ESMReader>& esm = mReader;
|
||||
|
||||
assert (mCell);
|
||||
|
||||
if (mCell->mContextList.empty())
|
||||
@ -617,8 +621,9 @@ namespace MWWorld
|
||||
try
|
||||
{
|
||||
// Reopen the ESM reader and seek to the right position.
|
||||
int index = mCell->mContextList[i].index;
|
||||
mCell->restore (esm[index], i);
|
||||
const std::size_t index = static_cast<std::size_t>(mCell->mContextList[i].index);
|
||||
const ESM::ReadersCache::BusyItem reader = mReaders.get(index);
|
||||
mCell->restore(*reader, i);
|
||||
|
||||
ESM::CellRef ref;
|
||||
ref.mRefNum.unset();
|
||||
@ -628,7 +633,7 @@ namespace MWWorld
|
||||
cMRef.mRefNum.mIndex = 0;
|
||||
bool deleted = false;
|
||||
bool moved = false;
|
||||
while (ESM::Cell::getNextRef(esm[index], ref, deleted, cMRef, moved, ESM::Cell::GetNextRefMode::LoadOnlyNotMoved))
|
||||
while (ESM::Cell::getNextRef(*reader, ref, deleted, cMRef, moved, ESM::Cell::GetNextRefMode::LoadOnlyNotMoved))
|
||||
{
|
||||
if (moved)
|
||||
continue;
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ReadersCache;
|
||||
struct Cell;
|
||||
struct CellState;
|
||||
struct CellId;
|
||||
@ -61,7 +62,7 @@ namespace MWWorld
|
||||
private:
|
||||
|
||||
const MWWorld::ESMStore& mStore;
|
||||
std::vector<ESM::ESMReader>& mReader;
|
||||
ESM::ReadersCache& mReaders;
|
||||
|
||||
// Even though fog actually belongs to the player and not cells,
|
||||
// it makes sense to store it here since we need it once for each cell.
|
||||
@ -211,9 +212,7 @@ namespace MWWorld
|
||||
}
|
||||
|
||||
/// @param readerList The readers to use for loading of the cell on-demand.
|
||||
CellStore (const ESM::Cell *cell_,
|
||||
const MWWorld::ESMStore& store,
|
||||
std::vector<ESM::ESMReader>& readerList);
|
||||
CellStore(const ESM::Cell* cell, const MWWorld::ESMStore& store, ESM::ReadersCache& readers);
|
||||
|
||||
const ESM::Cell *getCell() const;
|
||||
|
||||
|
@ -2,13 +2,13 @@
|
||||
#include "esmstore.hpp"
|
||||
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,
|
||||
ToUTF8::Utf8Encoder* encoder)
|
||||
: mEsm(readers)
|
||||
EsmLoader::EsmLoader(MWWorld::ESMStore& store, ESM::ReadersCache& readers, ToUTF8::Utf8Encoder* encoder)
|
||||
: mReaders(readers)
|
||||
, mStore(store)
|
||||
, mEncoder(encoder)
|
||||
, mDialogue(nullptr) // A content file containing INFO records without a DIAL record appends them to the previous file's dialogue
|
||||
@ -17,13 +17,25 @@ EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& read
|
||||
|
||||
void EsmLoader::load(const boost::filesystem::path& filepath, int& index, Loading::Listener* listener)
|
||||
{
|
||||
ESM::ESMReader lEsm;
|
||||
lEsm.setEncoder(mEncoder);
|
||||
lEsm.setIndex(index);
|
||||
lEsm.open(filepath.string());
|
||||
lEsm.resolveParentFileIndices(mEsm);
|
||||
mEsm[index] = std::move(lEsm);
|
||||
mStore.load(mEsm[index], listener, mDialogue);
|
||||
const ESM::ReadersCache::BusyItem reader = mReaders.get(static_cast<std::size_t>(index));
|
||||
|
||||
reader->setEncoder(mEncoder);
|
||||
reader->setIndex(index);
|
||||
reader->open(filepath.string());
|
||||
reader->resolveParentFileIndices(mReaders);
|
||||
|
||||
assert(reader->getGameFiles().size() == reader->getParentFileIndices().size());
|
||||
for (std::size_t i = 0, n = reader->getParentFileIndices().size(); i < n; ++i)
|
||||
if (i == static_cast<std::size_t>(reader->getIndex()))
|
||||
throw std::runtime_error("File " + reader->getName() + " asks for parent file "
|
||||
+ reader->getGameFiles()[i].name
|
||||
+ ", but it is not available or has been loaded in the wrong order. "
|
||||
"Please run the launcher to fix this issue.");
|
||||
|
||||
mStore.load(*reader, listener, mDialogue);
|
||||
|
||||
if (!mMasterFileFormat.has_value() && Misc::StringUtils::ciEndsWith(reader->getName(), ".esm") && !Misc::StringUtils::ciEndsWith(reader->getName(), ".omwgame"))
|
||||
mMasterFileFormat = reader->getFormat();
|
||||
}
|
||||
|
||||
} /* namespace MWWorld */
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef ESMLOADER_HPP
|
||||
#define ESMLOADER_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "contentloader.hpp"
|
||||
|
||||
@ -12,7 +12,7 @@ namespace ToUTF8
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ReadersCache;
|
||||
struct Dialogue;
|
||||
}
|
||||
|
||||
@ -23,16 +23,18 @@ class ESMStore;
|
||||
|
||||
struct EsmLoader : public ContentLoader
|
||||
{
|
||||
EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,
|
||||
ToUTF8::Utf8Encoder* encoder);
|
||||
explicit EsmLoader(MWWorld::ESMStore& store, ESM::ReadersCache& readers, ToUTF8::Utf8Encoder* encoder);
|
||||
|
||||
std::optional<int> getMasterFileFormat() const { return mMasterFileFormat; }
|
||||
|
||||
void load(const boost::filesystem::path& filepath, int& index, Loading::Listener* listener) override;
|
||||
|
||||
private:
|
||||
std::vector<ESM::ESMReader>& mEsm;
|
||||
ESM::ReadersCache& mReaders;
|
||||
MWWorld::ESMStore& mStore;
|
||||
ToUTF8::Utf8Encoder* mEncoder;
|
||||
ESM::Dialogue* mDialogue;
|
||||
std::optional<int> mMasterFileFormat;
|
||||
};
|
||||
|
||||
} /* namespace MWWorld */
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <components/loadinglistener/loadinglistener.hpp>
|
||||
#include <components/lua/configuration.hpp>
|
||||
#include <components/misc/algorithm.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
#include "../mwmechanics/spelllist.hpp"
|
||||
|
||||
@ -24,19 +25,18 @@ namespace
|
||||
|
||||
constexpr std::size_t deletedRefID = std::numeric_limits<std::size_t>::max();
|
||||
|
||||
void readRefs(const ESM::Cell& cell, std::vector<Ref>& refs, std::vector<std::string>& refIDs, std::vector<ESM::ESMReader>& readers)
|
||||
void readRefs(const ESM::Cell& cell, std::vector<Ref>& refs, std::vector<std::string>& refIDs, ESM::ReadersCache& readers)
|
||||
{
|
||||
// TODO: we have many similar copies of this code.
|
||||
for (size_t i = 0; i < cell.mContextList.size(); i++)
|
||||
{
|
||||
size_t index = cell.mContextList[i].index;
|
||||
if (readers.size() <= index)
|
||||
readers.resize(index + 1);
|
||||
cell.restore(readers[index], i);
|
||||
const std::size_t index = static_cast<std::size_t>(cell.mContextList[i].index);
|
||||
const ESM::ReadersCache::BusyItem reader = readers.get(index);
|
||||
cell.restore(*reader, i);
|
||||
ESM::CellRef ref;
|
||||
ref.mRefNum.unset();
|
||||
bool deleted = false;
|
||||
while(cell.getNextRef(readers[index], ref, deleted))
|
||||
while (cell.getNextRef(*reader, ref, deleted))
|
||||
{
|
||||
if(deleted)
|
||||
refs.emplace_back(ref.mRefNum, deletedRefID);
|
||||
@ -241,7 +241,7 @@ ESM::LuaScriptsCfg ESMStore::getLuaScriptsCfg() const
|
||||
return cfg;
|
||||
}
|
||||
|
||||
void ESMStore::setUp(bool validateRecords)
|
||||
void ESMStore::setUp()
|
||||
{
|
||||
mIds.clear();
|
||||
|
||||
@ -267,15 +267,15 @@ void ESMStore::setUp(bool validateRecords)
|
||||
mMagicEffects.setUp();
|
||||
mAttributes.setUp();
|
||||
mDialogs.setUp();
|
||||
|
||||
if (validateRecords)
|
||||
{
|
||||
validate();
|
||||
countAllCellRefs();
|
||||
}
|
||||
}
|
||||
|
||||
void ESMStore::countAllCellRefs()
|
||||
void ESMStore::validateRecords(ESM::ReadersCache& readers)
|
||||
{
|
||||
validate();
|
||||
countAllCellRefs(readers);
|
||||
}
|
||||
|
||||
void ESMStore::countAllCellRefs(ESM::ReadersCache& readers)
|
||||
{
|
||||
// TODO: We currently need to read entire files here again.
|
||||
// We should consider consolidating or deferring this reading.
|
||||
@ -283,7 +283,6 @@ void ESMStore::countAllCellRefs()
|
||||
return;
|
||||
std::vector<Ref> refs;
|
||||
std::vector<std::string> refIDs;
|
||||
std::vector<ESM::ESMReader> readers;
|
||||
for(auto it = mCells.intBegin(); it != mCells.intEnd(); ++it)
|
||||
readRefs(*it, refs, refIDs, readers);
|
||||
for(auto it = mCells.extBegin(); it != mCells.extEnd(); ++it)
|
||||
|
@ -20,6 +20,11 @@ namespace MWMechanics
|
||||
class SpellList;
|
||||
}
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ReadersCache;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class ESMStore
|
||||
@ -90,7 +95,7 @@ namespace MWWorld
|
||||
/// Validate entries in store after setup
|
||||
void validate();
|
||||
|
||||
void countAllCellRefs();
|
||||
void countAllCellRefs(ESM::ReadersCache& readers);
|
||||
|
||||
template<class T>
|
||||
void removeMissingObjects(Store<T>& store);
|
||||
@ -266,7 +271,8 @@ namespace MWWorld
|
||||
|
||||
// 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(bool validateRecords = false);
|
||||
void setUp();
|
||||
void validateRecords(ESM::ReadersCache& readers);
|
||||
|
||||
int countSavedGameRecords() const;
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esmloader/load.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
@ -11,7 +12,7 @@ namespace MWWorld
|
||||
query.mLoadStatics = true;
|
||||
query.mLoadCells = true;
|
||||
|
||||
std::vector<ESM::ESMReader> readers(groundcoverFiles.size());
|
||||
ESM::ReadersCache readers;
|
||||
const ::EsmLoader::EsmData content = ::EsmLoader::loadEsmData(query, groundcoverFiles, fileCollections, readers, encoder);
|
||||
|
||||
for (const ESM::Static& stat : statics)
|
||||
|
@ -145,8 +145,8 @@ namespace MWWorld
|
||||
ToUTF8::Utf8Encoder* encoder, int activationDistanceOverride,
|
||||
const std::string& startCell, const std::string& startupScript,
|
||||
const std::string& resourcePath, const std::string& userDataPath)
|
||||
: mResourceSystem(resourceSystem), mLocalScripts (mStore),
|
||||
mCells (mStore, mEsm), mSky (true),
|
||||
: mResourceSystem(resourceSystem), mLocalScripts(mStore),
|
||||
mCells(mStore, mReaders), mSky(true),
|
||||
mGodMode(false), mScriptsEnabled(true), mDiscardMovements(true), mContentFiles (contentFiles),
|
||||
mUserDataPath(userDataPath),
|
||||
mDefaultHalfExtents(Settings::Manager::getVector3("default actor pathfind half extents", "Game")),
|
||||
@ -156,30 +156,20 @@ namespace MWWorld
|
||||
mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0),
|
||||
mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f)
|
||||
{
|
||||
mEsm.resize(contentFiles.size());
|
||||
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
||||
listener->loadingOn();
|
||||
|
||||
loadContentFiles(fileCollections, contentFiles, mStore, mEsm, encoder, listener);
|
||||
loadContentFiles(fileCollections, contentFiles, encoder, listener);
|
||||
loadGroundcoverFiles(fileCollections, groundcoverFiles, encoder);
|
||||
|
||||
listener->loadingOff();
|
||||
|
||||
// Find main game file
|
||||
for (const ESM::ESMReader& reader : mEsm)
|
||||
{
|
||||
if (!Misc::StringUtils::ciEndsWith(reader.getName(), ".esm") && !Misc::StringUtils::ciEndsWith(reader.getName(), ".omwgame"))
|
||||
continue;
|
||||
if (reader.getFormat() == 0)
|
||||
ensureNeededRecords(); // and insert records that may not be present in all versions of MW.
|
||||
break;
|
||||
}
|
||||
|
||||
mCurrentDate = std::make_unique<DateTimeManager>();
|
||||
|
||||
fillGlobalVariables();
|
||||
|
||||
mStore.setUp(true);
|
||||
mStore.setUp();
|
||||
mStore.validateRecords(mReaders);
|
||||
mStore.movePlayerRecord();
|
||||
|
||||
mSwimHeightScale = mStore.get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat();
|
||||
@ -417,23 +407,6 @@ namespace MWWorld
|
||||
}
|
||||
}
|
||||
|
||||
void World::validateMasterFiles(const std::vector<ESM::ESMReader>& readers)
|
||||
{
|
||||
for (const auto& esm : readers)
|
||||
{
|
||||
assert(esm.getGameFiles().size() == esm.getParentFileIndices().size());
|
||||
for (unsigned int i=0; i<esm.getParentFileIndices().size(); ++i)
|
||||
{
|
||||
if (!esm.isValidParentFileIndex(i))
|
||||
{
|
||||
std::string fstring = "File " + esm.getName() + " asks for parent file " + esm.getGameFiles()[i].name
|
||||
+ ", but it is not available or has been loaded in the wrong order. Please run the launcher to fix this issue.";
|
||||
throw std::runtime_error(fstring);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void World::ensureNeededRecords()
|
||||
{
|
||||
std::map<std::string, ESM::Variant> gmst;
|
||||
@ -641,11 +614,6 @@ namespace MWWorld
|
||||
return mStore;
|
||||
}
|
||||
|
||||
std::vector<ESM::ESMReader>& World::getEsmReader()
|
||||
{
|
||||
return mEsm;
|
||||
}
|
||||
|
||||
LocalScripts& World::getLocalScripts()
|
||||
{
|
||||
return mLocalScripts;
|
||||
@ -2939,11 +2907,11 @@ namespace MWWorld
|
||||
return mScriptsEnabled;
|
||||
}
|
||||
|
||||
void World::loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content, ESMStore& store, std::vector<ESM::ESMReader>& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener)
|
||||
void World::loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content,
|
||||
ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener)
|
||||
{
|
||||
GameContentLoader gameContentLoader;
|
||||
EsmLoader esmLoader(store, readers, encoder);
|
||||
validateMasterFiles(readers);
|
||||
EsmLoader esmLoader(mStore, mReaders, encoder);
|
||||
|
||||
gameContentLoader.addLoader(".esm", esmLoader);
|
||||
gameContentLoader.addLoader(".esp", esmLoader);
|
||||
@ -2951,7 +2919,7 @@ namespace MWWorld
|
||||
gameContentLoader.addLoader(".omwaddon", esmLoader);
|
||||
gameContentLoader.addLoader(".project", esmLoader);
|
||||
|
||||
OMWScriptsLoader omwScriptsLoader(store);
|
||||
OMWScriptsLoader omwScriptsLoader(mStore);
|
||||
gameContentLoader.addLoader(".omwscripts", omwScriptsLoader);
|
||||
|
||||
int idx = 0;
|
||||
@ -2970,6 +2938,9 @@ namespace MWWorld
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (const auto v = esmLoader.getMasterFileFormat(); v.has_value() && *v == 0)
|
||||
ensureNeededRecords(); // Insert records that may not be present in all versions of master files.
|
||||
}
|
||||
|
||||
void World::loadGroundcoverFiles(const Files::Collections& fileCollections, const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder)
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
@ -81,7 +82,7 @@ namespace MWWorld
|
||||
private:
|
||||
Resource::ResourceSystem* mResourceSystem;
|
||||
|
||||
std::vector<ESM::ESMReader> mEsm;
|
||||
ESM::ReadersCache mReaders;
|
||||
MWWorld::ESMStore mStore;
|
||||
GroundcoverStore mGroundcoverStore;
|
||||
LocalScripts mLocalScripts;
|
||||
@ -162,13 +163,13 @@ namespace MWWorld
|
||||
void updateNavigatorObject(const MWPhysics::Object& object);
|
||||
|
||||
void ensureNeededRecords();
|
||||
void validateMasterFiles(const std::vector<ESM::ESMReader>& readers);
|
||||
|
||||
void fillGlobalVariables();
|
||||
|
||||
void updateSkyDate();
|
||||
|
||||
void loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content, ESMStore& store, std::vector<ESM::ESMReader>& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener);
|
||||
void loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content,
|
||||
ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener);
|
||||
|
||||
void loadGroundcoverFiles(const Files::Collections& fileCollections, const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder);
|
||||
|
||||
@ -241,8 +242,6 @@ namespace MWWorld
|
||||
|
||||
const MWWorld::ESMStore& getStore() const override;
|
||||
|
||||
std::vector<ESM::ESMReader>& getEsmReader() override;
|
||||
|
||||
LocalScripts& getLocalScripts() override;
|
||||
|
||||
bool hasCellChanged() const override;
|
||||
|
@ -81,6 +81,8 @@ if (GTEST_FOUND AND GMOCK_FOUND)
|
||||
|
||||
fx/lexer.cpp
|
||||
fx/technique.cpp
|
||||
|
||||
esm3/readerscache.cpp
|
||||
)
|
||||
|
||||
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})
|
||||
|
91
apps/openmw_test_suite/esm3/readerscache.cpp
Normal file
91
apps/openmw_test_suite/esm3/readerscache.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
#include <components/files/collections.hpp>
|
||||
#include <components/files/multidircollection.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#ifndef OPENMW_DATA_DIR
|
||||
#error "OPENMW_DATA_DIR is not defined"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace testing;
|
||||
using namespace ESM;
|
||||
|
||||
TEST(ESM3ReadersCache, onAttemptToRequestTheSameReaderTwiceShouldThrowException)
|
||||
{
|
||||
ReadersCache readers(1);
|
||||
const ReadersCache::BusyItem reader = readers.get(0);
|
||||
EXPECT_THROW(readers.get(0), std::logic_error);
|
||||
}
|
||||
|
||||
TEST(ESM3ReadersCache, shouldAllowToHaveBusyItemsMoreThanCapacity)
|
||||
{
|
||||
ReadersCache readers(1);
|
||||
const ReadersCache::BusyItem reader0 = readers.get(0);
|
||||
const ReadersCache::BusyItem reader1 = readers.get(1);
|
||||
}
|
||||
|
||||
TEST(ESM3ReadersCache, shouldKeepClosedReleasedClosedItem)
|
||||
{
|
||||
ReadersCache readers(1);
|
||||
readers.get(0);
|
||||
const ReadersCache::BusyItem reader = readers.get(0);
|
||||
EXPECT_FALSE(reader->isOpen());
|
||||
}
|
||||
|
||||
struct ESM3ReadersCacheWithContentFile : Test
|
||||
{
|
||||
static constexpr std::size_t sInitialOffset = 324;
|
||||
static constexpr std::size_t sSkip = 100;
|
||||
const Files::PathContainer mDataDirs {{std::string(OPENMW_DATA_DIR)}};
|
||||
const Files::Collections mFileCollections {mDataDirs, true};
|
||||
const std::string mContentFile = "template.omwgame";
|
||||
const std::string mContentFilePath = mFileCollections.getCollection(".omwgame").getPath(mContentFile).string();
|
||||
};
|
||||
|
||||
TEST_F(ESM3ReadersCacheWithContentFile, shouldKeepOpenReleasedOpenReader)
|
||||
{
|
||||
ReadersCache readers(1);
|
||||
{
|
||||
const ReadersCache::BusyItem reader = readers.get(0);
|
||||
reader->open(mContentFilePath);
|
||||
ASSERT_TRUE(reader->isOpen());
|
||||
ASSERT_EQ(reader->getFileOffset(), sInitialOffset);
|
||||
ASSERT_GT(reader->getFileSize(), sInitialOffset + sSkip);
|
||||
reader->skip(sSkip);
|
||||
ASSERT_EQ(reader->getFileOffset(), sInitialOffset + sSkip);
|
||||
}
|
||||
{
|
||||
const ReadersCache::BusyItem reader = readers.get(0);
|
||||
EXPECT_TRUE(reader->isOpen());
|
||||
EXPECT_EQ(reader->getName(), mContentFilePath);
|
||||
EXPECT_EQ(reader->getFileOffset(), sInitialOffset + sSkip);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ESM3ReadersCacheWithContentFile, shouldCloseFreeReaderWhenReachingCapacityLimit)
|
||||
{
|
||||
ReadersCache readers(1);
|
||||
{
|
||||
const ReadersCache::BusyItem reader = readers.get(0);
|
||||
reader->open(mContentFilePath);
|
||||
ASSERT_TRUE(reader->isOpen());
|
||||
ASSERT_EQ(reader->getFileOffset(), sInitialOffset);
|
||||
ASSERT_GT(reader->getFileSize(), sInitialOffset + sSkip);
|
||||
reader->skip(sSkip);
|
||||
ASSERT_EQ(reader->getFileOffset(), sInitialOffset + sSkip);
|
||||
}
|
||||
{
|
||||
const ReadersCache::BusyItem reader = readers.get(1);
|
||||
reader->open(mContentFilePath);
|
||||
ASSERT_TRUE(reader->isOpen());
|
||||
}
|
||||
{
|
||||
const ReadersCache::BusyItem reader = readers.get(0);
|
||||
EXPECT_TRUE(reader->isOpen());
|
||||
EXPECT_EQ(reader->getFileOffset(), sInitialOffset);
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
#include <components/files/collections.hpp>
|
||||
#include <components/files/multidircollection.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@ -39,7 +40,7 @@ namespace
|
||||
query.mLoadGameSettings = true;
|
||||
query.mLoadLands = true;
|
||||
query.mLoadStatics = true;
|
||||
std::vector<ESM::ESMReader> readers(mContentFiles.size());
|
||||
ESM::ReadersCache readers;
|
||||
ToUTF8::Utf8Encoder* const encoder = nullptr;
|
||||
const EsmData esmData = loadEsmData(query, mContentFiles, mFileCollections, readers, encoder);
|
||||
EXPECT_EQ(esmData.mActivators.size(), 0);
|
||||
@ -61,7 +62,7 @@ namespace
|
||||
query.mLoadGameSettings = true;
|
||||
query.mLoadLands = true;
|
||||
query.mLoadStatics = true;
|
||||
std::vector<ESM::ESMReader> readers(mContentFiles.size());
|
||||
ESM::ReadersCache readers;
|
||||
ToUTF8::Utf8Encoder* const encoder = nullptr;
|
||||
const EsmData esmData = loadEsmData(query, mContentFiles, mFileCollections, readers, encoder);
|
||||
EXPECT_EQ(esmData.mActivators.size(), 0);
|
||||
@ -83,7 +84,7 @@ namespace
|
||||
query.mLoadGameSettings = false;
|
||||
query.mLoadLands = true;
|
||||
query.mLoadStatics = true;
|
||||
std::vector<ESM::ESMReader> readers(mContentFiles.size());
|
||||
ESM::ReadersCache readers;
|
||||
ToUTF8::Utf8Encoder* const encoder = nullptr;
|
||||
const EsmData esmData = loadEsmData(query, mContentFiles, mFileCollections, readers, encoder);
|
||||
EXPECT_EQ(esmData.mActivators.size(), 0);
|
||||
@ -98,7 +99,7 @@ namespace
|
||||
TEST_F(EsmLoaderTest, shouldIgnoreAllWithDefaultQuery)
|
||||
{
|
||||
const Query query;
|
||||
std::vector<ESM::ESMReader> readers(mContentFiles.size());
|
||||
ESM::ReadersCache readers;
|
||||
ToUTF8::Utf8Encoder* const encoder = nullptr;
|
||||
const EsmData esmData = loadEsmData(query, mContentFiles, mFileCollections, readers, encoder);
|
||||
EXPECT_EQ(esmData.mActivators.size(), 0);
|
||||
@ -121,7 +122,7 @@ namespace
|
||||
query.mLoadLands = true;
|
||||
query.mLoadStatics = true;
|
||||
const std::vector<std::string> contentFiles {{"script.omwscripts"}};
|
||||
std::vector<ESM::ESMReader> readers(contentFiles.size());
|
||||
ESM::ReadersCache readers;
|
||||
ToUTF8::Utf8Encoder* const encoder = nullptr;
|
||||
const EsmData esmData = loadEsmData(query, contentFiles, mFileCollections, readers, encoder);
|
||||
EXPECT_EQ(esmData.mActivators.size(), 0);
|
||||
|
@ -95,7 +95,7 @@ add_component_dir (esm3
|
||||
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap
|
||||
inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats
|
||||
weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile
|
||||
aisequence magiceffects custommarkerstate stolenitems transport animationstate controlsstate mappings
|
||||
aisequence magiceffects custommarkerstate stolenitems transport animationstate controlsstate mappings readerscache
|
||||
)
|
||||
|
||||
add_component_dir (esm3terrain
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "esmreader.hpp"
|
||||
|
||||
#include "readerscache.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/files/openfile.hpp>
|
||||
|
||||
@ -61,21 +63,22 @@ void ESMReader::clearCtx()
|
||||
mCtx.subName.clear();
|
||||
}
|
||||
|
||||
void ESMReader::resolveParentFileIndices(const std::vector<ESMReader>& allPlugins)
|
||||
void ESMReader::resolveParentFileIndices(ReadersCache& readers)
|
||||
{
|
||||
mCtx.parentFileIndices.clear();
|
||||
const std::vector<Header::MasterData> &masters = getGameFiles();
|
||||
for (size_t j = 0; j < masters.size(); j++) {
|
||||
const Header::MasterData &mast = masters[j];
|
||||
std::string fname = mast.name;
|
||||
for (const Header::MasterData &mast : getGameFiles())
|
||||
{
|
||||
const std::string& fname = mast.name;
|
||||
int index = getIndex();
|
||||
for (int i = 0; i < getIndex(); i++) {
|
||||
const ESMReader& reader = allPlugins.at(i);
|
||||
if (reader.getFileSize() == 0)
|
||||
for (int i = 0; i < getIndex(); i++)
|
||||
{
|
||||
const ESM::ReadersCache::BusyItem reader = readers.get(static_cast<std::size_t>(i));
|
||||
if (reader->getFileSize() == 0)
|
||||
continue; // Content file in non-ESM format
|
||||
const std::string& candidate = reader.getName();
|
||||
const std::string& candidate = reader->getName();
|
||||
std::string fnamecandidate = std::filesystem::path(candidate).filename().string();
|
||||
if (Misc::StringUtils::ciEqual(fname, fnamecandidate)) {
|
||||
if (Misc::StringUtils::ciEqual(fname, fnamecandidate))
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
|
@ -16,6 +16,8 @@
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ReadersCache;
|
||||
|
||||
class ESMReader
|
||||
{
|
||||
public:
|
||||
@ -39,6 +41,7 @@ public:
|
||||
const NAME &retSubName() const { return mCtx.subName; }
|
||||
uint32_t getSubSize() const { return mCtx.leftSub; }
|
||||
const std::string& getName() const { return mCtx.filename; };
|
||||
bool isOpen() const { return mEsm != nullptr; }
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
@ -85,9 +88,8 @@ public:
|
||||
// all files/readers used by the engine. This is required for correct adjustRefNum() results
|
||||
// as required for handling moved, deleted and edited CellRefs.
|
||||
/// @note Does not validate.
|
||||
void resolveParentFileIndices(const std::vector<ESMReader>& files);
|
||||
void resolveParentFileIndices(ReadersCache& readers);
|
||||
const std::vector<int>& getParentFileIndices() const { return mCtx.parentFileIndices; }
|
||||
bool isValidParentFileIndex(int i) const { return i != getIndex(); }
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
|
87
components/esm3/readerscache.cpp
Normal file
87
components/esm3/readerscache.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
#include "readerscache.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
ReadersCache::BusyItem::BusyItem(ReadersCache& owner, std::list<Item>::iterator item) noexcept
|
||||
: mOwner(owner)
|
||||
, mItem(item)
|
||||
{}
|
||||
|
||||
ReadersCache::BusyItem::~BusyItem() noexcept
|
||||
{
|
||||
mOwner.releaseItem(mItem);
|
||||
}
|
||||
|
||||
ReadersCache::ReadersCache(std::size_t capacity)
|
||||
: mCapacity(capacity)
|
||||
{}
|
||||
|
||||
ReadersCache::BusyItem ReadersCache::get(std::size_t index)
|
||||
{
|
||||
const auto indexIt = mIndex.find(index);
|
||||
std::list<Item>::iterator it;
|
||||
if (indexIt == mIndex.end())
|
||||
{
|
||||
closeExtraReaders();
|
||||
it = mBusyItems.emplace(mBusyItems.end());
|
||||
mIndex.emplace(index, it);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (indexIt->second->mState)
|
||||
{
|
||||
case State::Busy:
|
||||
throw std::logic_error("ESMReader at index " + std::to_string(index) + " is busy");
|
||||
case State::Free:
|
||||
it = indexIt->second;
|
||||
mBusyItems.splice(mBusyItems.end(), mFreeItems, it);
|
||||
break;
|
||||
case State::Closed:
|
||||
closeExtraReaders();
|
||||
it = indexIt->second;
|
||||
if (it->mName.has_value())
|
||||
{
|
||||
it->mReader.open(*it->mName);
|
||||
it->mName.reset();
|
||||
}
|
||||
mBusyItems.splice(mBusyItems.end(), mClosedItems, it);
|
||||
break;
|
||||
}
|
||||
it->mState = State::Busy;
|
||||
}
|
||||
|
||||
return BusyItem(*this, it);
|
||||
}
|
||||
|
||||
void ReadersCache::closeExtraReaders()
|
||||
{
|
||||
while (!mFreeItems.empty() && mBusyItems.size() + mFreeItems.size() + 1 > mCapacity)
|
||||
{
|
||||
const auto it = mFreeItems.begin();
|
||||
if (it->mReader.isOpen())
|
||||
{
|
||||
it->mName = it->mReader.getName();
|
||||
it->mReader.close();
|
||||
}
|
||||
mClosedItems.splice(mClosedItems.end(), mFreeItems, it);
|
||||
it->mState = State::Closed;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadersCache::releaseItem(std::list<Item>::iterator it) noexcept
|
||||
{
|
||||
assert(it->mState == State::Busy);
|
||||
if (it->mReader.isOpen())
|
||||
{
|
||||
mFreeItems.splice(mFreeItems.end(), mBusyItems, it);
|
||||
it->mState = State::Free;
|
||||
}
|
||||
else
|
||||
{
|
||||
mClosedItems.splice(mClosedItems.end(), mBusyItems, it);
|
||||
it->mState = State::Closed;
|
||||
}
|
||||
}
|
||||
}
|
71
components/esm3/readerscache.hpp
Normal file
71
components/esm3/readerscache.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef OPENMW_COMPONENTS_ESM3_READERSCACHE_H
|
||||
#define OPENMW_COMPONENTS_ESM3_READERSCACHE_H
|
||||
|
||||
#include "esmreader.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ReadersCache
|
||||
{
|
||||
private:
|
||||
enum class State
|
||||
{
|
||||
Busy,
|
||||
Free,
|
||||
Closed,
|
||||
};
|
||||
|
||||
struct Item
|
||||
{
|
||||
State mState = State::Busy;
|
||||
ESMReader mReader;
|
||||
std::optional<std::string> mName;
|
||||
|
||||
Item() = default;
|
||||
};
|
||||
|
||||
public:
|
||||
class BusyItem
|
||||
{
|
||||
public:
|
||||
explicit BusyItem(ReadersCache& owner, std::list<Item>::iterator item) noexcept;
|
||||
|
||||
BusyItem(const BusyItem& other) = delete;
|
||||
|
||||
~BusyItem() noexcept;
|
||||
|
||||
BusyItem& operator=(const BusyItem& other) = delete;
|
||||
|
||||
ESMReader& operator*() const noexcept { return mItem->mReader; }
|
||||
|
||||
ESMReader* operator->() const noexcept { return &mItem->mReader; }
|
||||
|
||||
private:
|
||||
ReadersCache& mOwner;
|
||||
std::list<Item>::iterator mItem;
|
||||
};
|
||||
|
||||
explicit ReadersCache(std::size_t capacity = 100);
|
||||
|
||||
BusyItem get(std::size_t index);
|
||||
|
||||
private:
|
||||
const std::size_t mCapacity;
|
||||
std::map<std::size_t, std::list<Item>::iterator> mIndex;
|
||||
std::list<Item> mBusyItems;
|
||||
std::list<Item> mFreeItems;
|
||||
std::list<Item> mClosedItems;
|
||||
|
||||
inline void closeExtraReaders();
|
||||
|
||||
inline void releaseItem(std::list<Item>::iterator it) noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -18,6 +18,7 @@
|
||||
#include <components/files/multidircollection.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
@ -207,7 +208,7 @@ namespace EsmLoader
|
||||
}
|
||||
|
||||
ShallowContent shallowLoad(const Query& query, const std::vector<std::string>& contentFiles,
|
||||
const Files::Collections& fileCollections, std::vector<ESM::ESMReader>& readers,
|
||||
const Files::Collections& fileCollections, ESM::ReadersCache& readers,
|
||||
ToUTF8::Utf8Encoder* encoder)
|
||||
{
|
||||
ShallowContent result;
|
||||
@ -233,14 +234,14 @@ namespace EsmLoader
|
||||
|
||||
const Files::MultiDirCollection& collection = fileCollections.getCollection(extension);
|
||||
|
||||
ESM::ESMReader& reader = readers[i];
|
||||
reader.setEncoder(encoder);
|
||||
reader.setIndex(static_cast<int>(i));
|
||||
reader.open(collection.getPath(file).string());
|
||||
const ESM::ReadersCache::BusyItem reader = readers.get(i);
|
||||
reader->setEncoder(encoder);
|
||||
reader->setIndex(static_cast<int>(i));
|
||||
reader->open(collection.getPath(file).string());
|
||||
if (query.mLoadCells)
|
||||
reader.resolveParentFileIndices(readers);
|
||||
reader->resolveParentFileIndices(readers);
|
||||
|
||||
loadEsm(query, readers[i], result);
|
||||
loadEsm(query, *reader, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -289,7 +290,7 @@ namespace EsmLoader
|
||||
}
|
||||
|
||||
EsmData loadEsmData(const Query& query, const std::vector<std::string>& contentFiles,
|
||||
const Files::Collections& fileCollections, std::vector<ESM::ESMReader>& readers, ToUTF8::Utf8Encoder* encoder)
|
||||
const Files::Collections& fileCollections, ESM::ReadersCache& readers, ToUTF8::Utf8Encoder* encoder)
|
||||
{
|
||||
Log(Debug::Info) << "Loading ESM data...";
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace EsmLoader
|
||||
};
|
||||
|
||||
EsmData loadEsmData(const Query& query, const std::vector<std::string>& contentFiles,
|
||||
const Files::Collections& fileCollections, std::vector<ESM::ESMReader>& readers,
|
||||
const Files::Collections& fileCollections, ESM::ReadersCache& readers,
|
||||
ToUTF8::Utf8Encoder* encoder);
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <components/resource/bulletshapemanager.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/esm3/readerscache.hpp>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
@ -50,17 +51,17 @@ namespace Resource
|
||||
}
|
||||
|
||||
std::vector<CellRef> loadCellRefs(const ESM::Cell& cell, const EsmLoader::EsmData& esmData,
|
||||
std::vector<ESM::ESMReader>& readers)
|
||||
ESM::ReadersCache& readers)
|
||||
{
|
||||
std::vector<EsmLoader::Record<CellRef>> cellRefs;
|
||||
|
||||
for (std::size_t i = 0; i < cell.mContextList.size(); i++)
|
||||
{
|
||||
ESM::ESMReader& reader = readers[static_cast<std::size_t>(cell.mContextList[i].index)];
|
||||
cell.restore(reader, static_cast<int>(i));
|
||||
const ESM::ReadersCache::BusyItem reader = readers.get(static_cast<std::size_t>(cell.mContextList[i].index));
|
||||
cell.restore(*reader, static_cast<int>(i));
|
||||
ESM::CellRef cellRef;
|
||||
bool deleted = false;
|
||||
while (ESM::Cell::getNextRef(reader, cellRef, deleted))
|
||||
while (ESM::Cell::getNextRef(*reader, cellRef, deleted))
|
||||
{
|
||||
Misc::StringUtils::lowerCaseInPlace(cellRef.mRefID);
|
||||
const ESM::RecNameInts type = getType(esmData, cellRef.mRefID);
|
||||
@ -83,7 +84,7 @@ namespace Resource
|
||||
|
||||
template <class F>
|
||||
void forEachObject(const ESM::Cell& cell, const EsmLoader::EsmData& esmData, const VFS::Manager& vfs,
|
||||
Resource::BulletShapeManager& bulletShapeManager, std::vector<ESM::ESMReader>& readers,
|
||||
Resource::BulletShapeManager& bulletShapeManager, ESM::ReadersCache& readers,
|
||||
F&& f)
|
||||
{
|
||||
std::vector<CellRef> cellRefs = loadCellRefs(cell, esmData, readers);
|
||||
@ -130,7 +131,7 @@ namespace Resource
|
||||
}
|
||||
}
|
||||
|
||||
void forEachBulletObject(std::vector<ESM::ESMReader>& readers, const VFS::Manager& vfs,
|
||||
void forEachBulletObject(ESM::ReadersCache& readers, const VFS::Manager& vfs,
|
||||
Resource::BulletShapeManager& bulletShapeManager, const EsmLoader::EsmData& esmData,
|
||||
std::function<void (const ESM::Cell& cell, const BulletObject& object)> callback)
|
||||
{
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ReadersCache;
|
||||
struct Cell;
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ namespace Resource
|
||||
float mScale;
|
||||
};
|
||||
|
||||
void forEachBulletObject(std::vector<ESM::ESMReader>& readers, const VFS::Manager& vfs,
|
||||
void forEachBulletObject(ESM::ReadersCache& readers, const VFS::Manager& vfs,
|
||||
Resource::BulletShapeManager& bulletShapeManager, const EsmLoader::EsmData& esmData,
|
||||
std::function<void (const ESM::Cell&, const BulletObject& object)> callback);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user