mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-30 08:15:37 +00:00
Adjust FormId in FormIdRefId and for items in containers when loading saves
This commit is contained in:
parent
d2f16774d9
commit
9a9f9d7081
14 changed files with 57 additions and 44 deletions
|
@ -132,7 +132,7 @@ namespace MWBase
|
|||
|
||||
virtual void write(ESM::ESMWriter& writer, Loading::Listener& listener) const = 0;
|
||||
|
||||
virtual void readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap) = 0;
|
||||
virtual void readRecord(ESM::ESMReader& reader, uint32_t type) = 0;
|
||||
|
||||
virtual void useDeathCamera() = 0;
|
||||
|
||||
|
|
|
@ -240,20 +240,13 @@ namespace MWScript
|
|||
}
|
||||
}
|
||||
|
||||
bool GlobalScripts::readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap)
|
||||
bool GlobalScripts::readRecord(ESM::ESMReader& reader, uint32_t type)
|
||||
{
|
||||
if (type == ESM::REC_GSCR)
|
||||
{
|
||||
ESM::GlobalScript script;
|
||||
script.load(reader);
|
||||
|
||||
if (script.mTargetRef.hasContentFile())
|
||||
{
|
||||
auto iter = contentFileMap.find(script.mTargetRef.mContentFile);
|
||||
if (iter != contentFileMap.end())
|
||||
script.mTargetRef.mContentFile = iter->second;
|
||||
}
|
||||
|
||||
auto iter = mScripts.find(script.mId);
|
||||
|
||||
if (iter == mScripts.end())
|
||||
|
|
|
@ -78,7 +78,7 @@ namespace MWScript
|
|||
|
||||
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
||||
|
||||
bool readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap);
|
||||
bool readRecord(ESM::ESMReader& reader, uint32_t type);
|
||||
///< Records for variables that do not exist are dropped silently.
|
||||
///
|
||||
/// \return Known type?
|
||||
|
|
|
@ -412,6 +412,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
|||
"to the newest OpenMW version to load this file.");
|
||||
|
||||
std::map<int, int> contentFileMap = buildContentFileIndexMap(reader);
|
||||
reader.setContentFileMapping(&contentFileMap);
|
||||
MWBase::Environment::get().getLuaManager()->setContentFileMapping(contentFileMap);
|
||||
|
||||
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
||||
|
@ -485,7 +486,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
|||
case ESM::REC_CREA:
|
||||
case ESM::REC_CONT:
|
||||
case ESM::REC_RAND:
|
||||
MWBase::Environment::get().getWorld()->readRecord(reader, n.toInt(), contentFileMap);
|
||||
MWBase::Environment::get().getWorld()->readRecord(reader, n.toInt());
|
||||
break;
|
||||
|
||||
case ESM::REC_CAM_:
|
||||
|
@ -494,8 +495,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
|||
|
||||
case ESM::REC_GSCR:
|
||||
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord(
|
||||
reader, n.toInt(), contentFileMap);
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord(reader, n.toInt());
|
||||
break;
|
||||
|
||||
case ESM::REC_GMAP:
|
||||
|
|
|
@ -211,7 +211,7 @@ namespace
|
|||
|
||||
template <typename T>
|
||||
void readReferenceCollection(ESM::ESMReader& reader, MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref,
|
||||
const std::map<int, int>& contentFileMap, const MWWorld::ESMStore& esmStore, MWWorld::CellStore* cellstore)
|
||||
const MWWorld::ESMStore& esmStore, MWWorld::CellStore* cellstore)
|
||||
{
|
||||
using StateType = typename RecordToState<T>::StateType;
|
||||
StateType state;
|
||||
|
@ -219,15 +219,8 @@ namespace
|
|||
state.load(reader);
|
||||
|
||||
// If the reference came from a content file, make sure this content file is loaded
|
||||
if (state.mRef.mRefNum.hasContentFile())
|
||||
{
|
||||
std::map<int, int>::const_iterator iter = contentFileMap.find(state.mRef.mRefNum.mContentFile);
|
||||
|
||||
if (iter == contentFileMap.end())
|
||||
return; // content file has been removed -> skip
|
||||
|
||||
state.mRef.mRefNum.mContentFile = iter->second;
|
||||
}
|
||||
if (!reader.applyContentFileMapping(state.mRef.mRefNum))
|
||||
return; // content file has been removed -> skip
|
||||
|
||||
if (!MWWorld::LiveCellRef<T>::checkState(state))
|
||||
return; // not valid anymore with current content files -> skip
|
||||
|
@ -1017,8 +1010,7 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
void CellStore::readReferences(
|
||||
ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback)
|
||||
void CellStore::readReferences(ESM::ESMReader& reader, GetCellStoreCallback* callback)
|
||||
{
|
||||
mHasState = true;
|
||||
|
||||
|
@ -1047,12 +1039,12 @@ namespace MWWorld
|
|||
if (type != 0)
|
||||
{
|
||||
bool foundCorrespondingStore = false;
|
||||
Misc::tupleForEach(this->mCellStoreImp->mRefLists,
|
||||
[&reader, this, &cref, &contentFileMap, &foundCorrespondingStore, type](auto&& x) {
|
||||
Misc::tupleForEach(
|
||||
this->mCellStoreImp->mRefLists, [&reader, this, &cref, &foundCorrespondingStore, type](auto&& x) {
|
||||
recNameSwitcher(x, static_cast<ESM::RecNameInts>(type),
|
||||
[&reader, this, &cref, &contentFileMap, &foundCorrespondingStore](auto& store) {
|
||||
[&reader, this, &cref, &foundCorrespondingStore](auto& store) {
|
||||
foundCorrespondingStore = true;
|
||||
readReferenceCollection(reader, store, cref, contentFileMap, mStore, this);
|
||||
readReferenceCollection(reader, store, cref, mStore, this);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1066,14 +1058,14 @@ namespace MWWorld
|
|||
reader.cacheSubName();
|
||||
ESM::RefNum refnum = reader.getFormId(true, "MVRF");
|
||||
ESM::RefId movedToId = reader.getCellId();
|
||||
if (refnum.hasContentFile())
|
||||
if (!reader.applyContentFileMapping(refnum))
|
||||
{
|
||||
auto iter = contentFileMap.find(refnum.mContentFile);
|
||||
if (iter != contentFileMap.end())
|
||||
refnum.mContentFile = iter->second;
|
||||
Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex
|
||||
<< " (content file no longer exists)";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Search for the reference. It might no longer exist if its content file was removed.
|
||||
// Search for the reference. It might no longer exist if its content file was changed.
|
||||
Ptr movedRef = MWBase::Environment::get().getWorldModel()->getPtr(refnum);
|
||||
if (movedRef.isEmpty())
|
||||
{
|
||||
|
|
|
@ -328,8 +328,7 @@ namespace MWWorld
|
|||
|
||||
/// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved
|
||||
/// references)
|
||||
void readReferences(
|
||||
ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback);
|
||||
void readReferences(ESM::ESMReader& reader, GetCellStoreCallback* callback);
|
||||
|
||||
void respawn();
|
||||
///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded.
|
||||
|
|
|
@ -486,7 +486,7 @@ namespace MWWorld
|
|||
writer.endRecord(ESM::REC_CAM_);
|
||||
}
|
||||
|
||||
void World::readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap)
|
||||
void World::readRecord(ESM::ESMReader& reader, uint32_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
|
@ -522,8 +522,7 @@ namespace MWWorld
|
|||
break;
|
||||
default:
|
||||
if (!mStore.readRecord(reader, type) && !mGlobalVariables.readRecord(reader, type)
|
||||
&& !mWeatherManager->readRecord(reader, type)
|
||||
&& !mWorldModel.readRecord(reader, type, contentFileMap)
|
||||
&& !mWeatherManager->readRecord(reader, type) && !mWorldModel.readRecord(reader, type)
|
||||
&& !mProjectileManager->readRecord(reader, type))
|
||||
{
|
||||
throw std::runtime_error("unknown record in saved game");
|
||||
|
|
|
@ -219,7 +219,7 @@ namespace MWWorld
|
|||
|
||||
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const override;
|
||||
|
||||
void readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap) override;
|
||||
void readRecord(ESM::ESMReader& reader, uint32_t type) override;
|
||||
|
||||
// switch to POV before showing player's death animation
|
||||
void useDeathCamera() override;
|
||||
|
|
|
@ -446,7 +446,7 @@ public:
|
|||
MWWorld::CellStore* getCellStore(const ESM::RefId& cellId) override { return mWorldModel.findCell(cellId); }
|
||||
};
|
||||
|
||||
bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap)
|
||||
bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type)
|
||||
{
|
||||
if (type == ESM::REC_CSTA)
|
||||
{
|
||||
|
@ -472,7 +472,7 @@ bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type, cons
|
|||
|
||||
GetCellStoreCallback callback(*this);
|
||||
|
||||
cellStore->readReferences(reader, contentFileMap, &callback);
|
||||
cellStore->readReferences(reader, &callback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ namespace MWWorld
|
|||
|
||||
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
||||
|
||||
bool readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap);
|
||||
bool readRecord(ESM::ESMReader& reader, uint32_t type);
|
||||
|
||||
private:
|
||||
PtrRegistry mPtrRegistry; // defined before mCells because during destruction it should be the last
|
||||
|
|
|
@ -87,6 +87,18 @@ namespace ESM
|
|||
}
|
||||
}
|
||||
|
||||
bool ESMReader::applyContentFileMapping(FormId& id)
|
||||
{
|
||||
if (mContentFileMapping && id.hasContentFile())
|
||||
{
|
||||
auto iter = mContentFileMapping->find(id.mContentFile);
|
||||
if (iter == mContentFileMapping->end())
|
||||
return false;
|
||||
id.mContentFile = iter->second;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ESM::RefId ESMReader::getCellId()
|
||||
{
|
||||
if (mHeader.mFormatVersion <= ESM::MaxUseEsmCellIdFormatVersion)
|
||||
|
@ -485,7 +497,10 @@ namespace ESM
|
|||
{
|
||||
FormId formId{};
|
||||
getTSized<8>(formId);
|
||||
return RefId::formIdRefId(formId);
|
||||
if (applyContentFileMapping(formId))
|
||||
return RefId::formIdRefId(formId);
|
||||
else
|
||||
return RefId(); // content file was removed from load order
|
||||
}
|
||||
case RefIdType::Generated:
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <istream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
@ -112,6 +113,13 @@ namespace ESM
|
|||
void resolveParentFileIndices(ReadersCache& readers);
|
||||
const std::vector<int>& getParentFileIndices() const { return mCtx.parentFileIndices; }
|
||||
|
||||
// Used only when loading saves to adjust FormIds if load order was changes.
|
||||
void setContentFileMapping(const std::map<int, int>* mapping) { mContentFileMapping = mapping; }
|
||||
const std::map<int, int>* getContentFileMapping();
|
||||
|
||||
// Returns false if content file not found.
|
||||
bool applyContentFileMapping(FormId& id);
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Medium-level reading shortcuts
|
||||
|
@ -368,6 +376,8 @@ namespace ESM
|
|||
ToUTF8::Utf8Encoder* mEncoder;
|
||||
|
||||
size_t mFileSize;
|
||||
|
||||
const std::map<int, int>* mContentFileMapping = nullptr;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace ESM
|
|||
mTargetId = esm.getHNORefId("TARG");
|
||||
if (esm.peekNextSub("FRMR"))
|
||||
mTargetRef = esm.getFormId(true, "FRMR");
|
||||
esm.applyContentFileMapping(mTargetRef);
|
||||
}
|
||||
|
||||
void GlobalScript::save(ESMWriter& esm) const
|
||||
|
|
|
@ -47,6 +47,10 @@ namespace ESM
|
|||
state.mRef.loadId(esm, true);
|
||||
state.load(esm);
|
||||
|
||||
// Update content file index if load order was changed.
|
||||
if (!esm.applyContentFileMapping(state.mRef.mRefNum))
|
||||
state.mRef.mRefNum = FormId(); // content file removed; unset refnum, but keep object.
|
||||
|
||||
if (state.mCount == 0)
|
||||
continue;
|
||||
|
||||
|
|
Loading…
Reference in a new issue