1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-06 21:45:35 +00:00

Adjust FormId in FormIdRefId and for items in containers when loading saves

This commit is contained in:
Petr Mikheev 2023-07-30 01:22:47 +02:00
parent d2f16774d9
commit 9a9f9d7081
14 changed files with 57 additions and 44 deletions

View file

@ -132,7 +132,7 @@ namespace MWBase
virtual void write(ESM::ESMWriter& writer, Loading::Listener& listener) const = 0; 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; virtual void useDeathCamera() = 0;

View file

@ -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) if (type == ESM::REC_GSCR)
{ {
ESM::GlobalScript script; ESM::GlobalScript script;
script.load(reader); 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); auto iter = mScripts.find(script.mId);
if (iter == mScripts.end()) if (iter == mScripts.end())

View file

@ -78,7 +78,7 @@ namespace MWScript
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const; 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. ///< Records for variables that do not exist are dropped silently.
/// ///
/// \return Known type? /// \return Known type?

View file

@ -412,6 +412,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
"to the newest OpenMW version to load this file."); "to the newest OpenMW version to load this file.");
std::map<int, int> contentFileMap = buildContentFileIndexMap(reader); std::map<int, int> contentFileMap = buildContentFileIndexMap(reader);
reader.setContentFileMapping(&contentFileMap);
MWBase::Environment::get().getLuaManager()->setContentFileMapping(contentFileMap); MWBase::Environment::get().getLuaManager()->setContentFileMapping(contentFileMap);
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen(); 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_CREA:
case ESM::REC_CONT: case ESM::REC_CONT:
case ESM::REC_RAND: case ESM::REC_RAND:
MWBase::Environment::get().getWorld()->readRecord(reader, n.toInt(), contentFileMap); MWBase::Environment::get().getWorld()->readRecord(reader, n.toInt());
break; break;
case ESM::REC_CAM_: case ESM::REC_CAM_:
@ -494,8 +495,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
case ESM::REC_GSCR: case ESM::REC_GSCR:
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord( MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord(reader, n.toInt());
reader, n.toInt(), contentFileMap);
break; break;
case ESM::REC_GMAP: case ESM::REC_GMAP:

View file

@ -211,7 +211,7 @@ namespace
template <typename T> template <typename T>
void readReferenceCollection(ESM::ESMReader& reader, MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref, 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; using StateType = typename RecordToState<T>::StateType;
StateType state; StateType state;
@ -219,15 +219,8 @@ namespace
state.load(reader); state.load(reader);
// If the reference came from a content file, make sure this content file is loaded // If the reference came from a content file, make sure this content file is loaded
if (state.mRef.mRefNum.hasContentFile()) if (!reader.applyContentFileMapping(state.mRef.mRefNum))
{ return; // content file has been removed -> skip
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 (!MWWorld::LiveCellRef<T>::checkState(state)) if (!MWWorld::LiveCellRef<T>::checkState(state))
return; // not valid anymore with current content files -> skip return; // not valid anymore with current content files -> skip
@ -1017,8 +1010,7 @@ namespace MWWorld
} }
} }
void CellStore::readReferences( void CellStore::readReferences(ESM::ESMReader& reader, GetCellStoreCallback* callback)
ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback)
{ {
mHasState = true; mHasState = true;
@ -1047,12 +1039,12 @@ namespace MWWorld
if (type != 0) if (type != 0)
{ {
bool foundCorrespondingStore = false; bool foundCorrespondingStore = false;
Misc::tupleForEach(this->mCellStoreImp->mRefLists, Misc::tupleForEach(
[&reader, this, &cref, &contentFileMap, &foundCorrespondingStore, type](auto&& x) { this->mCellStoreImp->mRefLists, [&reader, this, &cref, &foundCorrespondingStore, type](auto&& x) {
recNameSwitcher(x, static_cast<ESM::RecNameInts>(type), recNameSwitcher(x, static_cast<ESM::RecNameInts>(type),
[&reader, this, &cref, &contentFileMap, &foundCorrespondingStore](auto& store) { [&reader, this, &cref, &foundCorrespondingStore](auto& store) {
foundCorrespondingStore = true; foundCorrespondingStore = true;
readReferenceCollection(reader, store, cref, contentFileMap, mStore, this); readReferenceCollection(reader, store, cref, mStore, this);
}); });
}); });
@ -1066,14 +1058,14 @@ namespace MWWorld
reader.cacheSubName(); reader.cacheSubName();
ESM::RefNum refnum = reader.getFormId(true, "MVRF"); ESM::RefNum refnum = reader.getFormId(true, "MVRF");
ESM::RefId movedToId = reader.getCellId(); ESM::RefId movedToId = reader.getCellId();
if (refnum.hasContentFile()) if (!reader.applyContentFileMapping(refnum))
{ {
auto iter = contentFileMap.find(refnum.mContentFile); Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex
if (iter != contentFileMap.end()) << " (content file no longer exists)";
refnum.mContentFile = iter->second; 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); Ptr movedRef = MWBase::Environment::get().getWorldModel()->getPtr(refnum);
if (movedRef.isEmpty()) if (movedRef.isEmpty())
{ {

View file

@ -328,8 +328,7 @@ namespace MWWorld
/// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved /// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved
/// references) /// references)
void readReferences( void readReferences(ESM::ESMReader& reader, GetCellStoreCallback* callback);
ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback);
void respawn(); void respawn();
///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded. ///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded.

View file

@ -486,7 +486,7 @@ namespace MWWorld
writer.endRecord(ESM::REC_CAM_); 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) switch (type)
{ {
@ -522,8 +522,7 @@ namespace MWWorld
break; break;
default: default:
if (!mStore.readRecord(reader, type) && !mGlobalVariables.readRecord(reader, type) if (!mStore.readRecord(reader, type) && !mGlobalVariables.readRecord(reader, type)
&& !mWeatherManager->readRecord(reader, type) && !mWeatherManager->readRecord(reader, type) && !mWorldModel.readRecord(reader, type)
&& !mWorldModel.readRecord(reader, type, contentFileMap)
&& !mProjectileManager->readRecord(reader, type)) && !mProjectileManager->readRecord(reader, type))
{ {
throw std::runtime_error("unknown record in saved game"); throw std::runtime_error("unknown record in saved game");

View file

@ -219,7 +219,7 @@ namespace MWWorld
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const override; 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 // switch to POV before showing player's death animation
void useDeathCamera() override; void useDeathCamera() override;

View file

@ -446,7 +446,7 @@ public:
MWWorld::CellStore* getCellStore(const ESM::RefId& cellId) override { return mWorldModel.findCell(cellId); } 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) if (type == ESM::REC_CSTA)
{ {
@ -472,7 +472,7 @@ bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type, cons
GetCellStoreCallback callback(*this); GetCellStoreCallback callback(*this);
cellStore->readReferences(reader, contentFileMap, &callback); cellStore->readReferences(reader, &callback);
return true; return true;
} }

View file

@ -99,7 +99,7 @@ namespace MWWorld
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const; 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: private:
PtrRegistry mPtrRegistry; // defined before mCells because during destruction it should be the last PtrRegistry mPtrRegistry; // defined before mCells because during destruction it should be the last

View file

@ -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() ESM::RefId ESMReader::getCellId()
{ {
if (mHeader.mFormatVersion <= ESM::MaxUseEsmCellIdFormatVersion) if (mHeader.mFormatVersion <= ESM::MaxUseEsmCellIdFormatVersion)
@ -485,7 +497,10 @@ namespace ESM
{ {
FormId formId{}; FormId formId{};
getTSized<8>(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: case RefIdType::Generated:
{ {

View file

@ -4,6 +4,7 @@
#include <cstdint> #include <cstdint>
#include <filesystem> #include <filesystem>
#include <istream> #include <istream>
#include <map>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
@ -112,6 +113,13 @@ namespace ESM
void resolveParentFileIndices(ReadersCache& readers); void resolveParentFileIndices(ReadersCache& readers);
const std::vector<int>& getParentFileIndices() const { return mCtx.parentFileIndices; } 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 * Medium-level reading shortcuts
@ -368,6 +376,8 @@ namespace ESM
ToUTF8::Utf8Encoder* mEncoder; ToUTF8::Utf8Encoder* mEncoder;
size_t mFileSize; size_t mFileSize;
const std::map<int, int>* mContentFileMapping = nullptr;
}; };
} }
#endif #endif

View file

@ -19,6 +19,7 @@ namespace ESM
mTargetId = esm.getHNORefId("TARG"); mTargetId = esm.getHNORefId("TARG");
if (esm.peekNextSub("FRMR")) if (esm.peekNextSub("FRMR"))
mTargetRef = esm.getFormId(true, "FRMR"); mTargetRef = esm.getFormId(true, "FRMR");
esm.applyContentFileMapping(mTargetRef);
} }
void GlobalScript::save(ESMWriter& esm) const void GlobalScript::save(ESMWriter& esm) const

View file

@ -47,6 +47,10 @@ namespace ESM
state.mRef.loadId(esm, true); state.mRef.loadId(esm, true);
state.load(esm); 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) if (state.mCount == 0)
continue; continue;