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 configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg
"${OpenMW_BINARY_DIR}/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) if (NOT WIN32 AND NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop 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) void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString &current)
{ {
<<<<<<< HEAD
setProfile(previous, current, true); setProfile(previous, current, true);
emit signalProfileChanged (ui.profilesComboBox->findText(current)); 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) 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")); QStringList masters = mSettings.values(QString("master"));
for (int i = masters.count(); i--;) { for (int i = masters.count(); i--;) {
stream << "master=" << masters.at(i) << "\n"; stream << "content=" << masters.at(i) << "\n";
} }
QStringList plugins = mSettings.values(QString("plugin")); QStringList plugins = mSettings.values(QString("plugin"));
for (int i = plugins.count(); i--;) { for (int i = plugins.count(); i--;) {
stream << "plugin=" << plugins.at(i) << "\n"; stream << "content=" << plugins.at(i) << "\n";
} }
return true; return true;

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

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

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

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

@ -1,8 +1,15 @@
#include "document.hpp" #include "document.hpp"
#include <cassert> #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, 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); assert (begin!=end);
@ -12,10 +19,10 @@ void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_i
--end2; --end2;
for (std::vector<boost::filesystem::path>::const_iterator iter (begin); iter!=end2; ++iter) for (std::vector<boost::filesystem::path>::const_iterator iter (begin); iter!=end2; ++iter)
getData().loadFile (*iter, true); getData().loadFile (*iter, true, false);
if (lastAsModified) if (lastAsModified)
getData().loadFile (*end2, false); getData().loadFile (*end2, false, false);
} }
void CSMDoc::Document::addGmsts() void CSMDoc::Document::addGmsts()
@ -2058,9 +2065,9 @@ void CSMDoc::Document::addOptionalGlobals()
{ {
static const char *sGlobals[] = static const char *sGlobals[] =
{ {
"dayspassed", "DaysPassed",
"pcwerewolf", "PCWerewolf",
"pcyear", "PCYear",
0 0
}; };
@ -2137,11 +2144,86 @@ void CSMDoc::Document::createBase()
getData().getSkills().add (record); 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, 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_)
const boost::filesystem::path& savePath, bool new_) : mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir),
: mSavePath (savePath), mTools (mData) mProjectPath ((configuration.getUserPath() / "projects") /
(savePath.filename().string() + ".project")),
mSaving (*this, mProjectPath)
{ {
if (files.empty()) if (files.empty())
throw std::runtime_error ("Empty content file sequence"); 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_); 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(); addOptionalGmsts();
addOptionalGlobals(); 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 (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int))); connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int)));
// dummy implementation -> remove when proper save is implemented. connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
mSaveCount = 0; connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int)));
connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); connect (&mSaving, SIGNAL (reportMessage (const QString&, int)),
this, SLOT (reportMessage (const QString&, int)));
} }
CSMDoc::Document::~Document() CSMDoc::Document::~Document()
@ -2187,7 +2298,7 @@ int CSMDoc::Document::getState() const
if (!mUndoStack.isClean()) if (!mUndoStack.isClean())
state |= State_Modified; state |= State_Modified;
if (mSaveCount) if (mSaving.isRunning())
state |= State_Locked | State_Saving | State_Operation; state |= State_Locked | State_Saving | State_Operation;
if (int operations = mTools.getRunningOperations()) if (int operations = mTools.getRunningOperations())
@ -2201,12 +2312,20 @@ const boost::filesystem::path& CSMDoc::Document::getSavePath() const
return mSavePath; return mSavePath;
} }
const std::vector<boost::filesystem::path>& CSMDoc::Document::getContentFiles() const
{
return mContentFiles;
}
void CSMDoc::Document::save() void CSMDoc::Document::save()
{ {
mSaveCount = 1; if (mSaving.isRunning())
mSaveTimer.start (500); throw std::logic_error (
"Failed to initiate save, because a save operation is already running.");
mSaving.start();
emit stateChanged (getState(), this); emit stateChanged (getState(), this);
emit progress (1, 16, State_Saving, 1, this);
} }
CSMWorld::UniversalId CSMDoc::Document::verify() CSMWorld::UniversalId CSMDoc::Document::verify()
@ -2218,44 +2337,26 @@ CSMWorld::UniversalId CSMDoc::Document::verify()
void CSMDoc::Document::abortOperation (int type) void CSMDoc::Document::abortOperation (int type)
{ {
mTools.abortOperation (type);
if (type==State_Saving) if (type==State_Saving)
{ mSaving.abort();
mSaveCount=0; else
mSaveTimer.stop(); mTools.abortOperation (type);
emit stateChanged (getState(), this);
}
} }
void CSMDoc::Document::modificationStateChanged (bool clean) void CSMDoc::Document::modificationStateChanged (bool clean)
{ {
emit stateChanged (getState(), this); emit stateChanged (getState(), this);
} }
void CSMDoc::Document::reportMessage (const QString& message, int type)
void CSMDoc::Document::operationDone (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 stateChanged (getState(), this);
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);
}
} }
const CSMWorld::Data& CSMDoc::Document::getData() const const CSMWorld::Data& CSMDoc::Document::getData() const

@ -14,6 +14,7 @@
#include "../tools/tools.hpp" #include "../tools/tools.hpp"
#include "state.hpp" #include "state.hpp"
#include "saving.hpp"
class QAbstractItemModel; class QAbstractItemModel;
@ -23,6 +24,11 @@ namespace ESM
struct Global; struct Global;
} }
namespace Files
{
class ConfigurationManager;
}
namespace CSMDoc namespace CSMDoc
{ {
class Document : public QObject class Document : public QObject
@ -32,16 +38,17 @@ namespace CSMDoc
private: private:
boost::filesystem::path mSavePath; boost::filesystem::path mSavePath;
std::vector<boost::filesystem::path> mContentFiles;
CSMWorld::Data mData; CSMWorld::Data mData;
CSMTools::Tools mTools; 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 // 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. // using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late.
QUndoStack mUndoStack; QUndoStack mUndoStack;
int mSaveCount; ///< dummy implementation -> remove when proper save is implemented.
QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented.
// not implemented // not implemented
Document (const Document&); Document (const Document&);
Document& operator= (const Document&); Document& operator= (const Document&);
@ -64,8 +71,7 @@ namespace CSMDoc
public: public:
Document (const std::vector<boost::filesystem::path>& files, Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_);
const boost::filesystem::path& savePath, bool new_);
~Document(); ~Document();
@ -75,6 +81,10 @@ namespace CSMDoc
const boost::filesystem::path& getSavePath() const; 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(); void save();
CSMWorld::UniversalId verify(); CSMWorld::UniversalId verify();
@ -98,10 +108,9 @@ namespace CSMDoc
void modificationStateChanged (bool clean); void modificationStateChanged (bool clean);
void operationDone (int type); void reportMessage (const QString& message, int type);
void saving(); void operationDone (int type);
///< dummy implementation -> remove when proper save is implemented.
public slots: public slots:
@ -110,3 +119,4 @@ namespace CSMDoc
} }
#endif #endif

@ -4,9 +4,22 @@
#include <algorithm> #include <algorithm>
#include <stdexcept> #include <stdexcept>
#include <boost/filesystem.hpp>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
#include "document.hpp" #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() 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, CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_) bool new_)
{ {
Document *document = new Document (files, savePath, new_); Document *document = new Document (mConfiguration, files, savePath, mResDir, new_);
mDocuments.push_back (document); mDocuments.push_back (document);
@ -35,4 +48,9 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document)
delete document; delete document;
return mDocuments.empty(); 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> #include <boost/filesystem/path.hpp>
namespace Files
{
class ConfigurationManager;
}
namespace CSMDoc namespace CSMDoc
{ {
class Document; class Document;
@ -13,18 +18,18 @@ namespace CSMDoc
class DocumentManager class DocumentManager
{ {
std::vector<Document *> mDocuments; std::vector<Document *> mDocuments;
const Files::ConfigurationManager& mConfiguration;
DocumentManager (const DocumentManager&); DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&); DocumentManager& operator= (const DocumentManager&);
public: public:
DocumentManager(); DocumentManager (const Files::ConfigurationManager& configuration);
~DocumentManager(); ~DocumentManager();
Document *addDocument (const std::vector<boost::filesystem::path>& files, Document *addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_);
const boost::filesystem::path& savePath, bool new_);
///< The ownership of the returned document is not transferred to the caller. ///< 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 /// \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); bool removeDocument (Document *document);
///< \return last document removed? ///< \return last document removed?
void setResourceDir (const boost::filesystem::path& parResDir);
private:
boost::filesystem::path mResDir;
}; };
} }

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

@ -1,11 +1,11 @@
#ifndef CSM_TOOLS_OPERATION_H #ifndef CSM_DOC_OPERATION_H
#define CSM_TOOLS_OPERATION_H #define CSM_DOC_OPERATION_H
#include <vector> #include <vector>
#include <QThread> #include <QThread>
namespace CSMTools namespace CSMDoc
{ {
class Stage; class Stage;
@ -19,12 +19,17 @@ namespace CSMTools
int mCurrentStep; int mCurrentStep;
int mCurrentStepTotal; int mCurrentStepTotal;
int mTotalSteps; int mTotalSteps;
int mOrdered;
bool mFinalAlways;
bool mError;
void prepareStages(); void prepareStages();
public: 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(); virtual ~Operation();
@ -35,19 +40,25 @@ namespace CSMTools
/// ///
/// \attention Do no call this function while this Operation is running. /// \attention Do no call this function while this Operation is running.
bool hasError() const;
signals: signals:
void progress (int current, int max, int type); void progress (int current, int max, int type);
void reportMessage (const QString& message, int type); void reportMessage (const QString& message, int type);
void done (int type);
public slots: public slots:
void abort(); void abort();
private slots: 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 #ifndef CSM_DOC_STAGE_H
#define CSM_TOOLS_STAGE_H #define CSM_DOC_STAGE_H
#include <vector> #include <vector>
#include <string> #include <string>
namespace CSMTools namespace CSMDoc
{ {
class Stage class Stage
{ {
@ -16,7 +16,7 @@ namespace CSMTools
///< \return number of steps ///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages) = 0; 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 "../world/idcollection.hpp"
#include "stage.hpp" #include "../doc/stage.hpp"
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that birthsign records are internally consistent /// \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; const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns;

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

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

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

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

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

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

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

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

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

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

@ -11,10 +11,13 @@ namespace CSMWorld
class UniversalId; class UniversalId;
} }
namespace CSMTools namespace CSMDoc
{ {
class Verifier;
class Operation; class Operation;
}
namespace CSMTools
{
class ReportModel; class ReportModel;
class Tools : public QObject class Tools : public QObject
@ -22,7 +25,7 @@ namespace CSMTools
Q_OBJECT Q_OBJECT
CSMWorld::Data& mData; CSMWorld::Data& mData;
Verifier *mVerifier; CSMDoc::Operation *mVerifier;
std::map<int, ReportModel *> mReports; std::map<int, ReportModel *> mReports;
int mNextReportNumber; int mNextReportNumber;
std::map<int, int> mActiveReports; // type, report number std::map<int, int> mActiveReports; // type, report number
@ -31,12 +34,12 @@ namespace CSMTools
Tools (const Tools&); Tools (const Tools&);
Tools& operator= (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. ///< 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. ///< Returns a 0-pointer, if operation hasn't been used yet.
public: public:
@ -58,8 +61,6 @@ namespace CSMTools
private slots: private slots:
void verifierDone();
void verifierMessage (const QString& message, int type); void verifierMessage (const QString& message, int type);
signals: 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 "collectionbase.hpp"
#include <stdexcept>
#include "columnbase.hpp"
CSMWorld::CollectionBase::CollectionBase() {} CSMWorld::CollectionBase::CollectionBase() {}
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 <string>
#include "universalid.hpp" #include "universalid.hpp"
#include "columns.hpp"
class QVariant; class QVariant;
@ -83,6 +84,13 @@ namespace CSMWorld
///< Return a sorted collection of all IDs ///< Return a sorted collection of all IDs
/// ///
/// \param listDeleted include deleted record in the list /// \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_CreatureType,
Display_WeaponType, Display_WeaponType,
Display_RecordState, Display_RecordState,
Display_RefRecordType Display_RefRecordType,
Display_DialogueType
}; };
int mColumnId; 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> template<typename ESXRecordT>
struct PosColumn : public Column<ESXRecordT> struct PosColumn : public Column<ESXRecordT>
{ {
@ -1284,6 +1315,39 @@ namespace CSMWorld
return true; 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 #endif

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

@ -152,6 +152,8 @@ namespace CSMWorld
ColumnId_DoorPositionXRot = 139, ColumnId_DoorPositionXRot = 139,
ColumnId_DoorPositionYRot = 140, ColumnId_DoorPositionYRot = 140,
ColumnId_DoorPositionZRot = 141, 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 // Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values. // 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()); 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) CSMWorld::Data::Data() : mRefs (mCells)
{ {
mGlobals.addColumn (new StringIdColumn<ESM::Global>); 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_StarterSpell, 0x2));
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AlwaysSucceeds, 0x4)); 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 StringIdColumn<Cell>);
mCells.addColumn (new RecordStateColumn<Cell>); mCells.addColumn (new RecordStateColumn<Cell>);
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_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 RecordStateColumn<CSMFilter::Filter>);
mFilters.addColumn (new FilterColumn<CSMFilter::Filter>); mFilters.addColumn (new FilterColumn<CSMFilter::Filter>);
mFilters.addColumn (new DescriptionColumn<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 (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); 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 (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region);
addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign); addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign);
addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); 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 (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell);
addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables,
UniversalId::Type_Referenceable); UniversalId::Type_Referenceable);
@ -319,6 +341,28 @@ CSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells()
return mSpells; 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 const CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells() const
{ {
return mCells; return mCells;
@ -387,7 +431,7 @@ void CSMWorld::Data::merge()
mGlobals.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; ESM::ESMReader reader;
@ -397,6 +441,9 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
reader.open (path.string()); 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 // Note: We do not need to send update signals here, because at this point the model is not connected
// to any view. // to any view.
while (reader.hasMoreRecs()) 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_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break;
case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); 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: default:
/// \todo throw an exception instead, once all records are implemented /// \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 || getRegions().searchId (id)!=-1 ||
getBirthsigns().searchId (id)!=-1 || getBirthsigns().searchId (id)!=-1 ||
getSpells().searchId (id)!=-1 || getSpells().searchId (id)!=-1 ||
getTopics().searchId (id)!=-1 ||
getJournals().searchId (id)!=-1 ||
getCells().searchId (id)!=-1 || getCells().searchId (id)!=-1 ||
getReferenceables().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> CSMWorld::Data::getIds (bool listDeleted) const
{ {
std::vector<std::string> ids; 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, mRegions, listDeleted);
appendIds (ids, mBirthsigns, listDeleted); appendIds (ids, mBirthsigns, listDeleted);
appendIds (ids, mSpells, listDeleted); appendIds (ids, mSpells, listDeleted);
appendIds (ids, mTopics, listDeleted);
appendIds (ids, mJournals, listDeleted);
appendIds (ids, mCells, listDeleted); appendIds (ids, mCells, listDeleted);
appendIds (ids, mReferenceables, listDeleted); appendIds (ids, mReferenceables, listDeleted);

@ -20,6 +20,7 @@
#include <components/esm/loadregn.hpp> #include <components/esm/loadregn.hpp>
#include <components/esm/loadbsgn.hpp> #include <components/esm/loadbsgn.hpp>
#include <components/esm/loadspel.hpp> #include <components/esm/loadspel.hpp>
#include <components/esm/loaddial.hpp>
#include "../filter/filter.hpp" #include "../filter/filter.hpp"
@ -48,12 +49,16 @@ namespace CSMWorld
IdCollection<ESM::Region> mRegions; IdCollection<ESM::Region> mRegions;
IdCollection<ESM::BirthSign> mBirthsigns; IdCollection<ESM::BirthSign> mBirthsigns;
IdCollection<ESM::Spell> mSpells; IdCollection<ESM::Spell> mSpells;
IdCollection<ESM::Dialogue> mTopics;
IdCollection<ESM::Dialogue> mJournals;
IdCollection<Cell> mCells; IdCollection<Cell> mCells;
RefIdCollection mReferenceables; RefIdCollection mReferenceables;
RefCollection mRefs; RefCollection mRefs;
IdCollection<CSMFilter::Filter> mFilters; IdCollection<CSMFilter::Filter> mFilters;
std::vector<QAbstractItemModel *> mModels; std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex; std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
std::string mAuthor;
std::string mDescription;
// not implemented // not implemented
Data (const Data&); Data (const Data&);
@ -66,6 +71,8 @@ namespace CSMWorld
bool listDeleted); bool listDeleted);
///< Append all IDs from collection to \a ids. ///< Append all IDs from collection to \a ids.
static int count (RecordBase::State state, const CollectionBase& collection);
public: public:
Data(); Data();
@ -116,6 +123,14 @@ namespace CSMWorld
IdCollection<ESM::Spell>& getSpells(); 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; const IdCollection<Cell>& getCells() const;
IdCollection<Cell>& getCells(); IdCollection<Cell>& getCells();
@ -141,8 +156,10 @@ namespace CSMWorld
void merge(); void merge();
///< Merge modified into base. ///< 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. ///< 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; bool hasId (const std::string& id) const;
@ -151,6 +168,17 @@ namespace CSMWorld
/// ///
/// \param listDeleted include deleted record in the list /// \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: signals:
void idListChanged(); void idListChanged();

@ -7,21 +7,24 @@
namespace CSMWorld namespace CSMWorld
{ {
/// \brief Single type collection of top level records /// \brief Single type collection of top level records
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> > template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class IdCollection : public Collection<ESXRecordT, IdAccessorT> class IdCollection : public Collection<ESXRecordT, IdAccessorT>
{ {
public: public:
void load (ESM::ESMReader& reader, bool base, 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 (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> template<typename ESXRecordT, typename IdAccessorT>
void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base, void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
UniversalId::Type type)
{ {
std::string id = reader.getHNOString ("NAME"); std::string id = reader.getHNOString ("NAME");
@ -56,30 +59,62 @@ namespace CSMWorld
IdAccessorT().getId (record) = id; IdAccessorT().getId (record) = id;
record.load (reader); record.load (reader);
int index = this->searchId (IdAccessorT().getId (record)); load (record, base);
}
}
if (index==-1) template<typename ESXRecordT, typename IdAccessorT>
{ void IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base)
// new record {
Record<ESXRecordT> record2; int index = this->searchId (IdAccessorT().getId (record));
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = 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 else
{ record2.setModified (record);
// old record
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
if (base) this->setRecord (index, record2);
record2.mBase = record; }
else }
record2.setModified (record);
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 CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const
{ {
int columns = mIdCollection->getColumns(); return mIdCollection->searchColumnIndex (id);
for (int i=0; i<columns; ++i)
if (mIdCollection->getColumn (i).mColumnId==id)
return i;
return -1;
} }
int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const
{ {
int index = searchColumnIndex (id); return mIdCollection->findColumnIndex (id);
if (index==-1)
throw std::logic_error ("invalid column index");
return index;
} }

@ -539,3 +539,8 @@ std::vector<std::string> CSMWorld::RefIdCollection::getIds (bool listDeleted) co
{ {
return mData.getIds (listDeleted); 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 "collectionbase.hpp"
#include "refiddata.hpp" #include "refiddata.hpp"
namespace ESM
{
class ESMWriter;
}
namespace CSMWorld namespace CSMWorld
{ {
class RefIdAdapter; class RefIdAdapter;
@ -94,6 +99,8 @@ namespace CSMWorld
///< Return a sorted collection of all IDs ///< Return a sorted collection of all IDs
/// ///
/// \param listDeleted include deleted record in the list /// \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; 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/loadweap.hpp>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
#include <components/esm/loadmisc.hpp> #include <components/esm/loadmisc.hpp>
#include <components/esm/esmwriter.hpp>
#include "record.hpp" #include "record.hpp"
#include "universalid.hpp" #include "universalid.hpp"
@ -51,6 +52,8 @@ namespace CSMWorld
virtual void erase (int index, int count) = 0; virtual void erase (int index, int count) = 0;
virtual std::string getId (int index) const = 0; virtual std::string getId (int index) const = 0;
virtual void save (int index, ESM::ESMWriter& writer) const = 0;
}; };
template<typename RecordT> template<typename RecordT>
@ -71,6 +74,8 @@ namespace CSMWorld
virtual void erase (int index, int count); virtual void erase (int index, int count);
virtual std::string getId (int index) const; virtual std::string getId (int index) const;
virtual void save (int index, ESM::ESMWriter& writer) const;
}; };
template<typename RecordT> template<typename RecordT>
@ -123,6 +128,31 @@ namespace CSMWorld
return mContainer.at (index).get().mId; 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 class RefIdData
{ {
public: public:
@ -187,6 +217,8 @@ namespace CSMWorld
///< Return a sorted collection of all IDs ///< Return a sorted collection of all IDs
/// ///
/// \param listDeleted include deleted record in the list /// \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_Regions, "Regions", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 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_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_Cells, "Cells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
"Referenceables", 0 }, "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_Region, "Region", ":./land.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":./birthsign.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_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_Cell, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },

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

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

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

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

@ -104,6 +104,17 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
layout->addWidget (createButtons()); layout->addWidget (createButtons());
layout->addWidget (createTools()); 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); setLayout (layout);
QRect scr = QApplication::desktop()->screenGeometry(); QRect scr = QApplication::desktop()->screenGeometry();

@ -163,6 +163,14 @@ void CSVDoc::View::setupMechanicsMenu()
QAction *spells = new QAction (tr ("Spells"), this); QAction *spells = new QAction (tr ("Spells"), this);
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
mechanics->addAction (spells); 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() void CSVDoc::View::setupAssetsMenu()
@ -412,6 +420,16 @@ void CSVDoc::View::addSceneSubView()
addSubView (CSMWorld::UniversalId::Type_Scene); 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) void CSVDoc::View::abortOperation (int type)
{ {
mDocument->abortOperation (type); mDocument->abortOperation (type);

@ -166,6 +166,10 @@ namespace CSVDoc
void addSceneSubView(); void addSceneSubView();
void addTopicsSubView();
void addJournalsSubView();
void toggleShowStatusBar (bool show); 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_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false },
{ CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false }, { CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false },
{ CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, 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) for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)

@ -6,6 +6,11 @@
#include "../../model/filter/filter.hpp" #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 std::string CSVFilter::FilterCreator::getNamespace() const
{ {
switch (mScope->currentIndex()) switch (mScope->currentIndex())
@ -28,6 +33,15 @@ std::string CSVFilter::FilterCreator::getId() const
return getNamespace() + GenericCreator::getId(); 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, CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id) const CSMWorld::UniversalId& id)
: GenericCreator (data, undoStack, id) : GenericCreator (data, undoStack, id)
@ -39,7 +53,7 @@ CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoS
mScope->addItem ("Project"); mScope->addItem ("Project");
mScope->addItem ("Session"); mScope->addItem ("Session");
/// \ŧodo re-enable for OpenMW 1.1 /// \todo re-enable for OpenMW 1.1
// mScope->addItem ("Content"); // mScope->addItem ("Content");
connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int))); connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int)));

@ -25,6 +25,8 @@ namespace CSVFilter
virtual std::string getId() const; virtual std::string getId() const;
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
public: public:
FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, 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 "referenceablecreator.hpp"
#include "referencecreator.hpp" #include "referencecreator.hpp"
#include "scenesubview.hpp" #include "scenesubview.hpp"
#include "dialoguecreator.hpp"
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{ {
@ -53,6 +54,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_References, manager.add (CSMWorld::UniversalId::Type_References,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<ReferenceCreator> >); 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 // Subviews for editing/viewing individual records
manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>); 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)); QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
// check record state
CSMWorld::RecordBase::State state = CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> ( static_cast<CSMWorld::RecordBase::State> (
mModel->data (mModel->index (index.row(), 1)).toInt()); mModel->data (mModel->index (index.row(), 1)).toInt());
if (state!=CSMWorld::RecordBase::State_Deleted) if (state==CSMWorld::RecordBase::State_Deleted)
{ continue;
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
std::string id = mModel->data (mModel->index (index.row(), columnIndex)). // check other columns (only relevant for a subset of the tables)
toString().toUtf8().constData(); 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 cells localscripts customdata weather inventorystore ptr actionopen actionread
actionequip timestamp actionalchemy cellstore actionapply actioneat actionequip timestamp actionalchemy cellstore actionapply actioneat
esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader omwloader
) )
add_openmw_dir (mwclass add_openmw_dir (mwclass

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

@ -68,8 +68,7 @@ namespace OMW
boost::filesystem::path mResDir; boost::filesystem::path mResDir;
OEngine::Render::OgreRenderer *mOgre; OEngine::Render::OgreRenderer *mOgre;
std::string mCellName; std::string mCellName;
std::vector<std::string> mMaster; std::vector<std::string> mContentFiles;
std::vector<std::string> mPlugins;
int mFpsLevel; int mFpsLevel;
bool mVerboseScripts; bool mVerboseScripts;
bool mNewGame; bool mNewGame;
@ -135,13 +134,11 @@ namespace OMW
/// Set start cell name (only interiors for now) /// Set start cell name (only interiors for now)
void setCell(const std::string& cellName); void setCell(const std::string& cellName);
/// Set master file (esm) /**
/// - If the given name does not have an extension, ".esm" is added automatically * @brief addContentFile - Adds content file (ie. esm/esp, or omwgame/omwaddon) to the content files container.
void addMaster(const std::string& master); * @param file - filename (extension is required)
*/
/// Same as "addMaster", but for plugin files (esp) void addContentFile(const std::string& file);
/// - If the given name does not have an extension, ".esp" is added automatically
void addPlugin(const std::string& plugin);
/// Enable fps counter /// Enable fps counter
void showFPS(int level); 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"), ("start", bpo::value<std::string>()->default_value("Beshara"),
"set initial cell") "set initial cell")
("master", bpo::value<StringsVector>()->default_value(StringsVector(), "") ("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
->multitoken(), "master file(s)") ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon")
("plugin", bpo::value<StringsVector>()->default_value(StringsVector(), "")
->multitoken(), "plugin file(s)")
("anim-verbose", bpo::value<bool>()->implicit_value(true) ("anim-verbose", bpo::value<bool>()->implicit_value(true)
->default_value(false), "output animation indices files") ->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"); ("activate-dist", bpo::value <int> ()->default_value (-1), "activation distance override");
;
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)
.options(desc).allow_unregistered().run(); .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>()); engine.setResourceDir(variables["resources"].as<std::string>());
// master and plugin StringsVector content = variables["content"].as<StringsVector>();
StringsVector master = variables["master"].as<StringsVector>(); if (content.empty())
if (master.empty())
{ {
std::cout << "No master file given. Aborting...\n"; std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl;
return false; return false;
} }
StringsVector plugin = variables["plugin"].as<StringsVector>(); StringsVector::const_iterator it(content.begin());
// Removed check for 255 files, which would be the hard-coded limit in Morrowind. StringsVector::const_iterator end(content.end());
// I'll keep the following variable in, maybe we can use it for something different. for (; it != end; ++it)
// 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++)
{ {
engine.addPlugin(plugin[i]); engine.addContentFile(*it);
} }
// startup-settings // 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" #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> #include <OgreSceneNode.h>
@ -31,6 +38,10 @@
#include "containerstore.hpp" #include "containerstore.hpp"
#include "inventorystore.hpp" #include "inventorystore.hpp"
#include "contentloader.hpp"
#include "esmloader.hpp"
#include "omwloader.hpp"
using namespace Ogre; using namespace Ogre;
namespace namespace
@ -80,6 +91,38 @@ namespace
namespace MWWorld 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) Ptr World::getPtrViaHandle (const std::string& handle, Ptr::CellStore& cell)
{ {
if (MWWorld::LiveCellRef<ESM::Activator> *ref = if (MWWorld::LiveCellRef<ESM::Activator> *ref =
@ -163,7 +206,7 @@ namespace MWWorld
World::World (OEngine::Render::OgreRenderer& renderer, World::World (OEngine::Render::OgreRenderer& renderer,
const Files::Collections& fileCollections, 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, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir,
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride) ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride)
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
@ -181,44 +224,22 @@ namespace MWWorld
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback);
int idx = 0;
// NOTE: We might need to reserve one more for the running game / save. // 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(); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
listener->loadingOn(); 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"; GameContentLoader gameContentLoader(*listener);
listener->setLabel(masterPath.filename().string()); EsmLoader esmLoader(mStore, mEsm, encoder, *listener);
OmwLoader omwLoader(*listener);
// This parses the ESM file gameContentLoader.addLoader(".esm", &esmLoader);
ESM::ESMReader lEsm; gameContentLoader.addLoader(".esp", &esmLoader);
lEsm.setEncoder(encoder); gameContentLoader.addLoader(".omwgame", &omwLoader);
lEsm.setIndex(idx); gameContentLoader.addLoader(".omwaddon", &omwLoader);
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]));
std::cout << "Loading ESP " << pluginPath.string() << "\n"; loadContentFiles(fileCollections, contentFiles, gameContentLoader);
listener->setLabel(pluginPath.filename().string());
// 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(); listener->loadingOff();
// insert records that may not be present in all versions of MW // insert records that may not be present in all versions of MW
@ -1983,4 +2004,19 @@ namespace MWWorld
return mGodMode; 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 "../mwbase/world.hpp"
#include "contentloader.hpp"
namespace Ogre namespace Ogre
{ {
class Vector3; class Vector3;
@ -41,6 +43,8 @@ namespace MWRender
class Animation; class Animation;
} }
struct ContentLoader;
namespace MWWorld namespace MWWorld
{ {
class WeatherManager; class WeatherManager;
@ -113,6 +117,15 @@ namespace MWWorld
void ensureNeededRecords(); 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; int mPlayIntro;
bool mTeleportEnabled; bool mTeleportEnabled;
@ -122,7 +135,7 @@ namespace MWWorld
World (OEngine::Render::OgreRenderer& renderer, World (OEngine::Render::OgreRenderer& renderer,
const Files::Collections& fileCollections, 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, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir,
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride); 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) { for (PackageIter it = mList.begin(); it != mList.end(); ++it) {
switch (it->mType) { switch (it->mType) {
case AI_Wander: case AI_Wander:

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

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

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

@ -36,6 +36,7 @@ struct Position
enum RecNameInts enum RecNameInts
{ {
// format 0 / legacy
REC_ACTI = 0x49544341, REC_ACTI = 0x49544341,
REC_ALCH = 0x48434c41, REC_ALCH = 0x48434c41,
REC_APPA = 0x41505041, REC_APPA = 0x41505041,
@ -80,7 +81,10 @@ enum RecNameInts
REC_SPEL = 0x4c455053, REC_SPEL = 0x4c455053,
REC_SSCR = 0x52435353, REC_SSCR = 0x52435353,
REC_STAT = 0x54415453, 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); esm.writeHNT<ENAMstruct>("ENAM", *it, 24);
} }
} }

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

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

@ -13,92 +13,103 @@ namespace ESM {
class ESMWriter class ESMWriter
{ {
struct RecordData struct RecordData
{ {
std::string name; std::string name;
std::streampos position; std::streampos position;
size_t size; 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 #endif

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

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

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

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

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

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

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

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

@ -2,6 +2,7 @@
#include "esmreader.hpp" #include "esmreader.hpp"
#include "esmwriter.hpp" #include "esmwriter.hpp"
#include "defs.hpp"
namespace ESM 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.writeHNT("INDX", it->mPart);
esm.writeHNOString("BNAM", it->mMale); 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) void Armor::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); mModel = esm.getHNString("MODL");
@ -39,7 +42,7 @@ void Armor::load(ESMReader &esm)
mEnchant = esm.getHNOString("ENAM"); mEnchant = esm.getHNOString("ENAM");
} }
void Armor::save(ESMWriter &esm) void Armor::save(ESMWriter &esm) const
{ {
esm.writeHNCString("MODL", mModel); esm.writeHNCString("MODL", mModel);
esm.writeHNCString("FNAM", mName); esm.writeHNCString("FNAM", mName);

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

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

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

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

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

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

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