forked from mirror/openmw-tes3mp
Merge branch 'refs'
Conflicts: apps/opencs/model/doc/savingstate.cpp components/esm/cellref.hpp
This commit is contained in:
commit
be7f344eca
16 changed files with 335 additions and 35 deletions
|
@ -65,7 +65,11 @@ CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& proje
|
|||
|
||||
appendStage (new WriteRefIdCollectionStage (mDocument, mState));
|
||||
|
||||
appendStage (new CollectionReferencesStage (mDocument, mState));
|
||||
|
||||
appendStage (new WriteCellCollectionStage (mDocument, mState));
|
||||
|
||||
// close file and clean up
|
||||
appendStage (new CloseSaveStage (mState));
|
||||
|
||||
appendStage (new FinalSavingStage (mDocument, mState));
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include <components/esm/loaddial.hpp>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "../world/infocollection.hpp"
|
||||
|
||||
#include "document.hpp"
|
||||
|
@ -216,6 +218,143 @@ void CSMDoc::WriteFilterStage::perform (int stage, Messages& messages)
|
|||
}
|
||||
|
||||
|
||||
CSMDoc::CollectionReferencesStage::CollectionReferencesStage (Document& document,
|
||||
SavingState& state)
|
||||
: mDocument (document), mState (state)
|
||||
{}
|
||||
|
||||
int CSMDoc::CollectionReferencesStage::setup()
|
||||
{
|
||||
mState.getSubRecords().clear();
|
||||
|
||||
int size = mDocument.getData().getReferences().getSize();
|
||||
|
||||
int steps = size/100;
|
||||
if (size%100) ++steps;
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages)
|
||||
{
|
||||
int size = mDocument.getData().getReferences().getSize();
|
||||
|
||||
for (int i=stage*100; i<stage*100+100 && i<size; ++i)
|
||||
{
|
||||
const CSMWorld::Record<CSMWorld::CellRef>& record =
|
||||
mDocument.getData().getReferences().getRecord (i);
|
||||
|
||||
if (record.mState==CSMWorld::RecordBase::State_Deleted ||
|
||||
record.mState==CSMWorld::RecordBase::State_Modified ||
|
||||
record.mState==CSMWorld::RecordBase::State_ModifiedOnly)
|
||||
{
|
||||
mState.getSubRecords()[Misc::StringUtils::lowerCase (record.get().mCell)]
|
||||
.push_back (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::WriteCellCollectionStage::WriteCellCollectionStage (Document& document,
|
||||
SavingState& state)
|
||||
: mDocument (document), mState (state)
|
||||
{}
|
||||
|
||||
int CSMDoc::WriteCellCollectionStage::setup()
|
||||
{
|
||||
return mDocument.getData().getCells().getSize();
|
||||
}
|
||||
|
||||
void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
|
||||
{
|
||||
const CSMWorld::Record<CSMWorld::Cell>& cell =
|
||||
mDocument.getData().getCells().getRecord (stage);
|
||||
|
||||
std::map<std::string, std::vector<int> >::const_iterator references =
|
||||
mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId));
|
||||
|
||||
if (cell.mState==CSMWorld::RecordBase::State_Modified ||
|
||||
cell.mState==CSMWorld::RecordBase::State_ModifiedOnly ||
|
||||
references!=mState.getSubRecords().end())
|
||||
{
|
||||
bool interior = cell.get().mId.substr (0, 1)!="#";
|
||||
|
||||
// write cell data
|
||||
mState.getWriter().startRecord (cell.mModified.sRecordId);
|
||||
|
||||
mState.getWriter().writeHNOCString ("NAME", cell.get().mName);
|
||||
|
||||
ESM::Cell cell2 = cell.get();
|
||||
|
||||
if (interior)
|
||||
cell2.mData.mFlags |= ESM::Cell::Interior;
|
||||
else
|
||||
{
|
||||
cell2.mData.mFlags &= ~ESM::Cell::Interior;
|
||||
|
||||
std::istringstream stream (cell.get().mId.c_str());
|
||||
char ignore;
|
||||
stream >> ignore >> cell2.mData.mX >> cell2.mData.mY;
|
||||
}
|
||||
cell2.save (mState.getWriter());
|
||||
|
||||
// write references
|
||||
if (references!=mState.getSubRecords().end())
|
||||
{
|
||||
// first pass: find highest RefNum
|
||||
int lastRefNum = -1;
|
||||
|
||||
for (std::vector<int>::const_iterator iter (references->second.begin());
|
||||
iter!=references->second.end(); ++iter)
|
||||
{
|
||||
const CSMWorld::Record<CSMWorld::CellRef>& ref =
|
||||
mDocument.getData().getReferences().getRecord (*iter);
|
||||
|
||||
if (ref.get().mRefNum.mContentFile==0 && ref.get().mRefNum.mIndex>lastRefNum)
|
||||
lastRefNum = ref.get().mRefNum.mIndex;
|
||||
}
|
||||
|
||||
// second pass: write
|
||||
for (std::vector<int>::const_iterator iter (references->second.begin());
|
||||
iter!=references->second.end(); ++iter)
|
||||
{
|
||||
const CSMWorld::Record<CSMWorld::CellRef>& ref =
|
||||
mDocument.getData().getReferences().getRecord (*iter);
|
||||
|
||||
if (ref.mState==CSMWorld::RecordBase::State_Modified ||
|
||||
ref.mState==CSMWorld::RecordBase::State_ModifiedOnly)
|
||||
{
|
||||
if (ref.get().mRefNum.mContentFile==-2)
|
||||
{
|
||||
if (lastRefNum>=0xffffff)
|
||||
throw std::runtime_error (
|
||||
"RefNums exhausted in cell: " + cell.get().mId);
|
||||
|
||||
ESM::CellRef ref2 = ref.get();
|
||||
ref2.mRefNum.mContentFile = 0;
|
||||
ref2.mRefNum.mIndex = ++lastRefNum;
|
||||
|
||||
ref2.save (mState.getWriter());
|
||||
}
|
||||
else
|
||||
ref.get().save (mState.getWriter());
|
||||
}
|
||||
else if (ref.mState==CSMWorld::RecordBase::State_Deleted)
|
||||
{
|
||||
/// \todo write record with delete flag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mState.getWriter().endRecord (cell.mModified.sRecordId);
|
||||
}
|
||||
else if (cell.mState==CSMWorld::RecordBase::State_Deleted)
|
||||
{
|
||||
/// \todo write record with delete flag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state)
|
||||
: mState (state)
|
||||
{}
|
||||
|
|
|
@ -163,8 +163,40 @@ namespace CSMDoc
|
|||
|
||||
virtual void perform (int stage, Messages& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
|
||||
};
|
||||
|
||||
class CollectionReferencesStage : public Stage
|
||||
{
|
||||
Document& mDocument;
|
||||
SavingState& mState;
|
||||
|
||||
public:
|
||||
|
||||
CollectionReferencesStage (Document& document, SavingState& state);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, Messages& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
class WriteCellCollectionStage : public Stage
|
||||
{
|
||||
Document& mDocument;
|
||||
SavingState& mState;
|
||||
|
||||
public:
|
||||
|
||||
WriteCellCollectionStage (Document& document, SavingState& state);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, Messages& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
class CloseSaveStage : public Stage
|
||||
{
|
||||
|
|
|
@ -25,6 +25,8 @@ void CSMDoc::SavingState::start (Document& document, bool project)
|
|||
|
||||
mStream.clear();
|
||||
|
||||
mSubRecords.clear();
|
||||
|
||||
if (project)
|
||||
mPath = mProjectPath;
|
||||
else
|
||||
|
@ -61,3 +63,8 @@ bool CSMDoc::SavingState::isProjectFile() const
|
|||
{
|
||||
return mProjectFile;
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<int> >& CSMDoc::SavingState::getSubRecords()
|
||||
{
|
||||
return mSubRecords;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define CSM_DOC_SAVINGSTATE_H
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
@ -25,6 +26,7 @@ namespace CSMDoc
|
|||
ESM::ESMWriter mWriter;
|
||||
boost::filesystem::path mProjectPath;
|
||||
bool mProjectFile;
|
||||
std::map<std::string, std::vector<int> > mSubRecords; // record ID, list of subrecords
|
||||
|
||||
public:
|
||||
|
||||
|
@ -46,6 +48,8 @@ namespace CSMDoc
|
|||
|
||||
bool isProjectFile() const;
|
||||
///< Currently saving project file? (instead of content file)
|
||||
|
||||
std::map<std::string, std::vector<int> >& getSubRecords();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -18,8 +18,3 @@ void CSMWorld::Cell::load (ESM::ESMReader &esm)
|
|||
mId = stream.str();
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::Cell::addRef (const std::string& id)
|
||||
{
|
||||
mRefs.push_back (std::make_pair (id, false));
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef CSM_WOLRD_CELL_H
|
||||
#define CSM_WOLRD_CELL_H
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/loadcell.hpp>
|
||||
|
@ -15,12 +15,12 @@ namespace CSMWorld
|
|||
struct Cell : public ESM::Cell
|
||||
{
|
||||
std::string mId;
|
||||
std::vector<std::pair<std::string, bool> > mRefs; // ID, modified
|
||||
std::vector<std::string> mDeletedRefs;
|
||||
|
||||
/// These are the references modified by the edited content file. These are stored in
|
||||
/// mModified only.
|
||||
std::set<std::string> mTouchedRefs;
|
||||
|
||||
void load (ESM::ESMReader &esm);
|
||||
|
||||
void addRef (const std::string& id);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "columnbase.hpp"
|
||||
#include "columns.hpp"
|
||||
#include "info.hpp"
|
||||
#include "cell.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
@ -87,6 +88,45 @@ namespace CSMWorld
|
|||
}
|
||||
};
|
||||
|
||||
/// \brief Specialisation that takes care of the modified reference tracking
|
||||
template<>
|
||||
struct RecordStateColumn<Cell> : public Column<Cell>
|
||||
{
|
||||
RecordStateColumn<Cell>()
|
||||
: Column<Cell> (Columns::ColumnId_Modification, ColumnBase::Display_RecordState)
|
||||
{}
|
||||
|
||||
virtual QVariant get (const Record<Cell>& record) const
|
||||
{
|
||||
if (record.mState==Record<Cell>::State_Erased)
|
||||
return static_cast<int> (Record<Cell>::State_Deleted);
|
||||
|
||||
if (!record.mModified.mTouchedRefs.empty() &&
|
||||
!record.mState==Record<Cell>::State_Deleted &&
|
||||
!record.mState==Record<Cell>::State_ModifiedOnly)
|
||||
{
|
||||
static_cast<int> (Record<Cell>::State_Modified);
|
||||
}
|
||||
|
||||
return static_cast<int> (record.mState);
|
||||
}
|
||||
|
||||
virtual void set (Record<Cell>& record, const QVariant& data)
|
||||
{
|
||||
record.mState = static_cast<RecordBase::State> (data.toInt());
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool isUserEditable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct FixedRecordTypeColumn : public Column<ESXRecordT>
|
||||
{
|
||||
|
|
|
@ -489,6 +489,7 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
|
|||
delete mReader;
|
||||
mReader = 0;
|
||||
mDialogue = 0;
|
||||
mRefLoadCache.clear();
|
||||
|
||||
mReader = new ESM::ESMReader;
|
||||
mReader->setEncoder (&mEncoder);
|
||||
|
@ -513,6 +514,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Stage::Messages& messages)
|
|||
delete mReader;
|
||||
mReader = 0;
|
||||
mDialogue = 0;
|
||||
mRefLoadCache.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -534,9 +536,12 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Stage::Messages& messages)
|
|||
case ESM::REC_SPEL: mSpells.load (*mReader, mBase); break;
|
||||
|
||||
case ESM::REC_CELL:
|
||||
{
|
||||
mCells.load (*mReader, mBase);
|
||||
mRefs.load (*mReader, mCells.getSize()-1, mBase);
|
||||
std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (mCells.getSize()-1));
|
||||
mRefs.load (*mReader, mCells.getSize()-1, mBase, mRefLoadCache[cellId], messages);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::REC_ACTI: mReferenceables.load (*mReader, mBase, UniversalId::Type_Activator); break;
|
||||
case ESM::REC_ALCH: mReferenceables.load (*mReader, mBase, UniversalId::Type_Potion); break;
|
||||
|
|
|
@ -77,6 +77,7 @@ namespace CSMWorld
|
|||
const ESM::Dialogue *mDialogue; // last loaded dialogue
|
||||
bool mBase;
|
||||
bool mProject;
|
||||
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
|
||||
|
||||
// not implemented
|
||||
Data (const Data&);
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
|
||||
#include "ref.hpp"
|
||||
|
||||
#include "cell.hpp"
|
||||
|
||||
void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string& id)
|
||||
CSMWorld::CellRef::CellRef()
|
||||
{
|
||||
mId = id;
|
||||
mCell = cell.mId;
|
||||
mRefNum.mIndex = 0;
|
||||
|
||||
cell.addRef (mId);
|
||||
// special marker: This reference does not have a RefNum assign to it yet.
|
||||
mRefNum.mContentFile = -2;
|
||||
}
|
|
@ -3,11 +3,6 @@
|
|||
|
||||
#include <components/esm/cellref.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class Cell;
|
||||
|
@ -18,8 +13,7 @@ namespace CSMWorld
|
|||
std::string mId;
|
||||
std::string mCell;
|
||||
|
||||
void load (ESM::ESMReader &esm, Cell& cell, const std::string& id);
|
||||
///< Load cell ref and register it with \a cell.
|
||||
CellRef();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
|
||||
#include <sstream>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "ref.hpp"
|
||||
#include "cell.hpp"
|
||||
#include "universalid.hpp"
|
||||
#include "record.hpp"
|
||||
|
||||
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base)
|
||||
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base,
|
||||
std::map<ESM::RefNum, std::string>& cache, CSMDoc::Stage::Messages& messages)
|
||||
{
|
||||
Record<Cell> cell = mCells.getRecord (cellIndex);
|
||||
|
||||
|
@ -17,19 +20,79 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
|||
CellRef ref;
|
||||
|
||||
bool deleted = false;
|
||||
while (cell2.getNextRef (reader, ref, deleted))
|
||||
|
||||
while (ESM::Cell::getNextRef (reader, ref, deleted))
|
||||
{
|
||||
/// \todo handle deleted and moved references
|
||||
ref.load (reader, cell2, getNewId());
|
||||
ref.mCell = cell2.mId;
|
||||
|
||||
Record<CellRef> record2;
|
||||
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||
(base ? record2.mBase : record2.mModified) = ref;
|
||||
/// \todo handle moved references
|
||||
|
||||
appendRecord (record2);
|
||||
std::map<ESM::RefNum, std::string>::iterator iter = cache.find (ref.mRefNum);
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
if (iter==cache.end())
|
||||
{
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell,
|
||||
mCells.getId (cellIndex));
|
||||
|
||||
messages.push_back (std::make_pair (id,
|
||||
"Attempt to delete a non-existing reference"));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int index = getIndex (iter->second);
|
||||
|
||||
Record<CellRef> record = getRecord (index);
|
||||
|
||||
if (record.mState==RecordBase::State_BaseOnly)
|
||||
{
|
||||
removeRows (index, 1);
|
||||
cache.erase (iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.mModified.mTouchedRefs.insert (Misc::StringUtils::lowerCase (
|
||||
mCells.getId (cellIndex)));
|
||||
record.mState = RecordBase::State_Deleted;
|
||||
setRecord (index, record);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!base)
|
||||
cell.mModified.mTouchedRefs.insert (Misc::StringUtils::lowerCase (
|
||||
mCells.getId (cellIndex)));
|
||||
|
||||
if (iter==cache.end())
|
||||
{
|
||||
// new reference
|
||||
ref.mId = getNewId();
|
||||
|
||||
Record<CellRef> record;
|
||||
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||
(base ? record.mBase : record.mModified) = ref;
|
||||
|
||||
appendRecord (record);
|
||||
|
||||
cache.insert (std::make_pair (ref.mRefNum, ref.mId));
|
||||
}
|
||||
else
|
||||
{
|
||||
// old reference -> merge
|
||||
ref.mId = iter->second;
|
||||
|
||||
int index = getIndex (ref.mId);
|
||||
|
||||
Record<CellRef> record = getRecord (index);
|
||||
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified;
|
||||
(base ? record.mBase : record.mModified) = ref;
|
||||
|
||||
setRecord (index, record);
|
||||
}
|
||||
}
|
||||
|
||||
mCells.setRecord (cellIndex, cell);
|
||||
}
|
||||
|
||||
std::string CSMWorld::RefCollection::getNewId()
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#ifndef CSM_WOLRD_REFCOLLECTION_H
|
||||
#define CSM_WOLRD_REFCOLLECTION_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
#include "collection.hpp"
|
||||
#include "ref.hpp"
|
||||
#include "record.hpp"
|
||||
|
@ -22,7 +26,9 @@ namespace CSMWorld
|
|||
: mCells (cells), mNextId (0)
|
||||
{}
|
||||
|
||||
void load (ESM::ESMReader& reader, int cellIndex, bool base);
|
||||
void load (ESM::ESMReader& reader, int cellIndex, bool base,
|
||||
std::map<ESM::RefNum, std::string>& cache,
|
||||
CSMDoc::Stage::Messages& messages);
|
||||
///< Load a sequence of references.
|
||||
|
||||
std::string getNewId();
|
||||
|
|
|
@ -172,3 +172,14 @@ bool ESM::operator== (const RefNum& left, const RefNum& right)
|
|||
{
|
||||
return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile;
|
||||
}
|
||||
|
||||
bool ESM::operator< (const RefNum& left, const RefNum& right)
|
||||
{
|
||||
if (left.mIndex<right.mIndex)
|
||||
return true;
|
||||
|
||||
if (left.mIndex>right.mIndex)
|
||||
return false;
|
||||
|
||||
return left.mContentFile<right.mContentFile;
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ namespace ESM
|
|||
};
|
||||
|
||||
bool operator== (const RefNum& left, const RefNum& right);
|
||||
bool operator< (const RefNum& left, const RefNum& right);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue