Merge branch 'mergetool'

Conflicts:
	apps/opencs/CMakeLists.txt
	apps/opencs/model/tools/tools.cpp
pull/757/head
Marc Zinnschlag 9 years ago
commit a445683312

@ -841,19 +841,13 @@ void Record<ESM::Land>::print()
std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl; std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl;
std::cout << " DataTypes: " << mData.mDataTypes << std::endl; std::cout << " DataTypes: " << mData.mDataTypes << std::endl;
// Seems like this should done with reference counting in the if (const ESM::Land::LandData *data = mData.getLandData (mData.mDataTypes))
// loader to me. But I'm not really knowledgable about this {
// record type yet. --Cory std::cout << " Height Offset: " << data->mHeightOffset << std::endl;
bool wasLoaded = (mData.mDataLoaded != 0);
if (mData.mDataTypes) mData.loadData(mData.mDataTypes);
if (mData.mDataLoaded)
{
std::cout << " Height Offset: " << mData.mLandData->mHeightOffset << std::endl;
// Lots of missing members. // Lots of missing members.
std::cout << " Unknown1: " << mData.mLandData->mUnk1 << std::endl; std::cout << " Unknown1: " << data->mUnk1 << std::endl;
std::cout << " Unknown2: " << mData.mLandData->mUnk2 << std::endl; std::cout << " Unknown2: " << data->mUnk2 << std::endl;
} }
if (!wasLoaded) mData.unloadData();
} }
template<> template<>

@ -35,13 +35,18 @@ opencs_hdrs_noqt (model/world
opencs_units (model/tools opencs_units (model/tools
tools reportmodel tools reportmodel mergeoperation
) )
opencs_units_noqt (model/tools opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
mergestages
)
opencs_hdrs_noqt (model/tools
mergestate
) )
@ -94,7 +99,7 @@ opencs_hdrs_noqt (view/render
opencs_units (view/tools opencs_units (view/tools
reportsubview reporttable searchsubview searchbox reportsubview reporttable searchsubview searchbox merge
) )
opencs_units_noqt (view/tools opencs_units_noqt (view/tools

@ -20,7 +20,8 @@
CS::Editor::Editor () CS::Editor::Editor ()
: mUserSettings (mCfgMgr), mDocumentManager (mCfgMgr), : mUserSettings (mCfgMgr), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager), mPid(""), mViewManager (mDocumentManager), mPid(""),
mLock(), mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL) mLock(), mMerge (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
{ {
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig(); std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
@ -39,9 +40,12 @@ CS::Editor::Editor ()
mNewGame.setLocalData (mLocal); mNewGame.setLocalData (mLocal);
mFileDialog.setLocalData (mLocal); mFileDialog.setLocalData (mLocal);
mMerge.setLocalData (mLocal);
connect (&mDocumentManager, SIGNAL (documentAdded (CSMDoc::Document *)), connect (&mDocumentManager, SIGNAL (documentAdded (CSMDoc::Document *)),
this, SLOT (documentAdded (CSMDoc::Document *))); this, SLOT (documentAdded (CSMDoc::Document *)));
connect (&mDocumentManager, SIGNAL (documentAboutToBeRemoved (CSMDoc::Document *)),
this, SLOT (documentAboutToBeRemoved (CSMDoc::Document *)));
connect (&mDocumentManager, SIGNAL (lastDocumentDeleted()), connect (&mDocumentManager, SIGNAL (lastDocumentDeleted()),
this, SLOT (lastDocumentDeleted())); this, SLOT (lastDocumentDeleted()));
@ -49,6 +53,7 @@ CS::Editor::Editor ()
connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ())); connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ())); connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
connect (&mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ())); connect (&mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ()));
connect (&mViewManager, SIGNAL (mergeDocument (CSMDoc::Document *)), this, SLOT (mergeDocument (CSMDoc::Document *)));
connect (&mStartup, SIGNAL (createGame()), this, SLOT (createGame ())); connect (&mStartup, SIGNAL (createGame()), this, SLOT (createGame ()));
connect (&mStartup, SIGNAL (createAddon()), this, SLOT (createAddon ())); connect (&mStartup, SIGNAL (createAddon()), this, SLOT (createAddon ()));
@ -359,7 +364,21 @@ void CS::Editor::documentAdded (CSMDoc::Document *document)
mViewManager.addView (document); mViewManager.addView (document);
} }
void CS::Editor::documentAboutToBeRemoved (CSMDoc::Document *document)
{
if (mMerge.getDocument()==document)
mMerge.cancel();
}
void CS::Editor::lastDocumentDeleted() void CS::Editor::lastDocumentDeleted()
{ {
QApplication::quit(); QApplication::quit();
} }
void CS::Editor::mergeDocument (CSMDoc::Document *document)
{
mMerge.configure (document);
mMerge.show();
mMerge.raise();
mMerge.activateWindow();
}

@ -27,11 +27,18 @@
#include "view/settings/dialog.hpp" #include "view/settings/dialog.hpp"
#include "view/tools/merge.hpp"
namespace VFS namespace VFS
{ {
class Manager; class Manager;
} }
namespace CSMDoc
{
class Document;
}
namespace CS namespace CS
{ {
class Editor : public QObject class Editor : public QObject
@ -55,6 +62,7 @@ namespace CS
boost::interprocess::file_lock mLock; boost::interprocess::file_lock mLock;
boost::filesystem::ofstream mPidFile; boost::filesystem::ofstream mPidFile;
bool mFsStrict; bool mFsStrict;
CSVTools::Merge mMerge;
void setupDataFiles (const Files::PathContainer& dataDirs); void setupDataFiles (const Files::PathContainer& dataDirs);
@ -94,8 +102,12 @@ namespace CS
void documentAdded (CSMDoc::Document *document); void documentAdded (CSMDoc::Document *document);
void documentAboutToBeRemoved (CSMDoc::Document *document);
void lastDocumentDeleted(); void lastDocumentDeleted();
void mergeDocument (CSMDoc::Document *document);
private: private:
QString mIpcServerName; QString mIpcServerName;

@ -2250,13 +2250,13 @@ CSMDoc::Document::Document (const VFS::Manager* vfs, const Files::ConfigurationM
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
const std::vector<std::string>& blacklistedScripts) const std::vector<std::string>& blacklistedScripts)
: mVFS(vfs), mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager), : mVFS(vfs), mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager),
mTools (*this), mTools (*this, encoding),
mProjectPath ((configuration.getUserDataPath() / "projects") / mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")), (savePath.filename().string() + ".project")),
mSavingOperation (*this, mProjectPath, encoding), mSavingOperation (*this, mProjectPath, encoding),
mSaving (&mSavingOperation), mSaving (&mSavingOperation),
mResDir(resDir), mResDir(resDir),
mRunner (mProjectPath), mIdCompletionManager(mData) mRunner (mProjectPath), mDirty (false), mIdCompletionManager(mData)
{ {
if (mContentFiles.empty()) if (mContentFiles.empty())
throw std::runtime_error ("Empty content file sequence"); throw std::runtime_error ("Empty content file sequence");
@ -2294,6 +2294,8 @@ CSMDoc::Document::Document (const VFS::Manager* vfs, const Files::ConfigurationM
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool))); connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
connect (&mTools, SIGNAL (mergeDone (CSMDoc::Document*)),
this, SIGNAL (mergeDone (CSMDoc::Document*)));
connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool))); connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
@ -2323,7 +2325,7 @@ int CSMDoc::Document::getState() const
{ {
int state = 0; int state = 0;
if (!mUndoStack.isClean()) if (!mUndoStack.isClean() || mDirty)
state |= State_Modified; state |= State_Modified;
if (mSaving.isRunning()) if (mSaving.isRunning())
@ -2388,6 +2390,12 @@ void CSMDoc::Document::runSearch (const CSMWorld::UniversalId& searchId, const C
emit stateChanged (getState(), this); emit stateChanged (getState(), this);
} }
void CSMDoc::Document::runMerge (std::auto_ptr<CSMDoc::Document> target)
{
mTools.runMerge (target);
emit stateChanged (getState(), this);
}
void CSMDoc::Document::abortOperation (int type) void CSMDoc::Document::abortOperation (int type)
{ {
if (type==State_Saving) if (type==State_Saving)
@ -2409,6 +2417,9 @@ void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type)
void CSMDoc::Document::operationDone (int type, bool failed) void CSMDoc::Document::operationDone (int type, bool failed)
{ {
if (type==CSMDoc::State_Saving && !failed)
mDirty = false;
emit stateChanged (getState(), this); emit stateChanged (getState(), this);
} }
@ -2485,3 +2496,8 @@ CSMWorld::IdCompletionManager &CSMDoc::Document::getIdCompletionManager()
{ {
return mIdCompletionManager; return mIdCompletionManager;
} }
void CSMDoc::Document::flagAsDirty()
{
mDirty = true;
}

@ -68,6 +68,7 @@ namespace CSMDoc
boost::filesystem::path mResDir; boost::filesystem::path mResDir;
Blacklist mBlacklist; Blacklist mBlacklist;
Runner mRunner; Runner mRunner;
bool mDirty;
CSMWorld::IdCompletionManager mIdCompletionManager; CSMWorld::IdCompletionManager mIdCompletionManager;
@ -129,7 +130,9 @@ namespace CSMDoc
CSMWorld::UniversalId newSearch(); CSMWorld::UniversalId newSearch();
void runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search); void runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search);
void runMerge (std::auto_ptr<CSMDoc::Document> target);
void abortOperation (int type); void abortOperation (int type);
const CSMWorld::Data& getData() const; const CSMWorld::Data& getData() const;
@ -150,12 +153,18 @@ namespace CSMDoc
CSMWorld::IdCompletionManager &getIdCompletionManager(); CSMWorld::IdCompletionManager &getIdCompletionManager();
void flagAsDirty();
signals: signals:
void stateChanged (int state, CSMDoc::Document *document); void stateChanged (int state, CSMDoc::Document *document);
void progress (int current, int max, int type, int threads, CSMDoc::Document *document); void progress (int current, int max, int type, int threads, CSMDoc::Document *document);
/// \attention When this signal is emitted, *this hands over the ownership of the
/// document. This signal must be handled to avoid a leak.
void mergeDone (CSMDoc::Document *document);
private slots: private slots:
void modificationStateChanged (bool clean); void modificationStateChanged (bool clean);
@ -173,4 +182,3 @@ namespace CSMDoc
} }
#endif #endif

@ -56,10 +56,24 @@ bool CSMDoc::DocumentManager::isEmpty()
void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath, void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_) bool new_)
{ {
Document *document = new Document (mVFS, mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager, mBlacklistedScripts); Document *document = makeDocument (files, savePath, new_);
insertDocument (document);
}
CSMDoc::Document *CSMDoc::DocumentManager::makeDocument (
const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_)
{
return new Document (mVFS, mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager, mBlacklistedScripts);
}
void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document)
{
mDocuments.push_back (document); mDocuments.push_back (document);
connect (document, SIGNAL (mergeDone (CSMDoc::Document*)),
this, SLOT (insertDocument (CSMDoc::Document*)));
emit loadRequest (document); emit loadRequest (document);
mLoader.hasThingsToDo().wakeAll(); mLoader.hasThingsToDo().wakeAll();
@ -72,6 +86,8 @@ void CSMDoc::DocumentManager::removeDocument (CSMDoc::Document *document)
if (iter==mDocuments.end()) if (iter==mDocuments.end())
throw std::runtime_error ("removing invalid document"); throw std::runtime_error ("removing invalid document");
emit documentAboutToBeRemoved (document);
mDocuments.erase (iter); mDocuments.erase (iter);
document->deleteLater(); document->deleteLater();

@ -56,6 +56,15 @@ namespace CSMDoc
///< \param new_ Do not load the last content file in \a files and instead create in an ///< \param new_ Do not load the last content file in \a files and instead create in an
/// appropriate way. /// appropriate way.
/// Create a new document. The ownership of the created document is transferred to
/// the calling function. The DocumentManager does not manage it. Loading has not
/// taken place at the point when the document is returned.
///
/// \param new_ Do not load the last content file in \a files and instead create in an
/// appropriate way.
Document *makeDocument (const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_);
void setResourceDir (const boost::filesystem::path& parResDir); void setResourceDir (const boost::filesystem::path& parResDir);
void setEncoding (ToUTF8::FromType encoding); void setEncoding (ToUTF8::FromType encoding);
@ -84,10 +93,16 @@ namespace CSMDoc
void removeDocument (CSMDoc::Document *document); void removeDocument (CSMDoc::Document *document);
///< Emits the lastDocumentDeleted signal, if applicable. ///< Emits the lastDocumentDeleted signal, if applicable.
/// Hand over document to *this. The ownership is transferred. The DocumentManager
/// will initiate the load procedure, if necessary
void insertDocument (CSMDoc::Document *document);
signals: signals:
void documentAdded (CSMDoc::Document *document); void documentAdded (CSMDoc::Document *document);
void documentAboutToBeRemoved (CSMDoc::Document *document);
void loadRequest (CSMDoc::Document *document); void loadRequest (CSMDoc::Document *document);
void lastDocumentDeleted(); void lastDocumentDeleted();

@ -83,7 +83,9 @@ namespace CSMDoc
void executeStage(); void executeStage();
void operationDone(); protected slots:
virtual void operationDone();
}; };
} }

@ -415,15 +415,16 @@ void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages)
if (land.mState==CSMWorld::RecordBase::State_Modified || if (land.mState==CSMWorld::RecordBase::State_Modified ||
land.mState==CSMWorld::RecordBase::State_ModifiedOnly) land.mState==CSMWorld::RecordBase::State_ModifiedOnly)
{ {
CSMWorld::Land record = land.get(); const CSMWorld::Land& record = land.get();
mState.getWriter().startRecord (record.mLand->sRecordId); mState.getWriter().startRecord (record.sRecordId);
record.save (mState.getWriter());
record.mLand->save (mState.getWriter()); if (const ESM::Land::LandData *data = record.getLandData (record.mDataTypes))
if(record.mLand->mLandData) data->save (mState.getWriter());
record.mLand->mLandData->save (mState.getWriter());
mState.getWriter().endRecord (record.mLand->sRecordId); mState.getWriter().endRecord (record.sRecordId);
} }
else if (land.mState==CSMWorld::RecordBase::State_Deleted) else if (land.mState==CSMWorld::RecordBase::State_Deleted)
{ {

@ -12,7 +12,7 @@ namespace CSMDoc
State_Saving = 16, State_Saving = 16,
State_Verifying = 32, State_Verifying = 32,
State_Compiling = 64, // not implemented yet State_Merging = 64,
State_Searching = 128, State_Searching = 128,
State_Loading = 256 // pseudo-state; can not be encountered in a loaded document State_Loading = 256 // pseudo-state; can not be encountered in a loaded document
}; };

@ -0,0 +1,59 @@
#include "mergeoperation.hpp"
#include "../doc/state.hpp"
#include "../doc/document.hpp"
#include "mergestages.hpp"
CSMTools::MergeOperation::MergeOperation (CSMDoc::Document& document, ToUTF8::FromType encoding)
: CSMDoc::Operation (CSMDoc::State_Merging, true), mState (document)
{
appendStage (new StartMergeStage (mState));
appendStage (new MergeIdCollectionStage<ESM::Global> (mState, &CSMWorld::Data::getGlobals));
appendStage (new MergeIdCollectionStage<ESM::GameSetting> (mState, &CSMWorld::Data::getGmsts));
appendStage (new MergeIdCollectionStage<ESM::Skill> (mState, &CSMWorld::Data::getSkills));
appendStage (new MergeIdCollectionStage<ESM::Class> (mState, &CSMWorld::Data::getClasses));
appendStage (new MergeIdCollectionStage<ESM::Faction> (mState, &CSMWorld::Data::getFactions));
appendStage (new MergeIdCollectionStage<ESM::Race> (mState, &CSMWorld::Data::getRaces));
appendStage (new MergeIdCollectionStage<ESM::Sound> (mState, &CSMWorld::Data::getSounds));
appendStage (new MergeIdCollectionStage<ESM::Script> (mState, &CSMWorld::Data::getScripts));
appendStage (new MergeIdCollectionStage<ESM::Region> (mState, &CSMWorld::Data::getRegions));
appendStage (new MergeIdCollectionStage<ESM::BirthSign> (mState, &CSMWorld::Data::getBirthsigns));
appendStage (new MergeIdCollectionStage<ESM::Spell> (mState, &CSMWorld::Data::getSpells));
appendStage (new MergeIdCollectionStage<ESM::Dialogue> (mState, &CSMWorld::Data::getTopics));
appendStage (new MergeIdCollectionStage<ESM::Dialogue> (mState, &CSMWorld::Data::getJournals));
appendStage (new MergeIdCollectionStage<CSMWorld::Cell> (mState, &CSMWorld::Data::getCells));
appendStage (new MergeIdCollectionStage<ESM::Filter> (mState, &CSMWorld::Data::getFilters));
appendStage (new MergeIdCollectionStage<ESM::Enchantment> (mState, &CSMWorld::Data::getEnchantments));
appendStage (new MergeIdCollectionStage<ESM::BodyPart> (mState, &CSMWorld::Data::getBodyParts));
appendStage (new MergeIdCollectionStage<ESM::DebugProfile> (mState, &CSMWorld::Data::getDebugProfiles));
appendStage (new MergeIdCollectionStage<ESM::SoundGenerator> (mState, &CSMWorld::Data::getSoundGens));
appendStage (new MergeIdCollectionStage<ESM::MagicEffect> (mState, &CSMWorld::Data::getMagicEffects));
appendStage (new MergeIdCollectionStage<ESM::StartScript> (mState, &CSMWorld::Data::getStartScripts));
appendStage (new MergeIdCollectionStage<CSMWorld::Pathgrid, CSMWorld::SubCellCollection<CSMWorld::Pathgrid> > (mState, &CSMWorld::Data::getPathgrids));
appendStage (new MergeIdCollectionStage<CSMWorld::Info, CSMWorld::InfoCollection> (mState, &CSMWorld::Data::getTopicInfos));
appendStage (new MergeIdCollectionStage<CSMWorld::Info, CSMWorld::InfoCollection> (mState, &CSMWorld::Data::getJournalInfos));
appendStage (new MergeRefIdsStage (mState));
appendStage (new MergeReferencesStage (mState));
appendStage (new MergeReferencesStage (mState));
appendStage (new ListLandTexturesMergeStage (mState));
appendStage (new MergeLandTexturesStage (mState));
appendStage (new MergeLandStage (mState));
appendStage (new FinishMergedDocumentStage (mState, encoding));
}
void CSMTools::MergeOperation::setTarget (std::auto_ptr<CSMDoc::Document> document)
{
mState.mTarget = document;
}
void CSMTools::MergeOperation::operationDone()
{
CSMDoc::Operation::operationDone();
if (mState.mCompleted)
emit mergeDone (mState.mTarget.release());
}

@ -0,0 +1,45 @@
#ifndef CSM_TOOLS_MERGEOPERATION_H
#define CSM_TOOLS_MERGEOPERATION_H
#include <memory>
#include <components/to_utf8/to_utf8.hpp>
#include "../doc/operation.hpp"
#include "mergestate.hpp"
namespace CSMDoc
{
class Document;
}
namespace CSMTools
{
class MergeOperation : public CSMDoc::Operation
{
Q_OBJECT
MergeState mState;
public:
MergeOperation (CSMDoc::Document& document, ToUTF8::FromType encoding);
/// \attention Do not call this function while a merge is running.
void setTarget (std::auto_ptr<CSMDoc::Document> document);
protected slots:
virtual void operationDone();
signals:
/// \attention When this signal is emitted, *this hands over the ownership of the
/// document. This signal must be handled to avoid a leak.
void mergeDone (CSMDoc::Document *document);
};
}
#endif

@ -0,0 +1,258 @@
#include "mergestages.hpp"
#include <sstream>
#include <components/misc/stringops.hpp>
#include "mergestate.hpp"
#include "../doc/document.hpp"
#include "../world/data.hpp"
CSMTools::StartMergeStage::StartMergeStage (MergeState& state)
: mState (state)
{}
int CSMTools::StartMergeStage::setup()
{
return 1;
}
void CSMTools::StartMergeStage::perform (int stage, CSMDoc::Messages& messages)
{
mState.mCompleted = false;
mState.mTextureIndices.clear();
}
CSMTools::FinishMergedDocumentStage::FinishMergedDocumentStage (MergeState& state, ToUTF8::FromType encoding)
: mState (state), mEncoder (encoding)
{}
int CSMTools::FinishMergedDocumentStage::setup()
{
return 1;
}
void CSMTools::FinishMergedDocumentStage::perform (int stage, CSMDoc::Messages& messages)
{
// We know that the content file list contains at least two entries and that the first one
// does exist on disc (otherwise it would have been impossible to initiate a merge on that
// document).
boost::filesystem::path path = mState.mSource.getContentFiles()[0];
ESM::ESMReader reader;
reader.setEncoder (&mEncoder);
reader.open (path.string());
CSMWorld::MetaData source;
source.mId = "sys::meta";
source.load (reader);
CSMWorld::MetaData target = mState.mTarget->getData().getMetaData();
target.mAuthor = source.mAuthor;
target.mDescription = source.mDescription;
mState.mTarget->getData().setMetaData (target);
mState.mCompleted = true;
}
CSMTools::MergeRefIdsStage::MergeRefIdsStage (MergeState& state) : mState (state) {}
int CSMTools::MergeRefIdsStage::setup()
{
return mState.mSource.getData().getReferenceables().getSize();
}
void CSMTools::MergeRefIdsStage::perform (int stage, CSMDoc::Messages& messages)
{
mState.mSource.getData().getReferenceables().copyTo (
stage, mState.mTarget->getData().getReferenceables());
}
CSMTools::MergeReferencesStage::MergeReferencesStage (MergeState& state)
: mState (state)
{}
int CSMTools::MergeReferencesStage::setup()
{
mIndex.clear();
return mState.mSource.getData().getReferences().getSize();
}
void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::CellRef>& record =
mState.mSource.getData().getReferences().getRecord (stage);
if (!record.isDeleted())
{
CSMWorld::CellRef ref = record.get();
ref.mOriginalCell = ref.mCell;
ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++;
ref.mRefNum.mContentFile = 0;
CSMWorld::Record<CSMWorld::CellRef> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref);
mState.mTarget->getData().getReferences().appendRecord (newRecord);
}
}
CSMTools::ListLandTexturesMergeStage::ListLandTexturesMergeStage (MergeState& state)
: mState (state)
{}
int CSMTools::ListLandTexturesMergeStage::setup()
{
return mState.mSource.getData().getLand().getSize();
}
void CSMTools::ListLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::Land>& record =
mState.mSource.getData().getLand().getRecord (stage);
if (!record.isDeleted())
{
const CSMWorld::Land& land = record.get();
// make sure record is loaded
land.loadData (ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |
ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM);
if (const ESM::Land::LandData *data = land.getLandData (ESM::Land::DATA_VTEX))
{
// list texture indices
std::pair<uint16_t, int> key;
key.second = land.mPlugin;
for (int i=0; i<ESM::Land::LAND_NUM_TEXTURES; ++i)
{
key.first = data->mTextures[i];
mState.mTextureIndices[key] = -1;
}
}
}
}
CSMTools::MergeLandTexturesStage::MergeLandTexturesStage (MergeState& state)
: mState (state), mNext (mState.mTextureIndices.end())
{}
int CSMTools::MergeLandTexturesStage::setup()
{
// Should use the size of mState.mTextureIndices instead, but that is not available at this
// point. Unless there are any errors in the land and land texture records this will not
// make a difference.
return mState.mSource.getData().getLandTextures().getSize();
}
void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& messages)
{
if (stage==0)
mNext = mState.mTextureIndices.begin();
bool found = false;
do
{
if (mNext==mState.mTextureIndices.end())
return;
mNext->second = stage+1;
std::ostringstream stream;
stream << mNext->first.first-1 << "_" << mNext->first.second;
int index = mState.mSource.getData().getLandTextures().searchId (stream.str());
if (index!=-1)
{
CSMWorld::LandTexture texture =
mState.mSource.getData().getLandTextures().getRecord (index).get();
std::ostringstream stream;
stream << mNext->second-1 << "_0";
texture.mIndex = mNext->second-1;
texture.mId = stream.str();
CSMWorld::Record<CSMWorld::LandTexture> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture);
mState.mTarget->getData().getLandTextures().appendRecord (newRecord);
found = true;
}
++mNext;
}
while (!found);
}
CSMTools::MergeLandStage::MergeLandStage (MergeState& state) : mState (state) {}
int CSMTools::MergeLandStage::setup()
{
return mState.mSource.getData().getLand().getSize();
}
void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::Land>& record =
mState.mSource.getData().getLand().getRecord (stage);
if (!record.isDeleted())
{
const CSMWorld::Land& land = record.get();
land.loadData (ESM::Land::DATA_VCLR | ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |
ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM);
CSMWorld::Land newLand (land);
newLand.mEsm = 0; // avoid potential dangling pointer (ESMReader isn't needed anyway,
// because record is already fully loaded)
newLand.mPlugin = 0;
if (land.mDataTypes & ESM::Land::DATA_VTEX)
{
// adjust land texture references
if (ESM::Land::LandData *data = newLand.getLandData())
{
std::pair<uint16_t, int> key;
key.second = land.mPlugin;
for (int i=0; i<ESM::Land::LAND_NUM_TEXTURES; ++i)
{
key.first = data->mTextures[i];
std::map<std::pair<uint16_t, int>, int>::const_iterator iter =
mState.mTextureIndices.find (key);
if (iter!=mState.mTextureIndices.end())
data->mTextures[i] = iter->second;
else
data->mTextures[i] = 0;
}
}
}
CSMWorld::Record<CSMWorld::Land> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand);
mState.mTarget->getData().getLand().appendRecord (newRecord);
}
}

@ -0,0 +1,166 @@
#ifndef CSM_TOOLS_MERGESTAGES_H
#define CSM_TOOLS_MERGESTAGES_H
#include <algorithm>
#include <map>
#include <components/to_utf8/to_utf8.hpp>
#include "../doc/stage.hpp"
#include "../world/data.hpp"
#include "mergestate.hpp"
namespace CSMTools
{
class StartMergeStage : public CSMDoc::Stage
{
MergeState& mState;
public:
StartMergeStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class FinishMergedDocumentStage : public CSMDoc::Stage
{
MergeState& mState;
ToUTF8::Utf8Encoder mEncoder;
public:
FinishMergedDocumentStage (MergeState& state, ToUTF8::FromType encoding);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
template<typename RecordType, typename Collection = CSMWorld::IdCollection<RecordType> >
class MergeIdCollectionStage : public CSMDoc::Stage
{
MergeState& mState;
Collection& (CSMWorld::Data::*mAccessor)();
public:
MergeIdCollectionStage (MergeState& state, Collection& (CSMWorld::Data::*accessor)());
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
template<typename RecordType, typename Collection>
MergeIdCollectionStage<RecordType, Collection>::MergeIdCollectionStage (MergeState& state, Collection& (CSMWorld::Data::*accessor)())
: mState (state), mAccessor (accessor)
{}
template<typename RecordType, typename Collection>
int MergeIdCollectionStage<RecordType, Collection>::setup()
{
return (mState.mSource.getData().*mAccessor)().getSize();
}
template<typename RecordType, typename Collection>
void MergeIdCollectionStage<RecordType, Collection>::perform (int stage, CSMDoc::Messages& messages)
{
const Collection& source = (mState.mSource.getData().*mAccessor)();
Collection& target = (mState.mTarget->getData().*mAccessor)();
const CSMWorld::Record<RecordType>& record = source.getRecord (stage);
if (!record.isDeleted())
target.appendRecord (CSMWorld::Record<RecordType> (CSMWorld::RecordBase::State_ModifiedOnly, 0, &record.get()));
}
class MergeRefIdsStage : public CSMDoc::Stage
{
MergeState& mState;
public:
MergeRefIdsStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class MergeReferencesStage : public CSMDoc::Stage
{
MergeState& mState;
std::map<std::string, int> mIndex;
public:
MergeReferencesStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class ListLandTexturesMergeStage : public CSMDoc::Stage
{
MergeState& mState;
public:
ListLandTexturesMergeStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class MergeLandTexturesStage : public CSMDoc::Stage
{
MergeState& mState;
std::map<std::pair<uint16_t, int>, int>::iterator mNext;
public:
MergeLandTexturesStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class MergeLandStage : public CSMDoc::Stage
{
MergeState& mState;
public:
MergeLandStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
}
#endif

@ -0,0 +1,24 @@
#ifndef CSM_TOOLS_MERGESTATE_H
#define CSM_TOOLS_MERGESTATE_H
#include <stdint.h>
#include <memory>
#include <map>
#include "../doc/document.hpp"
namespace CSMTools
{
struct MergeState
{
std::auto_ptr<CSMDoc::Document> mTarget;
CSMDoc::Document& mSource;
bool mCompleted;
std::map<std::pair<uint16_t, int>, int> mTextureIndices; // (texture, content file) -> new texture
MergeState (CSMDoc::Document& source) : mSource (source), mCompleted (false) {}
};
}
#endif

@ -28,6 +28,7 @@
#include "pathgridcheck.hpp" #include "pathgridcheck.hpp"
#include "soundgencheck.hpp" #include "soundgencheck.hpp"
#include "magiceffectcheck.hpp" #include "magiceffectcheck.hpp"
#include "mergeoperation.hpp"
CSMDoc::OperationHolder *CSMTools::Tools::get (int type) CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
{ {
@ -35,6 +36,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
{ {
case CSMDoc::State_Verifying: return &mVerifier; case CSMDoc::State_Verifying: return &mVerifier;
case CSMDoc::State_Searching: return &mSearch; case CSMDoc::State_Searching: return &mSearch;
case CSMDoc::State_Merging: return &mMerge;
} }
return 0; return 0;
@ -53,7 +55,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
std::vector<QString> settings; std::vector<QString> settings;
settings.push_back ("script-editor/warnings"); settings.push_back ("script-editor/warnings");
mVerifierOperation->configureSettings (settings); mVerifierOperation->configureSettings (settings);
connect (&mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); connect (&mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
@ -120,9 +122,9 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
return &mVerifier; return &mVerifier;
} }
CSMTools::Tools::Tools (CSMDoc::Document& document) CSMTools::Tools::Tools (CSMDoc::Document& document, ToUTF8::FromType encoding)
: mDocument (document), mData (document.getData()), mVerifierOperation (0), : mDocument (document), mData (document.getData()), mVerifierOperation (0),
mSearchOperation (0), mNextReportNumber (0) mSearchOperation (0), mMergeOperation (0), mNextReportNumber (0), mEncoding (encoding)
{ {
// index 0: load error log // index 0: load error log
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel)); mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));
@ -132,6 +134,10 @@ CSMTools::Tools::Tools (CSMDoc::Document& document)
connect (&mSearch, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool))); connect (&mSearch, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool)));
connect (&mSearch, SIGNAL (reportMessage (const CSMDoc::Message&, int)), connect (&mSearch, SIGNAL (reportMessage (const CSMDoc::Message&, int)),
this, SLOT (verifierMessage (const CSMDoc::Message&, int))); this, SLOT (verifierMessage (const CSMDoc::Message&, int)));
connect (&mMerge, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
connect (&mMerge, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool)));
// don't need to connect report message, since there are no messages for merge
} }
CSMTools::Tools::~Tools() CSMTools::Tools::~Tools()
@ -148,6 +154,12 @@ CSMTools::Tools::~Tools()
delete mSearchOperation; delete mSearchOperation;
} }
if (mMergeOperation)
{
mMerge.abortAndWait();
delete mMergeOperation;
}
for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter) for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter)
delete iter->second; delete iter->second;
} }
@ -159,7 +171,7 @@ CSMWorld::UniversalId CSMTools::Tools::runVerifier (const CSMWorld::UniversalId&
if (mReports.find (reportNumber)==mReports.end()) if (mReports.find (reportNumber)==mReports.end())
mReports.insert (std::make_pair (reportNumber, new ReportModel)); mReports.insert (std::make_pair (reportNumber, new ReportModel));
mActiveReports[CSMDoc::State_Verifying] = reportNumber; mActiveReports[CSMDoc::State_Verifying] = reportNumber;
getVerifier()->start(); getVerifier()->start();
@ -189,6 +201,25 @@ void CSMTools::Tools::runSearch (const CSMWorld::UniversalId& searchId, const Se
mSearch.start(); mSearch.start();
} }
void CSMTools::Tools::runMerge (std::auto_ptr<CSMDoc::Document> target)
{
// not setting an active report, because merge does not produce messages
if (!mMergeOperation)
{
mMergeOperation = new MergeOperation (mDocument, mEncoding);
mMerge.setOperation (mMergeOperation);
connect (mMergeOperation, SIGNAL (mergeDone (CSMDoc::Document*)),
this, SIGNAL (mergeDone (CSMDoc::Document*)));
}
target->flagAsDirty();
mMergeOperation->setTarget (target);
mMerge.start();
}
void CSMTools::Tools::abortOperation (int type) void CSMTools::Tools::abortOperation (int type)
{ {
if (CSMDoc::OperationHolder *operation = get (type)) if (CSMDoc::OperationHolder *operation = get (type))
@ -201,6 +232,7 @@ int CSMTools::Tools::getRunningOperations() const
{ {
CSMDoc::State_Verifying, CSMDoc::State_Verifying,
CSMDoc::State_Searching, CSMDoc::State_Searching,
CSMDoc::State_Merging,
-1 -1
}; };
@ -231,4 +263,3 @@ void CSMTools::Tools::verifierMessage (const CSMDoc::Message& message, int type)
if (iter!=mActiveReports.end()) if (iter!=mActiveReports.end())
mReports[iter->second]->add (message); mReports[iter->second]->add (message);
} }

@ -1,9 +1,14 @@
#ifndef CSM_TOOLS_TOOLS_H #ifndef CSM_TOOLS_TOOLS_H
#define CSM_TOOLS_TOOLS_H #define CSM_TOOLS_TOOLS_H
#include <memory>
#include <map>
#include <components/to_utf8/to_utf8.hpp>
#include <QObject> #include <QObject>
#include <map> #include <boost/filesystem/path.hpp>
#include "../doc/operationholder.hpp" #include "../doc/operationholder.hpp"
@ -24,6 +29,7 @@ namespace CSMTools
class ReportModel; class ReportModel;
class Search; class Search;
class SearchOperation; class SearchOperation;
class MergeOperation;
class Tools : public QObject class Tools : public QObject
{ {
@ -35,9 +41,12 @@ namespace CSMTools
CSMDoc::OperationHolder mVerifier; CSMDoc::OperationHolder mVerifier;
SearchOperation *mSearchOperation; SearchOperation *mSearchOperation;
CSMDoc::OperationHolder mSearch; CSMDoc::OperationHolder mSearch;
MergeOperation *mMergeOperation;
CSMDoc::OperationHolder mMerge;
std::map<int, ReportModel *> mReports; std::map<int, ReportModel *> mReports;
int mNextReportNumber; int mNextReportNumber;
std::map<int, int> mActiveReports; // type, report number std::map<int, int> mActiveReports; // type, report number
ToUTF8::FromType mEncoding;
// not implemented // not implemented
Tools (const Tools&); Tools (const Tools&);
@ -53,7 +62,7 @@ namespace CSMTools
public: public:
Tools (CSMDoc::Document& document); Tools (CSMDoc::Document& document, ToUTF8::FromType encoding);
virtual ~Tools(); virtual ~Tools();
@ -67,7 +76,9 @@ namespace CSMTools
CSMWorld::UniversalId newSearch(); CSMWorld::UniversalId newSearch();
void runSearch (const CSMWorld::UniversalId& searchId, const Search& search); void runSearch (const CSMWorld::UniversalId& searchId, const Search& search);
void runMerge (std::auto_ptr<CSMDoc::Document> target);
void abortOperation (int type); void abortOperation (int type);
///< \attention The operation is not aborted immediately. ///< \attention The operation is not aborted immediately.
@ -85,6 +96,10 @@ namespace CSMTools
void progress (int current, int max, int type); void progress (int current, int max, int type);
void done (int type, bool failed); void done (int type, bool failed);
/// \attention When this signal is emitted, *this hands over the ownership of the
/// document. This signal must be handled to avoid a leak.
void mergeDone (CSMDoc::Document *document);
}; };
} }

@ -485,7 +485,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mMetaData.addColumn (new FormatColumn<MetaData>); mMetaData.addColumn (new FormatColumn<MetaData>);
mMetaData.addColumn (new AuthorColumn<MetaData>); mMetaData.addColumn (new AuthorColumn<MetaData>);
mMetaData.addColumn (new FileDescriptionColumn<MetaData>); mMetaData.addColumn (new FileDescriptionColumn<MetaData>);
addModel (new IdTable (&mGlobals), UniversalId::Type_Global); addModel (new IdTable (&mGlobals), UniversalId::Type_Global);
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmst); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmst);
addModel (new IdTable (&mSkills), UniversalId::Type_Skill); addModel (new IdTable (&mSkills), UniversalId::Type_Skill);
@ -775,11 +775,21 @@ const CSMWorld::IdCollection<CSMWorld::Land>& CSMWorld::Data::getLand() const
return mLand; return mLand;
} }
CSMWorld::IdCollection<CSMWorld::Land>& CSMWorld::Data::getLand()
{
return mLand;
}
const CSMWorld::IdCollection<CSMWorld::LandTexture>& CSMWorld::Data::getLandTextures() const const CSMWorld::IdCollection<CSMWorld::LandTexture>& CSMWorld::Data::getLandTextures() const
{ {
return mLandTextures; return mLandTextures;
} }
CSMWorld::IdCollection<CSMWorld::LandTexture>& CSMWorld::Data::getLandTextures()
{
return mLandTextures;
}
const CSMWorld::IdCollection<ESM::SoundGenerator>& CSMWorld::Data::getSoundGens() const const CSMWorld::IdCollection<ESM::SoundGenerator>& CSMWorld::Data::getSoundGens() const
{ {
return mSoundGens; return mSoundGens;
@ -830,6 +840,12 @@ const CSMWorld::MetaData& CSMWorld::Data::getMetaData() const
return mMetaData.getRecord (0).get(); return mMetaData.getRecord (0).get();
} }
void CSMWorld::Data::setMetaData (const MetaData& metaData)
{
Record<MetaData> record (RecordBase::State_ModifiedOnly, 0, &metaData);
mMetaData.setRecord (0, record);
}
QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id) QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id)
{ {
std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType()); std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());
@ -882,7 +898,7 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, 0, &metaData)); mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, 0, &metaData));
} }
return mReader->getRecordCount(); return mReader->getRecordCount();
} }
@ -941,8 +957,10 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
{ {
int index = mLand.load(*mReader, mBase); int index = mLand.load(*mReader, mBase);
if (index!=-1 && !mBase) // Load all land data for now. A future optimisation may only load non-base data
mLand.getRecord (index).mModified.mLand->loadData ( // if a suitable mechanism for avoiding race conditions can be established.
if (index!=-1/* && !mBase*/)
mLand.getRecord (index).get().loadData (
ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR |
ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM); ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM);

@ -232,8 +232,12 @@ namespace CSMWorld
const IdCollection<CSMWorld::Land>& getLand() const; const IdCollection<CSMWorld::Land>& getLand() const;
IdCollection<CSMWorld::Land>& getLand();
const IdCollection<CSMWorld::LandTexture>& getLandTextures() const; const IdCollection<CSMWorld::LandTexture>& getLandTextures() const;
IdCollection<CSMWorld::LandTexture>& getLandTextures();
const IdCollection<ESM::SoundGenerator>& getSoundGens() const; const IdCollection<ESM::SoundGenerator>& getSoundGens() const;
IdCollection<ESM::SoundGenerator>& getSoundGens(); IdCollection<ESM::SoundGenerator>& getSoundGens();
@ -255,6 +259,8 @@ namespace CSMWorld
const MetaData& getMetaData() const; const MetaData& getMetaData() const;
void setMetaData (const MetaData& metaData);
QAbstractItemModel *getTableModel (const UniversalId& id); QAbstractItemModel *getTableModel (const UniversalId& id);
///< If no table model is available for \a id, an exception is thrown. ///< If no table model is available for \a id, an exception is thrown.
/// ///

@ -4,25 +4,13 @@
namespace CSMWorld namespace CSMWorld
{ {
Land::Land()
{
mLand.reset(new ESM::Land());
}
void Land::load(ESM::ESMReader &esm) void Land::load(ESM::ESMReader &esm)
{ {
mLand->load(esm); ESM::Land::load(esm);
std::ostringstream stream; std::ostringstream stream;
stream << "#" << mLand->mX << " " << mLand->mY; stream << "#" << mX << " " << mY;
mId = stream.str(); mId = stream.str();
} }
void Land::blank()
{
/// \todo
}
} }

@ -2,7 +2,7 @@
#define CSM_WORLD_LAND_H #define CSM_WORLD_LAND_H
#include <string> #include <string>
#include <boost/shared_ptr.hpp>
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
namespace CSMWorld namespace CSMWorld
@ -11,18 +11,12 @@ namespace CSMWorld
/// ///
/// \todo Add worldspace support to the Land record. /// \todo Add worldspace support to the Land record.
/// \todo Add a proper copy constructor (currently worked around using shared_ptr) /// \todo Add a proper copy constructor (currently worked around using shared_ptr)
struct Land struct Land : public ESM::Land
{ {
Land();
boost::shared_ptr<ESM::Land> mLand;
std::string mId; std::string mId;
/// Loads the metadata and ID /// Loads the metadata and ID
void load (ESM::ESMReader &esm); void load (ESM::ESMReader &esm);
void blank();
}; };
} }

@ -913,3 +913,8 @@ const CSMWorld::NestedRefIdAdapterBase& CSMWorld::RefIdCollection::getNestedAdap
} }
throw std::runtime_error("No such column in the nestedadapters"); throw std::runtime_error("No such column in the nestedadapters");
} }
void CSMWorld::RefIdCollection::copyTo (int index, RefIdCollection& target) const
{
mData.copyTo (index, target.mData);
}

@ -138,6 +138,7 @@ namespace CSMWorld
void save (int index, ESM::ESMWriter& writer) const; void save (int index, ESM::ESMWriter& writer) const;
const RefIdData& getDataSet() const; //I can't figure out a better name for this one :( const RefIdData& getDataSet() const; //I can't figure out a better name for this one :(
void copyTo (int index, RefIdCollection& target) const;
}; };
} }

@ -1,6 +1,7 @@
#include "refiddata.hpp" #include "refiddata.hpp"
#include <cassert> #include <cassert>
#include <memory>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
@ -344,7 +345,7 @@ const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStati
return mStatics; return mStatics;
} }
void CSMWorld::RefIdData::insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id) void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id)
{ {
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter = std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (type); mRecordContainers.find (type);
@ -357,3 +358,16 @@ void CSMWorld::RefIdData::insertRecord(CSMWorld::RecordBase& record, CSMWorld::U
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
LocalIndex (iter->second->getSize()-1, type))); LocalIndex (iter->second->getSize()-1, type)));
} }
void CSMWorld::RefIdData::copyTo (int index, RefIdData& target) const
{
LocalIndex localIndex = globalToLocalIndex (index);
RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second;
std::string id = source->getId (localIndex.first);
std::auto_ptr<CSMWorld::RecordBase> newRecord (source->getRecord (localIndex.first).modifiedCopy());
target.insertRecord (*newRecord, localIndex.second, id);
}

@ -210,7 +210,8 @@ namespace CSMWorld
void erase (int index, int count); void erase (int index, int count);
void insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id); void insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type,
const std::string& id);
const RecordBase& getRecord (const LocalIndex& index) const; const RecordBase& getRecord (const LocalIndex& index) const;
@ -252,11 +253,9 @@ namespace CSMWorld
const RefIdDataContainer<ESM::Probe >& getProbes() const; const RefIdDataContainer<ESM::Probe >& getProbes() const;
const RefIdDataContainer<ESM::Repair>& getRepairs() const; const RefIdDataContainer<ESM::Repair>& getRepairs() const;
const RefIdDataContainer<ESM::Static>& getStatics() const; const RefIdDataContainer<ESM::Static>& getStatics() const;
void copyTo (int index, RefIdData& target) const;
}; };
} }
#endif #endif

@ -63,6 +63,7 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)
QString message; QString message;
mValid = (!name.isEmpty()); mValid = (!name.isEmpty());
bool warning = false;
if (!mValid) if (!mValid)
{ {
@ -105,13 +106,14 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)
{ {
/// \todo add an user setting to make this an error. /// \todo add an user setting to make this an error.
message += "<p>A file with the same name already exists. If you continue, it will be overwritten."; message += "<p>A file with the same name already exists. If you continue, it will be overwritten.";
warning = true;
} }
} }
} }
mMessage->setText (message); mMessage->setText (message);
mIcon->setPixmap (style()->standardIcon ( mIcon->setPixmap (style()->standardIcon (
mValid ? QStyle::SP_MessageBoxInformation : QStyle::SP_MessageBoxWarning). mValid ? (warning ? QStyle::SP_MessageBoxWarning : QStyle::SP_MessageBoxInformation) : QStyle::SP_MessageBoxCritical).
pixmap (QSize (16, 16))); pixmap (QSize (16, 16)));
emit stateChanged (mValid); emit stateChanged (mValid);

@ -55,3 +55,11 @@ void CSVDoc::FileWidget::extensionLabelIsVisible(bool visible)
{ {
mType->setVisible(visible); mType->setVisible(visible);
} }
void CSVDoc::FileWidget::setName (const std::string& text)
{
QString text2 = QString::fromUtf8 (text.c_str());
mInput->setText (text2);
textChanged (text2);
}

@ -3,6 +3,8 @@
#include <QWidget> #include <QWidget>
#include <string>
class QLabel; class QLabel;
class QString; class QString;
class QLineEdit; class QLineEdit;
@ -29,6 +31,8 @@ namespace CSVDoc
void extensionLabelIsVisible(bool visible); void extensionLabelIsVisible(bool visible);
void setName (const std::string& text);
private slots: private slots:
void textChanged (const QString& text); void textChanged (const QString& text);

@ -19,6 +19,7 @@ void CSVDoc::Operation::updateLabel (int threads)
case CSMDoc::State_Saving: name = "saving"; break; case CSMDoc::State_Saving: name = "saving"; break;
case CSMDoc::State_Verifying: name = "verifying"; break; case CSMDoc::State_Verifying: name = "verifying"; break;
case CSMDoc::State_Searching: name = "searching"; break; case CSMDoc::State_Searching: name = "searching"; break;
case CSMDoc::State_Merging: name = "merging"; break;
} }
std::ostringstream stream; std::ostringstream stream;
@ -122,7 +123,7 @@ void CSVDoc::Operation::setBarColor (int type)
bottomColor = "#9ECB2D"; //green gloss bottomColor = "#9ECB2D"; //green gloss
break; break;
case CSMDoc::State_Compiling: case CSMDoc::State_Merging:
topColor = "#F3E2C7"; topColor = "#F3E2C7";
midTopColor = "#C19E67"; midTopColor = "#C19E67";

@ -66,6 +66,10 @@ void CSVDoc::View::setupFileMenu()
connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); connect (mVerify, SIGNAL (triggered()), this, SLOT (verify()));
file->addAction (mVerify); file->addAction (mVerify);
mMerge = new QAction (tr ("Merge"), this);
connect (mMerge, SIGNAL (triggered()), this, SLOT (merge()));
file->addAction (mMerge);
QAction *loadErrors = new QAction (tr ("Load Error Log"), this); QAction *loadErrors = new QAction (tr ("Load Error Log"), this);
connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog())); connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog()));
file->addAction (loadErrors); file->addAction (loadErrors);
@ -393,6 +397,9 @@ void CSVDoc::View::updateActions()
mGlobalDebugProfileMenu->updateActions (running); mGlobalDebugProfileMenu->updateActions (running);
mStopDebug->setEnabled (running); mStopDebug->setEnabled (running);
mMerge->setEnabled (mDocument->getContentFiles().size()>1 &&
!(mDocument->getState() & CSMDoc::State_Merging));
} }
CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews)
@ -471,6 +478,7 @@ void CSVDoc::View::updateDocumentState()
static const int operations[] = static const int operations[] =
{ {
CSMDoc::State_Saving, CSMDoc::State_Verifying, CSMDoc::State_Searching, CSMDoc::State_Saving, CSMDoc::State_Verifying, CSMDoc::State_Searching,
CSMDoc::State_Merging,
-1 // end marker -1 // end marker
}; };
@ -968,3 +976,8 @@ void CSVDoc::View::updateScrollbar()
else else
mSubViewWindow.setMinimumWidth(0); mSubViewWindow.setMinimumWidth(0);
} }
void CSVDoc::View::merge()
{
emit mergeDocument (mDocument);
}

@ -43,6 +43,7 @@ namespace CSVDoc
QAction *mVerify; QAction *mVerify;
QAction *mShowStatusBar; QAction *mShowStatusBar;
QAction *mStopDebug; QAction *mStopDebug;
QAction *mMerge;
std::vector<QAction *> mEditingActions; std::vector<QAction *> mEditingActions;
Operations *mOperations; Operations *mOperations;
SubViewFactoryManager mSubViewFactory; SubViewFactoryManager mSubViewFactory;
@ -129,6 +130,8 @@ namespace CSVDoc
void editSettingsRequest(); void editSettingsRequest();
void mergeDocument (CSMDoc::Document *document);
public slots: public slots:
void addSubView (const CSMWorld::UniversalId& id, const std::string& hint = ""); void addSubView (const CSMWorld::UniversalId& id, const std::string& hint = "");
@ -237,6 +240,8 @@ namespace CSVDoc
void closeRequest (SubView *subView); void closeRequest (SubView *subView);
void moveScrollBarToEnd(int min, int max); void moveScrollBarToEnd(int min, int max);
void merge();
}; };
} }

@ -173,6 +173,7 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document)
connect (view, SIGNAL (newAddonRequest ()), this, SIGNAL (newAddonRequest())); connect (view, SIGNAL (newAddonRequest ()), this, SIGNAL (newAddonRequest()));
connect (view, SIGNAL (loadDocumentRequest ()), this, SIGNAL (loadDocumentRequest())); connect (view, SIGNAL (loadDocumentRequest ()), this, SIGNAL (loadDocumentRequest()));
connect (view, SIGNAL (editSettingsRequest()), this, SIGNAL (editSettingsRequest())); connect (view, SIGNAL (editSettingsRequest()), this, SIGNAL (editSettingsRequest()));
connect (view, SIGNAL (mergeDocument (CSMDoc::Document *)), this, SIGNAL (mergeDocument (CSMDoc::Document *)));
connect (&CSMSettings::UserSettings::instance(), connect (&CSMSettings::UserSettings::instance(),
SIGNAL (userSettingUpdated(const QString &, const QStringList &)), SIGNAL (userSettingUpdated(const QString &, const QStringList &)),

@ -77,6 +77,8 @@ namespace CSVDoc
void editSettingsRequest(); void editSettingsRequest();
void mergeDocument (CSMDoc::Document *document);
public slots: public slots:
void exitApplication (CSVDoc::View *view); void exitApplication (CSVDoc::View *view);

@ -31,7 +31,7 @@ bool CSVRender::Cell::addObjects (int start, int end)
bool modified = false; bool modified = false;
const CSMWorld::RefCollection& collection = mData.getReferences(); const CSMWorld::RefCollection& collection = mData.getReferences();
for (int i=start; i<=end; ++i) for (int i=start; i<=end; ++i)
{ {
std::string cell = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mCell); std::string cell = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mCell);
@ -67,15 +67,16 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
int landIndex = land.searchId(mId); int landIndex = land.searchId(mId);
if (landIndex != -1) if (landIndex != -1)
{ {
const ESM::Land* esmLand = land.getRecord(mId).get().mLand.get(); const ESM::Land& esmLand = land.getRecord(mId).get();
if(esmLand && esmLand->mDataTypes&ESM::Land::DATA_VHGT)
if (esmLand.getLandData (ESM::Land::DATA_VHGT))
{ {
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Element_Terrain<<1)); mTerrain.reset(new Terrain::TerrainGrid(mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Element_Terrain<<1));
mTerrain->loadCell(esmLand->mX, mTerrain->loadCell(esmLand.mX,
esmLand->mY); esmLand.mY);
mX = esmLand->mX; mX = esmLand.mX;
mY = esmLand->mY; mY = esmLand.mY;
} }
} }
} }

@ -9,7 +9,7 @@ namespace CSVRender
{ {
} }
ESM::Land* TerrainStorage::getLand(int cellX, int cellY) const ESM::Land* TerrainStorage::getLand(int cellX, int cellY)
{ {
std::ostringstream stream; std::ostringstream stream;
stream << "#" << cellX << " " << cellY; stream << "#" << cellX << " " << cellY;
@ -20,11 +20,10 @@ namespace CSVRender
if (index == -1) if (index == -1)
return NULL; return NULL;
ESM::Land* land = mData.getLand().getRecord(index).get().mLand.get(); const ESM::Land& land = mData.getLand().getRecord(index).get();
int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX;
if (!land->isDataLoaded(mask)) land.loadData (mask);
land->loadData(mask); return &land;
return land;
} }
const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)

@ -18,7 +18,7 @@ namespace CSVRender
private: private:
const CSMWorld::Data& mData; const CSMWorld::Data& mData;
virtual ESM::Land* getLand (int cellX, int cellY); virtual const ESM::Land* getLand (int cellX, int cellY);
virtual const ESM::LandTexture* getLandTexture(int index, short plugin); virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);

@ -0,0 +1,142 @@
#include "merge.hpp"
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <QSplitter>
#include <QPushButton>
#include <QListWidget>
#include <QLabel>
#include <QKeyEvent>
#include "../../model/doc/document.hpp"
#include "../../model/doc/documentmanager.hpp"
#include "../doc/filewidget.hpp"
#include "../doc/adjusterwidget.hpp"
void CSVTools::Merge::keyPressEvent (QKeyEvent *event)
{
if (event->key()==Qt::Key_Escape)
{
event->accept();
cancel();
}
else
QWidget::keyPressEvent (event);
}
CSVTools::Merge::Merge (CSMDoc::DocumentManager& documentManager, QWidget *parent)
: QWidget (parent), mDocument (0), mDocumentManager (documentManager)
{
setWindowTitle ("Merge Content Files into a new Game File");
QVBoxLayout *mainLayout = new QVBoxLayout;
setLayout (mainLayout);
QSplitter *splitter = new QSplitter (Qt::Horizontal, this);
mainLayout->addWidget (splitter, 1);
// left panel (files to be merged)
QWidget *left = new QWidget (this);
left->setContentsMargins (0, 0, 0, 0);
splitter->addWidget (left);
QVBoxLayout *leftLayout = new QVBoxLayout;
left->setLayout (leftLayout);
leftLayout->addWidget (new QLabel ("Files to be merged", this));
mFiles = new QListWidget (this);
leftLayout->addWidget (mFiles, 1);
// right panel (new game file)
QWidget *right = new QWidget (this);
right->setContentsMargins (0, 0, 0, 0);
splitter->addWidget (right);
QVBoxLayout *rightLayout = new QVBoxLayout;
rightLayout->setAlignment (Qt::AlignTop);
right->setLayout (rightLayout);
rightLayout->addWidget (new QLabel ("New game file", this));
mNewFile = new CSVDoc::FileWidget (this);
mNewFile->setType (false);
mNewFile->extensionLabelIsVisible (true);
rightLayout->addWidget (mNewFile);
mAdjuster = new CSVDoc::AdjusterWidget (this);
rightLayout->addWidget (mAdjuster);
connect (mNewFile, SIGNAL (nameChanged (const QString&, bool)),
mAdjuster, SLOT (setName (const QString&, bool)));
connect (mAdjuster, SIGNAL (stateChanged (bool)), this, SLOT (stateChanged (bool)));
// buttons
QDialogButtonBox *buttons = new QDialogButtonBox (QDialogButtonBox::Cancel, Qt::Horizontal, this);
connect (buttons->button (QDialogButtonBox::Cancel), SIGNAL (clicked()), this, SLOT (cancel()));
mOkay = new QPushButton ("Merge", this);
connect (mOkay, SIGNAL (clicked()), this, SLOT (accept()));
mOkay->setDefault (true);
buttons->addButton (mOkay, QDialogButtonBox::AcceptRole);
mainLayout->addWidget (buttons);
}
void CSVTools::Merge::configure (CSMDoc::Document *document)
{
mDocument = document;
mNewFile->setName ("");
// content files
while (mFiles->count())
delete mFiles->takeItem (0);
std::vector<boost::filesystem::path> files = document->getContentFiles();
for (std::vector<boost::filesystem::path>::const_iterator iter (files.begin());
iter!=files.end(); ++iter)
mFiles->addItem (QString::fromUtf8 (iter->filename().string().c_str()));
}
void CSVTools::Merge::setLocalData (const boost::filesystem::path& localData)
{
mAdjuster->setLocalData (localData);
}
CSMDoc::Document *CSVTools::Merge::getDocument() const
{
return mDocument;
}
void CSVTools::Merge::cancel()
{
mDocument = 0;
hide();
}
void CSVTools::Merge::accept()
{
if ((mDocument->getState() & CSMDoc::State_Merging)==0)
{
std::vector< boost::filesystem::path > files (1, mAdjuster->getPath());
std::auto_ptr<CSMDoc::Document> target (
mDocumentManager.makeDocument (files, files[0], true));
mDocument->runMerge (target);
hide();
}
}
void CSVTools::Merge::stateChanged (bool valid)
{
mOkay->setEnabled (valid);
}

@ -0,0 +1,61 @@
#ifndef CSV_TOOLS_REPORTTABLE_H
#define CSV_TOOLS_REPORTTABLE_H
#include <QWidget>
#include <boost/filesystem/path.hpp>
class QPushButton;
class QListWidget;
namespace CSMDoc
{
class Document;
class DocumentManager;
}
namespace CSVDoc
{
class FileWidget;
class AdjusterWidget;
}
namespace CSVTools
{
class Merge : public QWidget
{
Q_OBJECT
CSMDoc::Document *mDocument;
QPushButton *mOkay;
QListWidget *mFiles;
CSVDoc::FileWidget *mNewFile;
CSVDoc::AdjusterWidget *mAdjuster;
CSMDoc::DocumentManager& mDocumentManager;
void keyPressEvent (QKeyEvent *event);
public:
Merge (CSMDoc::DocumentManager& documentManager, QWidget *parent = 0);
/// Configure dialogue for a new merge
void configure (CSMDoc::Document *document);
void setLocalData (const boost::filesystem::path& localData);
CSMDoc::Document *getDocument() const;
public slots:
void cancel();
private slots:
void accept();
void stateChanged (bool valid);
};
}
#endif

@ -461,7 +461,7 @@ namespace MWPhysics
class HeightField class HeightField
{ {
public: public:
HeightField(float* heights, int x, int y, float triSize, float sqrtVerts) HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts)
{ {
// find the minimum and maximum heights (needed for bullet) // find the minimum and maximum heights (needed for bullet)
float minh = heights[0]; float minh = heights[0];
@ -928,7 +928,7 @@ namespace MWPhysics
return MovementSolver::traceDown(ptr, found->second, mCollisionWorld, maxHeight); return MovementSolver::traceDown(ptr, found->second, mCollisionWorld, maxHeight);
} }
void PhysicsSystem::addHeightField (float* heights, int x, int y, float triSize, float sqrtVerts) void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts)
{ {
HeightField *heightfield = new HeightField(heights, x, y, triSize, sqrtVerts); HeightField *heightfield = new HeightField(heights, x, y, triSize, sqrtVerts);
mHeightFields[std::make_pair(x,y)] = heightfield; mHeightFields[std::make_pair(x,y)] = heightfield;

@ -71,7 +71,7 @@ namespace MWPhysics
void updatePosition (const MWWorld::Ptr& ptr); void updatePosition (const MWWorld::Ptr& ptr);
void addHeightField (float* heights, int x, int y, float triSize, float sqrtVerts); void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts);
void removeHeightField (int x, int y); void removeHeightField (int x, int y);

@ -156,6 +156,9 @@ namespace MWRender
land->loadData(mask); land->loadData(mask);
} }
const ESM::Land::LandData *landData =
land ? land->getLandData (ESM::Land::DATA_WNAM) : 0;
for (int cellY=0; cellY<mCellSize; ++cellY) for (int cellY=0; cellY<mCellSize; ++cellY)
{ {
for (int cellX=0; cellX<mCellSize; ++cellX) for (int cellX=0; cellX<mCellSize; ++cellX)
@ -163,15 +166,14 @@ namespace MWRender
int vertexX = static_cast<int>(float(cellX)/float(mCellSize) * 9); int vertexX = static_cast<int>(float(cellX)/float(mCellSize) * 9);
int vertexY = static_cast<int>(float(cellY) / float(mCellSize) * 9); int vertexY = static_cast<int>(float(cellY) / float(mCellSize) * 9);
int texelX = (x-mMinX) * mCellSize + cellX; int texelX = (x-mMinX) * mCellSize + cellX;
int texelY = (mHeight-1) - ((y-mMinY) * mCellSize + cellY); int texelY = (mHeight-1) - ((y-mMinY) * mCellSize + cellY);
unsigned char r,g,b; unsigned char r,g,b;
float y = 0; float y = 0;
if (land && land->mDataTypes & ESM::Land::DATA_WNAM) if (landData)
y = (land->mLandData->mWnam[vertexY * 9 + vertexX] << 4) / 2048.f; y = (landData->mWnam[vertexY * 9 + vertexX] << 4) / 2048.f;
else else
y = (SCHAR_MIN << 4) / 2048.f; y = (SCHAR_MIN << 4) / 2048.f;
if (y < 0) if (y < 0)

@ -51,7 +51,7 @@ namespace MWRender
maxY += 1; maxY += 1;
} }
ESM::Land* TerrainStorage::getLand(int cellX, int cellY) const ESM::Land* TerrainStorage::getLand(int cellX, int cellY)
{ {
const MWWorld::ESMStore &esmStore = const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore(); MWBase::Environment::get().getWorld()->getStore();

@ -10,7 +10,7 @@ namespace MWRender
class TerrainStorage : public ESMTerrain::Storage class TerrainStorage : public ESMTerrain::Storage
{ {
private: private:
virtual ESM::Land* getLand (int cellX, int cellY); virtual const ESM::Land* getLand (int cellX, int cellY);
virtual const ESM::LandTexture* getLandTexture(int index, short plugin); virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
public: public:

@ -250,9 +250,9 @@ namespace MWWorld
// Actually only VHGT is needed here, but we'll need the rest for rendering anyway. // Actually only VHGT is needed here, but we'll need the rest for rendering anyway.
// Load everything now to reduce IO overhead. // Load everything now to reduce IO overhead.
const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX;
if (!land->isDataLoaded(flags))
land->loadData(flags); const ESM::Land::LandData *data = land->getLandData (flags);
mPhysics->addHeightField (land->mLandData->mHeights, cell->getCell()->getGridX(), cell->getCell()->getGridY(), mPhysics->addHeightField (data->mHeights, cell->getCell()->getGridX(), cell->getCell()->getGridY(),
worldsize / (verts-1), verts); worldsize / (verts-1), verts);
} }
} }

@ -1,5 +1,7 @@
#include "loadland.hpp" #include "loadland.hpp"
#include <utility>
#include "esmreader.hpp" #include "esmreader.hpp"
#include "esmwriter.hpp" #include "esmwriter.hpp"
#include "defs.hpp" #include "defs.hpp"
@ -8,7 +10,7 @@ namespace ESM
{ {
unsigned int Land::sRecordId = REC_LAND; unsigned int Land::sRecordId = REC_LAND;
void Land::LandData::save(ESMWriter &esm) void Land::LandData::save(ESMWriter &esm) const
{ {
if (mDataTypes & Land::DATA_VNML) { if (mDataTypes & Land::DATA_VNML) {
esm.writeHNT("VNML", mNormals, sizeof(mNormals)); esm.writeHNT("VNML", mNormals, sizeof(mNormals));
@ -53,7 +55,7 @@ void Land::LandData::save(ESMWriter &esm)
} }
} }
void Land::LandData::transposeTextureData(uint16_t *in, uint16_t *out) void Land::LandData::transposeTextureData(const uint16_t *in, uint16_t *out)
{ {
int readPos = 0; //bit ugly, but it works int readPos = 0; //bit ugly, but it works
for ( int y1 = 0; y1 < 4; y1++ ) for ( int y1 = 0; y1 < 4; y1++ )
@ -137,7 +139,7 @@ void Land::save(ESMWriter &esm) const
esm.writeHNT("DATA", mFlags); esm.writeHNT("DATA", mFlags);
} }
void Land::loadData(int flags) void Land::loadData(int flags) const
{ {
// Try to load only available data // Try to load only available data
flags = flags & mDataTypes; flags = flags & mDataTypes;
@ -199,7 +201,7 @@ void Land::unloadData()
} }
} }
bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const
{ {
if ((mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) { if ((mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) {
mEsm->getHExact(ptr, size); mEsm->getHExact(ptr, size);
@ -215,4 +217,69 @@ bool Land::isDataLoaded(int flags) const
return (mDataLoaded & flags) == (flags & mDataTypes); return (mDataLoaded & flags) == (flags & mDataTypes);
} }
Land::Land (const Land& land)
: mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin),
mEsm (land.mEsm), mContext (land.mContext), mDataTypes (land.mDataTypes),
mDataLoaded (land.mDataLoaded),
mLandData (land.mLandData ? new LandData (*land.mLandData) : 0)
{}
Land& Land::operator= (Land land)
{
swap (land);
return *this;
}
void Land::swap (Land& land)
{
std::swap (mFlags, land.mFlags);
std::swap (mX, land.mX);
std::swap (mY, land.mY);
std::swap (mPlugin, land.mPlugin);
std::swap (mEsm, land.mEsm);
std::swap (mContext, land.mContext);
std::swap (mDataTypes, land.mDataTypes);
std::swap (mDataLoaded, land.mDataLoaded);
std::swap (mLandData, land.mLandData);
}
const Land::LandData *Land::getLandData (int flags) const
{
if (!(flags & mDataTypes))
return 0;
loadData (flags);
return mLandData;
}
const Land::LandData *Land::getLandData() const
{
return mLandData;
}
Land::LandData *Land::getLandData()
{
return mLandData;
}
void Land::add (int flags)
{
if (!mLandData)
mLandData = new LandData;
mDataTypes |= flags;
mDataLoaded |= flags;
}
void Land::remove (int flags)
{
mDataTypes &= ~flags;
mDataLoaded &= ~flags;
if (!mDataLoaded)
{
delete mLandData;
mLandData = 0;
}
}
} }

@ -35,7 +35,6 @@ struct Land
ESM_Context mContext; ESM_Context mContext;
int mDataTypes; int mDataTypes;
int mDataLoaded;
enum enum
{ {
@ -91,12 +90,10 @@ struct Land
short mUnk1; short mUnk1;
uint8_t mUnk2; uint8_t mUnk2;
void save(ESMWriter &esm); void save(ESMWriter &esm) const;
static void transposeTextureData(uint16_t *in, uint16_t *out); static void transposeTextureData(const uint16_t *in, uint16_t *out);
}; };
LandData *mLandData;
void load(ESMReader &esm); void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
@ -105,7 +102,7 @@ struct Land
/** /**
* Actually loads data * Actually loads data
*/ */
void loadData(int flags); void loadData(int flags) const;
/** /**
* Frees memory allocated for land data * Frees memory allocated for land data
@ -116,14 +113,41 @@ struct Land
/// @note We only check data types that *can* be loaded (present in mDataTypes) /// @note We only check data types that *can* be loaded (present in mDataTypes)
bool isDataLoaded(int flags) const; bool isDataLoaded(int flags) const;
Land (const Land& land);
Land& operator= (Land land);
void swap (Land& land);
/// Return land data with at least the data types specified in \a flags loaded (if they
/// are available). Will return a 0-pointer if there is no data for any of the
/// specified types.
const LandData *getLandData (int flags) const;
/// Return land data without loading first anything. Can return a 0-pointer.
const LandData *getLandData() const;
/// Return land data without loading first anything. Can return a 0-pointer.
LandData *getLandData();
/// \attention Must not be called on objects that aren't fully loaded.
///
/// \note Added data fields will be uninitialised
void add (int flags);
/// \attention Must not be called on objects that aren't fully loaded.
void remove (int flags);
private: private:
Land(const Land& land);
Land& operator=(const Land& land);
/// Loads data and marks it as loaded /// Loads data and marks it as loaded
/// \return true if data is actually loaded from file, false otherwise /// \return true if data is actually loaded from file, false otherwise
/// including the case when data is already loaded /// including the case when data is already loaded
bool condLoad(int flags, int dataFlag, void *ptr, unsigned int size); bool condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const;
mutable int mDataLoaded;
mutable LandData *mLandData;
}; };
} }

@ -18,6 +18,14 @@ namespace ESMTerrain
{ {
} }
const ESM::Land::LandData *Storage::getLandData (int cellX, int cellY, int flags)
{
if (const ESM::Land *land = getLand (cellX, cellY))
return land->getLandData (flags);
return 0;
}
bool Storage::getMinMaxHeights(float size, const osg::Vec2f &center, float &min, float &max) bool Storage::getMinMaxHeights(float size, const osg::Vec2f &center, float &min, float &max)
{ {
assert (size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell"); assert (size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell");
@ -32,24 +40,25 @@ namespace ESMTerrain
int cellX = static_cast<int>(origin.x()); int cellX = static_cast<int>(origin.x());
int cellY = static_cast<int>(origin.y()); int cellY = static_cast<int>(origin.y());
const ESM::Land* land = getLand(cellX, cellY); if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VHGT))
if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT))
return false;
min = std::numeric_limits<float>::max();
max = -std::numeric_limits<float>::max();
for (int row=0; row<ESM::Land::LAND_SIZE; ++row)
{ {
for (int col=0; col<ESM::Land::LAND_SIZE; ++col) min = std::numeric_limits<float>::max();
max = -std::numeric_limits<float>::max();
for (int row=0; row<ESM::Land::LAND_SIZE; ++row)
{ {
float h = land->mLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; for (int col=0; col<ESM::Land::LAND_SIZE; ++col)
if (h > max) {
max = h; float h = data->mHeights[col*ESM::Land::LAND_SIZE+row];
if (h < min) if (h > max)
min = h; max = h;
if (h < min)
min = h;
}
} }
return true;
} }
return true;
return false;
} }
void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row) void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row)
@ -74,12 +83,12 @@ namespace ESMTerrain
--cellX; --cellX;
row += ESM::Land::LAND_SIZE-1; row += ESM::Land::LAND_SIZE-1;
} }
ESM::Land* land = getLand(cellX, cellY);
if (land && land->mDataTypes&ESM::Land::DATA_VNML) if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VNML))
{ {
normal.x() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
normal.y() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
normal.z() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; normal.z() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
normal.normalize(); normal.normalize();
} }
else else
@ -109,12 +118,12 @@ namespace ESMTerrain
++cellX; ++cellX;
row = 0; row = 0;
} }
ESM::Land* land = getLand(cellX, cellY);
if (land && land->mDataTypes&ESM::Land::DATA_VCLR) if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VCLR))
{ {
color.r() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
color.g() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
color.b() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f;
} }
else else
{ {
@ -158,9 +167,9 @@ namespace ESMTerrain
float vertX_ = 0; // of current cell corner float vertX_ = 0; // of current cell corner
for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX) for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX)
{ {
ESM::Land* land = getLand(cellX, cellY); const ESM::Land::LandData *heightData = getLandData (cellX, cellY, ESM::Land::DATA_VHGT);
if (land && !(land->mDataTypes&ESM::Land::DATA_VHGT)) const ESM::Land::LandData *normalData = getLandData (cellX, cellY, ESM::Land::DATA_VNML);
land = NULL; const ESM::Land::LandData *colourData = getLandData (cellX, cellY, ESM::Land::DATA_VCLR);
int rowStart = 0; int rowStart = 0;
int colStart = 0; int colStart = 0;
@ -177,20 +186,22 @@ namespace ESMTerrain
vertX = vertX_; vertX = vertX_;
for (int row=rowStart; row<ESM::Land::LAND_SIZE; row += increment) for (int row=rowStart; row<ESM::Land::LAND_SIZE; row += increment)
{ {
int arrayIndex = col*ESM::Land::LAND_SIZE*3+row*3;
float height = -2048; float height = -2048;
if (land) if (heightData)
height = land->mLandData->mHeights[col*ESM::Land::LAND_SIZE + row]; height = heightData->mHeights[col*ESM::Land::LAND_SIZE + row];
(*positions)[static_cast<unsigned int>(vertX*numVerts + vertY)] (*positions)[static_cast<unsigned int>(vertX*numVerts + vertY)]
= osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * 8192, = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * 8192,
(vertY / float(numVerts - 1) - 0.5f) * size * 8192, (vertY / float(numVerts - 1) - 0.5f) * size * 8192,
height); height);
if (land && land->mDataTypes&ESM::Land::DATA_VNML) if (normalData)
{ {
normal.x() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; for (int i=0; i<3; ++i)
normal.y() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; normal[i] = normalData->mNormals[arrayIndex+i];
normal.z() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
normal.normalize(); normal.normalize();
} }
else else
@ -208,11 +219,10 @@ namespace ESMTerrain
(*normals)[static_cast<unsigned int>(vertX*numVerts + vertY)] = normal; (*normals)[static_cast<unsigned int>(vertX*numVerts + vertY)] = normal;
if (land && land->mDataTypes&ESM::Land::DATA_VCLR) if (colourData)
{ {
color.r() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; for (int i=0; i<3; ++i)
color.g() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; color[i] = colourData->mColours[arrayIndex+i] / 255.f;
color.b() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f;
} }
else else
{ {
@ -262,13 +272,12 @@ namespace ESMTerrain
assert(x<ESM::Land::LAND_TEXTURE_SIZE); assert(x<ESM::Land::LAND_TEXTURE_SIZE);
assert(y<ESM::Land::LAND_TEXTURE_SIZE); assert(y<ESM::Land::LAND_TEXTURE_SIZE);
ESM::Land* land = getLand(cellX, cellY); if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VTEX))
if (land && (land->mDataTypes&ESM::Land::DATA_VTEX))
{ {
int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; int tex = data->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
if (tex == 0) if (tex == 0)
return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin
return std::make_pair(tex, land->mPlugin); return std::make_pair(tex, getLand (cellX, cellY)->mPlugin);
} }
else else
return std::make_pair(0,0); return std::make_pair(0,0);
@ -368,7 +377,7 @@ namespace ESMTerrain
int cellX = static_cast<int>(std::floor(worldPos.x() / 8192.f)); int cellX = static_cast<int>(std::floor(worldPos.x() / 8192.f));
int cellY = static_cast<int>(std::floor(worldPos.y() / 8192.f)); int cellY = static_cast<int>(std::floor(worldPos.y() / 8192.f));
ESM::Land* land = getLand(cellX, cellY); const ESM::Land* land = getLand(cellX, cellY);
if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT)) if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT))
return -2048; return -2048;
@ -447,7 +456,7 @@ namespace ESMTerrain
{ {
assert(x < ESM::Land::LAND_SIZE); assert(x < ESM::Land::LAND_SIZE);
assert(y < ESM::Land::LAND_SIZE); assert(y < ESM::Land::LAND_SIZE);
return land->mLandData->mHeights[y * ESM::Land::LAND_SIZE + x]; return land->getLandData()->mHeights[y * ESM::Land::LAND_SIZE + x];
} }
Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture)

@ -21,12 +21,17 @@ namespace ESMTerrain
private: private:
// Not implemented in this class, because we need different Store implementations for game and editor // Not implemented in this class, because we need different Store implementations for game and editor
virtual ESM::Land* getLand (int cellX, int cellY) = 0; virtual const ESM::Land* getLand (int cellX, int cellY)= 0;
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0;
public: public:
Storage(const VFS::Manager* vfs); Storage(const VFS::Manager* vfs);
/// Data is loaded first, if necessary. Will return a 0-pointer if there is no data for
/// any of the data types specified via \a flags. Will also return a 0-pointer if there
/// is no land record for the coordinates \a cellX / \a cellY.
const ESM::Land::LandData *getLandData (int cellX, int cellY, int flags);
// Not implemented in this class, because we need different Store implementations for game and editor // Not implemented in this class, because we need different Store implementations for game and editor
/// Get bounds of the whole terrain in cell units /// Get bounds of the whole terrain in cell units
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0; virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0;

Loading…
Cancel
Save