Merge branch 'refs'

Conflicts:
	apps/opencs/model/doc/savingstate.cpp
	components/esm/cellref.hpp
deque
Marc Zinnschlag 11 years ago
commit be7f344eca

@ -65,7 +65,11 @@ CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& proje
appendStage (new WriteRefIdCollectionStage (mDocument, mState)); 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 CloseSaveStage (mState));
appendStage (new FinalSavingStage (mDocument, mState)); appendStage (new FinalSavingStage (mDocument, mState));

@ -9,6 +9,8 @@
#include <components/esm/loaddial.hpp> #include <components/esm/loaddial.hpp>
#include <components/misc/stringops.hpp>
#include "../world/infocollection.hpp" #include "../world/infocollection.hpp"
#include "document.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) CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state)
: mState (state) : mState (state)
{} {}

@ -161,10 +161,42 @@ namespace CSMDoc
WriteFilterStage (Document& document, SavingState& state, CSMFilter::Filter::Scope scope); WriteFilterStage (Document& document, SavingState& state, CSMFilter::Filter::Scope scope);
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); virtual void perform (int stage, Messages& messages);
///< Messages resulting from this stage will be appended to \a 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 class CloseSaveStage : public Stage
{ {

@ -25,6 +25,8 @@ void CSMDoc::SavingState::start (Document& document, bool project)
mStream.clear(); mStream.clear();
mSubRecords.clear();
if (project) if (project)
mPath = mProjectPath; mPath = mProjectPath;
else else
@ -61,3 +63,8 @@ bool CSMDoc::SavingState::isProjectFile() const
{ {
return mProjectFile; return mProjectFile;
} }
std::map<std::string, std::vector<int> >& CSMDoc::SavingState::getSubRecords()
{
return mSubRecords;
}

@ -2,6 +2,7 @@
#define CSM_DOC_SAVINGSTATE_H #define CSM_DOC_SAVINGSTATE_H
#include <fstream> #include <fstream>
#include <map>
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
@ -25,6 +26,7 @@ namespace CSMDoc
ESM::ESMWriter mWriter; ESM::ESMWriter mWriter;
boost::filesystem::path mProjectPath; boost::filesystem::path mProjectPath;
bool mProjectFile; bool mProjectFile;
std::map<std::string, std::vector<int> > mSubRecords; // record ID, list of subrecords
public: public:
@ -46,6 +48,8 @@ namespace CSMDoc
bool isProjectFile() const; bool isProjectFile() const;
///< Currently saving project file? (instead of content file) ///< 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(); 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 #ifndef CSM_WOLRD_CELL_H
#define CSM_WOLRD_CELL_H #define CSM_WOLRD_CELL_H
#include <vector> #include <set>
#include <string> #include <string>
#include <components/esm/loadcell.hpp> #include <components/esm/loadcell.hpp>
@ -15,12 +15,12 @@ namespace CSMWorld
struct Cell : public ESM::Cell struct Cell : public ESM::Cell
{ {
std::string mId; std::string mId;
std::vector<std::pair<std::string, bool> > mRefs; // ID, modified
std::vector<std::string> mDeletedRefs;
void load (ESM::ESMReader &esm); /// These are the references modified by the edited content file. These are stored in
/// mModified only.
std::set<std::string> mTouchedRefs;
void addRef (const std::string& id); void load (ESM::ESMReader &esm);
}; };
} }

@ -10,6 +10,7 @@
#include "columnbase.hpp" #include "columnbase.hpp"
#include "columns.hpp" #include "columns.hpp"
#include "info.hpp" #include "info.hpp"
#include "cell.hpp"
namespace CSMWorld 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> template<typename ESXRecordT>
struct FixedRecordTypeColumn : public Column<ESXRecordT> struct FixedRecordTypeColumn : public Column<ESXRecordT>
{ {

@ -489,6 +489,7 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
delete mReader; delete mReader;
mReader = 0; mReader = 0;
mDialogue = 0; mDialogue = 0;
mRefLoadCache.clear();
mReader = new ESM::ESMReader; mReader = new ESM::ESMReader;
mReader->setEncoder (&mEncoder); mReader->setEncoder (&mEncoder);
@ -513,6 +514,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Stage::Messages& messages)
delete mReader; delete mReader;
mReader = 0; mReader = 0;
mDialogue = 0; mDialogue = 0;
mRefLoadCache.clear();
return true; 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_SPEL: mSpells.load (*mReader, mBase); break;
case ESM::REC_CELL: case ESM::REC_CELL:
{
mCells.load (*mReader, mBase); 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; break;
}
case ESM::REC_ACTI: mReferenceables.load (*mReader, mBase, UniversalId::Type_Activator); 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; 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 const ESM::Dialogue *mDialogue; // last loaded dialogue
bool mBase; bool mBase;
bool mProject; bool mProject;
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
// not implemented // not implemented
Data (const Data&); Data (const Data&);

@ -1,12 +1,10 @@
#include "ref.hpp" #include "ref.hpp"
#include "cell.hpp" CSMWorld::CellRef::CellRef()
void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string& id)
{ {
mId = id; mRefNum.mIndex = 0;
mCell = cell.mId;
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> #include <components/esm/cellref.hpp>
namespace ESM
{
class ESMReader;
}
namespace CSMWorld namespace CSMWorld
{ {
class Cell; class Cell;
@ -18,8 +13,7 @@ namespace CSMWorld
std::string mId; std::string mId;
std::string mCell; std::string mCell;
void load (ESM::ESMReader &esm, Cell& cell, const std::string& id); CellRef();
///< Load cell ref and register it with \a cell.
}; };
} }

@ -3,12 +3,15 @@
#include <sstream> #include <sstream>
#include <components/misc/stringops.hpp>
#include "ref.hpp" #include "ref.hpp"
#include "cell.hpp" #include "cell.hpp"
#include "universalid.hpp" #include "universalid.hpp"
#include "record.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); Record<Cell> cell = mCells.getRecord (cellIndex);
@ -17,19 +20,79 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
CellRef ref; CellRef ref;
bool deleted = false; bool deleted = false;
while (cell2.getNextRef (reader, ref, deleted))
while (ESM::Cell::getNextRef (reader, ref, deleted))
{ {
/// \todo handle deleted and moved references ref.mCell = cell2.mId;
ref.load (reader, cell2, getNewId());
Record<CellRef> record2; /// \todo handle moved references
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = ref;
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);
}
mCells.setRecord (cellIndex, cell); 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);
}
}
} }
std::string CSMWorld::RefCollection::getNewId() std::string CSMWorld::RefCollection::getNewId()

@ -1,6 +1,10 @@
#ifndef CSM_WOLRD_REFCOLLECTION_H #ifndef CSM_WOLRD_REFCOLLECTION_H
#define CSM_WOLRD_REFCOLLECTION_H #define CSM_WOLRD_REFCOLLECTION_H
#include <map>
#include "../doc/stage.hpp"
#include "collection.hpp" #include "collection.hpp"
#include "ref.hpp" #include "ref.hpp"
#include "record.hpp" #include "record.hpp"
@ -22,7 +26,9 @@ namespace CSMWorld
: mCells (cells), mNextId (0) : 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. ///< Load a sequence of references.
std::string getNewId(); 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; 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);
bool operator< (const RefNum& left, const RefNum& right);
} }
#endif #endif

Loading…
Cancel
Save