merge from saving

Merge branch 'saving' of https://github.com/zinnschlag/openmw into esxSelector

Conflicts:
	apps/launcher/datafilespage.cpp
	apps/opencs/editor.cpp
	apps/opencs/view/doc/filedialog.cpp
	apps/opencs/view/doc/filedialog.hpp
actorid
graffy76 11 years ago
commit 9ce4a04a2d

@ -319,6 +319,9 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg
"${OpenMW_BINARY_DIR}/opencs.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
"${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY)
if (NOT WIN32 AND NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop

@ -161,8 +161,42 @@ void Launcher::DataFilesPage::slotProfileDeleted (const QString &item)
void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString &current)
{
<<<<<<< HEAD
setProfile(previous, current, true);
emit signalProfileChanged (ui.profilesComboBox->findText(current));
=======
if (mContentModel->rowCount() < 1)
return;
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
if (profile.isEmpty()) {
profile = profilesComboBox->currentText();
mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile);
}
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master"));
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin"));
mGameSettings.remove(QString("master"));
mGameSettings.remove(QString("plugins"));
mGameSettings.remove(QString("content"));
ContentSelectorModel::ContentFileList items = mContentModel->checkedItems();
foreach(const ContentSelectorModel::EsmFile *item, items) {
if (item->gameFiles().size() == 0) {
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item->fileName());
mGameSettings.setMultiValue(QString("content"), item->fileName());
} else {
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item->fileName());
mGameSettings.setMultiValue(QString("content"), item->fileName());
}
}
>>>>>>> 3146af34d642a28b15b55f7eb9999d8ac50168a0
}
void Launcher::DataFilesPage::slotProfileRenamed(const QString &previous, const QString &current)

@ -163,12 +163,12 @@ bool Launcher::GameSettings::writeFile(QTextStream &stream)
QStringList masters = mSettings.values(QString("master"));
for (int i = masters.count(); i--;) {
stream << "master=" << masters.at(i) << "\n";
stream << "content=" << masters.at(i) << "\n";
}
QStringList plugins = mSettings.values(QString("plugin"));
for (int i = plugins.count(); i--;) {
stream << "plugin=" << plugins.at(i) << "\n";
stream << "content=" << plugins.at(i) << "\n";
}
return true;

@ -813,8 +813,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
}
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const {
std::vector<std::string> esmFiles;
std::vector<std::string> espFiles;
std::vector<std::string> contentFiles;
std::string baseGameFile("Game Files:GameFile");
std::string gameFile("");
@ -832,29 +831,19 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) co
std::string filetype(entry->substr(entry->length()-3));
Misc::StringUtils::toLower(filetype);
if(filetype.compare("esm") == 0) {
esmFiles.push_back(*entry);
}
else if(filetype.compare("esp") == 0) {
espFiles.push_back(*entry);
if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) {
contentFiles.push_back(*entry);
}
}
gameFile = "";
}
cfg.erase("master");
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("master", std::vector<std::string>() ) );
for(std::vector<std::string>::const_iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) {
cfg["master"].push_back(*it);
}
cfg.erase("plugin");
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("plugin", std::vector<std::string>() ) );
cfg.erase("content");
cfg.insert( std::make_pair("content", std::vector<std::string>() ) );
for(std::vector<std::string>::const_iterator it=espFiles.begin(); it!=espFiles.end(); ++it) {
cfg["plugin"].push_back(*it);
for(std::vector<std::string>::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) {
cfg["content"].push_back(*it);
}
}

@ -5,11 +5,11 @@ opencs_units (. editor)
set (CMAKE_BUILD_TYPE DEBUG)
opencs_units (model/doc
document
document operation saving
)
opencs_units_noqt (model/doc
documentmanager
documentmanager stage savingstate savingstages
)
opencs_hdrs_noqt (model/doc
@ -33,11 +33,11 @@ opencs_hdrs_noqt (model/world
opencs_units (model/tools
tools operation reportmodel
tools reportmodel
)
opencs_units_noqt (model/tools
stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck
)
@ -66,7 +66,7 @@ opencs_units (view/world
opencs_units_noqt (view/world
dialoguesubview subviews
enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator
scripthighlighter idvalidator dialoguecreator
)

@ -8,15 +8,18 @@
#include "model/doc/document.hpp"
#include "model/world/data.hpp"
#include <iostream>
CS::Editor::Editor() : mViewManager (mDocumentManager)
CS::Editor::Editor()
: mDocumentManager (mCfgMgr), mViewManager (mDocumentManager)
{
mIpcServerName = "org.openmw.OpenCS";
setupDataFiles();
mNewGame.setLocalData (mLocal);
mFileDialog.setLocalData (mLocal);
connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ()));
connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
@ -29,22 +32,24 @@ CS::Editor::Editor() : mViewManager (mDocumentManager)
connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ()));
connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles()));
connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile()));
connect (&mFileDialog, SIGNAL(createNewFile (const boost::filesystem::path&)),
this, SLOT(createNewFile (const boost::filesystem::path&)));
connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)),
this, SLOT (createNewGame (const boost::filesystem::path&)));
this, SLOT (createNewGame (const boost::filesystem::path&)));
}
void CS::Editor::setupDataFiles()
{
boost::program_options::variables_map variables;
boost::program_options::options_description desc;
boost::program_options::options_description desc("Syntax: opencs <options>\nAllowed options");
desc.add_options()
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
("data-local", boost::program_options::value<std::string>()->default_value(""))
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
("encoding", boost::program_options::value<std::string>()->default_value("win1252"))
("resources", boost::program_options::value<std::string>()->default_value("resources"));
boost::program_options::notify(variables);
@ -79,11 +84,13 @@ void CS::Editor::setupDataFiles()
}
// Set the charset for reading the esm/esp files
// QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
// QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
//mFileDialog.setEncoding(encoding);
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
mDocumentManager.setResourceDir (variables["resources"].as<std::string>());
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{
QString path = QString::fromStdString(iter->string());
@ -134,7 +141,7 @@ void CS::Editor::openFiles()
mFileDialog.hide();
}
void CS::Editor::createNewFile()
void CS::Editor::createNewFile (const boost::filesystem::path& savePath)
{
std::vector<boost::filesystem::path> files;

@ -26,6 +26,7 @@ namespace CS
{
Q_OBJECT
Files::ConfigurationManager mCfgMgr;
CSMSettings::UserSettings mUserSettings;
CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager;
@ -34,7 +35,6 @@ namespace CS
CSVSettings::UserSettingsDialog mSettings;
CSVDoc::FileDialog mFileDialog;
Files::ConfigurationManager mCfgMgr;
boost::filesystem::path mLocal;
void setupDataFiles();
@ -60,7 +60,7 @@ namespace CS
void loadDocument();
void openFiles();
void createNewFile();
void createNewFile (const boost::filesystem::path& savePath);
void createNewGame (const boost::filesystem::path& file);
void showStartup();

@ -1,8 +1,15 @@
#include "document.hpp"
#include <cassert>
#include <boost/filesystem.hpp>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_iterator& begin,
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified)
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified)
{
assert (begin!=end);
@ -12,10 +19,10 @@ void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_i
--end2;
for (std::vector<boost::filesystem::path>::const_iterator iter (begin); iter!=end2; ++iter)
getData().loadFile (*iter, true);
getData().loadFile (*iter, true, false);
if (lastAsModified)
getData().loadFile (*end2, false);
getData().loadFile (*end2, false, false);
}
void CSMDoc::Document::addGmsts()
@ -2058,9 +2065,9 @@ void CSMDoc::Document::addOptionalGlobals()
{
static const char *sGlobals[] =
{
"dayspassed",
"pcwerewolf",
"pcyear",
"DaysPassed",
"PCWerewolf",
"PCYear",
0
};
@ -2137,11 +2144,86 @@ void CSMDoc::Document::createBase()
getData().getSkills().add (record);
}
static const char *sVoice[] =
{
"Intruder",
"Attack",
"Hello",
"Thief",
"Alarm",
"Idle",
"Flee",
"Hit",
0
};
for (int i=0; sVoice[i]; ++i)
{
ESM::Dialogue record;
record.mId = sVoice[i];
record.mType = ESM::Dialogue::Voice;
record.blank();
getData().getTopics().add (record);
}
static const char *sGreetings[] =
{
"Greeting 0",
"Greeting 1",
"Greeting 2",
"Greeting 3",
"Greeting 4",
"Greeting 5",
"Greeting 6",
"Greeting 7",
"Greeting 8",
"Greeting 9",
0
};
for (int i=0; sGreetings[i]; ++i)
{
ESM::Dialogue record;
record.mId = sGreetings[i];
record.mType = ESM::Dialogue::Greeting;
record.blank();
getData().getTopics().add (record);
}
static const char *sPersuasion[] =
{
"Intimidate Success",
"Intimidate Fail",
"Service Refusal",
"Admire Success",
"Taunt Success",
"Bribe Success",
"Info Refusal",
"Admire Fail",
"Taunt Fail",
"Bribe Fail",
0
};
for (int i=0; sPersuasion[i]; ++i)
{
ESM::Dialogue record;
record.mId = sPersuasion[i];
record.mType = ESM::Dialogue::Persuasion;
record.blank();
getData().getTopics().add (record);
}
}
CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
const boost::filesystem::path& savePath, bool new_)
: mSavePath (savePath), mTools (mData)
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_)
: mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir),
mProjectPath ((configuration.getUserPath() / "projects") /
(savePath.filename().string() + ".project")),
mSaving (*this, mProjectPath)
{
if (files.empty())
throw std::runtime_error ("Empty content file sequence");
@ -2158,6 +2240,34 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
load (files.begin(), end, !new_);
}
if (new_)
{
mData.setDescription ("");
mData.setAuthor ("");
}
/// \todo un-outcomment the else, once loading an existing content file works properly again.
// else
{
if (boost::filesystem::exists (mProjectPath))
{
getData().loadFile (mProjectPath, false, true);
}
else
{
boost::filesystem::path locCustomFiltersPath (configuration.getUserPath());
locCustomFiltersPath /= "defaultfilters";
if (boost::filesystem::exists(locCustomFiltersPath))
{
boost::filesystem::copy_file (locCustomFiltersPath, mProjectPath);
} else {
boost::filesystem::path filters(mResDir);
filters /= "defaultfilters";
boost::filesystem::copy_file(filters, mProjectPath);
}
getData().loadFile (mProjectPath, false, true);
}
}
addOptionalGmsts();
addOptionalGlobals();
@ -2166,9 +2276,10 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int)));
// dummy implementation -> remove when proper save is implemented.
mSaveCount = 0;
connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving()));
connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int)));
connect (&mSaving, SIGNAL (reportMessage (const QString&, int)),
this, SLOT (reportMessage (const QString&, int)));
}
CSMDoc::Document::~Document()
@ -2187,7 +2298,7 @@ int CSMDoc::Document::getState() const
if (!mUndoStack.isClean())
state |= State_Modified;
if (mSaveCount)
if (mSaving.isRunning())
state |= State_Locked | State_Saving | State_Operation;
if (int operations = mTools.getRunningOperations())
@ -2201,12 +2312,20 @@ const boost::filesystem::path& CSMDoc::Document::getSavePath() const
return mSavePath;
}
const std::vector<boost::filesystem::path>& CSMDoc::Document::getContentFiles() const
{
return mContentFiles;
}
void CSMDoc::Document::save()
{
mSaveCount = 1;
mSaveTimer.start (500);
if (mSaving.isRunning())
throw std::logic_error (
"Failed to initiate save, because a save operation is already running.");
mSaving.start();
emit stateChanged (getState(), this);
emit progress (1, 16, State_Saving, 1, this);
}
CSMWorld::UniversalId CSMDoc::Document::verify()
@ -2218,44 +2337,26 @@ CSMWorld::UniversalId CSMDoc::Document::verify()
void CSMDoc::Document::abortOperation (int type)
{
mTools.abortOperation (type);
if (type==State_Saving)
{
mSaveCount=0;
mSaveTimer.stop();
emit stateChanged (getState(), this);
}
mSaving.abort();
else
mTools.abortOperation (type);
}
void CSMDoc::Document::modificationStateChanged (bool clean)
{
emit stateChanged (getState(), this);
}
void CSMDoc::Document::operationDone (int type)
void CSMDoc::Document::reportMessage (const QString& message, int type)
{
emit stateChanged (getState(), this);
/// \todo find a better way to get these messages to the user.
std::cout << message.toUtf8().constData() << std::endl;
}
void CSMDoc::Document::saving()
void CSMDoc::Document::operationDone (int type)
{
++mSaveCount;
emit progress (mSaveCount, 16, State_Saving, 1, this);
if (mSaveCount>15)
{
//clear the stack before resetting the save state
//to avoid emitting incorrect states
mUndoStack.setClean();
mSaveCount = 0;
mSaveTimer.stop();
emit stateChanged (getState(), this);
}
emit stateChanged (getState(), this);
}
const CSMWorld::Data& CSMDoc::Document::getData() const

@ -14,6 +14,7 @@
#include "../tools/tools.hpp"
#include "state.hpp"
#include "saving.hpp"
class QAbstractItemModel;
@ -23,6 +24,11 @@ namespace ESM
struct Global;
}
namespace Files
{
class ConfigurationManager;
}
namespace CSMDoc
{
class Document : public QObject
@ -32,16 +38,17 @@ namespace CSMDoc
private:
boost::filesystem::path mSavePath;
std::vector<boost::filesystem::path> mContentFiles;
CSMWorld::Data mData;
CSMTools::Tools mTools;
boost::filesystem::path mProjectPath;
Saving mSaving;
boost::filesystem::path mResDir;
// It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is
// using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late.
QUndoStack mUndoStack;
int mSaveCount; ///< dummy implementation -> remove when proper save is implemented.
QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented.
// not implemented
Document (const Document&);
Document& operator= (const Document&);
@ -64,8 +71,7 @@ namespace CSMDoc
public:
Document (const std::vector<boost::filesystem::path>& files,
const boost::filesystem::path& savePath, bool new_);
Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_);
~Document();
@ -75,6 +81,10 @@ namespace CSMDoc
const boost::filesystem::path& getSavePath() const;
const std::vector<boost::filesystem::path>& getContentFiles() const;
///< \attention The last element in this collection is the file that is being edited,
/// but with its original path instead of the save path.
void save();
CSMWorld::UniversalId verify();
@ -98,10 +108,9 @@ namespace CSMDoc
void modificationStateChanged (bool clean);
void operationDone (int type);
void reportMessage (const QString& message, int type);
void saving();
///< dummy implementation -> remove when proper save is implemented.
void operationDone (int type);
public slots:
@ -110,3 +119,4 @@ namespace CSMDoc
}
#endif

@ -4,9 +4,22 @@
#include <algorithm>
#include <stdexcept>
#include <boost/filesystem.hpp>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
#include "document.hpp"
CSMDoc::DocumentManager::DocumentManager() {}
CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
: mConfiguration (configuration)
{
boost::filesystem::path projectPath = configuration.getUserPath() / "projects";
if (!boost::filesystem::is_directory (projectPath))
boost::filesystem::create_directories (projectPath);
}
CSMDoc::DocumentManager::~DocumentManager()
{
@ -17,7 +30,7 @@ CSMDoc::DocumentManager::~DocumentManager()
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_)
{
Document *document = new Document (files, savePath, new_);
Document *document = new Document (mConfiguration, files, savePath, mResDir, new_);
mDocuments.push_back (document);
@ -35,4 +48,9 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document)
delete document;
return mDocuments.empty();
}
void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir)
{
mResDir = boost::filesystem::system_complete(parResDir);
}

@ -6,6 +6,11 @@
#include <boost/filesystem/path.hpp>
namespace Files
{
class ConfigurationManager;
}
namespace CSMDoc
{
class Document;
@ -13,18 +18,18 @@ namespace CSMDoc
class DocumentManager
{
std::vector<Document *> mDocuments;
const Files::ConfigurationManager& mConfiguration;
DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&);
public:
DocumentManager();
DocumentManager (const Files::ConfigurationManager& configuration);
~DocumentManager();
Document *addDocument (const std::vector<boost::filesystem::path>& files,
const boost::filesystem::path& savePath, bool new_);
Document *addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_);
///< The ownership of the returned document is not transferred to the caller.
///
/// \param new_ Do not load the last content file in \a files and instead create in an
@ -32,6 +37,10 @@ namespace CSMDoc
bool removeDocument (Document *document);
///< \return last document removed?
void setResourceDir (const boost::filesystem::path& parResDir);
private:
boost::filesystem::path mResDir;
};
}

@ -6,16 +6,16 @@
#include <QTimer>
#include "../doc/state.hpp"
#include "state.hpp"
#include "stage.hpp"
void CSMTools::Operation::prepareStages()
void CSMDoc::Operation::prepareStages()
{
mCurrentStage = mStages.begin();
mCurrentStep = 0;
mCurrentStepTotal = 0;
mTotalSteps = 0;
mError = false;
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
{
@ -24,38 +24,61 @@ void CSMTools::Operation::prepareStages()
}
}
CSMTools::Operation::Operation (int type) : mType (type) {}
CSMDoc::Operation::Operation (int type, bool ordered, bool finalAlways)
: mType (type), mOrdered (ordered), mFinalAlways (finalAlways)
{
connect (this, SIGNAL (finished()), this, SLOT (operationDone()));
}
CSMTools::Operation::~Operation()
CSMDoc::Operation::~Operation()
{
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
delete iter->first;
}
void CSMTools::Operation::run()
void CSMDoc::Operation::run()
{
prepareStages();
QTimer timer;
timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify()));
timer.connect (&timer, SIGNAL (timeout()), this, SLOT (executeStage()));
timer.start (0);
exec();
}
void CSMTools::Operation::appendStage (Stage *stage)
void CSMDoc::Operation::appendStage (Stage *stage)
{
mStages.push_back (std::make_pair (stage, 0));
}
void CSMTools::Operation::abort()
bool CSMDoc::Operation::hasError() const
{
exit();
return mError;
}
void CSMTools::Operation::verify()
void CSMDoc::Operation::abort()
{
if (!isRunning())
return;
mError = true;
if (mFinalAlways)
{
if (mStages.begin()!=mStages.end() && mCurrentStage!=--mStages.end())
{
mCurrentStep = 0;
mCurrentStage = --mStages.end();
}
}
else
mCurrentStage = mStages.end();
}
void CSMDoc::Operation::executeStage()
{
std::vector<std::string> messages;
@ -68,7 +91,16 @@ void CSMTools::Operation::verify()
}
else
{
mCurrentStage->first->perform (mCurrentStep++, messages);
try
{
mCurrentStage->first->perform (mCurrentStep++, messages);
}
catch (const std::exception& e)
{
emit reportMessage (e.what(), mType);
abort();
}
++mCurrentStepTotal;
break;
}
@ -81,4 +113,9 @@ void CSMTools::Operation::verify()
if (mCurrentStage==mStages.end())
exit();
}
void CSMDoc::Operation::operationDone()
{
emit done (mType);
}

@ -1,11 +1,11 @@
#ifndef CSM_TOOLS_OPERATION_H
#define CSM_TOOLS_OPERATION_H
#ifndef CSM_DOC_OPERATION_H
#define CSM_DOC_OPERATION_H
#include <vector>
#include <QThread>
namespace CSMTools
namespace CSMDoc
{
class Stage;
@ -19,12 +19,17 @@ namespace CSMTools
int mCurrentStep;
int mCurrentStepTotal;
int mTotalSteps;
int mOrdered;
bool mFinalAlways;
bool mError;
void prepareStages();
public:
Operation (int type);
Operation (int type, bool ordered, bool finalAlways = false);
///< \param ordered Stages must be executed in the given order.
/// \param finalAlways Execute last stage even if an error occurred during earlier stages.
virtual ~Operation();
@ -35,19 +40,25 @@ namespace CSMTools
///
/// \attention Do no call this function while this Operation is running.
bool hasError() const;
signals:
void progress (int current, int max, int type);
void reportMessage (const QString& message, int type);
void done (int type);
public slots:
void abort();
private slots:
void verify();
void executeStage();
void operationDone();
};
}

@ -0,0 +1,74 @@
#include "saving.hpp"
#include "../world/data.hpp"
#include "../world/idcollection.hpp"
#include "state.hpp"
#include "savingstages.hpp"
#include "document.hpp"
CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& projectPath)
: Operation (State_Saving, true, true), mDocument (document), mState (*this, projectPath)
{
// save project file
appendStage (new OpenSaveStage (mDocument, mState, true));
appendStage (new WriteHeaderStage (mDocument, mState, true));
appendStage (new WriteFilterStage (mDocument, mState, CSMFilter::Filter::Scope_Project));
appendStage (new CloseSaveStage (mState));
// save content file
appendStage (new OpenSaveStage (mDocument, mState, false));
appendStage (new WriteHeaderStage (mDocument, mState, false));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Global> >
(mDocument.getData().getGlobals(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::GameSetting> >
(mDocument.getData().getGmsts(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Skill> >
(mDocument.getData().getSkills(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Class> >
(mDocument.getData().getClasses(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Faction> >
(mDocument.getData().getFactions(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Race> >
(mDocument.getData().getRaces(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Sound> >
(mDocument.getData().getSounds(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Script> >
(mDocument.getData().getScripts(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Region> >
(mDocument.getData().getRegions(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::BirthSign> >
(mDocument.getData().getBirthsigns(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Spell> >
(mDocument.getData().getSpells(), mState));
/// \todo deal with info records for topcis and journals
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Dialogue> >
(mDocument.getData().getTopics(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Dialogue> >
(mDocument.getData().getJournals(), mState));
appendStage (new WriteRefIdCollectionStage (mDocument, mState));
appendStage (new CloseSaveStage (mState));
appendStage (new FinalSavingStage (mDocument, mState));
}

@ -0,0 +1,27 @@
#ifndef CSM_DOC_SAVING_H
#define CSM_DOC_SAVING_H
#include <boost/filesystem/path.hpp>
#include "operation.hpp"
#include "savingstate.hpp"
namespace CSMDoc
{
class Document;
class Saving : public Operation
{
Q_OBJECT
Document& mDocument;
SavingState mState;
public:
Saving (Document& document, const boost::filesystem::path& projectPath);
};
}
#endif

@ -0,0 +1,161 @@
#include "savingstages.hpp"
#include <fstream>
#include <boost/filesystem.hpp>
#include <QUndoStack>
#include "document.hpp"
#include "savingstate.hpp"
CSMDoc::OpenSaveStage::OpenSaveStage (Document& document, SavingState& state, bool projectFile)
: mDocument (document), mState (state), mProjectFile (projectFile)
{}
int CSMDoc::OpenSaveStage::setup()
{
return 1;
}
void CSMDoc::OpenSaveStage::perform (int stage, std::vector<std::string>& messages)
{
mState.start (mDocument, mProjectFile);
mState.getStream().open ((mProjectFile ? mState.getPath() : mState.getTmpPath()).string().c_str());
if (!mState.getStream().is_open())
throw std::runtime_error ("failed to open stream for saving");
}
CSMDoc::WriteHeaderStage::WriteHeaderStage (Document& document, SavingState& state, bool simple)
: mDocument (document), mState (state), mSimple (simple)
{}
int CSMDoc::WriteHeaderStage::setup()
{
return 1;
}
void CSMDoc::WriteHeaderStage::perform (int stage, std::vector<std::string>& messages)
{
mState.getWriter().setVersion();
mState.getWriter().clearMaster();
mState.getWriter().setFormat (0);
if (mSimple)
{
mState.getWriter().setAuthor ("");
mState.getWriter().setDescription ("");
mState.getWriter().setRecordCount (0);
}
else
{
mState.getWriter().setAuthor (mDocument.getData().getAuthor());
mState.getWriter().setDescription (mDocument.getData().getDescription());
mState.getWriter().setRecordCount (
mDocument.getData().count (CSMWorld::RecordBase::State_Modified) +
mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) +
mDocument.getData().count (CSMWorld::RecordBase::State_Deleted));
/// \todo refine dependency list (at least remove redundant dependencies)
std::vector<boost::filesystem::path> dependencies = mDocument.getContentFiles();
std::vector<boost::filesystem::path>::const_iterator end (--dependencies.end());
for (std::vector<boost::filesystem::path>::const_iterator iter (dependencies.begin());
iter!=end; ++iter)
{
std::string name = iter->filename().string();
uint64_t size = boost::filesystem::file_size (*iter);
mState.getWriter().addMaster (name, size);
}
}
mState.getWriter().save (mState.getStream());
}
CSMDoc::WriteRefIdCollectionStage::WriteRefIdCollectionStage (Document& document, SavingState& state)
: mDocument (document), mState (state)
{}
int CSMDoc::WriteRefIdCollectionStage::setup()
{
return mDocument.getData().getReferenceables().getSize();
}
void CSMDoc::WriteRefIdCollectionStage::perform (int stage, std::vector<std::string>& messages)
{
mDocument.getData().getReferenceables().save (stage, mState.getWriter());
}
CSMDoc::WriteFilterStage::WriteFilterStage (Document& document, SavingState& state,
CSMFilter::Filter::Scope scope)
: WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> > (document.getData().getFilters(),
state),
mDocument (document), mScope (scope)
{}
void CSMDoc::WriteFilterStage::perform (int stage, std::vector<std::string>& messages)
{
const CSMWorld::Record<CSMFilter::Filter>& record =
mDocument.getData().getFilters().getRecord (stage);
if (record.get().mScope==mScope)
WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> >::perform (stage, messages);
}
CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state)
: mState (state)
{}
int CSMDoc::CloseSaveStage::setup()
{
return 1;
}
void CSMDoc::CloseSaveStage::perform (int stage, std::vector<std::string>& messages)
{
mState.getStream().close();
if (!mState.getStream())
throw std::runtime_error ("saving failed");
}
CSMDoc::FinalSavingStage::FinalSavingStage (Document& document, SavingState& state)
: mDocument (document), mState (state)
{}
int CSMDoc::FinalSavingStage::setup()
{
return 1;
}
void CSMDoc::FinalSavingStage::perform (int stage, std::vector<std::string>& messages)
{
if (mState.hasError())
{
mState.getWriter().close();
mState.getStream().close();
if (boost::filesystem::exists (mState.getTmpPath()))
boost::filesystem::remove (mState.getTmpPath());
}
else if (!mState.isProjectFile())
{
if (boost::filesystem::exists (mState.getPath()))
boost::filesystem::remove (mState.getPath());
boost::filesystem::rename (mState.getTmpPath(), mState.getPath());
mDocument.getUndoStack().setClean();
}
}

@ -0,0 +1,172 @@
#ifndef CSM_DOC_SAVINGSTAGES_H
#define CSM_DOC_SAVINGSTAGES_H
#include "stage.hpp"
#include "savingstate.hpp"
#include "../world/record.hpp"
#include "../world/idcollection.hpp"
#include "../filter/filter.hpp"
namespace CSMDoc
{
class Document;
class SavingState;
class OpenSaveStage : public Stage
{
Document& mDocument;
SavingState& mState;
bool mProjectFile;
public:
OpenSaveStage (Document& document, SavingState& state, bool projectFile);
///< \param projectFile Saving the project file instead of the content file.
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class WriteHeaderStage : public Stage
{
Document& mDocument;
SavingState& mState;
bool mSimple;
public:
WriteHeaderStage (Document& document, SavingState& state, bool simple);
///< \param simple Simplified header (used for project files).
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
template<class CollectionT>
class WriteCollectionStage : public Stage
{
const CollectionT& mCollection;
SavingState& mState;
public:
WriteCollectionStage (const CollectionT& collection, SavingState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
template<class CollectionT>
WriteCollectionStage<CollectionT>::WriteCollectionStage (const CollectionT& collection,
SavingState& state)
: mCollection (collection), mState (state)
{}
template<class CollectionT>
int WriteCollectionStage<CollectionT>::setup()
{
return mCollection.getSize();
}
template<class CollectionT>
void WriteCollectionStage<CollectionT>::perform (int stage, std::vector<std::string>& messages)
{
CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState;
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly)
{
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&mCollection.getRecord (stage).mModified.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage));
mCollection.getRecord (stage).mModified.save (mState.getWriter());
mState.getWriter().endRecord (type);
}
else if (state==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
}
}
class WriteRefIdCollectionStage : public Stage
{
Document& mDocument;
SavingState& mState;
public:
WriteRefIdCollectionStage (Document& document, SavingState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class WriteFilterStage : public WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> >
{
Document& mDocument;
CSMFilter::Filter::Scope mScope;
public:
WriteFilterStage (Document& document, SavingState& state, CSMFilter::Filter::Scope scope);
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class CloseSaveStage : public Stage
{
SavingState& mState;
public:
CloseSaveStage (SavingState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class FinalSavingStage : public Stage
{
Document& mDocument;
SavingState& mState;
public:
FinalSavingStage (Document& document, SavingState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
}
#endif

@ -0,0 +1,65 @@
#include "savingstate.hpp"
#include "operation.hpp"
#include "document.hpp"
CSMDoc::SavingState::SavingState (Operation& operation, const boost::filesystem::path& projectPath)
: mOperation (operation),
/// \todo set encoding properly, once config implementation has been fixed.
mEncoder (ToUTF8::calculateEncoding ("win1252")),
mProjectPath (projectPath), mProjectFile (false)
{
mWriter.setEncoder (&mEncoder);
}
bool CSMDoc::SavingState::hasError() const
{
return mOperation.hasError();
}
void CSMDoc::SavingState::start (Document& document, bool project)
{
mProjectFile = project;
if (mStream.is_open())
mStream.close();
mStream.clear();
if (project)
mPath = mProjectPath;
else
mPath = document.getSavePath();
boost::filesystem::path file (mPath.filename().string() + ".tmp");
mTmpPath = mPath.parent_path();
mTmpPath /= file;
}
const boost::filesystem::path& CSMDoc::SavingState::getPath() const
{
return mPath;
}
const boost::filesystem::path& CSMDoc::SavingState::getTmpPath() const
{
return mTmpPath;
}
std::ofstream& CSMDoc::SavingState::getStream()
{
return mStream;
}
ESM::ESMWriter& CSMDoc::SavingState::getWriter()
{
return mWriter;
}
bool CSMDoc::SavingState::isProjectFile() const
{
return mProjectFile;
}

@ -0,0 +1,50 @@
#ifndef CSM_DOC_SAVINGSTATE_H
#define CSM_DOC_SAVINGSTATE_H
#include <fstream>
#include <boost/filesystem/path.hpp>
#include <components/esm/esmwriter.hpp>
namespace CSMDoc
{
class Operation;
class Document;
class SavingState
{
Operation& mOperation;
boost::filesystem::path mPath;
boost::filesystem::path mTmpPath;
ToUTF8::Utf8Encoder mEncoder;
std::ofstream mStream;
ESM::ESMWriter mWriter;
boost::filesystem::path mProjectPath;
bool mProjectFile;
public:
SavingState (Operation& operation, const boost::filesystem::path& projectPath);
bool hasError() const;
void start (Document& document, bool project);
///< \param project Save project file instead of content file.
const boost::filesystem::path& getPath() const;
const boost::filesystem::path& getTmpPath() const;
std::ofstream& getStream();
ESM::ESMWriter& getWriter();
bool isProjectFile() const;
///< Currently saving project file? (instead of content file)
};
}
#endif

@ -0,0 +1,4 @@
#include "stage.hpp"
CSMDoc::Stage::~Stage() {}

@ -1,10 +1,10 @@
#ifndef CSM_TOOLS_STAGE_H
#define CSM_TOOLS_STAGE_H
#ifndef CSM_DOC_STAGE_H
#define CSM_DOC_STAGE_H
#include <vector>
#include <string>
namespace CSMTools
namespace CSMDoc
{
class Stage
{
@ -16,7 +16,7 @@ namespace CSMTools
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages) = 0;
///< Messages resulting from this tage will be appended to \a messages.
///< Messages resulting from this stage will be appended to \a messages.
};
}

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp"
#include "stage.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that birthsign records are internally consistent
class BirthsignCheckStage : public Stage
class BirthsignCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp"
#include "stage.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that class records are internally consistent
class ClassCheckStage : public Stage
class ClassCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Class>& mClasses;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp"
#include "stage.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that faction records are internally consistent
class FactionCheckStage : public Stage
class FactionCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Faction>& mFactions;

@ -6,7 +6,7 @@
#include "../world/universalid.hpp"
#include "stage.hpp"
#include "../doc/stage.hpp"
namespace CSMWorld
{
@ -16,7 +16,7 @@ namespace CSMWorld
namespace CSMTools
{
/// \brief Verify stage: make sure that records with specific IDs exist.
class MandatoryIdStage : public Stage
class MandatoryIdStage : public CSMDoc::Stage
{
const CSMWorld::CollectionBase& mIdCollection;
CSMWorld::UniversalId mCollectionId;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp"
#include "stage.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that race records are internally consistent
class RaceCheckStage : public Stage
class RaceCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Race>& mRaces;
bool mPlayable;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp"
#include "stage.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that region records are internally consistent
class RegionCheckStage : public Stage
class RegionCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Region>& mRegions;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp"
#include "stage.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that skill records are internally consistent
class SkillCheckStage : public Stage
class SkillCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Skill>& mSkills;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp"
#include "stage.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that sound records are internally consistent
class SoundCheckStage : public Stage
class SoundCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Sound>& mSounds;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp"
#include "stage.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that spell records are internally consistent
class SpellCheckStage : public Stage
class SpellCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Spell>& mSpells;

@ -1,4 +0,0 @@
#include "stage.hpp"
CSMTools::Stage::~Stage() {}

@ -3,9 +3,8 @@
#include <QThreadPool>
#include "verifier.hpp"
#include "../doc/state.hpp"
#include "../doc/operation.hpp"
#include "../world/data.hpp"
#include "../world/universalid.hpp"
@ -21,7 +20,7 @@
#include "birthsigncheck.hpp"
#include "spellcheck.hpp"
CSMTools::Operation *CSMTools::Tools::get (int type)
CSMDoc::Operation *CSMTools::Tools::get (int type)
{
switch (type)
{
@ -31,19 +30,19 @@ CSMTools::Operation *CSMTools::Tools::get (int type)
return 0;
}
const CSMTools::Operation *CSMTools::Tools::get (int type) const
const CSMDoc::Operation *CSMTools::Tools::get (int type) const
{
return const_cast<Tools *> (this)->get (type);
}
CSMTools::Verifier *CSMTools::Tools::getVerifier()
CSMDoc::Operation *CSMTools::Tools::getVerifier()
{
if (!mVerifier)
{
mVerifier = new Verifier;
mVerifier = new CSMDoc::Operation (CSMDoc::State_Verifying, false);
connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone()));
connect (mVerifier, SIGNAL (done (int)), this, SIGNAL (done (int)));
connect (mVerifier, SIGNAL (reportMessage (const QString&, int)),
this, SLOT (verifierMessage (const QString&, int)));
@ -103,7 +102,7 @@ CSMWorld::UniversalId CSMTools::Tools::runVerifier()
void CSMTools::Tools::abortOperation (int type)
{
if (Operation *operation = get (type))
if (CSMDoc::Operation *operation = get (type))
operation->abort();
}
@ -118,7 +117,7 @@ int CSMTools::Tools::getRunningOperations() const
int result = 0;
for (int i=0; sOperations[i]!=-1; ++i)
if (const Operation *operation = get (sOperations[i]))
if (const CSMDoc::Operation *operation = get (sOperations[i]))
if (operation->isRunning())
result |= sOperations[i];
@ -133,11 +132,6 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId&
return mReports.at (id.getIndex());
}
void CSMTools::Tools::verifierDone()
{
emit done (CSMDoc::State_Verifying);
}
void CSMTools::Tools::verifierMessage (const QString& message, int type)
{
std::map<int, int>::iterator iter = mActiveReports.find (type);

@ -11,10 +11,13 @@ namespace CSMWorld
class UniversalId;
}
namespace CSMTools
namespace CSMDoc
{
class Verifier;
class Operation;
}
namespace CSMTools
{
class ReportModel;
class Tools : public QObject
@ -22,7 +25,7 @@ namespace CSMTools
Q_OBJECT
CSMWorld::Data& mData;
Verifier *mVerifier;
CSMDoc::Operation *mVerifier;
std::map<int, ReportModel *> mReports;
int mNextReportNumber;
std::map<int, int> mActiveReports; // type, report number
@ -31,12 +34,12 @@ namespace CSMTools
Tools (const Tools&);
Tools& operator= (const Tools&);
Verifier *getVerifier();
CSMDoc::Operation *getVerifier();
Operation *get (int type);
CSMDoc::Operation *get (int type);
///< Returns a 0-pointer, if operation hasn't been used yet.
const Operation *get (int type) const;
const CSMDoc::Operation *get (int type) const;
///< Returns a 0-pointer, if operation hasn't been used yet.
public:
@ -58,8 +61,6 @@ namespace CSMTools
private slots:
void verifierDone();
void verifierMessage (const QString& message, int type);
signals:

@ -1,7 +0,0 @@
#include "verifier.hpp"
#include "../doc/state.hpp"
CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying)
{}

@ -1,17 +0,0 @@
#ifndef CSM_TOOLS_VERIFIER_H
#define CSM_TOOLS_VERIFIER_H
#include "operation.hpp"
namespace CSMTools
{
class Verifier : public Operation
{
public:
Verifier();
};
}
#endif

@ -1,6 +1,31 @@
#include "collectionbase.hpp"
#include <stdexcept>
#include "columnbase.hpp"
CSMWorld::CollectionBase::CollectionBase() {}
CSMWorld::CollectionBase::~CollectionBase() {}
int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const
{
int columns = getColumns();
for (int i=0; i<columns; ++i)
if (getColumn (i).mColumnId==id)
return i;
return -1;
}
int CSMWorld::CollectionBase::findColumnIndex (Columns::ColumnId id) const
{
int index = searchColumnIndex (id);
if (index==-1)
throw std::logic_error ("invalid column index");
return index;
}

@ -4,6 +4,7 @@
#include <string>
#include "universalid.hpp"
#include "columns.hpp"
class QVariant;
@ -83,6 +84,13 @@ namespace CSMWorld
///< Return a sorted collection of all IDs
///
/// \param listDeleted include deleted record in the list
int searchColumnIndex (Columns::ColumnId id) const;
///< Return index of column with the given \a id. If no such column exists, -1 is returned.
int findColumnIndex (Columns::ColumnId id) const;
///< Return index of column with the given \a id. If no such column exists, an exception is
/// thrown.
};
}

@ -43,7 +43,8 @@ namespace CSMWorld
Display_CreatureType,
Display_WeaponType,
Display_RecordState,
Display_RefRecordType
Display_RefRecordType,
Display_DialogueType
};
int mColumnId;

@ -1217,6 +1217,37 @@ namespace CSMWorld
}
};
template<typename ESXRecordT>
struct ScopeColumn : public Column<ESXRecordT>
{
ScopeColumn()
: Column<ESXRecordT> (Columns::ColumnId_Scope, ColumnBase::Display_Integer, 0)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return static_cast<int> (record.get().mScope);
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mScope = static_cast<CSMFilter::Filter::Scope> (data.toInt());
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
virtual bool isUserEditable() const
{
return false;
}
};
template<typename ESXRecordT>
struct PosColumn : public Column<ESXRecordT>
{
@ -1284,6 +1315,39 @@ namespace CSMWorld
return true;
}
};
template<typename ESXRecordT>
struct DialogueTypeColumn : public Column<ESXRecordT>
{
DialogueTypeColumn (bool hidden = false)
: Column<ESXRecordT> (Columns::ColumnId_DialogueType, ColumnBase::Display_DialogueType,
hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return static_cast<int> (record.get().mType);
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mType = data.toInt();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
virtual bool isUserEditable() const
{
return false;
}
};
}
#endif

@ -159,6 +159,8 @@ namespace CSMWorld
{ ColumnId_DoorPositionXRot, "Teleport Rot X" },
{ ColumnId_DoorPositionYRot, "Teleport Rot Y" },
{ ColumnId_DoorPositionZRot, "Teleport Rot Z" },
{ ColumnId_DialogueType, "Dialogue Type" },
{ ColumnId_Scope, "Scope", },
{ ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" },
@ -269,6 +271,11 @@ namespace
"unknown", "none", "short", "integer", "long", "float", "string", 0
};
static const char *sDialogueTypeEnums[] =
{
"Topic", "Voice", "Greeting", "Persuasion", 0
};
const char **getEnumNames (CSMWorld::Columns::ColumnId column)
{
switch (column)
@ -283,6 +290,7 @@ namespace
case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes;
case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums;
case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums;
case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums;
default: return 0;
}

@ -152,6 +152,8 @@ namespace CSMWorld
ColumnId_DoorPositionXRot = 139,
ColumnId_DoorPositionYRot = 140,
ColumnId_DoorPositionZRot = 141,
ColumnId_DialogueType = 142,
ColumnId_Scope = 143,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values.

@ -44,6 +44,17 @@ void CSMWorld::Data::appendIds (std::vector<std::string>& ids, const CollectionB
ids.insert (ids.end(), ids2.begin(), ids2.end());
}
int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collection)
{
int number = 0;
for (int i=0; i<collection.getSize(); ++i)
if (collection.getRecord (i).mState==state)
++number;
return number;
}
CSMWorld::Data::Data() : mRefs (mCells)
{
mGlobals.addColumn (new StringIdColumn<ESM::Global>);
@ -141,6 +152,14 @@ CSMWorld::Data::Data() : mRefs (mCells)
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_StarterSpell, 0x2));
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AlwaysSucceeds, 0x4));
mTopics.addColumn (new StringIdColumn<ESM::Dialogue>);
mTopics.addColumn (new RecordStateColumn<ESM::Dialogue>);
mTopics.addColumn (new DialogueTypeColumn<ESM::Dialogue>);
mJournals.addColumn (new StringIdColumn<ESM::Dialogue>);
mJournals.addColumn (new RecordStateColumn<ESM::Dialogue>);
mJournals.addColumn (new DialogueTypeColumn<ESM::Dialogue> (true));
mCells.addColumn (new StringIdColumn<Cell>);
mCells.addColumn (new RecordStateColumn<Cell>);
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));
@ -184,6 +203,7 @@ CSMWorld::Data::Data() : mRefs (mCells)
mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>);
mFilters.addColumn (new FilterColumn<CSMFilter::Filter>);
mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>);
mFilters.addColumn (new ScopeColumn<CSMFilter::Filter>);
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst);
@ -196,6 +216,8 @@ CSMWorld::Data::Data() : mRefs (mCells)
addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region);
addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign);
addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell);
addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic);
addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal);
addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell);
addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables,
UniversalId::Type_Referenceable);
@ -319,6 +341,28 @@ CSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells()
return mSpells;
}
const CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getTopics() const
{
return mTopics;
}
CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getTopics()
{
return mTopics;
}
const CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals() const
{
return mJournals;
}
CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals()
{
return mJournals;
}
const CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells() const
{
return mCells;
@ -387,7 +431,7 @@ void CSMWorld::Data::merge()
mGlobals.merge();
}
void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, bool project)
{
ESM::ESMReader reader;
@ -397,6 +441,9 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
reader.open (path.string());
mAuthor = reader.getAuthor();
mDescription = reader.getDesc();
// Note: We do not need to send update signals here, because at this point the model is not connected
// to any view.
while (reader.hasMoreRecs())
@ -447,6 +494,54 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
case ESM::REC_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break;
case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break;
case ESM::REC_DIAL:
{
std::string id = reader.getHNOString ("NAME");
ESM::Dialogue record;
record.mId = id;
record.load (reader);
if (record.mType==ESM::Dialogue::Journal)
{
mJournals.load (record, base);
}
else if (record.mType==ESM::Dialogue::Deleted)
{
if (mJournals.tryDelete (id))
{
/// \todo handle info records
}
else if (mTopics.tryDelete (id))
{
/// \todo handle info records
}
else
{
/// \todo report deletion of non-existing record
}
}
else
{
mTopics.load (record, base);
}
break;
}
case ESM::REC_FILT:
if (project)
{
mFilters.load (reader, base);
mFilters.setData (mFilters.getSize()-1,
mFilters.findColumnIndex (CSMWorld::Columns::ColumnId_Scope),
static_cast<int> (CSMFilter::Filter::Scope_Project));
break;
}
// fall through (filter record in a content file is an error with format 0)
default:
/// \todo throw an exception instead, once all records are implemented
@ -469,10 +564,50 @@ bool CSMWorld::Data::hasId (const std::string& id) const
getRegions().searchId (id)!=-1 ||
getBirthsigns().searchId (id)!=-1 ||
getSpells().searchId (id)!=-1 ||
getTopics().searchId (id)!=-1 ||
getJournals().searchId (id)!=-1 ||
getCells().searchId (id)!=-1 ||
getReferenceables().searchId (id)!=-1;
}
int CSMWorld::Data::count (RecordBase::State state) const
{
return
count (state, mGlobals) +
count (state, mGmsts) +
count (state, mSkills) +
count (state, mClasses) +
count (state, mFactions) +
count (state, mRaces) +
count (state, mSounds) +
count (state, mScripts) +
count (state, mRegions) +
count (state, mBirthsigns) +
count (state, mSpells) +
count (state, mCells) +
count (state, mReferenceables);
}
void CSMWorld::Data::setDescription (const std::string& description)
{
mDescription = description;
}
std::string CSMWorld::Data::getDescription() const
{
return mDescription;
}
void CSMWorld::Data::setAuthor (const std::string& author)
{
mAuthor = author;
}
std::string CSMWorld::Data::getAuthor() const
{
return mAuthor;
}
std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
{
std::vector<std::string> ids;
@ -487,6 +622,8 @@ std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
appendIds (ids, mRegions, listDeleted);
appendIds (ids, mBirthsigns, listDeleted);
appendIds (ids, mSpells, listDeleted);
appendIds (ids, mTopics, listDeleted);
appendIds (ids, mJournals, listDeleted);
appendIds (ids, mCells, listDeleted);
appendIds (ids, mReferenceables, listDeleted);

@ -20,6 +20,7 @@
#include <components/esm/loadregn.hpp>
#include <components/esm/loadbsgn.hpp>
#include <components/esm/loadspel.hpp>
#include <components/esm/loaddial.hpp>
#include "../filter/filter.hpp"
@ -48,12 +49,16 @@ namespace CSMWorld
IdCollection<ESM::Region> mRegions;
IdCollection<ESM::BirthSign> mBirthsigns;
IdCollection<ESM::Spell> mSpells;
IdCollection<ESM::Dialogue> mTopics;
IdCollection<ESM::Dialogue> mJournals;
IdCollection<Cell> mCells;
RefIdCollection mReferenceables;
RefCollection mRefs;
IdCollection<CSMFilter::Filter> mFilters;
std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
std::string mAuthor;
std::string mDescription;
// not implemented
Data (const Data&);
@ -66,6 +71,8 @@ namespace CSMWorld
bool listDeleted);
///< Append all IDs from collection to \a ids.
static int count (RecordBase::State state, const CollectionBase& collection);
public:
Data();
@ -116,6 +123,14 @@ namespace CSMWorld
IdCollection<ESM::Spell>& getSpells();
const IdCollection<ESM::Dialogue>& getTopics() const;
IdCollection<ESM::Dialogue>& getTopics();
const IdCollection<ESM::Dialogue>& getJournals() const;
IdCollection<ESM::Dialogue>& getJournals();
const IdCollection<Cell>& getCells() const;
IdCollection<Cell>& getCells();
@ -141,8 +156,10 @@ namespace CSMWorld
void merge();
///< Merge modified into base.
void loadFile (const boost::filesystem::path& path, bool base);
void loadFile (const boost::filesystem::path& path, bool base, bool project);
///< Merging content of a file into base or modified.
///
/// \param project load project file instead of content file
bool hasId (const std::string& id) const;
@ -151,6 +168,17 @@ namespace CSMWorld
///
/// \param listDeleted include deleted record in the list
int count (RecordBase::State state) const;
///< Return number of top-level records with the given \a state.
void setDescription (const std::string& description);
std::string getDescription() const;
void setAuthor (const std::string& author);
std::string getAuthor() const;
signals:
void idListChanged();

@ -7,21 +7,24 @@
namespace CSMWorld
{
/// \brief Single type collection of top level records
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class IdCollection : public Collection<ESXRecordT, IdAccessorT>
{
public:
void load (ESM::ESMReader& reader, bool base,
UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types
void load (ESM::ESMReader& reader, bool base);
void load (const ESXRecordT& record, bool base);
bool tryDelete (const std::string& id);
///< Try deleting \a id. If the id does not exist or can't be deleted the call is ignored.
///
/// \return Has the ID been deleted?
};
template<typename ESXRecordT, typename IdAccessorT>
void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base,
UniversalId::Type type)
void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
{
std::string id = reader.getHNOString ("NAME");
@ -56,30 +59,62 @@ namespace CSMWorld
IdAccessorT().getId (record) = id;
record.load (reader);
int index = this->searchId (IdAccessorT().getId (record));
load (record, base);
}
}
if (index==-1)
{
// new record
Record<ESXRecordT> record2;
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = record;
template<typename ESXRecordT, typename IdAccessorT>
void IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base)
{
int index = this->searchId (IdAccessorT().getId (record));
this->appendRecord (record2);
}
if (index==-1)
{
// new record
Record<ESXRecordT> record2;
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = record;
this->appendRecord (record2);
}
else
{
// old record
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
if (base)
record2.mBase = record;
else
{
// old record
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
record2.setModified (record);
if (base)
record2.mBase = record;
else
record2.setModified (record);
this->setRecord (index, record2);
}
}
this->setRecord (index, record2);
}
template<typename ESXRecordT, typename IdAccessorT>
bool IdCollection<ESXRecordT, IdAccessorT>::tryDelete (const std::string& id)
{
int index = this->searchId (id);
if (index==-1)
return false;
Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
if (record.isDeleted())
return false;
if (record.mState==RecordBase::State_ModifiedOnly)
{
Collection<ESXRecordT, IdAccessorT>::removeRows (index, 1);
}
else
{
record.mState = RecordBase::State_Deleted;
setRecord (index, record);
}
return true;
}
}

@ -161,21 +161,10 @@ const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id)
int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const
{
int columns = mIdCollection->getColumns();
for (int i=0; i<columns; ++i)
if (mIdCollection->getColumn (i).mColumnId==id)
return i;
return -1;
return mIdCollection->searchColumnIndex (id);
}
int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const
{
int index = searchColumnIndex (id);
if (index==-1)
throw std::logic_error ("invalid column index");
return index;
return mIdCollection->findColumnIndex (id);
}

@ -539,3 +539,8 @@ std::vector<std::string> CSMWorld::RefIdCollection::getIds (bool listDeleted) co
{
return mData.getIds (listDeleted);
}
void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const
{
mData.save (index, writer);
}

@ -9,6 +9,11 @@
#include "collectionbase.hpp"
#include "refiddata.hpp"
namespace ESM
{
class ESMWriter;
}
namespace CSMWorld
{
class RefIdAdapter;
@ -94,6 +99,8 @@ namespace CSMWorld
///< Return a sorted collection of all IDs
///
/// \param listDeleted include deleted record in the list
void save (int index, ESM::ESMWriter& writer) const;
};
}

@ -218,3 +218,16 @@ std::vector<std::string> CSMWorld::RefIdData::getIds (bool listDeleted) const
return ids;
}
void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const
{
LocalIndex localIndex = globalToLocalIndex (index);
std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter =
mRecordContainers.find (localIndex.second);
if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
iter->second->save (localIndex.first, writer);
}

@ -23,6 +23,7 @@
#include <components/esm/loadweap.hpp>
#include <components/esm/loadnpc.hpp>
#include <components/esm/loadmisc.hpp>
#include <components/esm/esmwriter.hpp>
#include "record.hpp"
#include "universalid.hpp"
@ -51,6 +52,8 @@ namespace CSMWorld
virtual void erase (int index, int count) = 0;
virtual std::string getId (int index) const = 0;
virtual void save (int index, ESM::ESMWriter& writer) const = 0;
};
template<typename RecordT>
@ -71,6 +74,8 @@ namespace CSMWorld
virtual void erase (int index, int count);
virtual std::string getId (int index) const;
virtual void save (int index, ESM::ESMWriter& writer) const;
};
template<typename RecordT>
@ -123,6 +128,31 @@ namespace CSMWorld
return mContainer.at (index).get().mId;
}
template<typename RecordT>
void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
{
CSMWorld::RecordBase::State state = mContainer.at (index).mState;
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly)
{
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&mContainer.at (index).mModified.sRecordId)[i];
writer.startRecord (type);
writer.writeHNCString ("NAME", getId (index));
mContainer.at (index).mModified.save (writer);
writer.endRecord (type);
}
else if (state==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
}
}
class RefIdData
{
public:
@ -187,6 +217,8 @@ namespace CSMWorld
///< Return a sorted collection of all IDs
///
/// \param listDeleted include deleted record in the list
void save (int index, ESM::ESMWriter& writer) const;
};
}

@ -29,6 +29,8 @@ namespace
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
"Referenceables", 0 },
@ -54,6 +56,8 @@ namespace
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":./land.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":./birthsign.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },

@ -87,6 +87,10 @@ namespace CSMWorld
Type_RegionMap,
Type_Filter,
Type_Filters,
Type_Topics,
Type_Topic,
Type_Journals,
Type_Journal,
Type_Scene
};

@ -128,3 +128,8 @@ void CSVDoc::FileDialog::slotRejected()
emit rejected();
close();
}
void CSVDoc::FileDialog::createNewFile()
{
emit createNewFile (mAdjusterWidget->getPath());
}

@ -55,7 +55,7 @@ namespace CSVDoc
signals:
void openFiles();
void createNewFile();
void createNewFile (const boost::filesystem::path& savePath);
void signalUpdateCreateButton (bool, int);
void signalUpdateCreateButtonFlags(int);

@ -6,7 +6,10 @@
#include <QDialog>
#include <QMetaType>
#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED
#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED
Q_DECLARE_METATYPE (boost::filesystem::path)
#endif
class QPushButton;

@ -104,6 +104,17 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
layout->addWidget (createButtons());
layout->addWidget (createTools());
/// \todo remove this label once loading and saving are fully implemented
QLabel *warning = new QLabel ("<font color=Red>WARNING:<p>OpenCS is in alpha stage.<br>The code for loading and saving is incomplete.<br>This version of OpenCS is only a preview.<br>Do NOT use it for real editing!<br>You will lose records both on loading and on saving.<p>Please note:<br>If you lose data and come to the OpenMW forum to complain,<br>we will mock you.</font color>");
QFont font;
font.setPointSize (12);
font.setBold (true);
warning->setFont (font);
layout->addWidget (warning, 1);
setLayout (layout);
QRect scr = QApplication::desktop()->screenGeometry();

@ -163,6 +163,14 @@ void CSVDoc::View::setupMechanicsMenu()
QAction *spells = new QAction (tr ("Spells"), this);
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
mechanics->addAction (spells);
QAction *topics = new QAction (tr ("Topics"), this);
connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView()));
mechanics->addAction (topics);
QAction *journals = new QAction (tr ("Journals"), this);
connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView()));
mechanics->addAction (journals);
}
void CSVDoc::View::setupAssetsMenu()
@ -412,6 +420,16 @@ void CSVDoc::View::addSceneSubView()
addSubView (CSMWorld::UniversalId::Type_Scene);
}
void CSVDoc::View::addTopicsSubView()
{
addSubView (CSMWorld::UniversalId::Type_Topics);
}
void CSVDoc::View::addJournalsSubView()
{
addSubView (CSMWorld::UniversalId::Type_Journals);
}
void CSVDoc::View::abortOperation (int type)
{
mDocument->abortOperation (type);

@ -166,6 +166,10 @@ namespace CSVDoc
void addSceneSubView();
void addTopicsSubView();
void addJournalsSubView();
void toggleShowStatusBar (bool show);
};
}

@ -74,7 +74,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
{ CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false },
{ CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false },
{ CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false },
{ CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false }
{ CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false },
{ CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false }
};
for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)

@ -6,6 +6,11 @@
#include "../../model/filter/filter.hpp"
#include "../../model/world/data.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/columns.hpp"
#include "../../model/world/idtable.hpp"
std::string CSVFilter::FilterCreator::getNamespace() const
{
switch (mScope->currentIndex())
@ -28,6 +33,15 @@ std::string CSVFilter::FilterCreator::getId() const
return getNamespace() + GenericCreator::getId();
}
void CSVFilter::FilterCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
{
int index =
dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).
findColumnIndex (CSMWorld::Columns::ColumnId_Scope);
command.addValue (index, mScope->currentIndex());
}
CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id)
: GenericCreator (data, undoStack, id)
@ -39,7 +53,7 @@ CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoS
mScope->addItem ("Project");
mScope->addItem ("Session");
/// \ŧodo re-enable for OpenMW 1.1
/// \todo re-enable for OpenMW 1.1
// mScope->addItem ("Content");
connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int)));

@ -25,6 +25,8 @@ namespace CSVFilter
virtual std::string getId() const;
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
public:
FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack,

@ -0,0 +1,35 @@
#include "dialoguecreator.hpp"
#include <components/esm/loaddial.hpp>
#include "../../model/world/data.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/columns.hpp"
#include "../../model/world/idtable.hpp"
void CSVWorld::DialogueCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
{
int index =
dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).
findColumnIndex (CSMWorld::Columns::ColumnId_DialogueType);
command.addValue (index, mType);
}
CSVWorld::DialogueCreator::DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id, int type)
: GenericCreator (data, undoStack, id), mType (type)
{}
CSVWorld::Creator *CSVWorld::TopicCreatorFactory::makeCreator (CSMWorld::Data& data,
QUndoStack& undoStack, const CSMWorld::UniversalId& id) const
{
return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Topic);
}
CSVWorld::Creator *CSVWorld::JournalCreatorFactory::makeCreator (CSMWorld::Data& data,
QUndoStack& undoStack, const CSMWorld::UniversalId& id) const
{
return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Journal);
}

@ -0,0 +1,41 @@
#ifndef CSV_WORLD_DIALOGUECREATOR_H
#define CSV_WORLD_DIALOGUECREATOR_H
#include "genericcreator.hpp"
namespace CSVWorld
{
class DialogueCreator : public GenericCreator
{
int mType;
protected:
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
public:
DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id, int type);
};
class TopicCreatorFactory : public CreatorFactoryBase
{
public:
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id) const;
///< The ownership of the returned Creator is transferred to the caller.
};
class JournalCreatorFactory : public CreatorFactoryBase
{
public:
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id) const;
///< The ownership of the returned Creator is transferred to the caller.
};
}
#endif

@ -14,6 +14,7 @@
#include "referenceablecreator.hpp"
#include "referencecreator.hpp"
#include "scenesubview.hpp"
#include "dialoguecreator.hpp"
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{
@ -53,6 +54,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_References,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<ReferenceCreator> >);
manager.add (CSMWorld::UniversalId::Type_Topics,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, TopicCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_Journal,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, JournalCreatorFactory>);
// Subviews for editing/viewing individual records
manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>);

@ -87,19 +87,33 @@ std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
// check record state
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (
mModel->data (mModel->index (index.row(), 1)).toInt());
if (state!=CSMWorld::RecordBase::State_Deleted)
{
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
if (state==CSMWorld::RecordBase::State_Deleted)
continue;
std::string id = mModel->data (mModel->index (index.row(), columnIndex)).
toString().toUtf8().constData();
// check other columns (only relevant for a subset of the tables)
int dialogueTypeIndex =
mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType);
deletableIds.push_back (id);
if (dialogueTypeIndex!=-1)
{
int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt();
if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal)
continue;
}
// add the id to the collection
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
std::string id = mModel->data (mModel->index (index.row(), columnIndex)).
toString().toUtf8().constData();
deletableIds.push_back (id);
}
}

@ -58,6 +58,7 @@ add_openmw_dir (mwworld
cells localscripts customdata weather inventorystore ptr actionopen actionread
actionequip timestamp actionalchemy cellstore actionapply actioneat
esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader omwloader
)
add_openmw_dir (mwclass

@ -261,34 +261,14 @@ void OMW::Engine::setCell (const std::string& cellName)
mCellName = cellName;
}
// Set master file (esm)
// - If the given name does not have an extension, ".esm" is added automatically
void OMW::Engine::addMaster (const std::string& master)
void OMW::Engine::addContentFile(const std::string& file)
{
mMaster.push_back(master);
std::string &str = mMaster.back();
// Append .esm if not already there
std::string::size_type sep = str.find_last_of (".");
if (sep == std::string::npos)
{
str += ".esm";
}
}
if (file.find_last_of(".") == std::string::npos)
{
throw std::runtime_error("Missing extension in content file!");
}
// Add plugin file (esp)
void OMW::Engine::addPlugin (const std::string& plugin)
{
mPlugins.push_back(plugin);
std::string &str = mPlugins.back();
// Append .esp if not already there
std::string::size_type sep = str.find_last_of (".");
if (sep == std::string::npos)
{
str += ".esp";
}
mContentFiles.push_back(file);
}
void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
@ -401,7 +381,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mEnvironment.setWindowManager (window);
// Create the world
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins,
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles,
mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap,
mActivationDistanceOverride));
MWBase::Environment::get().getWorld()->setupPlayer();
@ -416,8 +396,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
//Load translation data
mTranslationDataStorage.setEncoder(mEncoder);
for (size_t i = 0; i < mMaster.size(); i++)
mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster[i]);
for (size_t i = 0; i < mContentFiles.size(); i++)
mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]);
Compiler::registerExtensions (mExtensions);
@ -482,7 +462,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
void OMW::Engine::go()
{
assert (!mCellName.empty());
assert (!mMaster.empty());
assert (!mContentFiles.empty());
assert (!mOgre);
Settings::Manager settings;

@ -68,8 +68,7 @@ namespace OMW
boost::filesystem::path mResDir;
OEngine::Render::OgreRenderer *mOgre;
std::string mCellName;
std::vector<std::string> mMaster;
std::vector<std::string> mPlugins;
std::vector<std::string> mContentFiles;
int mFpsLevel;
bool mVerboseScripts;
bool mNewGame;
@ -135,13 +134,11 @@ namespace OMW
/// Set start cell name (only interiors for now)
void setCell(const std::string& cellName);
/// Set master file (esm)
/// - If the given name does not have an extension, ".esm" is added automatically
void addMaster(const std::string& master);
/// Same as "addMaster", but for plugin files (esp)
/// - If the given name does not have an extension, ".esp" is added automatically
void addPlugin(const std::string& plugin);
/**
* @brief addContentFile - Adds content file (ie. esm/esp, or omwgame/omwaddon) to the content files container.
* @param file - filename (extension is required)
*/
void addContentFile(const std::string& file);
/// Enable fps counter
void showFPS(int level);

@ -110,11 +110,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("start", bpo::value<std::string>()->default_value("Beshara"),
"set initial cell")
("master", bpo::value<StringsVector>()->default_value(StringsVector(), "")
->multitoken(), "master file(s)")
("plugin", bpo::value<StringsVector>()->default_value(StringsVector(), "")
->multitoken(), "plugin file(s)")
("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon")
("anim-verbose", bpo::value<bool>()->implicit_value(true)
->default_value(false), "output animation indices files")
@ -152,8 +149,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("activate-dist", bpo::value <int> ()->default_value (-1), "activation distance override");
;
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)
.options(desc).allow_unregistered().run();
@ -211,29 +206,18 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.setResourceDir(variables["resources"].as<std::string>());
// master and plugin
StringsVector master = variables["master"].as<StringsVector>();
if (master.empty())
StringsVector content = variables["content"].as<StringsVector>();
if (content.empty())
{
std::cout << "No master file given. Aborting...\n";
return false;
std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl;
return false;
}
StringsVector plugin = variables["plugin"].as<StringsVector>();
// Removed check for 255 files, which would be the hard-coded limit in Morrowind.
// I'll keep the following variable in, maybe we can use it for something different.
// Say, a feedback like "loading file x/cnt".
// Commenting this out for now to silence compiler warning.
//int cnt = master.size() + plugin.size();
// Prepare loading master/plugin files (i.e. send filenames to engine)
for (std::vector<std::string>::size_type i = 0; i < master.size(); i++)
{
engine.addMaster(master[i]);
}
for (std::vector<std::string>::size_type i = 0; i < plugin.size(); i++)
StringsVector::const_iterator it(content.begin());
StringsVector::const_iterator end(content.end());
for (; it != end; ++it)
{
engine.addPlugin(plugin[i]);
engine.addContentFile(*it);
}
// startup-settings

@ -0,0 +1,35 @@
#ifndef CONTENTLOADER_HPP
#define CONTENTLOADER_HPP
#include <iosfwd>
#include <boost/filesystem/path.hpp>
#include "components/loadinglistener/loadinglistener.hpp"
namespace MWWorld
{
struct ContentLoader
{
ContentLoader(Loading::Listener& listener)
: mListener(listener)
{
}
virtual ~ContentLoader()
{
}
virtual void load(const boost::filesystem::path& filepath, int& index)
{
std::cout << "Loading content file " << filepath.string() << std::endl;
mListener.setLabel(filepath.string());
}
protected:
Loading::Listener& mListener;
};
} /* namespace MWWorld */
#endif /* CONTENTLOADER_HPP */

@ -0,0 +1,31 @@
#include "esmloader.hpp"
#include "esmstore.hpp"
#include "components/to_utf8/to_utf8.hpp"
namespace MWWorld
{
EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,
ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener)
: ContentLoader(listener)
, mStore(store)
, mEsm(readers)
, mEncoder(encoder)
{
}
void EsmLoader::load(const boost::filesystem::path& filepath, int& index)
{
ContentLoader::load(filepath.filename(), index);
ESM::ESMReader lEsm;
lEsm.setEncoder(mEncoder);
lEsm.setIndex(index);
lEsm.setGlobalReaderList(&mEsm);
lEsm.open(filepath.string());
mEsm[index] = lEsm;
mStore.load(mEsm[index], &mListener);
}
} /* namespace MWWorld */

@ -0,0 +1,34 @@
#ifndef ESMLOADER_HPP
#define ESMLOADER_HPP
#include <vector>
#include "contentloader.hpp"
#include "components/esm/esmreader.hpp"
namespace ToUTF8
{
class Utf8Encoder;
}
namespace MWWorld
{
class ESMStore;
struct EsmLoader : public ContentLoader
{
EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,
ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener);
void load(const boost::filesystem::path& filepath, int& index);
private:
std::vector<ESM::ESMReader>& mEsm;
MWWorld::ESMStore& mStore;
ToUTF8::Utf8Encoder* mEncoder;
};
} /* namespace MWWorld */
#endif // ESMLOADER_HPP

@ -0,0 +1,17 @@
#include "omwloader.hpp"
namespace MWWorld
{
OmwLoader::OmwLoader(Loading::Listener& listener)
: ContentLoader(listener)
{
}
void OmwLoader::load(const boost::filesystem::path& filepath, int& index)
{
ContentLoader::load(filepath.filename(), index);
}
} /* namespace MWWorld */

@ -0,0 +1,21 @@
#ifndef OMWLOADER_HPP
#define OMWLOADER_HPP
#include "contentloader.hpp"
namespace MWWorld
{
/**
* @brief Placeholder for real OpenMW content loader
*/
struct OmwLoader : public ContentLoader
{
OmwLoader(Loading::Listener& listener);
void load(const boost::filesystem::path& filepath, int& index);
};
} /* namespace MWWorld */
#endif /* OMWLOADER_HPP */

@ -1,4 +1,11 @@
#include "worldimp.hpp"
#ifdef _WIN32
#include <boost/tr1/tr1/unordered_map>
#elif defined HAVE_UNORDERED_MAP
#include <unordered_map>
#else
#include <tr1/unordered_map>
#endif
#include <OgreSceneNode.h>
@ -31,6 +38,10 @@
#include "containerstore.hpp"
#include "inventorystore.hpp"
#include "contentloader.hpp"
#include "esmloader.hpp"
#include "omwloader.hpp"
using namespace Ogre;
namespace
@ -80,6 +91,38 @@ namespace
namespace MWWorld
{
struct GameContentLoader : public ContentLoader
{
GameContentLoader(Loading::Listener& listener)
: ContentLoader(listener)
{
}
bool addLoader(const std::string& extension, ContentLoader* loader)
{
return mLoaders.insert(std::make_pair(extension, loader)).second;
}
void load(const boost::filesystem::path& filepath, int& index)
{
LoadersContainer::iterator it(mLoaders.find(filepath.extension().string()));
if (it != mLoaders.end())
{
it->second->load(filepath, index);
}
else
{
std::string msg("Cannot load file: ");
msg += filepath.string();
throw std::runtime_error(msg.c_str());
}
}
private:
typedef std::tr1::unordered_map<std::string, ContentLoader*> LoadersContainer;
LoadersContainer mLoaders;
};
Ptr World::getPtrViaHandle (const std::string& handle, Ptr::CellStore& cell)
{
if (MWWorld::LiveCellRef<ESM::Activator> *ref =
@ -163,7 +206,7 @@ namespace MWWorld
World::World (OEngine::Render::OgreRenderer& renderer,
const Files::Collections& fileCollections,
const std::vector<std::string>& master, const std::vector<std::string>& plugins,
const std::vector<std::string>& contentFiles,
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir,
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride)
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
@ -181,44 +224,22 @@ namespace MWWorld
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback);
int idx = 0;
// NOTE: We might need to reserve one more for the running game / save.
mEsm.resize(master.size() + plugins.size());
mEsm.resize(contentFiles.size());
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
listener->loadingOn();
for (std::vector<std::string>::size_type i = 0; i < master.size(); i++, idx++)
{
boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master[i]));
std::cout << "Loading ESM " << masterPath.string() << "\n";
listener->setLabel(masterPath.filename().string());
GameContentLoader gameContentLoader(*listener);
EsmLoader esmLoader(mStore, mEsm, encoder, *listener);
OmwLoader omwLoader(*listener);
// This parses the ESM file
ESM::ESMReader lEsm;
lEsm.setEncoder(encoder);
lEsm.setIndex(idx);
lEsm.setGlobalReaderList(&mEsm);
lEsm.open (masterPath.string());
mEsm[idx] = lEsm;
mStore.load (mEsm[idx], listener);
}
for (std::vector<std::string>::size_type i = 0; i < plugins.size(); i++, idx++)
{
boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i]));
gameContentLoader.addLoader(".esm", &esmLoader);
gameContentLoader.addLoader(".esp", &esmLoader);
gameContentLoader.addLoader(".omwgame", &omwLoader);
gameContentLoader.addLoader(".omwaddon", &omwLoader);
std::cout << "Loading ESP " << pluginPath.string() << "\n";
listener->setLabel(pluginPath.filename().string());
loadContentFiles(fileCollections, contentFiles, gameContentLoader);
// This parses the ESP file
ESM::ESMReader lEsm;
lEsm.setEncoder(encoder);
lEsm.setIndex(idx);
lEsm.setGlobalReaderList(&mEsm);
lEsm.open (pluginPath.string());
mEsm[idx] = lEsm;
mStore.load (mEsm[idx], listener);
}
listener->loadingOff();
// insert records that may not be present in all versions of MW
@ -1983,4 +2004,19 @@ namespace MWWorld
return mGodMode;
}
void World::loadContentFiles(const Files::Collections& fileCollections,
const std::vector<std::string>& content, ContentLoader& contentLoader)
{
std::vector<std::string>::const_iterator it(content.begin());
std::vector<std::string>::const_iterator end(content.end());
for (int idx = 0; it != end; ++it, ++idx)
{
boost::filesystem::path filename(*it);
const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string());
if (col.doesExist(*it))
{
contentLoader.load(col.getPath(*it), idx);
}
}
}
}

@ -14,6 +14,8 @@
#include "../mwbase/world.hpp"
#include "contentloader.hpp"
namespace Ogre
{
class Vector3;
@ -41,6 +43,8 @@ namespace MWRender
class Animation;
}
struct ContentLoader;
namespace MWWorld
{
class WeatherManager;
@ -113,6 +117,15 @@ namespace MWWorld
void ensureNeededRecords();
/**
* @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon)
* @param fileCollections- Container which holds content file names and their paths
* @param content - Container which holds content file names
* @param contentLoader -
*/
void loadContentFiles(const Files::Collections& fileCollections,
const std::vector<std::string>& content, ContentLoader& contentLoader);
int mPlayIntro;
bool mTeleportEnabled;
@ -122,7 +135,7 @@ namespace MWWorld
World (OEngine::Render::OgreRenderer& renderer,
const Files::Collections& fileCollections,
const std::vector<std::string>& master, const std::vector<std::string>& plugins,
const std::vector<std::string>& contentFiles,
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir,
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride);

@ -44,9 +44,9 @@ namespace ESM
}
}
void AIPackageList::save(ESMWriter &esm)
void AIPackageList::save(ESMWriter &esm) const
{
typedef std::vector<AIPackage>::iterator PackageIter;
typedef std::vector<AIPackage>::const_iterator PackageIter;
for (PackageIter it = mList.begin(); it != mList.end(); ++it) {
switch (it->mType) {
case AI_Wander:

@ -93,7 +93,7 @@ namespace ESM
/// it needs to use retSubName() if needed. But, hey, there
/// is only one field left (XSCL) and only two records uses AI
void load(ESMReader &esm);
void save(ESMWriter &esm);
void save(ESMWriter &esm) const;
};
}

@ -3,7 +3,7 @@
#include "esmwriter.hpp"
void ESM::CellRef::save(ESMWriter &esm)
void ESM::CellRef::save(ESMWriter &esm) const
{
esm.writeHNT("FRMR", mRefnum);
esm.writeHNCString("NAME", mRefID);

@ -83,7 +83,7 @@ namespace ESM
// Position and rotation of this object within the cell
Position mPos;
void save(ESMWriter &esm);
void save(ESMWriter &esm) const;
void blank();
};

@ -36,6 +36,7 @@ struct Position
enum RecNameInts
{
// format 0 / legacy
REC_ACTI = 0x49544341,
REC_ALCH = 0x48434c41,
REC_APPA = 0x41505041,
@ -80,7 +81,10 @@ enum RecNameInts
REC_SPEL = 0x4c455053,
REC_SSCR = 0x52435353,
REC_STAT = 0x54415453,
REC_WEAP = 0x50414557
REC_WEAP = 0x50414557,
// format 1
REC_FILT = 0x544C4946
};
}

@ -14,9 +14,9 @@ void EffectList::load(ESMReader &esm)
}
}
void EffectList::save(ESMWriter &esm)
void EffectList::save(ESMWriter &esm) const
{
for (std::vector<ENAMstruct>::iterator it = mList.begin(); it != mList.end(); ++it) {
for (std::vector<ENAMstruct>::const_iterator it = mList.begin(); it != mList.end(); ++it) {
esm.writeHNT<ENAMstruct>("ENAM", *it, 24);
}
}

@ -35,9 +35,9 @@ namespace ESM
std::vector<ENAMstruct> mList;
void load(ESMReader &esm);
void save(ESMWriter &esm);
void save(ESMWriter &esm) const;
};
}
#endif

@ -2,185 +2,188 @@
#include <cassert>
#include <fstream>
#include <iostream>
bool count = true;
#include <stdexcept>
namespace ESM
{
ESMWriter::ESMWriter() : mRecordCount (0), mCounting (true) {}
int ESMWriter::getVersion()
{
return mHeader.mData.version;
}
void ESMWriter::setVersion(int ver)
{
mHeader.mData.version = ver;
}
unsigned int ESMWriter::getVersion() const
{
return mHeader.mData.version;
}
void ESMWriter::setAuthor(const std::string& auth)
{
mHeader.mData.author.assign (auth);
}
void ESMWriter::setVersion(unsigned int ver)
{
mHeader.mData.version = ver;
}
void ESMWriter::setDescription(const std::string& desc)
{
mHeader.mData.desc.assign (desc);
}
void ESMWriter::setAuthor(const std::string& auth)
{
mHeader.mData.author.assign (auth);
}
void ESMWriter::setRecordCount (int count)
{
mHeader.mData.records = count;
}
void ESMWriter::setDescription(const std::string& desc)
{
mHeader.mData.desc.assign (desc);
}
void ESMWriter::setFormat (int format)
{
mHeader.mFormat = format;
}
void ESMWriter::setRecordCount (int count)
{
mHeader.mData.records = count;
}
void ESMWriter::addMaster(const std::string& name, uint64_t size)
{
Header::MasterData d;
d.name = name;
d.size = size;
mHeader.mMaster.push_back(d);
}
void ESMWriter::setFormat (int format)
{
mHeader.mFormat = format;
}
void ESMWriter::save(const std::string& file)
{
std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc);
save(fs);
}
void ESMWriter::clearMaster()
{
mHeader.mMaster.clear();
}
void ESMWriter::save(std::ostream& file)
{
m_recordCount = 0;
m_stream = &file;
void ESMWriter::addMaster(const std::string& name, uint64_t size)
{
Header::MasterData d;
d.name = name;
d.size = size;
mHeader.mMaster.push_back(d);
}
startRecord("TES3", 0);
void ESMWriter::save(const std::string& file)
{
std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc);
save(fs);
}
mHeader.save (*this);
void ESMWriter::save(std::ostream& file)
{
mRecordCount = 0;
mRecords.clear();
mCounting = true;
mStream = &file;
endRecord("TES3");
}
startRecord("TES3", 0);
void ESMWriter::close()
{
m_stream->flush();
mHeader.save (*this);
if (!m_records.empty())
throw "Unclosed record remaining";
}
void ESMWriter::startRecord(const std::string& name, uint32_t flags)
{
m_recordCount++;
writeName(name);
RecordData rec;
rec.name = name;
rec.position = m_stream->tellp();
rec.size = 0;
writeT<int>(0); // Size goes here
writeT<int>(0); // Unused header?
writeT(flags);
m_records.push_back(rec);
assert(m_records.back().size == 0);
}
endRecord("TES3");
}
void ESMWriter::startSubRecord(const std::string& name)
{
writeName(name);
RecordData rec;
rec.name = name;
rec.position = m_stream->tellp();
rec.size = 0;
writeT<int>(0); // Size goes here
m_records.push_back(rec);
assert(m_records.back().size == 0);
}
void ESMWriter::close()
{
if (!mRecords.empty())
throw std::runtime_error ("Unclosed record remaining");
}
void ESMWriter::endRecord(const std::string& name)
{
RecordData rec = m_records.back();
assert(rec.name == name);
m_records.pop_back();
void ESMWriter::startRecord(const std::string& name, uint32_t flags)
{
mRecordCount++;
writeName(name);
RecordData rec;
rec.name = name;
rec.position = mStream->tellp();
rec.size = 0;
writeT<int>(0); // Size goes here
writeT<int>(0); // Unused header?
writeT(flags);
mRecords.push_back(rec);
assert(mRecords.back().size == 0);
}
m_stream->seekp(rec.position);
void ESMWriter::startSubRecord(const std::string& name)
{
writeName(name);
RecordData rec;
rec.name = name;
rec.position = mStream->tellp();
rec.size = 0;
writeT<int>(0); // Size goes here
mRecords.push_back(rec);
assert(mRecords.back().size == 0);
}
count = false;
write((char*)&rec.size, sizeof(int));
count = true;
void ESMWriter::endRecord(const std::string& name)
{
RecordData rec = mRecords.back();
assert(rec.name == name);
mRecords.pop_back();
m_stream->seekp(0, std::ios::end);
mStream->seekp(rec.position);
}
mCounting = false;
write (reinterpret_cast<const char*> (&rec.size), sizeof(int));
mCounting = true;
void ESMWriter::writeHNString(const std::string& name, const std::string& data)
{
startSubRecord(name);
writeHString(data);
endRecord(name);
}
mStream->seekp(0, std::ios::end);
void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size)
{
assert(data.size() <= size);
startSubRecord(name);
writeHString(data);
}
if (data.size() < size)
void ESMWriter::writeHNString(const std::string& name, const std::string& data)
{
for (size_t i = data.size(); i < size; ++i)
write("\0",1);
startSubRecord(name);
writeHString(data);
endRecord(name);
}
endRecord(name);
}
void ESMWriter::writeHString(const std::string& data)
{
if (data.size() == 0)
write("\0", 1);
else
void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size)
{
// Convert to UTF8 and return
std::string ascii = m_encoder->getLegacyEnc(data);
assert(data.size() <= size);
startSubRecord(name);
writeHString(data);
if (data.size() < size)
{
for (size_t i = data.size(); i < size; ++i)
write("\0",1);
}
write(ascii.c_str(), ascii.size());
endRecord(name);
}
}
void ESMWriter::writeHCString(const std::string& data)
{
writeHString(data);
if (data.size() > 0 && data[data.size()-1] != '\0')
write("\0", 1);
}
void ESMWriter::writeHString(const std::string& data)
{
if (data.size() == 0)
write("\0", 1);
else
{
// Convert to UTF8 and return
std::string ascii = mEncoder->getLegacyEnc(data);
write(ascii.c_str(), ascii.size());
}
}
void ESMWriter::writeName(const std::string& name)
{
assert((name.size() == 4 && name[3] != '\0'));
write(name.c_str(), name.size());
}
void ESMWriter::writeHCString(const std::string& data)
{
writeHString(data);
if (data.size() > 0 && data[data.size()-1] != '\0')
write("\0", 1);
}
void ESMWriter::write(const char* data, size_t size)
{
if (count && !m_records.empty())
void ESMWriter::writeName(const std::string& name)
{
for (std::list<RecordData>::iterator it = m_records.begin(); it != m_records.end(); ++it)
it->size += size;
assert((name.size() == 4 && name[3] != '\0'));
write(name.c_str(), name.size());
}
m_stream->write(data, size);
}
void ESMWriter::write(const char* data, size_t size)
{
if (mCounting && !mRecords.empty())
{
for (std::list<RecordData>::iterator it = mRecords.begin(); it != mRecords.end(); ++it)
it->size += size;
}
void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder)
{
m_encoder = encoder;
}
mStream->write(data, size);
}
void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder)
{
mEncoder = encoder;
}
}

@ -13,92 +13,103 @@ namespace ESM {
class ESMWriter
{
struct RecordData
{
std::string name;
std::streampos position;
size_t size;
struct RecordData
{
std::string name;
std::streampos position;
size_t size;
};
public:
ESMWriter();
unsigned int getVersion() const;
void setVersion(unsigned int ver = 0x3fa66666);
void setEncoder(ToUTF8::Utf8Encoder *encoding);
void setAuthor(const std::string& author);
void setDescription(const std::string& desc);
void setRecordCount (int count);
void setFormat (int format);
void clearMaster();
void addMaster(const std::string& name, uint64_t size);
void save(const std::string& file);
///< Start saving a file by writing the TES3 header.
void save(std::ostream& file);
///< Start saving a file by writing the TES3 header.
void close();
///< \note Does not close the stream.
void writeHNString(const std::string& name, const std::string& data);
void writeHNString(const std::string& name, const std::string& data, size_t size);
void writeHNCString(const std::string& name, const std::string& data)
{
startSubRecord(name);
writeHCString(data);
endRecord(name);
}
void writeHNOString(const std::string& name, const std::string& data)
{
if (!data.empty())
writeHNString(name, data);
}
void writeHNOCString(const std::string& name, const std::string& data)
{
if (!data.empty())
writeHNCString(name, data);
}
template<typename T>
void writeHNT(const std::string& name, const T& data)
{
startSubRecord(name);
writeT(data);
endRecord(name);
}
template<typename T>
void writeHNT(const std::string& name, const T& data, int size)
{
startSubRecord(name);
writeT(data, size);
endRecord(name);
}
template<typename T>
void writeT(const T& data)
{
write((char*)&data, sizeof(T));
}
template<typename T>
void writeT(const T& data, size_t size)
{
write((char*)&data, size);
}
void startRecord(const std::string& name, uint32_t flags = 0);
void startSubRecord(const std::string& name);
void endRecord(const std::string& name);
void writeHString(const std::string& data);
void writeHCString(const std::string& data);
void writeName(const std::string& data);
void write(const char* data, size_t size);
private:
std::list<RecordData> mRecords;
std::ostream* mStream;
std::streampos mHeaderPos;
ToUTF8::Utf8Encoder* mEncoder;
int mRecordCount;
bool mCounting;
Header mHeader;
};
public:
int getVersion();
void setVersion(int ver);
void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8?
void setAuthor(const std::string& author);
void setDescription(const std::string& desc);
void setRecordCount (int count);
void setFormat (int format);
void addMaster(const std::string& name, uint64_t size);
void save(const std::string& file);
void save(std::ostream& file);
void close();
void writeHNString(const std::string& name, const std::string& data);
void writeHNString(const std::string& name, const std::string& data, size_t size);
void writeHNCString(const std::string& name, const std::string& data)
{
startSubRecord(name);
writeHCString(data);
endRecord(name);
}
void writeHNOString(const std::string& name, const std::string& data)
{
if (!data.empty())
writeHNString(name, data);
}
void writeHNOCString(const std::string& name, const std::string& data)
{
if (!data.empty())
writeHNCString(name, data);
}
template<typename T>
void writeHNT(const std::string& name, const T& data)
{
startSubRecord(name);
writeT(data);
endRecord(name);
}
template<typename T>
void writeHNT(const std::string& name, const T& data, int size)
{
startSubRecord(name);
writeT(data, size);
endRecord(name);
}
template<typename T>
void writeT(const T& data)
{
write((char*)&data, sizeof(T));
}
template<typename T>
void writeT(const T& data, size_t size)
{
write((char*)&data, size);
}
void startRecord(const std::string& name, uint32_t flags);
void startSubRecord(const std::string& name);
void endRecord(const std::string& name);
void writeHString(const std::string& data);
void writeHCString(const std::string& data);
void writeName(const std::string& data);
void write(const char* data, size_t size);
private:
std::list<RecordData> m_records;
std::ostream* m_stream;
std::streampos m_headerPos;
ToUTF8::Utf8Encoder* m_encoder;
int m_recordCount;
Header mHeader;
};
}
#endif

@ -3,6 +3,9 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
unsigned int ESM::Filter::sRecordId = REC_FILT;
void ESM::Filter::load (ESMReader& esm)
{
@ -10,7 +13,7 @@ void ESM::Filter::load (ESMReader& esm)
mDescription = esm.getHNString ("DESC");
}
void ESM::Filter::save (ESMWriter& esm)
void ESM::Filter::save (ESMWriter& esm) const
{
esm.writeHNCString ("FILT", mFilter);
esm.writeHNCString ("DESC", mDescription);

@ -10,6 +10,8 @@ namespace ESM
struct Filter
{
static unsigned int sRecordId;
std::string mId;
std::string mDescription;
@ -17,7 +19,7 @@ namespace ESM
std::string mFilter;
void load (ESMReader& esm);
void save (ESMWriter& esm);
void save (ESMWriter& esm) const;
void blank();
///< Set record to default state (does not touch the ID).

@ -2,16 +2,19 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
namespace ESM
{
unsigned int Activator::sRecordId = REC_ACTI;
void Activator::load(ESMReader &esm)
{
mModel = esm.getHNString("MODL");
mName = esm.getHNString("FNAM");
mScript = esm.getHNOString("SCRI");
}
void Activator::save(ESMWriter &esm)
void Activator::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNCString("FNAM", mName);

@ -11,10 +11,12 @@ class ESMWriter;
struct Activator
{
static unsigned int sRecordId;
std::string mId, mName, mScript, mModel;
void load(ESMReader &esm);
void save(ESMWriter &esm);
void save(ESMWriter &esm) const;
void blank();
///< Set record to default state (does not touch the ID).

@ -2,9 +2,12 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
namespace ESM
{
unsigned int Potion::sRecordId = REC_ALCH;
void Potion::load(ESMReader &esm)
{
mModel = esm.getHNString("MODL");
@ -14,7 +17,7 @@ void Potion::load(ESMReader &esm)
esm.getHNT(mData, "ALDT", 12);
mEffects.load(esm);
}
void Potion::save(ESMWriter &esm)
void Potion::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("TEXT", mIcon);

@ -17,6 +17,8 @@ class ESMWriter;
struct Potion
{
static unsigned int sRecordId;
struct ALDTstruct
{
float mWeight;
@ -29,7 +31,7 @@ struct Potion
EffectList mEffects;
void load(ESMReader &esm);
void save(ESMWriter &esm);
void save(ESMWriter &esm) const;
void blank();
///< Set record to default state (does not touch the ID).

@ -2,9 +2,12 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
namespace ESM
{
unsigned int Apparatus::sRecordId = REC_APPA;
void Apparatus::load(ESMReader &esm)
{
// we will not treat duplicated subrecords as errors here
@ -28,7 +31,7 @@ void Apparatus::load(ESMReader &esm)
}
}
void Apparatus::save(ESMWriter &esm)
void Apparatus::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNCString("FNAM", mName);

@ -15,6 +15,8 @@ class ESMWriter;
struct Apparatus
{
static unsigned int sRecordId;
enum AppaType
{
MortarPestle = 0,
@ -35,7 +37,7 @@ struct Apparatus
std::string mId, mModel, mIcon, mScript, mName;
void load(ESMReader &esm);
void save(ESMWriter &esm);
void save(ESMWriter &esm) const;
void blank();
///< Set record to default state (does not touch the ID).

@ -2,6 +2,7 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
namespace ESM
{
@ -18,9 +19,9 @@ void PartReferenceList::load(ESMReader &esm)
}
}
void PartReferenceList::save(ESMWriter &esm)
void PartReferenceList::save(ESMWriter &esm) const
{
for (std::vector<PartReference>::iterator it = mParts.begin(); it != mParts.end(); ++it)
for (std::vector<PartReference>::const_iterator it = mParts.begin(); it != mParts.end(); ++it)
{
esm.writeHNT("INDX", it->mPart);
esm.writeHNOString("BNAM", it->mMale);
@ -28,6 +29,8 @@ void PartReferenceList::save(ESMWriter &esm)
}
}
unsigned int Armor::sRecordId = REC_ARMO;
void Armor::load(ESMReader &esm)
{
mModel = esm.getHNString("MODL");
@ -39,7 +42,7 @@ void Armor::load(ESMReader &esm)
mEnchant = esm.getHNOString("ENAM");
}
void Armor::save(ESMWriter &esm)
void Armor::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNCString("FNAM", mName);

@ -56,11 +56,13 @@ struct PartReferenceList
std::vector<PartReference> mParts;
void load(ESMReader &esm);
void save(ESMWriter &esm);
void save(ESMWriter &esm) const;
};
struct Armor
{
static unsigned int sRecordId;
enum Type
{
Helmet = 0,
@ -89,7 +91,7 @@ struct Armor
std::string mId, mName, mModel, mIcon, mScript, mEnchant;
void load(ESMReader &esm);
void save(ESMWriter &esm);
void save(ESMWriter &esm) const;
void blank();
///< Set record to default state (does not touch the ID).

@ -2,9 +2,12 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
namespace ESM
{
unsigned int BodyPart::sRecordId = REC_BODY;
void BodyPart::load(ESMReader &esm)
{
@ -12,7 +15,7 @@ void BodyPart::load(ESMReader &esm)
mRace = esm.getHNString("FNAM");
esm.getHNT(mData, "BYDT", 4);
}
void BodyPart::save(ESMWriter &esm)
void BodyPart::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNCString("FNAM", mRace);

@ -11,6 +11,8 @@ class ESMWriter;
struct BodyPart
{
static unsigned int sRecordId;
enum MeshPart
{
MP_Head = 0,
@ -57,7 +59,7 @@ struct BodyPart
std::string mId, mModel, mRace;
void load(ESMReader &esm);
void save(ESMWriter &esm);
void save(ESMWriter &esm) const;
};
}
#endif

@ -2,9 +2,11 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
namespace ESM
{
unsigned int Book::sRecordId = REC_BOOK;
void Book::load(ESMReader &esm)
{
@ -16,7 +18,7 @@ void Book::load(ESMReader &esm)
mText = esm.getHNOString("TEXT");
mEnchant = esm.getHNOString("ENAM");
}
void Book::save(ESMWriter &esm)
void Book::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);

@ -14,6 +14,8 @@ class ESMWriter;
struct Book
{
static unsigned int sRecordId;
struct BKDTstruct
{
float mWeight;
@ -25,7 +27,7 @@ struct Book
std::string mId;
void load(ESMReader &esm);
void save(ESMWriter &esm);
void save(ESMWriter &esm) const;
void blank();
///< Set record to default state (does not touch the ID).

@ -2,9 +2,11 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
namespace ESM
{
unsigned int BirthSign::sRecordId = REC_BSGN;
void BirthSign::load(ESMReader &esm)
{
@ -15,7 +17,7 @@ void BirthSign::load(ESMReader &esm)
mPowers.load(esm);
}
void BirthSign::save(ESMWriter &esm)
void BirthSign::save(ESMWriter &esm) const
{
esm.writeHNCString("FNAM", mName);
esm.writeHNOCString("TNAM", mTexture);

@ -13,13 +13,15 @@ class ESMWriter;
struct BirthSign
{
static unsigned int sRecordId;
std::string mId, mName, mDescription, mTexture;
// List of powers and abilities that come with this birth sign.
SpellList mPowers;
void load(ESMReader &esm);
void save(ESMWriter &esm);
void save(ESMWriter &esm) const;
void blank();
///< Set record to default state (does not touch the ID/index).

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save