diff --git a/CMakeLists.txt b/CMakeLists.txt index 625239aa0..cfb682cdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -319,6 +319,9 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg "${OpenMW_BINARY_DIR}/opencs.cfg") + +configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters + "${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index e246b4515..d36ab65e9 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -161,8 +161,42 @@ void Launcher::DataFilesPage::slotProfileDeleted (const QString &item) void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤t) { +<<<<<<< HEAD setProfile(previous, current, true); emit signalProfileChanged (ui.profilesComboBox->findText(current)); +======= + if (mContentModel->rowCount() < 1) + return; + + QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); + + if (profile.isEmpty()) { + profile = profilesComboBox->currentText(); + mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile); + } + + mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master")); + mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); + + mGameSettings.remove(QString("master")); + mGameSettings.remove(QString("plugins")); + mGameSettings.remove(QString("content")); + + ContentSelectorModel::ContentFileList items = mContentModel->checkedItems(); + + foreach(const ContentSelectorModel::EsmFile *item, items) { + + if (item->gameFiles().size() == 0) { + mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item->fileName()); + mGameSettings.setMultiValue(QString("content"), item->fileName()); + + } else { + mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item->fileName()); + mGameSettings.setMultiValue(QString("content"), item->fileName()); + } + } + +>>>>>>> 3146af34d642a28b15b55f7eb9999d8ac50168a0 } void Launcher::DataFilesPage::slotProfileRenamed(const QString &previous, const QString ¤t) diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 5231753f2..603fab9dc 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -163,12 +163,12 @@ bool Launcher::GameSettings::writeFile(QTextStream &stream) QStringList masters = mSettings.values(QString("master")); for (int i = masters.count(); i--;) { - stream << "master=" << masters.at(i) << "\n"; + stream << "content=" << masters.at(i) << "\n"; } QStringList plugins = mSettings.values(QString("plugin")); for (int i = plugins.count(); i--;) { - stream << "plugin=" << plugins.at(i) << "\n"; + stream << "content=" << plugins.at(i) << "\n"; } return true; diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 8732b3eab..b8b7e4c9d 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -813,8 +813,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con } void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const { - std::vector esmFiles; - std::vector espFiles; + std::vector contentFiles; std::string baseGameFile("Game Files:GameFile"); std::string gameFile(""); @@ -832,29 +831,19 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) co std::string filetype(entry->substr(entry->length()-3)); Misc::StringUtils::toLower(filetype); - if(filetype.compare("esm") == 0) { - esmFiles.push_back(*entry); - } - else if(filetype.compare("esp") == 0) { - espFiles.push_back(*entry); + if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) { + contentFiles.push_back(*entry); } } gameFile = ""; } - cfg.erase("master"); - cfg.insert( std::make_pair > ("master", std::vector() ) ); - - for(std::vector::const_iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) { - cfg["master"].push_back(*it); - } - - cfg.erase("plugin"); - cfg.insert( std::make_pair > ("plugin", std::vector() ) ); + cfg.erase("content"); + cfg.insert( std::make_pair("content", std::vector() ) ); - for(std::vector::const_iterator it=espFiles.begin(); it!=espFiles.end(); ++it) { - cfg["plugin"].push_back(*it); + for(std::vector::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) { + cfg["content"].push_back(*it); } } diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 2d6490bd4..d317331e5 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -5,11 +5,11 @@ opencs_units (. editor) set (CMAKE_BUILD_TYPE DEBUG) opencs_units (model/doc - document + document operation saving ) opencs_units_noqt (model/doc - documentmanager + documentmanager stage savingstate savingstages ) opencs_hdrs_noqt (model/doc @@ -33,11 +33,11 @@ opencs_hdrs_noqt (model/world opencs_units (model/tools - tools operation reportmodel + tools reportmodel ) opencs_units_noqt (model/tools - stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck + mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck birthsigncheck spellcheck ) @@ -66,7 +66,7 @@ opencs_units (view/world opencs_units_noqt (view/world dialoguesubview subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate - scripthighlighter idvalidator + scripthighlighter idvalidator dialoguecreator ) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 6396563f2..03758b16e 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -8,15 +8,18 @@ #include "model/doc/document.hpp" #include "model/world/data.hpp" +#include -CS::Editor::Editor() : mViewManager (mDocumentManager) +CS::Editor::Editor() + : mDocumentManager (mCfgMgr), mViewManager (mDocumentManager) { mIpcServerName = "org.openmw.OpenCS"; setupDataFiles(); mNewGame.setLocalData (mLocal); + mFileDialog.setLocalData (mLocal); connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ())); connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ())); @@ -29,22 +32,24 @@ CS::Editor::Editor() : mViewManager (mDocumentManager) connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ())); connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles())); - connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile())); + connect (&mFileDialog, SIGNAL(createNewFile (const boost::filesystem::path&)), + this, SLOT(createNewFile (const boost::filesystem::path&))); connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), - this, SLOT (createNewGame (const boost::filesystem::path&))); + this, SLOT (createNewGame (const boost::filesystem::path&))); } void CS::Editor::setupDataFiles() { boost::program_options::variables_map variables; - boost::program_options::options_description desc; + boost::program_options::options_description desc("Syntax: opencs \nAllowed options"); desc.add_options() ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) ("data-local", boost::program_options::value()->default_value("")) ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) - ("encoding", boost::program_options::value()->default_value("win1252")); + ("encoding", boost::program_options::value()->default_value("win1252")) + ("resources", boost::program_options::value()->default_value("resources")); boost::program_options::notify(variables); @@ -79,11 +84,13 @@ void CS::Editor::setupDataFiles() } // Set the charset for reading the esm/esp files - // QString encoding = QString::fromStdString(variables["encoding"].as()); + // QString encoding = QString::fromStdString(variables["encoding"].as()); //mFileDialog.setEncoding(encoding); dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); + mDocumentManager.setResourceDir (variables["resources"].as()); + for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { QString path = QString::fromStdString(iter->string()); @@ -134,7 +141,7 @@ void CS::Editor::openFiles() mFileDialog.hide(); } -void CS::Editor::createNewFile() +void CS::Editor::createNewFile (const boost::filesystem::path& savePath) { std::vector files; diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 248ebf2c5..16f6b9516 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -26,6 +26,7 @@ namespace CS { Q_OBJECT + Files::ConfigurationManager mCfgMgr; CSMSettings::UserSettings mUserSettings; CSMDoc::DocumentManager mDocumentManager; CSVDoc::ViewManager mViewManager; @@ -34,7 +35,6 @@ namespace CS CSVSettings::UserSettingsDialog mSettings; CSVDoc::FileDialog mFileDialog; - Files::ConfigurationManager mCfgMgr; boost::filesystem::path mLocal; void setupDataFiles(); @@ -60,7 +60,7 @@ namespace CS void loadDocument(); void openFiles(); - void createNewFile(); + void createNewFile (const boost::filesystem::path& savePath); void createNewGame (const boost::filesystem::path& file); void showStartup(); diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index d7138f671..cc886f9cc 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -1,8 +1,15 @@ #include "document.hpp" + #include +#include + +#ifndef Q_MOC_RUN +#include +#endif + void CSMDoc::Document::load (const std::vector::const_iterator& begin, - const std::vector::const_iterator& end, bool lastAsModified) + const std::vector::const_iterator& end, bool lastAsModified) { assert (begin!=end); @@ -12,10 +19,10 @@ void CSMDoc::Document::load (const std::vector::const_i --end2; for (std::vector::const_iterator iter (begin); iter!=end2; ++iter) - getData().loadFile (*iter, true); + getData().loadFile (*iter, true, false); if (lastAsModified) - getData().loadFile (*end2, false); + getData().loadFile (*end2, false, false); } void CSMDoc::Document::addGmsts() @@ -2058,9 +2065,9 @@ void CSMDoc::Document::addOptionalGlobals() { static const char *sGlobals[] = { - "dayspassed", - "pcwerewolf", - "pcyear", + "DaysPassed", + "PCWerewolf", + "PCYear", 0 }; @@ -2137,11 +2144,86 @@ void CSMDoc::Document::createBase() getData().getSkills().add (record); } + + static const char *sVoice[] = + { + "Intruder", + "Attack", + "Hello", + "Thief", + "Alarm", + "Idle", + "Flee", + "Hit", + 0 + }; + + for (int i=0; sVoice[i]; ++i) + { + ESM::Dialogue record; + record.mId = sVoice[i]; + record.mType = ESM::Dialogue::Voice; + record.blank(); + + getData().getTopics().add (record); + } + + static const char *sGreetings[] = + { + "Greeting 0", + "Greeting 1", + "Greeting 2", + "Greeting 3", + "Greeting 4", + "Greeting 5", + "Greeting 6", + "Greeting 7", + "Greeting 8", + "Greeting 9", + 0 + }; + + for (int i=0; sGreetings[i]; ++i) + { + ESM::Dialogue record; + record.mId = sGreetings[i]; + record.mType = ESM::Dialogue::Greeting; + record.blank(); + + getData().getTopics().add (record); + } + + static const char *sPersuasion[] = + { + "Intimidate Success", + "Intimidate Fail", + "Service Refusal", + "Admire Success", + "Taunt Success", + "Bribe Success", + "Info Refusal", + "Admire Fail", + "Taunt Fail", + "Bribe Fail", + 0 + }; + + for (int i=0; sPersuasion[i]; ++i) + { + ESM::Dialogue record; + record.mId = sPersuasion[i]; + record.mType = ESM::Dialogue::Persuasion; + record.blank(); + + getData().getTopics().add (record); + } } -CSMDoc::Document::Document (const std::vector& files, - const boost::filesystem::path& savePath, bool new_) -: mSavePath (savePath), mTools (mData) +CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_) + : mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir), + mProjectPath ((configuration.getUserPath() / "projects") / + (savePath.filename().string() + ".project")), + mSaving (*this, mProjectPath) { if (files.empty()) throw std::runtime_error ("Empty content file sequence"); @@ -2158,6 +2240,34 @@ CSMDoc::Document::Document (const std::vector& files, load (files.begin(), end, !new_); } + if (new_) + { + mData.setDescription (""); + mData.setAuthor (""); + } +/// \todo un-outcomment the else, once loading an existing content file works properly again. +// else + { + if (boost::filesystem::exists (mProjectPath)) + { + getData().loadFile (mProjectPath, false, true); + } + else + { + boost::filesystem::path locCustomFiltersPath (configuration.getUserPath()); + locCustomFiltersPath /= "defaultfilters"; + if (boost::filesystem::exists(locCustomFiltersPath)) + { + boost::filesystem::copy_file (locCustomFiltersPath, mProjectPath); + } else { + boost::filesystem::path filters(mResDir); + filters /= "defaultfilters"; + boost::filesystem::copy_file(filters, mProjectPath); + } + getData().loadFile (mProjectPath, false, true); + } + } + addOptionalGmsts(); addOptionalGlobals(); @@ -2166,9 +2276,10 @@ CSMDoc::Document::Document (const std::vector& files, connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int))); - // dummy implementation -> remove when proper save is implemented. - mSaveCount = 0; - connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); + connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); + connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int))); + connect (&mSaving, SIGNAL (reportMessage (const QString&, int)), + this, SLOT (reportMessage (const QString&, int))); } CSMDoc::Document::~Document() @@ -2187,7 +2298,7 @@ int CSMDoc::Document::getState() const if (!mUndoStack.isClean()) state |= State_Modified; - if (mSaveCount) + if (mSaving.isRunning()) state |= State_Locked | State_Saving | State_Operation; if (int operations = mTools.getRunningOperations()) @@ -2201,12 +2312,20 @@ const boost::filesystem::path& CSMDoc::Document::getSavePath() const return mSavePath; } +const std::vector& CSMDoc::Document::getContentFiles() const +{ + return mContentFiles; +} + void CSMDoc::Document::save() { - mSaveCount = 1; - mSaveTimer.start (500); + if (mSaving.isRunning()) + throw std::logic_error ( + "Failed to initiate save, because a save operation is already running."); + + mSaving.start(); + emit stateChanged (getState(), this); - emit progress (1, 16, State_Saving, 1, this); } CSMWorld::UniversalId CSMDoc::Document::verify() @@ -2218,44 +2337,26 @@ CSMWorld::UniversalId CSMDoc::Document::verify() void CSMDoc::Document::abortOperation (int type) { - mTools.abortOperation (type); - if (type==State_Saving) - { - mSaveCount=0; - mSaveTimer.stop(); - emit stateChanged (getState(), this); - } + mSaving.abort(); + else + mTools.abortOperation (type); } - void CSMDoc::Document::modificationStateChanged (bool clean) { emit stateChanged (getState(), this); } - -void CSMDoc::Document::operationDone (int type) +void CSMDoc::Document::reportMessage (const QString& message, int type) { - emit stateChanged (getState(), this); + /// \todo find a better way to get these messages to the user. + std::cout << message.toUtf8().constData() << std::endl; } -void CSMDoc::Document::saving() +void CSMDoc::Document::operationDone (int type) { - ++mSaveCount; - - emit progress (mSaveCount, 16, State_Saving, 1, this); - - if (mSaveCount>15) - { - //clear the stack before resetting the save state - //to avoid emitting incorrect states - mUndoStack.setClean(); - - mSaveCount = 0; - mSaveTimer.stop(); - emit stateChanged (getState(), this); - } + emit stateChanged (getState(), this); } const CSMWorld::Data& CSMDoc::Document::getData() const diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 3532721ea..437b0c513 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -14,6 +14,7 @@ #include "../tools/tools.hpp" #include "state.hpp" +#include "saving.hpp" class QAbstractItemModel; @@ -23,6 +24,11 @@ namespace ESM struct Global; } +namespace Files +{ + class ConfigurationManager; +} + namespace CSMDoc { class Document : public QObject @@ -32,16 +38,17 @@ namespace CSMDoc private: boost::filesystem::path mSavePath; + std::vector mContentFiles; CSMWorld::Data mData; CSMTools::Tools mTools; + boost::filesystem::path mProjectPath; + Saving mSaving; + boost::filesystem::path mResDir; // It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is // using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late. QUndoStack mUndoStack; - int mSaveCount; ///< dummy implementation -> remove when proper save is implemented. - QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented. - // not implemented Document (const Document&); Document& operator= (const Document&); @@ -64,8 +71,7 @@ namespace CSMDoc public: - Document (const std::vector& files, - const boost::filesystem::path& savePath, bool new_); + Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_); ~Document(); @@ -75,6 +81,10 @@ namespace CSMDoc const boost::filesystem::path& getSavePath() const; + const std::vector& getContentFiles() const; + ///< \attention The last element in this collection is the file that is being edited, + /// but with its original path instead of the save path. + void save(); CSMWorld::UniversalId verify(); @@ -98,10 +108,9 @@ namespace CSMDoc void modificationStateChanged (bool clean); - void operationDone (int type); + void reportMessage (const QString& message, int type); - void saving(); - ///< dummy implementation -> remove when proper save is implemented. + void operationDone (int type); public slots: @@ -110,3 +119,4 @@ namespace CSMDoc } #endif + diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index b079109ea..024c46bea 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -4,9 +4,22 @@ #include #include +#include + +#ifndef Q_MOC_RUN +#include +#endif + #include "document.hpp" -CSMDoc::DocumentManager::DocumentManager() {} +CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration) +: mConfiguration (configuration) +{ + boost::filesystem::path projectPath = configuration.getUserPath() / "projects"; + + if (!boost::filesystem::is_directory (projectPath)) + boost::filesystem::create_directories (projectPath); +} CSMDoc::DocumentManager::~DocumentManager() { @@ -17,7 +30,7 @@ CSMDoc::DocumentManager::~DocumentManager() CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector& files, const boost::filesystem::path& savePath, bool new_) { - Document *document = new Document (files, savePath, new_); + Document *document = new Document (mConfiguration, files, savePath, mResDir, new_); mDocuments.push_back (document); @@ -35,4 +48,9 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document) delete document; return mDocuments.empty(); +} + +void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir) +{ + mResDir = boost::filesystem::system_complete(parResDir); } \ No newline at end of file diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index dfded8d5c..b80a18642 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -6,6 +6,11 @@ #include +namespace Files +{ + class ConfigurationManager; +} + namespace CSMDoc { class Document; @@ -13,18 +18,18 @@ namespace CSMDoc class DocumentManager { std::vector mDocuments; + const Files::ConfigurationManager& mConfiguration; DocumentManager (const DocumentManager&); DocumentManager& operator= (const DocumentManager&); public: - DocumentManager(); + DocumentManager (const Files::ConfigurationManager& configuration); ~DocumentManager(); - Document *addDocument (const std::vector& files, - const boost::filesystem::path& savePath, bool new_); + Document *addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_); ///< The ownership of the returned document is not transferred to the caller. /// /// \param new_ Do not load the last content file in \a files and instead create in an @@ -32,6 +37,10 @@ namespace CSMDoc bool removeDocument (Document *document); ///< \return last document removed? + void setResourceDir (const boost::filesystem::path& parResDir); + + private: + boost::filesystem::path mResDir; }; } diff --git a/apps/opencs/model/tools/operation.cpp b/apps/opencs/model/doc/operation.cpp similarity index 50% rename from apps/opencs/model/tools/operation.cpp rename to apps/opencs/model/doc/operation.cpp index 71761cdae..d29cc2631 100644 --- a/apps/opencs/model/tools/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -6,16 +6,16 @@ #include -#include "../doc/state.hpp" - +#include "state.hpp" #include "stage.hpp" -void CSMTools::Operation::prepareStages() +void CSMDoc::Operation::prepareStages() { mCurrentStage = mStages.begin(); mCurrentStep = 0; mCurrentStepTotal = 0; mTotalSteps = 0; + mError = false; for (std::vector >::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 >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter) delete iter->first; } -void CSMTools::Operation::run() +void CSMDoc::Operation::run() { prepareStages(); QTimer timer; - timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify())); + timer.connect (&timer, SIGNAL (timeout()), this, SLOT (executeStage())); timer.start (0); exec(); } -void CSMTools::Operation::appendStage (Stage *stage) +void CSMDoc::Operation::appendStage (Stage *stage) { mStages.push_back (std::make_pair (stage, 0)); } -void CSMTools::Operation::abort() +bool CSMDoc::Operation::hasError() const { - exit(); + return mError; } -void CSMTools::Operation::verify() +void CSMDoc::Operation::abort() +{ + if (!isRunning()) + return; + + mError = true; + + if (mFinalAlways) + { + if (mStages.begin()!=mStages.end() && mCurrentStage!=--mStages.end()) + { + mCurrentStep = 0; + mCurrentStage = --mStages.end(); + } + } + else + mCurrentStage = mStages.end(); +} + +void CSMDoc::Operation::executeStage() { std::vector messages; @@ -68,7 +91,16 @@ void CSMTools::Operation::verify() } else { - mCurrentStage->first->perform (mCurrentStep++, messages); + try + { + mCurrentStage->first->perform (mCurrentStep++, messages); + } + catch (const std::exception& e) + { + emit reportMessage (e.what(), mType); + abort(); + } + ++mCurrentStepTotal; break; } @@ -81,4 +113,9 @@ void CSMTools::Operation::verify() if (mCurrentStage==mStages.end()) exit(); +} + +void CSMDoc::Operation::operationDone() +{ + emit done (mType); } \ No newline at end of file diff --git a/apps/opencs/model/tools/operation.hpp b/apps/opencs/model/doc/operation.hpp similarity index 63% rename from apps/opencs/model/tools/operation.hpp rename to apps/opencs/model/doc/operation.hpp index 4731c58fa..316eda78f 100644 --- a/apps/opencs/model/tools/operation.hpp +++ b/apps/opencs/model/doc/operation.hpp @@ -1,11 +1,11 @@ -#ifndef CSM_TOOLS_OPERATION_H -#define CSM_TOOLS_OPERATION_H +#ifndef CSM_DOC_OPERATION_H +#define CSM_DOC_OPERATION_H #include #include -namespace CSMTools +namespace CSMDoc { class Stage; @@ -19,12 +19,17 @@ namespace CSMTools int mCurrentStep; int mCurrentStepTotal; int mTotalSteps; + int mOrdered; + bool mFinalAlways; + bool mError; void prepareStages(); public: - Operation (int type); + Operation (int type, bool ordered, bool finalAlways = false); + ///< \param ordered Stages must be executed in the given order. + /// \param finalAlways Execute last stage even if an error occurred during earlier stages. virtual ~Operation(); @@ -35,19 +40,25 @@ namespace CSMTools /// /// \attention Do no call this function while this Operation is running. + bool hasError() const; + signals: void progress (int current, int max, int type); void reportMessage (const QString& message, int type); + void done (int type); + public slots: void abort(); private slots: - void verify(); + void executeStage(); + + void operationDone(); }; } diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp new file mode 100644 index 000000000..b756a9dc1 --- /dev/null +++ b/apps/opencs/model/doc/saving.cpp @@ -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 > + (mDocument.getData().getGlobals(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getGmsts(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getSkills(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getClasses(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getFactions(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getRaces(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getSounds(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getScripts(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getRegions(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getBirthsigns(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getSpells(), mState)); + + /// \todo deal with info records for topcis and journals + appendStage (new WriteCollectionStage > + (mDocument.getData().getTopics(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getJournals(), mState)); + + appendStage (new WriteRefIdCollectionStage (mDocument, mState)); + + + appendStage (new CloseSaveStage (mState)); + + appendStage (new FinalSavingStage (mDocument, mState)); +} \ No newline at end of file diff --git a/apps/opencs/model/doc/saving.hpp b/apps/opencs/model/doc/saving.hpp new file mode 100644 index 000000000..cd1bbef98 --- /dev/null +++ b/apps/opencs/model/doc/saving.hpp @@ -0,0 +1,27 @@ +#ifndef CSM_DOC_SAVING_H +#define CSM_DOC_SAVING_H + +#include + +#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 diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp new file mode 100644 index 000000000..d68c72317 --- /dev/null +++ b/apps/opencs/model/doc/savingstages.cpp @@ -0,0 +1,161 @@ + +#include "savingstages.hpp" + +#include + +#include + +#include + +#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& 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& 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 dependencies = mDocument.getContentFiles(); + std::vector::const_iterator end (--dependencies.end()); + + for (std::vector::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& messages) +{ + mDocument.getData().getReferenceables().save (stage, mState.getWriter()); +} + + +CSMDoc::WriteFilterStage::WriteFilterStage (Document& document, SavingState& state, + CSMFilter::Filter::Scope scope) +: WriteCollectionStage > (document.getData().getFilters(), + state), + mDocument (document), mScope (scope) +{} + +void CSMDoc::WriteFilterStage::perform (int stage, std::vector& messages) +{ + const CSMWorld::Record& record = + mDocument.getData().getFilters().getRecord (stage); + + if (record.get().mScope==mScope) + WriteCollectionStage >::perform (stage, messages); +} + + +CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state) +: mState (state) +{} + +int CSMDoc::CloseSaveStage::setup() +{ + return 1; +} + +void CSMDoc::CloseSaveStage::perform (int stage, std::vector& 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& 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(); + } +} \ No newline at end of file diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp new file mode 100644 index 000000000..ff94116fd --- /dev/null +++ b/apps/opencs/model/doc/savingstages.hpp @@ -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& 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& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + + template + 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& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + template + WriteCollectionStage::WriteCollectionStage (const CollectionT& collection, + SavingState& state) + : mCollection (collection), mState (state) + {} + + template + int WriteCollectionStage::setup() + { + return mCollection.getSize(); + } + + template + void WriteCollectionStage::perform (int stage, std::vector& 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 (&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& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + + class WriteFilterStage : public WriteCollectionStage > + { + Document& mDocument; + CSMFilter::Filter::Scope mScope; + + public: + + WriteFilterStage (Document& document, SavingState& state, CSMFilter::Filter::Scope scope); + + virtual void perform (int stage, std::vector& 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& 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& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/doc/savingstate.cpp b/apps/opencs/model/doc/savingstate.cpp new file mode 100644 index 000000000..4a1abb888 --- /dev/null +++ b/apps/opencs/model/doc/savingstate.cpp @@ -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; +} \ No newline at end of file diff --git a/apps/opencs/model/doc/savingstate.hpp b/apps/opencs/model/doc/savingstate.hpp new file mode 100644 index 000000000..8cf7883e5 --- /dev/null +++ b/apps/opencs/model/doc/savingstate.hpp @@ -0,0 +1,50 @@ +#ifndef CSM_DOC_SAVINGSTATE_H +#define CSM_DOC_SAVINGSTATE_H + +#include + +#include + +#include + +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 \ No newline at end of file diff --git a/apps/opencs/model/doc/stage.cpp b/apps/opencs/model/doc/stage.cpp new file mode 100644 index 000000000..99b765770 --- /dev/null +++ b/apps/opencs/model/doc/stage.cpp @@ -0,0 +1,4 @@ + +#include "stage.hpp" + +CSMDoc::Stage::~Stage() {} \ No newline at end of file diff --git a/apps/opencs/model/tools/stage.hpp b/apps/opencs/model/doc/stage.hpp similarity index 65% rename from apps/opencs/model/tools/stage.hpp rename to apps/opencs/model/doc/stage.hpp index 3020936f3..1f96c60b4 100644 --- a/apps/opencs/model/tools/stage.hpp +++ b/apps/opencs/model/doc/stage.hpp @@ -1,10 +1,10 @@ -#ifndef CSM_TOOLS_STAGE_H -#define CSM_TOOLS_STAGE_H +#ifndef CSM_DOC_STAGE_H +#define CSM_DOC_STAGE_H #include #include -namespace CSMTools +namespace CSMDoc { class Stage { @@ -16,7 +16,7 @@ namespace CSMTools ///< \return number of steps virtual void perform (int stage, std::vector& messages) = 0; - ///< Messages resulting from this tage will be appended to \a messages. + ///< Messages resulting from this stage will be appended to \a messages. }; } diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp index 42b5a6b24..bdd65b44a 100644 --- a/apps/opencs/model/tools/birthsigncheck.hpp +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that birthsign records are internally consistent - class BirthsignCheckStage : public Stage + class BirthsignCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mBirthsigns; diff --git a/apps/opencs/model/tools/classcheck.hpp b/apps/opencs/model/tools/classcheck.hpp index a29d7c8b7..3604b451c 100644 --- a/apps/opencs/model/tools/classcheck.hpp +++ b/apps/opencs/model/tools/classcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that class records are internally consistent - class ClassCheckStage : public Stage + class ClassCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mClasses; diff --git a/apps/opencs/model/tools/factioncheck.hpp b/apps/opencs/model/tools/factioncheck.hpp index 868650572..7cd80347d 100644 --- a/apps/opencs/model/tools/factioncheck.hpp +++ b/apps/opencs/model/tools/factioncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that faction records are internally consistent - class FactionCheckStage : public Stage + class FactionCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mFactions; diff --git a/apps/opencs/model/tools/mandatoryid.hpp b/apps/opencs/model/tools/mandatoryid.hpp index 342e2d754..5fddf08d3 100644 --- a/apps/opencs/model/tools/mandatoryid.hpp +++ b/apps/opencs/model/tools/mandatoryid.hpp @@ -6,7 +6,7 @@ #include "../world/universalid.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMWorld { @@ -16,7 +16,7 @@ namespace CSMWorld namespace CSMTools { /// \brief Verify stage: make sure that records with specific IDs exist. - class MandatoryIdStage : public Stage + class MandatoryIdStage : public CSMDoc::Stage { const CSMWorld::CollectionBase& mIdCollection; CSMWorld::UniversalId mCollectionId; diff --git a/apps/opencs/model/tools/racecheck.hpp b/apps/opencs/model/tools/racecheck.hpp index 155f79902..ff9948bf6 100644 --- a/apps/opencs/model/tools/racecheck.hpp +++ b/apps/opencs/model/tools/racecheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that race records are internally consistent - class RaceCheckStage : public Stage + class RaceCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mRaces; bool mPlayable; diff --git a/apps/opencs/model/tools/regioncheck.hpp b/apps/opencs/model/tools/regioncheck.hpp index b42135651..c8c437cbd 100644 --- a/apps/opencs/model/tools/regioncheck.hpp +++ b/apps/opencs/model/tools/regioncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that region records are internally consistent - class RegionCheckStage : public Stage + class RegionCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mRegions; diff --git a/apps/opencs/model/tools/skillcheck.hpp b/apps/opencs/model/tools/skillcheck.hpp index 30a3f01ca..662bdadee 100644 --- a/apps/opencs/model/tools/skillcheck.hpp +++ b/apps/opencs/model/tools/skillcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that skill records are internally consistent - class SkillCheckStage : public Stage + class SkillCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSkills; diff --git a/apps/opencs/model/tools/soundcheck.hpp b/apps/opencs/model/tools/soundcheck.hpp index a309763a1..00b45cd93 100644 --- a/apps/opencs/model/tools/soundcheck.hpp +++ b/apps/opencs/model/tools/soundcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that sound records are internally consistent - class SoundCheckStage : public Stage + class SoundCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSounds; diff --git a/apps/opencs/model/tools/spellcheck.hpp b/apps/opencs/model/tools/spellcheck.hpp index 056639219..880ddafcd 100644 --- a/apps/opencs/model/tools/spellcheck.hpp +++ b/apps/opencs/model/tools/spellcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that spell records are internally consistent - class SpellCheckStage : public Stage + class SpellCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSpells; diff --git a/apps/opencs/model/tools/stage.cpp b/apps/opencs/model/tools/stage.cpp deleted file mode 100644 index 6f4567e57..000000000 --- a/apps/opencs/model/tools/stage.cpp +++ /dev/null @@ -1,4 +0,0 @@ - -#include "stage.hpp" - -CSMTools::Stage::~Stage() {} \ No newline at end of file diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 803861203..cd4653280 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -3,9 +3,8 @@ #include -#include "verifier.hpp" - #include "../doc/state.hpp" +#include "../doc/operation.hpp" #include "../world/data.hpp" #include "../world/universalid.hpp" @@ -21,7 +20,7 @@ #include "birthsigncheck.hpp" #include "spellcheck.hpp" -CSMTools::Operation *CSMTools::Tools::get (int type) +CSMDoc::Operation *CSMTools::Tools::get (int type) { switch (type) { @@ -31,19 +30,19 @@ CSMTools::Operation *CSMTools::Tools::get (int type) return 0; } -const CSMTools::Operation *CSMTools::Tools::get (int type) const +const CSMDoc::Operation *CSMTools::Tools::get (int type) const { return const_cast (this)->get (type); } -CSMTools::Verifier *CSMTools::Tools::getVerifier() +CSMDoc::Operation *CSMTools::Tools::getVerifier() { if (!mVerifier) { - mVerifier = new Verifier; + mVerifier = new CSMDoc::Operation (CSMDoc::State_Verifying, false); connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); - connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone())); + connect (mVerifier, SIGNAL (done (int)), this, SIGNAL (done (int))); connect (mVerifier, SIGNAL (reportMessage (const QString&, int)), this, SLOT (verifierMessage (const QString&, int))); @@ -103,7 +102,7 @@ CSMWorld::UniversalId CSMTools::Tools::runVerifier() void CSMTools::Tools::abortOperation (int type) { - if (Operation *operation = get (type)) + if (CSMDoc::Operation *operation = get (type)) operation->abort(); } @@ -118,7 +117,7 @@ int CSMTools::Tools::getRunningOperations() const int result = 0; for (int i=0; sOperations[i]!=-1; ++i) - if (const Operation *operation = get (sOperations[i])) + if (const CSMDoc::Operation *operation = get (sOperations[i])) if (operation->isRunning()) result |= sOperations[i]; @@ -133,11 +132,6 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& return mReports.at (id.getIndex()); } -void CSMTools::Tools::verifierDone() -{ - emit done (CSMDoc::State_Verifying); -} - void CSMTools::Tools::verifierMessage (const QString& message, int type) { std::map::iterator iter = mActiveReports.find (type); diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index 652345c6d..0079fab34 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -11,10 +11,13 @@ namespace CSMWorld class UniversalId; } -namespace CSMTools +namespace CSMDoc { - class Verifier; class Operation; +} + +namespace CSMTools +{ class ReportModel; class Tools : public QObject @@ -22,7 +25,7 @@ namespace CSMTools Q_OBJECT CSMWorld::Data& mData; - Verifier *mVerifier; + CSMDoc::Operation *mVerifier; std::map mReports; int mNextReportNumber; std::map mActiveReports; // type, report number @@ -31,12 +34,12 @@ namespace CSMTools Tools (const Tools&); Tools& operator= (const Tools&); - Verifier *getVerifier(); + CSMDoc::Operation *getVerifier(); - Operation *get (int type); + CSMDoc::Operation *get (int type); ///< Returns a 0-pointer, if operation hasn't been used yet. - const Operation *get (int type) const; + const CSMDoc::Operation *get (int type) const; ///< Returns a 0-pointer, if operation hasn't been used yet. public: @@ -58,8 +61,6 @@ namespace CSMTools private slots: - void verifierDone(); - void verifierMessage (const QString& message, int type); signals: diff --git a/apps/opencs/model/tools/verifier.cpp b/apps/opencs/model/tools/verifier.cpp deleted file mode 100644 index 9c00d4ea7..000000000 --- a/apps/opencs/model/tools/verifier.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -#include "verifier.hpp" - -#include "../doc/state.hpp" - -CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying) -{} diff --git a/apps/opencs/model/tools/verifier.hpp b/apps/opencs/model/tools/verifier.hpp deleted file mode 100644 index 054f87169..000000000 --- a/apps/opencs/model/tools/verifier.hpp +++ /dev/null @@ -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 diff --git a/apps/opencs/model/world/collectionbase.cpp b/apps/opencs/model/world/collectionbase.cpp index 932ea27b5..241f198cb 100644 --- a/apps/opencs/model/world/collectionbase.cpp +++ b/apps/opencs/model/world/collectionbase.cpp @@ -1,6 +1,31 @@ #include "collectionbase.hpp" +#include + +#include "columnbase.hpp" + CSMWorld::CollectionBase::CollectionBase() {} CSMWorld::CollectionBase::~CollectionBase() {} + +int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const +{ + int columns = getColumns(); + + for (int i=0; i #include "universalid.hpp" +#include "columns.hpp" class QVariant; @@ -83,6 +84,13 @@ namespace CSMWorld ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list + + int searchColumnIndex (Columns::ColumnId id) const; + ///< Return index of column with the given \a id. If no such column exists, -1 is returned. + + int findColumnIndex (Columns::ColumnId id) const; + ///< Return index of column with the given \a id. If no such column exists, an exception is + /// thrown. }; } diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index c1b423c94..9b8d7dafb 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -43,7 +43,8 @@ namespace CSMWorld Display_CreatureType, Display_WeaponType, Display_RecordState, - Display_RefRecordType + Display_RefRecordType, + Display_DialogueType }; int mColumnId; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index a13ac9a8a..8575480fd 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1217,6 +1217,37 @@ namespace CSMWorld } }; + template + struct ScopeColumn : public Column + { + ScopeColumn() + : Column (Columns::ColumnId_Scope, ColumnBase::Display_Integer, 0) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mScope); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mScope = static_cast (data.toInt()); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + + virtual bool isUserEditable() const + { + return false; + } + }; + + template struct PosColumn : public Column { @@ -1284,6 +1315,39 @@ namespace CSMWorld return true; } }; + + template + struct DialogueTypeColumn : public Column + { + DialogueTypeColumn (bool hidden = false) + : Column (Columns::ColumnId_DialogueType, ColumnBase::Display_DialogueType, + hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mType); + } + + virtual void set (Record& 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 diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index ca37840ad..a842e7b8d 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -159,6 +159,8 @@ namespace CSMWorld { ColumnId_DoorPositionXRot, "Teleport Rot X" }, { ColumnId_DoorPositionYRot, "Teleport Rot Y" }, { ColumnId_DoorPositionZRot, "Teleport Rot Z" }, + { ColumnId_DialogueType, "Dialogue Type" }, + { ColumnId_Scope, "Scope", }, { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, @@ -269,6 +271,11 @@ namespace "unknown", "none", "short", "integer", "long", "float", "string", 0 }; + static const char *sDialogueTypeEnums[] = + { + "Topic", "Voice", "Greeting", "Persuasion", 0 + }; + const char **getEnumNames (CSMWorld::Columns::ColumnId column) { switch (column) @@ -283,6 +290,7 @@ namespace case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes; case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums; case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums; + case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums; default: return 0; } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 9b26cac4c..55d085a96 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -152,6 +152,8 @@ namespace CSMWorld ColumnId_DoorPositionXRot = 139, ColumnId_DoorPositionYRot = 140, ColumnId_DoorPositionZRot = 141, + ColumnId_DialogueType = 142, + ColumnId_Scope = 143, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 1e290d45f..10e56765f 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -44,6 +44,17 @@ void CSMWorld::Data::appendIds (std::vector& ids, const CollectionB ids.insert (ids.end(), ids2.begin(), ids2.end()); } +int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collection) +{ + int number = 0; + + for (int i=0; i); @@ -141,6 +152,14 @@ CSMWorld::Data::Data() : mRefs (mCells) mSpells.addColumn (new FlagColumn (Columns::ColumnId_StarterSpell, 0x2)); mSpells.addColumn (new FlagColumn (Columns::ColumnId_AlwaysSucceeds, 0x4)); + mTopics.addColumn (new StringIdColumn); + mTopics.addColumn (new RecordStateColumn); + mTopics.addColumn (new DialogueTypeColumn); + + mJournals.addColumn (new StringIdColumn); + mJournals.addColumn (new RecordStateColumn); + mJournals.addColumn (new DialogueTypeColumn (true)); + mCells.addColumn (new StringIdColumn); mCells.addColumn (new RecordStateColumn); mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); @@ -184,6 +203,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mFilters.addColumn (new RecordStateColumn); mFilters.addColumn (new FilterColumn); mFilters.addColumn (new DescriptionColumn); + mFilters.addColumn (new ScopeColumn); addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); @@ -196,6 +216,8 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region); addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign); addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); + addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); + addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); @@ -319,6 +341,28 @@ CSMWorld::IdCollection& CSMWorld::Data::getSpells() return mSpells; } + +const CSMWorld::IdCollection& CSMWorld::Data::getTopics() const +{ + return mTopics; +} + +CSMWorld::IdCollection& CSMWorld::Data::getTopics() +{ + return mTopics; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getJournals() const +{ + return mJournals; +} + +CSMWorld::IdCollection& CSMWorld::Data::getJournals() +{ + return mJournals; +} + + const CSMWorld::IdCollection& CSMWorld::Data::getCells() const { return mCells; @@ -387,7 +431,7 @@ void CSMWorld::Data::merge() mGlobals.merge(); } -void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) +void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, bool project) { ESM::ESMReader reader; @@ -397,6 +441,9 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) reader.open (path.string()); + mAuthor = reader.getAuthor(); + mDescription = reader.getDesc(); + // Note: We do not need to send update signals here, because at this point the model is not connected // to any view. while (reader.hasMoreRecs()) @@ -447,6 +494,54 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) case ESM::REC_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break; case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break; + case ESM::REC_DIAL: + { + std::string id = reader.getHNOString ("NAME"); + + ESM::Dialogue record; + record.mId = id; + record.load (reader); + + if (record.mType==ESM::Dialogue::Journal) + { + mJournals.load (record, base); + } + else if (record.mType==ESM::Dialogue::Deleted) + { + if (mJournals.tryDelete (id)) + { + /// \todo handle info records + } + else if (mTopics.tryDelete (id)) + { + /// \todo handle info records + } + else + { + /// \todo report deletion of non-existing record + } + } + else + { + mTopics.load (record, base); + } + + break; + } + + case ESM::REC_FILT: + + if (project) + { + mFilters.load (reader, base); + mFilters.setData (mFilters.getSize()-1, + mFilters.findColumnIndex (CSMWorld::Columns::ColumnId_Scope), + static_cast (CSMFilter::Filter::Scope_Project)); + break; + } + + // fall through (filter record in a content file is an error with format 0) + default: /// \todo throw an exception instead, once all records are implemented @@ -469,10 +564,50 @@ bool CSMWorld::Data::hasId (const std::string& id) const getRegions().searchId (id)!=-1 || getBirthsigns().searchId (id)!=-1 || getSpells().searchId (id)!=-1 || + getTopics().searchId (id)!=-1 || + getJournals().searchId (id)!=-1 || getCells().searchId (id)!=-1 || getReferenceables().searchId (id)!=-1; } +int CSMWorld::Data::count (RecordBase::State state) const +{ + return + count (state, mGlobals) + + count (state, mGmsts) + + count (state, mSkills) + + count (state, mClasses) + + count (state, mFactions) + + count (state, mRaces) + + count (state, mSounds) + + count (state, mScripts) + + count (state, mRegions) + + count (state, mBirthsigns) + + count (state, mSpells) + + count (state, mCells) + + count (state, mReferenceables); +} + +void CSMWorld::Data::setDescription (const std::string& description) +{ + mDescription = description; +} + +std::string CSMWorld::Data::getDescription() const +{ + return mDescription; +} + +void CSMWorld::Data::setAuthor (const std::string& author) +{ + mAuthor = author; +} + +std::string CSMWorld::Data::getAuthor() const +{ + return mAuthor; +} + std::vector CSMWorld::Data::getIds (bool listDeleted) const { std::vector ids; @@ -487,6 +622,8 @@ std::vector CSMWorld::Data::getIds (bool listDeleted) const appendIds (ids, mRegions, listDeleted); appendIds (ids, mBirthsigns, listDeleted); appendIds (ids, mSpells, listDeleted); + appendIds (ids, mTopics, listDeleted); + appendIds (ids, mJournals, listDeleted); appendIds (ids, mCells, listDeleted); appendIds (ids, mReferenceables, listDeleted); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index e900bb10f..1a9eae2d2 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "../filter/filter.hpp" @@ -48,12 +49,16 @@ namespace CSMWorld IdCollection mRegions; IdCollection mBirthsigns; IdCollection mSpells; + IdCollection mTopics; + IdCollection mJournals; IdCollection mCells; RefIdCollection mReferenceables; RefCollection mRefs; IdCollection mFilters; std::vector mModels; std::map mModelIndex; + std::string mAuthor; + std::string mDescription; // not implemented Data (const Data&); @@ -66,6 +71,8 @@ namespace CSMWorld bool listDeleted); ///< Append all IDs from collection to \a ids. + static int count (RecordBase::State state, const CollectionBase& collection); + public: Data(); @@ -116,6 +123,14 @@ namespace CSMWorld IdCollection& getSpells(); + const IdCollection& getTopics() const; + + IdCollection& getTopics(); + + const IdCollection& getJournals() const; + + IdCollection& getJournals(); + const IdCollection& getCells() const; IdCollection& getCells(); @@ -141,8 +156,10 @@ namespace CSMWorld void merge(); ///< Merge modified into base. - void loadFile (const boost::filesystem::path& path, bool base); + void loadFile (const boost::filesystem::path& path, bool base, bool project); ///< Merging content of a file into base or modified. + /// + /// \param project load project file instead of content file bool hasId (const std::string& id) const; @@ -151,6 +168,17 @@ namespace CSMWorld /// /// \param listDeleted include deleted record in the list + int count (RecordBase::State state) const; + ///< Return number of top-level records with the given \a state. + + void setDescription (const std::string& description); + + std::string getDescription() const; + + void setAuthor (const std::string& author); + + std::string getAuthor() const; + signals: void idListChanged(); diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 04e65eea7..0d723bef1 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -7,21 +7,24 @@ namespace CSMWorld { - /// \brief Single type collection of top level records template > class IdCollection : public Collection { public: - void load (ESM::ESMReader& reader, bool base, - UniversalId::Type type = UniversalId::Type_None); - ///< \param type Will be ignored, unless the collection supports multiple record types + void load (ESM::ESMReader& reader, bool base); + + void load (const ESXRecordT& record, bool base); + + bool tryDelete (const std::string& id); + ///< Try deleting \a id. If the id does not exist or can't be deleted the call is ignored. + /// + /// \return Has the ID been deleted? }; template - void IdCollection::load (ESM::ESMReader& reader, bool base, - UniversalId::Type type) + void IdCollection::load (ESM::ESMReader& reader, bool base) { std::string id = reader.getHNOString ("NAME"); @@ -56,30 +59,62 @@ namespace CSMWorld IdAccessorT().getId (record) = id; record.load (reader); - int index = this->searchId (IdAccessorT().getId (record)); + load (record, base); + } + } - if (index==-1) - { - // new record - Record record2; - record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; - (base ? record2.mBase : record2.mModified) = record; + template + void IdCollection::load (const ESXRecordT& record, bool base) + { + int index = this->searchId (IdAccessorT().getId (record)); - this->appendRecord (record2); - } + if (index==-1) + { + // new record + Record record2; + record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + (base ? record2.mBase : record2.mModified) = record; + + this->appendRecord (record2); + } + else + { + // old record + Record record2 = Collection::getRecord (index); + + if (base) + record2.mBase = record; else - { - // old record - Record record2 = Collection::getRecord (index); + record2.setModified (record); - if (base) - record2.mBase = record; - else - record2.setModified (record); + this->setRecord (index, record2); + } + } - this->setRecord (index, record2); - } + template + bool IdCollection::tryDelete (const std::string& id) + { + int index = this->searchId (id); + + if (index==-1) + return false; + + Record record = Collection::getRecord (index); + + if (record.isDeleted()) + return false; + + if (record.mState==RecordBase::State_ModifiedOnly) + { + Collection::removeRows (index, 1); } + else + { + record.mState = RecordBase::State_Deleted; + setRecord (index, record); + } + + return true; } } diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index baaf75289..b7b1a9db0 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -161,21 +161,10 @@ const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const { - int columns = mIdCollection->getColumns(); - - for (int i=0; igetColumn (i).mColumnId==id) - return i; - - return -1; + return mIdCollection->searchColumnIndex (id); } int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const { - int index = searchColumnIndex (id); - - if (index==-1) - throw std::logic_error ("invalid column index"); - - return index; + return mIdCollection->findColumnIndex (id); } \ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index cda2711cc..3a6f70d31 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -539,3 +539,8 @@ std::vector CSMWorld::RefIdCollection::getIds (bool listDeleted) co { return mData.getIds (listDeleted); } + +void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const +{ + mData.save (index, writer); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 22f83150d..a479735db 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -9,6 +9,11 @@ #include "collectionbase.hpp" #include "refiddata.hpp" +namespace ESM +{ + class ESMWriter; +} + namespace CSMWorld { class RefIdAdapter; @@ -94,6 +99,8 @@ namespace CSMWorld ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list + + void save (int index, ESM::ESMWriter& writer) const; }; } diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 9457937f1..8f59b0fe7 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -218,3 +218,16 @@ std::vector CSMWorld::RefIdData::getIds (bool listDeleted) const return ids; } + +void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const +{ + LocalIndex localIndex = globalToLocalIndex (index); + + std::map::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); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index e221fbc7c..9595ab23b 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "record.hpp" #include "universalid.hpp" @@ -51,6 +52,8 @@ namespace CSMWorld virtual void erase (int index, int count) = 0; virtual std::string getId (int index) const = 0; + + virtual void save (int index, ESM::ESMWriter& writer) const = 0; }; template @@ -71,6 +74,8 @@ namespace CSMWorld virtual void erase (int index, int count); virtual std::string getId (int index) const; + + virtual void save (int index, ESM::ESMWriter& writer) const; }; template @@ -123,6 +128,31 @@ namespace CSMWorld return mContainer.at (index).get().mId; } + template + void RefIdDataContainer::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 (&mContainer.at (index).mModified.sRecordId)[i]; + + writer.startRecord (type); + writer.writeHNCString ("NAME", getId (index)); + mContainer.at (index).mModified.save (writer); + writer.endRecord (type); + } + else if (state==CSMWorld::RecordBase::State_Deleted) + { + /// \todo write record with delete flag + } + } + + class RefIdData { public: @@ -187,6 +217,8 @@ namespace CSMWorld ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list + + void save (int index, ESM::ESMWriter& writer) const; }; } diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index c9edd0c16..6201a3cda 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -29,6 +29,8 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Referenceables", 0 }, @@ -54,6 +56,8 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":./land.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":./birthsign.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 246640733..ffd99e572 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -87,6 +87,10 @@ namespace CSMWorld Type_RegionMap, Type_Filter, Type_Filters, + Type_Topics, + Type_Topic, + Type_Journals, + Type_Journal, Type_Scene }; diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index e82cc30cb..0bf7c6951 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -128,3 +128,8 @@ void CSVDoc::FileDialog::slotRejected() emit rejected(); close(); } + +void CSVDoc::FileDialog::createNewFile() +{ + emit createNewFile (mAdjusterWidget->getPath()); +} diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 3b93f42a8..be268f372 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -55,7 +55,7 @@ namespace CSVDoc signals: void openFiles(); - void createNewFile(); + void createNewFile (const boost::filesystem::path& savePath); void signalUpdateCreateButton (bool, int); void signalUpdateCreateButtonFlags(int); diff --git a/apps/opencs/view/doc/newgame.hpp b/apps/opencs/view/doc/newgame.hpp index aa97682ff..9ad7ea169 100644 --- a/apps/opencs/view/doc/newgame.hpp +++ b/apps/opencs/view/doc/newgame.hpp @@ -6,7 +6,10 @@ #include #include +#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED +#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED Q_DECLARE_METATYPE (boost::filesystem::path) +#endif class QPushButton; diff --git a/apps/opencs/view/doc/startup.cpp b/apps/opencs/view/doc/startup.cpp index 4cc64f2df..5d59492c6 100644 --- a/apps/opencs/view/doc/startup.cpp +++ b/apps/opencs/view/doc/startup.cpp @@ -104,6 +104,17 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2) layout->addWidget (createButtons()); layout->addWidget (createTools()); + /// \todo remove this label once loading and saving are fully implemented + QLabel *warning = new QLabel ("WARNING:

OpenCS is in alpha stage.
The code for loading and saving is incomplete.
This version of OpenCS is only a preview.
Do NOT use it for real editing!
You will lose records both on loading and on saving.

Please note:
If you lose data and come to the OpenMW forum to complain,
we will mock you.
"); + + QFont font; + font.setPointSize (12); + font.setBold (true); + + warning->setFont (font); + + layout->addWidget (warning, 1); + setLayout (layout); QRect scr = QApplication::desktop()->screenGeometry(); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index b29250d20..5713449f2 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -163,6 +163,14 @@ void CSVDoc::View::setupMechanicsMenu() QAction *spells = new QAction (tr ("Spells"), this); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); mechanics->addAction (spells); + + QAction *topics = new QAction (tr ("Topics"), this); + connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView())); + mechanics->addAction (topics); + + QAction *journals = new QAction (tr ("Journals"), this); + connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView())); + mechanics->addAction (journals); } void CSVDoc::View::setupAssetsMenu() @@ -412,6 +420,16 @@ void CSVDoc::View::addSceneSubView() addSubView (CSMWorld::UniversalId::Type_Scene); } +void CSVDoc::View::addTopicsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Topics); +} + +void CSVDoc::View::addJournalsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Journals); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 6f3c38daa..2a31d9d80 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -166,6 +166,10 @@ namespace CSVDoc void addSceneSubView(); + void addTopicsSubView(); + + void addJournalsSubView(); + void toggleShowStatusBar (bool show); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 83cd93e5d..a4849795b 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -74,7 +74,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) { CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false }, { CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false }, { CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false }, - { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false } + { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false }, + { CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false } }; for (std::size_t i=0; icurrentIndex()) @@ -28,6 +33,15 @@ std::string CSVFilter::FilterCreator::getId() const return getNamespace() + GenericCreator::getId(); } +void CSVFilter::FilterCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + int index = + dynamic_cast (*getData().getTableModel (getCollectionId())). + findColumnIndex (CSMWorld::Columns::ColumnId_Scope); + + command.addValue (index, mScope->currentIndex()); +} + CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) : GenericCreator (data, undoStack, id) @@ -39,7 +53,7 @@ CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoS mScope->addItem ("Project"); mScope->addItem ("Session"); - /// \ŧodo re-enable for OpenMW 1.1 + /// \todo re-enable for OpenMW 1.1 // mScope->addItem ("Content"); connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int))); diff --git a/apps/opencs/view/filter/filtercreator.hpp b/apps/opencs/view/filter/filtercreator.hpp index 82d38d22c..437d01c8d 100644 --- a/apps/opencs/view/filter/filtercreator.hpp +++ b/apps/opencs/view/filter/filtercreator.hpp @@ -25,6 +25,8 @@ namespace CSVFilter virtual std::string getId() const; + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + public: FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, diff --git a/apps/opencs/view/world/dialoguecreator.cpp b/apps/opencs/view/world/dialoguecreator.cpp new file mode 100644 index 000000000..c16214283 --- /dev/null +++ b/apps/opencs/view/world/dialoguecreator.cpp @@ -0,0 +1,35 @@ + +#include "dialoguecreator.hpp" + +#include + +#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 (*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); +} \ No newline at end of file diff --git a/apps/opencs/view/world/dialoguecreator.hpp b/apps/opencs/view/world/dialoguecreator.hpp new file mode 100644 index 000000000..26f866909 --- /dev/null +++ b/apps/opencs/view/world/dialoguecreator.hpp @@ -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 diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 0e3465b38..3d98cf73c 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -14,6 +14,7 @@ #include "referenceablecreator.hpp" #include "referencecreator.hpp" #include "scenesubview.hpp" +#include "dialoguecreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -53,6 +54,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_References, new CSVDoc::SubViewFactoryWithCreator >); + manager.add (CSMWorld::UniversalId::Type_Topics, + new CSVDoc::SubViewFactoryWithCreator); + + manager.add (CSMWorld::UniversalId::Type_Journal, + new CSVDoc::SubViewFactoryWithCreator); + // Subviews for editing/viewing individual records manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 6167c084a..a58eb873f 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -87,19 +87,33 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const { QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + // check record state CSMWorld::RecordBase::State state = static_cast ( mModel->data (mModel->index (index.row(), 1)).toInt()); - if (state!=CSMWorld::RecordBase::State_Deleted) - { - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + if (state==CSMWorld::RecordBase::State_Deleted) + continue; - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); + // check other columns (only relevant for a subset of the tables) + int dialogueTypeIndex = + mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); - deletableIds.push_back (id); + if (dialogueTypeIndex!=-1) + { + int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt(); + + if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) + continue; } + + // add the id to the collection + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + deletableIds.push_back (id); } } diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b367e2a1e..807b1b5ff 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -58,6 +58,7 @@ add_openmw_dir (mwworld cells localscripts customdata weather inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor + contentloader esmloader omwloader ) add_openmw_dir (mwclass diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e1fd3a0af..a5e400b20 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -261,34 +261,14 @@ void OMW::Engine::setCell (const std::string& cellName) mCellName = cellName; } -// Set master file (esm) -// - If the given name does not have an extension, ".esm" is added automatically - -void OMW::Engine::addMaster (const std::string& master) +void OMW::Engine::addContentFile(const std::string& file) { - mMaster.push_back(master); - std::string &str = mMaster.back(); - - // Append .esm if not already there - std::string::size_type sep = str.find_last_of ("."); - if (sep == std::string::npos) - { - str += ".esm"; - } -} + if (file.find_last_of(".") == std::string::npos) + { + throw std::runtime_error("Missing extension in content file!"); + } -// Add plugin file (esp) -void OMW::Engine::addPlugin (const std::string& plugin) -{ - mPlugins.push_back(plugin); - std::string &str = mPlugins.back(); - - // Append .esp if not already there - std::string::size_type sep = str.find_last_of ("."); - if (sep == std::string::npos) - { - str += ".esp"; - } + mContentFiles.push_back(file); } void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) @@ -401,7 +381,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mEnvironment.setWindowManager (window); // Create the world - mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, + mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles, mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap, mActivationDistanceOverride)); MWBase::Environment::get().getWorld()->setupPlayer(); @@ -416,8 +396,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) //Load translation data mTranslationDataStorage.setEncoder(mEncoder); - for (size_t i = 0; i < mMaster.size(); i++) - mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster[i]); + for (size_t i = 0; i < mContentFiles.size(); i++) + mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]); Compiler::registerExtensions (mExtensions); @@ -482,7 +462,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) void OMW::Engine::go() { assert (!mCellName.empty()); - assert (!mMaster.empty()); + assert (!mContentFiles.empty()); assert (!mOgre); Settings::Manager settings; diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 665b0094c..553d29068 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -68,8 +68,7 @@ namespace OMW boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; std::string mCellName; - std::vector mMaster; - std::vector mPlugins; + std::vector mContentFiles; int mFpsLevel; bool mVerboseScripts; bool mNewGame; @@ -135,13 +134,11 @@ namespace OMW /// Set start cell name (only interiors for now) void setCell(const std::string& cellName); - /// Set master file (esm) - /// - If the given name does not have an extension, ".esm" is added automatically - void addMaster(const std::string& master); - - /// Same as "addMaster", but for plugin files (esp) - /// - If the given name does not have an extension, ".esp" is added automatically - void addPlugin(const std::string& plugin); + /** + * @brief addContentFile - Adds content file (ie. esm/esp, or omwgame/omwaddon) to the content files container. + * @param file - filename (extension is required) + */ + void addContentFile(const std::string& file); /// Enable fps counter void showFPS(int level); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 27afd734a..33f740b31 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -110,11 +110,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("start", bpo::value()->default_value("Beshara"), "set initial cell") - ("master", bpo::value()->default_value(StringsVector(), "") - ->multitoken(), "master file(s)") - - ("plugin", bpo::value()->default_value(StringsVector(), "") - ->multitoken(), "plugin file(s)") + ("content", bpo::value()->default_value(StringsVector(), "") + ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") ("anim-verbose", bpo::value()->implicit_value(true) ->default_value(false), "output animation indices files") @@ -152,8 +149,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("activate-dist", bpo::value ()->default_value (-1), "activation distance override"); - ; - bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) .options(desc).allow_unregistered().run(); @@ -211,29 +206,18 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setResourceDir(variables["resources"].as()); - // master and plugin - StringsVector master = variables["master"].as(); - if (master.empty()) + StringsVector content = variables["content"].as(); + if (content.empty()) { - std::cout << "No master file given. Aborting...\n"; - return false; + std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl; + return false; } - StringsVector plugin = variables["plugin"].as(); - // Removed check for 255 files, which would be the hard-coded limit in Morrowind. - // I'll keep the following variable in, maybe we can use it for something different. - // Say, a feedback like "loading file x/cnt". - // Commenting this out for now to silence compiler warning. - //int cnt = master.size() + plugin.size(); - - // Prepare loading master/plugin files (i.e. send filenames to engine) - for (std::vector::size_type i = 0; i < master.size(); i++) - { - engine.addMaster(master[i]); - } - for (std::vector::size_type i = 0; i < plugin.size(); i++) + StringsVector::const_iterator it(content.begin()); + StringsVector::const_iterator end(content.end()); + for (; it != end; ++it) { - engine.addPlugin(plugin[i]); + engine.addContentFile(*it); } // startup-settings diff --git a/apps/openmw/mwworld/contentloader.hpp b/apps/openmw/mwworld/contentloader.hpp new file mode 100644 index 000000000..c57935c90 --- /dev/null +++ b/apps/openmw/mwworld/contentloader.hpp @@ -0,0 +1,35 @@ +#ifndef CONTENTLOADER_HPP +#define CONTENTLOADER_HPP + +#include +#include + +#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 */ diff --git a/apps/openmw/mwworld/esmloader.cpp b/apps/openmw/mwworld/esmloader.cpp new file mode 100644 index 000000000..1b8880d37 --- /dev/null +++ b/apps/openmw/mwworld/esmloader.cpp @@ -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& 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 */ diff --git a/apps/openmw/mwworld/esmloader.hpp b/apps/openmw/mwworld/esmloader.hpp new file mode 100644 index 000000000..d799c3f15 --- /dev/null +++ b/apps/openmw/mwworld/esmloader.hpp @@ -0,0 +1,34 @@ +#ifndef ESMLOADER_HPP +#define ESMLOADER_HPP + +#include + +#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& readers, + ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener); + + void load(const boost::filesystem::path& filepath, int& index); + + private: + std::vector& mEsm; + MWWorld::ESMStore& mStore; + ToUTF8::Utf8Encoder* mEncoder; +}; + +} /* namespace MWWorld */ + +#endif // ESMLOADER_HPP diff --git a/apps/openmw/mwworld/omwloader.cpp b/apps/openmw/mwworld/omwloader.cpp new file mode 100644 index 000000000..8562a4fe0 --- /dev/null +++ b/apps/openmw/mwworld/omwloader.cpp @@ -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 */ + diff --git a/apps/openmw/mwworld/omwloader.hpp b/apps/openmw/mwworld/omwloader.hpp new file mode 100644 index 000000000..cb9faa430 --- /dev/null +++ b/apps/openmw/mwworld/omwloader.hpp @@ -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 */ diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9635feaf3..6a4a380d6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,4 +1,11 @@ #include "worldimp.hpp" +#ifdef _WIN32 +#include +#elif defined HAVE_UNORDERED_MAP +#include +#else +#include +#endif #include @@ -31,6 +38,10 @@ #include "containerstore.hpp" #include "inventorystore.hpp" +#include "contentloader.hpp" +#include "esmloader.hpp" +#include "omwloader.hpp" + using namespace Ogre; namespace @@ -80,6 +91,38 @@ namespace namespace MWWorld { + struct GameContentLoader : public ContentLoader + { + GameContentLoader(Loading::Listener& listener) + : ContentLoader(listener) + { + } + + bool addLoader(const std::string& extension, ContentLoader* loader) + { + return mLoaders.insert(std::make_pair(extension, loader)).second; + } + + void load(const boost::filesystem::path& filepath, int& index) + { + LoadersContainer::iterator it(mLoaders.find(filepath.extension().string())); + if (it != mLoaders.end()) + { + it->second->load(filepath, index); + } + else + { + std::string msg("Cannot load file: "); + msg += filepath.string(); + throw std::runtime_error(msg.c_str()); + } + } + + private: + typedef std::tr1::unordered_map LoadersContainer; + LoadersContainer mLoaders; + }; + Ptr World::getPtrViaHandle (const std::string& handle, Ptr::CellStore& cell) { if (MWWorld::LiveCellRef *ref = @@ -163,7 +206,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::vector& master, const std::vector& plugins, + const std::vector& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), @@ -181,44 +224,22 @@ namespace MWWorld mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); - int idx = 0; // NOTE: We might need to reserve one more for the running game / save. - mEsm.resize(master.size() + plugins.size()); + mEsm.resize(contentFiles.size()); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); listener->loadingOn(); - for (std::vector::size_type i = 0; i < master.size(); i++, idx++) - { - boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master[i])); - std::cout << "Loading ESM " << masterPath.string() << "\n"; - listener->setLabel(masterPath.filename().string()); + GameContentLoader gameContentLoader(*listener); + EsmLoader esmLoader(mStore, mEsm, encoder, *listener); + OmwLoader omwLoader(*listener); - // This parses the ESM file - ESM::ESMReader lEsm; - lEsm.setEncoder(encoder); - lEsm.setIndex(idx); - lEsm.setGlobalReaderList(&mEsm); - lEsm.open (masterPath.string()); - mEsm[idx] = lEsm; - mStore.load (mEsm[idx], listener); - } - - for (std::vector::size_type i = 0; i < plugins.size(); i++, idx++) - { - boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i])); + gameContentLoader.addLoader(".esm", &esmLoader); + gameContentLoader.addLoader(".esp", &esmLoader); + gameContentLoader.addLoader(".omwgame", &omwLoader); + gameContentLoader.addLoader(".omwaddon", &omwLoader); - std::cout << "Loading ESP " << pluginPath.string() << "\n"; - listener->setLabel(pluginPath.filename().string()); + loadContentFiles(fileCollections, contentFiles, gameContentLoader); - // This parses the ESP file - ESM::ESMReader lEsm; - lEsm.setEncoder(encoder); - lEsm.setIndex(idx); - lEsm.setGlobalReaderList(&mEsm); - lEsm.open (pluginPath.string()); - mEsm[idx] = lEsm; - mStore.load (mEsm[idx], listener); - } listener->loadingOff(); // insert records that may not be present in all versions of MW @@ -1983,4 +2004,19 @@ namespace MWWorld return mGodMode; } + void World::loadContentFiles(const Files::Collections& fileCollections, + const std::vector& content, ContentLoader& contentLoader) + { + std::vector::const_iterator it(content.begin()); + std::vector::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); + } + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e275756e8..aadf7ce98 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -14,6 +14,8 @@ #include "../mwbase/world.hpp" +#include "contentloader.hpp" + namespace Ogre { class Vector3; @@ -41,6 +43,8 @@ namespace MWRender class Animation; } +struct ContentLoader; + namespace MWWorld { class WeatherManager; @@ -113,6 +117,15 @@ namespace MWWorld void ensureNeededRecords(); + /** + * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon) + * @param fileCollections- Container which holds content file names and their paths + * @param content - Container which holds content file names + * @param contentLoader - + */ + void loadContentFiles(const Files::Collections& fileCollections, + const std::vector& content, ContentLoader& contentLoader); + int mPlayIntro; bool mTeleportEnabled; @@ -122,7 +135,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::vector& master, const std::vector& plugins, + const std::vector& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride); diff --git a/components/esm/aipackage.cpp b/components/esm/aipackage.cpp index 1440dbd13..cf4951de7 100644 --- a/components/esm/aipackage.cpp +++ b/components/esm/aipackage.cpp @@ -44,9 +44,9 @@ namespace ESM } } - void AIPackageList::save(ESMWriter &esm) + void AIPackageList::save(ESMWriter &esm) const { - typedef std::vector::iterator PackageIter; + typedef std::vector::const_iterator PackageIter; for (PackageIter it = mList.begin(); it != mList.end(); ++it) { switch (it->mType) { case AI_Wander: diff --git a/components/esm/aipackage.hpp b/components/esm/aipackage.hpp index 38499b2dd..b06cb529a 100644 --- a/components/esm/aipackage.hpp +++ b/components/esm/aipackage.hpp @@ -93,7 +93,7 @@ namespace ESM /// it needs to use retSubName() if needed. But, hey, there /// is only one field left (XSCL) and only two records uses AI void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 95cf24d33..e91059b26 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -3,7 +3,7 @@ #include "esmwriter.hpp" -void ESM::CellRef::save(ESMWriter &esm) +void ESM::CellRef::save(ESMWriter &esm) const { esm.writeHNT("FRMR", mRefnum); esm.writeHNCString("NAME", mRefID); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 31889914c..47cb0b99e 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -83,7 +83,7 @@ namespace ESM // Position and rotation of this object within the cell Position mPos; - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); }; diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index bd86f9ba0..dd7ebfe93 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -36,6 +36,7 @@ struct Position enum RecNameInts { + // format 0 / legacy REC_ACTI = 0x49544341, REC_ALCH = 0x48434c41, REC_APPA = 0x41505041, @@ -80,7 +81,10 @@ enum RecNameInts REC_SPEL = 0x4c455053, REC_SSCR = 0x52435353, REC_STAT = 0x54415453, - REC_WEAP = 0x50414557 + REC_WEAP = 0x50414557, + + // format 1 + REC_FILT = 0x544C4946 }; } diff --git a/components/esm/effectlist.cpp b/components/esm/effectlist.cpp index 88f87d6e2..bc126846b 100644 --- a/components/esm/effectlist.cpp +++ b/components/esm/effectlist.cpp @@ -14,9 +14,9 @@ void EffectList::load(ESMReader &esm) } } -void EffectList::save(ESMWriter &esm) +void EffectList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) { + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNT("ENAM", *it, 24); } } diff --git a/components/esm/effectlist.hpp b/components/esm/effectlist.hpp index 9f5b87aed..04adcc5cd 100644 --- a/components/esm/effectlist.hpp +++ b/components/esm/effectlist.hpp @@ -35,9 +35,9 @@ namespace ESM std::vector mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; - + } #endif diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 3ea6bd350..f39aa2b89 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -2,185 +2,188 @@ #include #include -#include - -bool count = true; +#include namespace ESM { + ESMWriter::ESMWriter() : mRecordCount (0), mCounting (true) {} -int ESMWriter::getVersion() -{ - return mHeader.mData.version; -} - -void ESMWriter::setVersion(int ver) -{ - mHeader.mData.version = ver; -} + unsigned int ESMWriter::getVersion() const + { + return mHeader.mData.version; + } -void ESMWriter::setAuthor(const std::string& auth) -{ - mHeader.mData.author.assign (auth); -} + void ESMWriter::setVersion(unsigned int ver) + { + mHeader.mData.version = ver; + } -void ESMWriter::setDescription(const std::string& desc) -{ - mHeader.mData.desc.assign (desc); -} + void ESMWriter::setAuthor(const std::string& auth) + { + mHeader.mData.author.assign (auth); + } -void ESMWriter::setRecordCount (int count) -{ - mHeader.mData.records = count; -} + void ESMWriter::setDescription(const std::string& desc) + { + mHeader.mData.desc.assign (desc); + } -void ESMWriter::setFormat (int format) -{ - mHeader.mFormat = format; -} + void ESMWriter::setRecordCount (int count) + { + mHeader.mData.records = count; + } -void ESMWriter::addMaster(const std::string& name, uint64_t size) -{ - Header::MasterData d; - d.name = name; - d.size = size; - mHeader.mMaster.push_back(d); -} + void ESMWriter::setFormat (int format) + { + mHeader.mFormat = format; + } -void ESMWriter::save(const std::string& file) -{ - std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc); - save(fs); -} + void ESMWriter::clearMaster() + { + mHeader.mMaster.clear(); + } -void ESMWriter::save(std::ostream& file) -{ - m_recordCount = 0; - m_stream = &file; + void ESMWriter::addMaster(const std::string& name, uint64_t size) + { + Header::MasterData d; + d.name = name; + d.size = size; + mHeader.mMaster.push_back(d); + } - startRecord("TES3", 0); + void ESMWriter::save(const std::string& file) + { + std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc); + save(fs); + } - mHeader.save (*this); + void ESMWriter::save(std::ostream& file) + { + mRecordCount = 0; + mRecords.clear(); + mCounting = true; + mStream = &file; - endRecord("TES3"); -} + startRecord("TES3", 0); -void ESMWriter::close() -{ - m_stream->flush(); + mHeader.save (*this); - if (!m_records.empty()) - throw "Unclosed record remaining"; -} - -void ESMWriter::startRecord(const std::string& name, uint32_t flags) -{ - m_recordCount++; - - writeName(name); - RecordData rec; - rec.name = name; - rec.position = m_stream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - writeT(0); // Unused header? - writeT(flags); - m_records.push_back(rec); - - assert(m_records.back().size == 0); -} + endRecord("TES3"); + } -void ESMWriter::startSubRecord(const std::string& name) -{ - writeName(name); - RecordData rec; - rec.name = name; - rec.position = m_stream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - m_records.push_back(rec); - - assert(m_records.back().size == 0); -} + void ESMWriter::close() + { + if (!mRecords.empty()) + throw std::runtime_error ("Unclosed record remaining"); + } -void ESMWriter::endRecord(const std::string& name) -{ - RecordData rec = m_records.back(); - assert(rec.name == name); - m_records.pop_back(); + void ESMWriter::startRecord(const std::string& name, uint32_t flags) + { + mRecordCount++; + + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + writeT(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(0); // Size goes here + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } - count = false; - write((char*)&rec.size, sizeof(int)); - count = true; + void ESMWriter::endRecord(const std::string& name) + { + RecordData rec = mRecords.back(); + assert(rec.name == name); + mRecords.pop_back(); - m_stream->seekp(0, std::ios::end); + mStream->seekp(rec.position); -} + mCounting = false; + write (reinterpret_cast (&rec.size), sizeof(int)); + mCounting = true; -void ESMWriter::writeHNString(const std::string& name, const std::string& data) -{ - startSubRecord(name); - writeHString(data); - endRecord(name); -} + mStream->seekp(0, std::ios::end); -void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) -{ - assert(data.size() <= size); - startSubRecord(name); - writeHString(data); + } - if (data.size() < size) + void ESMWriter::writeHNString(const std::string& name, const std::string& data) { - for (size_t i = data.size(); i < size; ++i) - write("\0",1); + startSubRecord(name); + writeHString(data); + endRecord(name); } - endRecord(name); -} - -void ESMWriter::writeHString(const std::string& data) -{ - if (data.size() == 0) - write("\0", 1); - else + void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) { - // Convert to UTF8 and return - std::string ascii = m_encoder->getLegacyEnc(data); + assert(data.size() <= size); + startSubRecord(name); + writeHString(data); + + if (data.size() < size) + { + for (size_t i = data.size(); i < size; ++i) + write("\0",1); + } - write(ascii.c_str(), ascii.size()); + endRecord(name); } -} -void ESMWriter::writeHCString(const std::string& data) -{ - writeHString(data); - if (data.size() > 0 && data[data.size()-1] != '\0') - write("\0", 1); -} + void ESMWriter::writeHString(const std::string& data) + { + if (data.size() == 0) + write("\0", 1); + else + { + // Convert to UTF8 and return + std::string ascii = mEncoder->getLegacyEnc(data); + + write(ascii.c_str(), ascii.size()); + } + } -void ESMWriter::writeName(const std::string& name) -{ - assert((name.size() == 4 && name[3] != '\0')); - write(name.c_str(), name.size()); -} + void ESMWriter::writeHCString(const std::string& data) + { + writeHString(data); + if (data.size() > 0 && data[data.size()-1] != '\0') + write("\0", 1); + } -void ESMWriter::write(const char* data, size_t size) -{ - if (count && !m_records.empty()) + void ESMWriter::writeName(const std::string& name) { - for (std::list::iterator it = m_records.begin(); it != m_records.end(); ++it) - it->size += size; + assert((name.size() == 4 && name[3] != '\0')); + write(name.c_str(), name.size()); } - m_stream->write(data, size); -} + void ESMWriter::write(const char* data, size_t size) + { + if (mCounting && !mRecords.empty()) + { + for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) + it->size += size; + } -void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) -{ - m_encoder = encoder; -} + mStream->write(data, size); + } + void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) + { + mEncoder = encoder; + } } diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index be3ae33ab..104f97f90 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -13,92 +13,103 @@ namespace ESM { class ESMWriter { - struct RecordData - { - std::string name; - std::streampos position; - size_t size; + struct RecordData + { + std::string name; + std::streampos position; + size_t size; + }; + + public: + + ESMWriter(); + + unsigned int getVersion() const; + void setVersion(unsigned int ver = 0x3fa66666); + void setEncoder(ToUTF8::Utf8Encoder *encoding); + void setAuthor(const std::string& author); + void setDescription(const std::string& desc); + void setRecordCount (int count); + void setFormat (int format); + + void clearMaster(); + + void addMaster(const std::string& name, uint64_t size); + + void save(const std::string& file); + ///< Start saving a file by writing the TES3 header. + + void save(std::ostream& file); + ///< Start saving a file by writing the TES3 header. + + void close(); + ///< \note Does not close the stream. + + void writeHNString(const std::string& name, const std::string& data); + void writeHNString(const std::string& name, const std::string& data, size_t size); + void writeHNCString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHCString(data); + endRecord(name); + } + void writeHNOString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNString(name, data); + } + void writeHNOCString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNCString(name, data); + } + + template + void writeHNT(const std::string& name, const T& data) + { + startSubRecord(name); + writeT(data); + endRecord(name); + } + + template + void writeHNT(const std::string& name, const T& data, int size) + { + startSubRecord(name); + writeT(data, size); + endRecord(name); + } + + template + void writeT(const T& data) + { + write((char*)&data, sizeof(T)); + } + + template + 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 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 - void writeHNT(const std::string& name, const T& data) - { - startSubRecord(name); - writeT(data); - endRecord(name); - } - - template - void writeHNT(const std::string& name, const T& data, int size) - { - startSubRecord(name); - writeT(data, size); - endRecord(name); - } - - template - void writeT(const T& data) - { - write((char*)&data, sizeof(T)); - } - - template - 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 m_records; - std::ostream* m_stream; - std::streampos m_headerPos; - ToUTF8::Utf8Encoder* m_encoder; - int m_recordCount; - - Header mHeader; -}; - } + #endif diff --git a/components/esm/filter.cpp b/components/esm/filter.cpp index 7d4851a5f..a80427bbe 100644 --- a/components/esm/filter.cpp +++ b/components/esm/filter.cpp @@ -3,6 +3,9 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" + +unsigned int ESM::Filter::sRecordId = REC_FILT; void ESM::Filter::load (ESMReader& esm) { @@ -10,7 +13,7 @@ void ESM::Filter::load (ESMReader& esm) mDescription = esm.getHNString ("DESC"); } -void ESM::Filter::save (ESMWriter& esm) +void ESM::Filter::save (ESMWriter& esm) const { esm.writeHNCString ("FILT", mFilter); esm.writeHNCString ("DESC", mDescription); diff --git a/components/esm/filter.hpp b/components/esm/filter.hpp index 0fd564361..bc3dd7bdc 100644 --- a/components/esm/filter.hpp +++ b/components/esm/filter.hpp @@ -10,6 +10,8 @@ namespace ESM struct Filter { + static unsigned int sRecordId; + std::string mId; std::string mDescription; @@ -17,7 +19,7 @@ namespace ESM std::string mFilter; void load (ESMReader& esm); - void save (ESMWriter& esm); + void save (ESMWriter& esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadacti.cpp b/components/esm/loadacti.cpp index fd022af7e..6ba0df0b3 100644 --- a/components/esm/loadacti.cpp +++ b/components/esm/loadacti.cpp @@ -2,16 +2,19 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Activator::sRecordId = REC_ACTI; + void Activator::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); mName = esm.getHNString("FNAM"); mScript = esm.getHNOString("SCRI"); } -void Activator::save(ESMWriter &esm) +void Activator::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadacti.hpp b/components/esm/loadacti.hpp index a62990590..88f27de27 100644 --- a/components/esm/loadacti.hpp +++ b/components/esm/loadacti.hpp @@ -11,10 +11,12 @@ class ESMWriter; struct Activator { + static unsigned int sRecordId; + std::string mId, mName, mScript, mModel; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadalch.cpp b/components/esm/loadalch.cpp index dbb69c066..f6bfc6a11 100644 --- a/components/esm/loadalch.cpp +++ b/components/esm/loadalch.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Potion::sRecordId = REC_ALCH; + void Potion::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); @@ -14,7 +17,7 @@ void Potion::load(ESMReader &esm) esm.getHNT(mData, "ALDT", 12); mEffects.load(esm); } -void Potion::save(ESMWriter &esm) +void Potion::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("TEXT", mIcon); diff --git a/components/esm/loadalch.hpp b/components/esm/loadalch.hpp index 3ede85342..141765aa8 100644 --- a/components/esm/loadalch.hpp +++ b/components/esm/loadalch.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Potion { + static unsigned int sRecordId; + struct ALDTstruct { float mWeight; @@ -29,7 +31,7 @@ struct Potion EffectList mEffects; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadappa.cpp b/components/esm/loadappa.cpp index 4b8d2b763..29ea78acc 100644 --- a/components/esm/loadappa.cpp +++ b/components/esm/loadappa.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Apparatus::sRecordId = REC_APPA; + void Apparatus::load(ESMReader &esm) { // we will not treat duplicated subrecords as errors here @@ -28,7 +31,7 @@ void Apparatus::load(ESMReader &esm) } } -void Apparatus::save(ESMWriter &esm) +void Apparatus::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadappa.hpp b/components/esm/loadappa.hpp index ed9d335be..adc8e071f 100644 --- a/components/esm/loadappa.hpp +++ b/components/esm/loadappa.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Apparatus { + static unsigned int sRecordId; + enum AppaType { MortarPestle = 0, @@ -35,7 +37,7 @@ struct Apparatus std::string mId, mModel, mIcon, mScript, mName; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadarmo.cpp b/components/esm/loadarmo.cpp index e64c8705d..ec8ff4f20 100644 --- a/components/esm/loadarmo.cpp +++ b/components/esm/loadarmo.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -18,9 +19,9 @@ void PartReferenceList::load(ESMReader &esm) } } -void PartReferenceList::save(ESMWriter &esm) +void PartReferenceList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mParts.begin(); it != mParts.end(); ++it) + for (std::vector::const_iterator it = mParts.begin(); it != mParts.end(); ++it) { esm.writeHNT("INDX", it->mPart); esm.writeHNOString("BNAM", it->mMale); @@ -28,6 +29,8 @@ void PartReferenceList::save(ESMWriter &esm) } } +unsigned int Armor::sRecordId = REC_ARMO; + void Armor::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); @@ -39,7 +42,7 @@ void Armor::load(ESMReader &esm) mEnchant = esm.getHNOString("ENAM"); } -void Armor::save(ESMWriter &esm) +void Armor::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadarmo.hpp b/components/esm/loadarmo.hpp index eaef42be8..991f4e185 100644 --- a/components/esm/loadarmo.hpp +++ b/components/esm/loadarmo.hpp @@ -56,11 +56,13 @@ struct PartReferenceList std::vector mParts; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; struct Armor { + static unsigned int sRecordId; + enum Type { Helmet = 0, @@ -89,7 +91,7 @@ struct Armor std::string mId, mName, mModel, mIcon, mScript, mEnchant; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadbody.cpp b/components/esm/loadbody.cpp index e95a8a860..4015e6c91 100644 --- a/components/esm/loadbody.cpp +++ b/components/esm/loadbody.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int BodyPart::sRecordId = REC_BODY; + void BodyPart::load(ESMReader &esm) { @@ -12,7 +15,7 @@ void BodyPart::load(ESMReader &esm) mRace = esm.getHNString("FNAM"); esm.getHNT(mData, "BYDT", 4); } -void BodyPart::save(ESMWriter &esm) +void BodyPart::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mRace); diff --git a/components/esm/loadbody.hpp b/components/esm/loadbody.hpp index 3ad9b1b95..9623caa31 100644 --- a/components/esm/loadbody.hpp +++ b/components/esm/loadbody.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct BodyPart { + static unsigned int sRecordId; + enum MeshPart { MP_Head = 0, @@ -57,7 +59,7 @@ struct BodyPart std::string mId, mModel, mRace; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadbook.cpp b/components/esm/loadbook.cpp index 3a70ac786..c8b7e9478 100644 --- a/components/esm/loadbook.cpp +++ b/components/esm/loadbook.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Book::sRecordId = REC_BOOK; void Book::load(ESMReader &esm) { @@ -16,7 +18,7 @@ void Book::load(ESMReader &esm) mText = esm.getHNOString("TEXT"); mEnchant = esm.getHNOString("ENAM"); } -void Book::save(ESMWriter &esm) +void Book::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadbook.hpp b/components/esm/loadbook.hpp index 68042e246..f96fbd709 100644 --- a/components/esm/loadbook.hpp +++ b/components/esm/loadbook.hpp @@ -14,6 +14,8 @@ class ESMWriter; struct Book { + static unsigned int sRecordId; + struct BKDTstruct { float mWeight; @@ -25,7 +27,7 @@ struct Book std::string mId; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadbsgn.cpp b/components/esm/loadbsgn.cpp index cb500f674..55e1e7f65 100644 --- a/components/esm/loadbsgn.cpp +++ b/components/esm/loadbsgn.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int BirthSign::sRecordId = REC_BSGN; void BirthSign::load(ESMReader &esm) { @@ -15,7 +17,7 @@ void BirthSign::load(ESMReader &esm) mPowers.load(esm); } -void BirthSign::save(ESMWriter &esm) +void BirthSign::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); esm.writeHNOCString("TNAM", mTexture); diff --git a/components/esm/loadbsgn.hpp b/components/esm/loadbsgn.hpp index 434ddf68e..9f9435c8f 100644 --- a/components/esm/loadbsgn.hpp +++ b/components/esm/loadbsgn.hpp @@ -13,13 +13,15 @@ class ESMWriter; struct BirthSign { + static unsigned int sRecordId; + std::string mId, mName, mDescription, mTexture; // List of powers and abilities that come with this birth sign. SpellList mPowers; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 9793391ed..c22c1b22b 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -7,9 +7,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Cell::sRecordId = REC_CELL; /// Some overloaded compare operators. bool operator==(const MovedCellRef& ref, int pRefnum) @@ -89,7 +91,7 @@ void Cell::postLoad(ESMReader &esm) esm.skipRecord(); } -void Cell::save(ESMWriter &esm) +void Cell::save(ESMWriter &esm) const { esm.writeHNT("DATA", mData, 12); if (mData.mFlags & Interior) diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 51288b291..61d586b9d 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -16,7 +16,6 @@ namespace MWWorld namespace ESM { - class ESMReader; class ESMWriter; @@ -55,6 +54,8 @@ typedef std::list CellRefTracker; */ struct Cell { + static unsigned int sRecordId; + enum Flags { Interior = 0x01, // Interior cell @@ -102,7 +103,7 @@ struct Cell // This method is left in for compatibility with esmtool. Parsing moved references currently requires // passing ESMStore, bit it does not know about this parameter, so we do it this way. void load(ESMReader &esm, bool saveContext = true); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; bool isExterior() const { diff --git a/components/esm/loadclas.cpp b/components/esm/loadclas.cpp index bdc461462..33489eec4 100644 --- a/components/esm/loadclas.cpp +++ b/components/esm/loadclas.cpp @@ -4,9 +4,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Class::sRecordId = REC_CLAS; const Class::Specialization Class::sSpecializationIds[3] = { Class::Combat, @@ -47,7 +49,7 @@ void Class::load(ESMReader &esm) mDescription = esm.getHNOString("DESC"); } -void Class::save(ESMWriter &esm) +void Class::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); esm.writeHNT("CLDT", mData, 60); diff --git a/components/esm/loadclas.hpp b/components/esm/loadclas.hpp index 4f85e6ee8..3e489bb58 100644 --- a/components/esm/loadclas.hpp +++ b/components/esm/loadclas.hpp @@ -17,6 +17,8 @@ class ESMWriter; // class struct Class { + static unsigned int sRecordId; + enum AutoCalc { Weapon = 0x00001, @@ -70,7 +72,7 @@ struct Class CLDTstruct mData; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadclot.cpp b/components/esm/loadclot.cpp index 10b00970f..d64564d77 100644 --- a/components/esm/loadclot.cpp +++ b/components/esm/loadclot.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Clothing::sRecordId = REC_CLOT; void Clothing::load(ESMReader &esm) { @@ -20,7 +22,7 @@ void Clothing::load(ESMReader &esm) mEnchant = esm.getHNOString("ENAM"); } -void Clothing::save(ESMWriter &esm) +void Clothing::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadclot.hpp b/components/esm/loadclot.hpp index 816d03cb2..50896622a 100644 --- a/components/esm/loadclot.hpp +++ b/components/esm/loadclot.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Clothing { + static unsigned int sRecordId; + enum Type { Pants = 0, @@ -45,7 +47,7 @@ struct Clothing std::string mId, mName, mModel, mIcon, mEnchant, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadcont.cpp b/components/esm/loadcont.cpp index 853c8bd50..7bdf9f05b 100644 --- a/components/esm/loadcont.cpp +++ b/components/esm/loadcont.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -16,14 +17,16 @@ void InventoryList::load(ESMReader &esm) } } -void InventoryList::save(ESMWriter &esm) +void InventoryList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNT("NPCO", *it, 36); } } + unsigned int Container::sRecordId = REC_CONT; + void Container::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); @@ -41,7 +44,7 @@ void Container::load(ESMReader &esm) mInventory.load(esm); } -void Container::save(ESMWriter &esm) +void Container::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadcont.hpp b/components/esm/loadcont.hpp index b2bbab73d..2808b67b5 100644 --- a/components/esm/loadcont.hpp +++ b/components/esm/loadcont.hpp @@ -27,11 +27,13 @@ struct InventoryList std::vector mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; struct Container { + static unsigned int sRecordId; + enum Flags { Organic = 1, // Objects cannot be placed in this container @@ -46,7 +48,7 @@ struct Container InventoryList mInventory; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index 86d05b8a5..650de0801 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Creature::sRecordId = REC_CREA; + void Creature::load(ESMReader &esm) { mPersistent = esm.getRecordFlags() & 0x0400; @@ -35,7 +38,7 @@ void Creature::load(ESMReader &esm) esm.skipRecord(); } -void Creature::save(ESMWriter &esm) +void Creature::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("CNAM", mOriginal); diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 279e2ea3f..99c4f5225 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct Creature { + static unsigned int sRecordId; + // Default is 0x48? enum Flags { @@ -86,7 +88,7 @@ struct Creature AIPackageList mAiPackage; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadcrec.hpp b/components/esm/loadcrec.hpp index 6904df15a..280739aca 100644 --- a/components/esm/loadcrec.hpp +++ b/components/esm/loadcrec.hpp @@ -17,6 +17,8 @@ class ESMWriter; /// Changes a creature struct LoadCREC { + static unsigned int sRecordId; + std::string mId; void load(ESMReader &esm) @@ -24,7 +26,7 @@ struct LoadCREC esm.skipRecord(); } - void save(ESMWriter &esm) + void save(ESMWriter &esm) const { } }; @@ -39,7 +41,7 @@ struct LoadCNTC esm.skipRecord(); } - void save(ESMWriter &esm) + void save(ESMWriter &esm) const { } }; diff --git a/components/esm/loaddial.cpp b/components/esm/loaddial.cpp index fb50d5e9f..fb43ee858 100644 --- a/components/esm/loaddial.cpp +++ b/components/esm/loaddial.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Dialogue::sRecordId = REC_DIAL; void Dialogue::load(ESMReader &esm) { @@ -25,7 +27,7 @@ void Dialogue::load(ESMReader &esm) esm.fail("Unknown sub record size"); } -void Dialogue::save(ESMWriter &esm) +void Dialogue::save(ESMWriter &esm) const { if (mType != Deleted) esm.writeHNT("DATA", mType); @@ -36,4 +38,9 @@ void Dialogue::save(ESMWriter &esm) } } + void Dialogue::blank() + { + mInfo.clear(); + } + } diff --git a/components/esm/loaddial.hpp b/components/esm/loaddial.hpp index 61f3f763d..63d78833e 100644 --- a/components/esm/loaddial.hpp +++ b/components/esm/loaddial.hpp @@ -19,6 +19,8 @@ class ESMWriter; struct Dialogue { + static unsigned int sRecordId; + enum Type { Topic = 0, @@ -34,7 +36,10 @@ struct Dialogue std::vector mInfo; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; + + void blank(); + ///< Set record to default state (does not touch the ID and does not change the type). }; } #endif diff --git a/components/esm/loaddoor.cpp b/components/esm/loaddoor.cpp index a4c7b7d58..c56b06337 100644 --- a/components/esm/loaddoor.cpp +++ b/components/esm/loaddoor.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Door::sRecordId = REC_DOOR; void Door::load(ESMReader &esm) { @@ -15,7 +17,7 @@ void Door::load(ESMReader &esm) mCloseSound = esm.getHNOString("ANAM"); } -void Door::save(ESMWriter &esm) +void Door::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loaddoor.hpp b/components/esm/loaddoor.hpp index 77ffc6489..ee2b7f7ac 100644 --- a/components/esm/loaddoor.hpp +++ b/components/esm/loaddoor.hpp @@ -11,10 +11,12 @@ class ESMWriter; struct Door { + static unsigned int sRecordId; + std::string mId, mName, mModel, mScript, mOpenSound, mCloseSound; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadench.cpp b/components/esm/loadench.cpp index c4e278368..a1e885f23 100644 --- a/components/esm/loadench.cpp +++ b/components/esm/loadench.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Enchantment::sRecordId = REC_ENCH; void Enchantment::load(ESMReader &esm) { @@ -12,7 +14,7 @@ void Enchantment::load(ESMReader &esm) mEffects.load(esm); } -void Enchantment::save(ESMWriter &esm) +void Enchantment::save(ESMWriter &esm) const { esm.writeHNT("ENDT", mData, 16); mEffects.save(esm); diff --git a/components/esm/loadench.hpp b/components/esm/loadench.hpp index 999f93ad9..f6ba8c6ab 100644 --- a/components/esm/loadench.hpp +++ b/components/esm/loadench.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Enchantment { + static unsigned int sRecordId; + enum Type { CastOnce = 0, @@ -39,7 +41,7 @@ struct Enchantment EffectList mEffects; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadfact.cpp b/components/esm/loadfact.cpp index e2712d462..61fa90263 100644 --- a/components/esm/loadfact.cpp +++ b/components/esm/loadfact.cpp @@ -4,9 +4,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Faction::sRecordId = REC_FACT; + int& Faction::FADTstruct::getSkill (int index, bool ignored) { if (index<0 || index>=6) @@ -47,7 +50,7 @@ void Faction::load(ESMReader &esm) mReactions.push_back(r); } } -void Faction::save(ESMWriter &esm) +void Faction::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); @@ -61,7 +64,7 @@ void Faction::save(ESMWriter &esm) esm.writeHNT("FADT", mData, 240); - for (std::vector::iterator it = mReactions.begin(); it != mReactions.end(); ++it) + for (std::vector::const_iterator it = mReactions.begin(); it != mReactions.end(); ++it) { esm.writeHNString("ANAM", it->mFaction); esm.writeHNT("INTV", it->mReaction); diff --git a/components/esm/loadfact.hpp b/components/esm/loadfact.hpp index 891b99647..9c257e068 100644 --- a/components/esm/loadfact.hpp +++ b/components/esm/loadfact.hpp @@ -29,6 +29,8 @@ struct RankData struct Faction { + static unsigned int sRecordId; + std::string mId, mName; struct FADTstruct @@ -63,7 +65,7 @@ struct Faction std::string mRanks[10]; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadglob.cpp b/components/esm/loadglob.cpp index ccb519acd..a78ed1a1b 100644 --- a/components/esm/loadglob.cpp +++ b/components/esm/loadglob.cpp @@ -1,13 +1,17 @@ #include "loadglob.hpp" +#include "defs.hpp" + namespace ESM { + unsigned int Global::sRecordId = REC_GLOB; + void Global::load (ESMReader &esm) { mValue.read (esm, ESM::Variant::Format_Global); } - void Global::save (ESMWriter &esm) + void Global::save (ESMWriter &esm) const { mValue.write (esm, ESM::Variant::Format_Global); } diff --git a/components/esm/loadglob.hpp b/components/esm/loadglob.hpp index 72e16c0ce..51b2e2dc9 100644 --- a/components/esm/loadglob.hpp +++ b/components/esm/loadglob.hpp @@ -17,11 +17,13 @@ class ESMWriter; struct Global { + static unsigned int sRecordId; + std::string mId; Variant mValue; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadgmst.cpp b/components/esm/loadgmst.cpp index fe1cc1b04..21d66339a 100644 --- a/components/esm/loadgmst.cpp +++ b/components/esm/loadgmst.cpp @@ -1,13 +1,17 @@ #include "loadgmst.hpp" +#include "defs.hpp" + namespace ESM { + unsigned int GameSetting::sRecordId = REC_GMST; + void GameSetting::load (ESMReader &esm) { mValue.read (esm, ESM::Variant::Format_Gmst); } - void GameSetting::save (ESMWriter &esm) + void GameSetting::save (ESMWriter &esm) const { mValue.write (esm, ESM::Variant::Format_Gmst); } diff --git a/components/esm/loadgmst.hpp b/components/esm/loadgmst.hpp index a6e0c2ecb..6b66ac832 100644 --- a/components/esm/loadgmst.hpp +++ b/components/esm/loadgmst.hpp @@ -18,13 +18,15 @@ class ESMWriter; struct GameSetting { + static unsigned int sRecordId; + std::string mId; Variant mValue; void load(ESMReader &esm); - /// \todo remove the get* functions (redundant, since mValue as equivalent functions now). + /// \todo remove the get* functions (redundant, since mValue has equivalent functions now). int getInt() const; ///< Throws an exception if GMST is not of type int or float. @@ -35,7 +37,7 @@ struct GameSetting std::string getString() const; ///< Throwns an exception if GMST is not of type string. - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp index 90f8fcf35..4f248cc65 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int DialInfo::sRecordId = REC_INFO; void DialInfo::load(ESMReader &esm) { @@ -120,7 +122,7 @@ void DialInfo::load(ESMReader &esm) esm.skipRecord(); } -void DialInfo::save(ESMWriter &esm) +void DialInfo::save(ESMWriter &esm) const { esm.writeHNCString("INAM", mId); esm.writeHNCString("PNAM", mPrev); @@ -135,7 +137,7 @@ void DialInfo::save(ESMWriter &esm) esm.writeHNOCString("SNAM", mSound); esm.writeHNOString("NAME", mResponse); - for (std::vector::iterator it = mSelects.begin(); it != mSelects.end(); ++it) + for (std::vector::const_iterator it = mSelects.begin(); it != mSelects.end(); ++it) { esm.writeHNString("SCVR", it->mSelectRule); it->mValue.write (esm, Variant::Format_Info); diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 2361ed9eb..2589ea7b8 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct DialInfo { + static unsigned int sRecordId; + enum Gender { Male = 0, @@ -99,7 +101,7 @@ struct DialInfo }; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/loadingr.cpp b/components/esm/loadingr.cpp index 7e31a4116..0e0243362 100644 --- a/components/esm/loadingr.cpp +++ b/components/esm/loadingr.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Ingredient::sRecordId = REC_INGR; void Ingredient::load(ESMReader &esm) { @@ -37,7 +39,7 @@ void Ingredient::load(ESMReader &esm) } } -void Ingredient::save(ESMWriter &esm) +void Ingredient::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadingr.hpp b/components/esm/loadingr.hpp index 5e286535f..85f2d5e7d 100644 --- a/components/esm/loadingr.hpp +++ b/components/esm/loadingr.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Ingredient { + static unsigned int sRecordId; + struct IRDTstruct { float mWeight; @@ -28,7 +30,7 @@ struct Ingredient std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 60c475040..ede200d79 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Land::sRecordId = REC_LAND; void Land::LandData::save(ESMWriter &esm) { @@ -16,14 +18,14 @@ void Land::LandData::save(ESMWriter &esm) offsets.mHeightOffset = mHeights[0] / HEIGHT_SCALE; offsets.mUnk1 = mUnk1; offsets.mUnk2 = mUnk2; - + float prevY = mHeights[0], prevX; int number = 0; // avoid multiplication for (int i = 0; i < LAND_SIZE; ++i) { float diff = (mHeights[number] - prevY) / HEIGHT_SCALE; offsets.mHeightData[number] = (diff >= 0) ? (int8_t) (diff + 0.5) : (int8_t) (diff - 0.5); - + prevX = prevY = mHeights[number]; ++number; @@ -132,7 +134,7 @@ void Land::load(ESMReader &esm) mLandData = NULL; } -void Land::save(ESMWriter &esm) +void Land::save(ESMWriter &esm) const { esm.startSubRecord("INTV"); esm.writeT(mX); @@ -140,18 +142,6 @@ void Land::save(ESMWriter &esm) esm.endRecord("INTV"); esm.writeHNT("DATA", mFlags); - - // TODO: Land! - bool wasLoaded = mDataLoaded; - if (mDataTypes) { - // Try to load all available data before saving - loadData(mDataTypes); - } - if (mDataLoaded) - mLandData->save(esm); - - if (!wasLoaded) - unloadData(); // Don't need to keep the data loaded if it wasn't already } /// \todo remove memory allocation when only defaults needed diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 9c1fd1f5c..5649f9980 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Land { + static unsigned int sRecordId; + Land(); ~Land(); @@ -94,7 +96,7 @@ struct Land LandData *mLandData; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; /** * Actually loads data diff --git a/components/esm/loadlevlist.cpp b/components/esm/loadlevlist.cpp index b54a91276..6385b9a71 100644 --- a/components/esm/loadlevlist.cpp +++ b/components/esm/loadlevlist.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -33,13 +34,13 @@ void LeveledListBase::load(ESMReader &esm) esm.getHNT(li.mLevel, "INTV"); } } -void LeveledListBase::save(ESMWriter &esm) +void LeveledListBase::save(ESMWriter &esm) const { esm.writeHNT("DATA", mFlags); esm.writeHNT("NNAM", mChanceNone); esm.writeHNT("INDX", mList.size()); - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNCString(mRecName, it->mId); esm.writeHNT("INTV", it->mLevel); @@ -52,4 +53,8 @@ void LeveledListBase::save(ESMWriter &esm) mChanceNone = 0; mList.clear(); } + + unsigned int CreatureLevList::sRecordId = REC_LEVC; + + unsigned int ItemLevList::sRecordId = REC_LEVI; } diff --git a/components/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp index 7339cac56..9dcc6177a 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -51,7 +51,7 @@ struct LeveledListBase std::vector mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). @@ -59,6 +59,8 @@ struct LeveledListBase struct CreatureLevList: LeveledListBase { + static unsigned int sRecordId; + CreatureLevList() { mRecName = "CNAM"; @@ -67,6 +69,8 @@ struct CreatureLevList: LeveledListBase struct ItemLevList: LeveledListBase { + static unsigned int sRecordId; + ItemLevList() { mRecName = "INAM"; diff --git a/components/esm/loadligh.cpp b/components/esm/loadligh.cpp index 89a2b8c65..c02bb46b6 100644 --- a/components/esm/loadligh.cpp +++ b/components/esm/loadligh.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Light::sRecordId = REC_LIGH; void Light::load(ESMReader &esm) { @@ -16,7 +18,7 @@ void Light::load(ESMReader &esm) mScript = esm.getHNOString("SCRI"); mSound = esm.getHNOString("SNAM"); } -void Light::save(ESMWriter &esm) +void Light::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadligh.hpp b/components/esm/loadligh.hpp index 3f0b76d6e..74eb37197 100644 --- a/components/esm/loadligh.hpp +++ b/components/esm/loadligh.hpp @@ -16,6 +16,8 @@ class ESMWriter; struct Light { + static unsigned int sRecordId; + enum Flags { Dynamic = 0x001, @@ -44,7 +46,7 @@ struct Light std::string mSound, mScript, mModel, mIcon, mName, mId; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadlock.cpp b/components/esm/loadlock.cpp index 03eac52bd..9ffce78a7 100644 --- a/components/esm/loadlock.cpp +++ b/components/esm/loadlock.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Lockpick::sRecordId = REC_LOCK; void Lockpick::load(ESMReader &esm) { @@ -17,7 +19,7 @@ void Lockpick::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); } -void Lockpick::save(ESMWriter &esm) +void Lockpick::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadlock.hpp b/components/esm/loadlock.hpp index 953066cb2..c44e2b006 100644 --- a/components/esm/loadlock.hpp +++ b/components/esm/loadlock.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Lockpick { + static unsigned int sRecordId; + struct Data { float mWeight; @@ -24,7 +26,7 @@ struct Lockpick std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadltex.cpp b/components/esm/loadltex.cpp index e523e9fa7..bd28c8488 100644 --- a/components/esm/loadltex.cpp +++ b/components/esm/loadltex.cpp @@ -2,16 +2,18 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int LandTexture::sRecordId = REC_LTEX; void LandTexture::load(ESMReader &esm) { esm.getHNT(mIndex, "INTV"); mTexture = esm.getHNString("DATA"); } -void LandTexture::save(ESMWriter &esm) +void LandTexture::save(ESMWriter &esm) const { esm.writeHNT("INTV", mIndex); esm.writeHNCString("DATA", mTexture); diff --git a/components/esm/loadltex.hpp b/components/esm/loadltex.hpp index 6e6d987d4..5e84428b2 100644 --- a/components/esm/loadltex.hpp +++ b/components/esm/loadltex.hpp @@ -27,11 +27,13 @@ class ESMWriter; struct LandTexture { + static unsigned int sRecordId; + std::string mId, mTexture; int mIndex; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadmgef.cpp b/components/esm/loadmgef.cpp index 19d535600..1a90f5b09 100644 --- a/components/esm/loadmgef.cpp +++ b/components/esm/loadmgef.cpp @@ -6,6 +6,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace { @@ -34,6 +35,7 @@ namespace namespace ESM { + unsigned int MagicEffect::sRecordId = REC_MGEF; void MagicEffect::load(ESMReader &esm) { @@ -58,15 +60,11 @@ void MagicEffect::load(ESMReader &esm) mDescription = esm.getHNOString("DESC"); } -void MagicEffect::save(ESMWriter &esm) +void MagicEffect::save(ESMWriter &esm) const { esm.writeHNT("INDX", mIndex); - mData.mFlags &= 0xe00; esm.writeHNT("MEDT", mData, 36); - if (mIndex>=0 && mIndex::iterator DestIter; + typedef std::vector::const_iterator DestIter; for (DestIter it = mTransport.begin(); it != mTransport.end(); ++it) { esm.writeHNT("DODT", it->mPos, sizeof(it->mPos)); esm.writeHNOCString("DNAM", it->mCellName); diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 009bc5ef3..d9e691669 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct NPC { + static unsigned int sRecordId; + // Services enum Services { @@ -117,7 +119,7 @@ struct NPC std::string mHair, mHead; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; bool isMale() const; diff --git a/components/esm/loadnpcc.hpp b/components/esm/loadnpcc.hpp index 79d92397f..c87c2545f 100644 --- a/components/esm/loadnpcc.hpp +++ b/components/esm/loadnpcc.hpp @@ -78,13 +78,15 @@ class ESMWriter; struct LoadNPCC { + static unsigned int sRecordId; + std::string mId; void load(ESMReader &esm) { esm.skipRecord(); } - void save(ESMWriter &esm) + void save(ESMWriter &esm) const { } }; diff --git a/components/esm/loadpgrd.cpp b/components/esm/loadpgrd.cpp index 882addcb9..3b5330e9f 100644 --- a/components/esm/loadpgrd.cpp +++ b/components/esm/loadpgrd.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Pathgrid::sRecordId = REC_PGRD; void Pathgrid::load(ESMReader &esm) { @@ -70,25 +72,25 @@ void Pathgrid::load(ESMReader &esm) } } } -void Pathgrid::save(ESMWriter &esm) +void Pathgrid::save(ESMWriter &esm) const { esm.writeHNT("DATA", mData, 12); esm.writeHNCString("NAME", mCell); - + if (!mPoints.empty()) { esm.startSubRecord("PGRP"); - for (PointList::iterator it = mPoints.begin(); it != mPoints.end(); ++it) + for (PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it) { esm.writeT(*it); } esm.endRecord("PGRP"); } - + if (!mEdges.empty()) { esm.startSubRecord("PGRC"); - for (std::vector::iterator it = mEdges.begin(); it != mEdges.end(); ++it) + for (std::vector::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it) { esm.writeT(it->mV1); } diff --git a/components/esm/loadpgrd.hpp b/components/esm/loadpgrd.hpp index c3f50fc4d..9ee49552d 100644 --- a/components/esm/loadpgrd.hpp +++ b/components/esm/loadpgrd.hpp @@ -15,6 +15,8 @@ class ESMWriter; */ struct Pathgrid { + static unsigned int sRecordId; + struct DATAstruct { int mX, mY; // Grid location, matches cell for exterior cells @@ -46,7 +48,7 @@ struct Pathgrid EdgeList mEdges; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadprob.cpp b/components/esm/loadprob.cpp index 729f8404e..caa3d7e0e 100644 --- a/components/esm/loadprob.cpp +++ b/components/esm/loadprob.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Probe::sRecordId = REC_PROB; void Probe::load(ESMReader &esm) { @@ -17,7 +19,7 @@ void Probe::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); } -void Probe::save(ESMWriter &esm) +void Probe::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadprob.hpp b/components/esm/loadprob.hpp index 55b896bcd..b89b2ddeb 100644 --- a/components/esm/loadprob.hpp +++ b/components/esm/loadprob.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Probe { + static unsigned int sRecordId; + struct Data { float mWeight; @@ -24,7 +26,7 @@ struct Probe std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadrace.cpp b/components/esm/loadrace.cpp index 955424e2b..e50e43a74 100644 --- a/components/esm/loadrace.cpp +++ b/components/esm/loadrace.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Race::sRecordId = REC_RACE; + int Race::MaleFemale::getValue (bool male) const { return male ? mMale : mFemale; @@ -22,7 +25,7 @@ void Race::load(ESMReader &esm) mPowers.load(esm); mDescription = esm.getHNOString("DESC"); } -void Race::save(ESMWriter &esm) +void Race::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); esm.writeHNT("RADT", mData, 140); diff --git a/components/esm/loadrace.hpp b/components/esm/loadrace.hpp index 6ecec8ebb..7d5736d9b 100644 --- a/components/esm/loadrace.hpp +++ b/components/esm/loadrace.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Race { + static unsigned int sRecordId; + struct SkillBonus { int mSkill; // SkillEnum @@ -65,7 +67,7 @@ struct Race SpellList mPowers; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadregn.cpp b/components/esm/loadregn.cpp index 41c7f507a..fa4271e26 100644 --- a/components/esm/loadregn.cpp +++ b/components/esm/loadregn.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Region::sRecordId = REC_REGN; void Region::load(ESMReader &esm) { @@ -28,7 +30,7 @@ void Region::load(ESMReader &esm) mSoundList.push_back(sr); } } -void Region::save(ESMWriter &esm) +void Region::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); @@ -40,7 +42,7 @@ void Region::save(ESMWriter &esm) esm.writeHNOCString("BNAM", mSleepList); esm.writeHNT("CNAM", mMapColor); - for (std::vector::iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) + for (std::vector::const_iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) { esm.writeHNT("SNAM", *it); } diff --git a/components/esm/loadregn.hpp b/components/esm/loadregn.hpp index f2a3d9a10..1992c951b 100644 --- a/components/esm/loadregn.hpp +++ b/components/esm/loadregn.hpp @@ -18,6 +18,8 @@ class ESMWriter; struct Region { + static unsigned int sRecordId; + #pragma pack(push) #pragma pack(1) struct WEATstruct @@ -47,7 +49,7 @@ struct Region std::vector mSoundList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadrepa.cpp b/components/esm/loadrepa.cpp index ced6daa2e..a7132828d 100644 --- a/components/esm/loadrepa.cpp +++ b/components/esm/loadrepa.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Repair::sRecordId = REC_REPA; void Repair::load(ESMReader &esm) { @@ -17,7 +19,7 @@ void Repair::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); } -void Repair::save(ESMWriter &esm) +void Repair::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadrepa.hpp b/components/esm/loadrepa.hpp index 83812bad9..5b404b0e4 100644 --- a/components/esm/loadrepa.hpp +++ b/components/esm/loadrepa.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Repair { + static unsigned int sRecordId; + struct Data { float mWeight; @@ -24,7 +26,7 @@ struct Repair std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 2c1b018d9..30460c17a 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -12,6 +13,8 @@ struct SCHD Script::SCHDstruct mData; }; + unsigned int Script::sRecordId = REC_SCPT; + void Script::load(ESMReader &esm) { SCHD data; @@ -50,11 +53,11 @@ void Script::load(ESMReader &esm) // Script text mScriptText = esm.getHNOString("SCTX"); } -void Script::save(ESMWriter &esm) +void Script::save(ESMWriter &esm) const { std::string varNameString; if (!mVarNames.empty()) - for (std::vector::iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) + for (std::vector::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) varNameString.append(*it); SCHD data; @@ -68,7 +71,7 @@ void Script::save(ESMWriter &esm) if (!mVarNames.empty()) { esm.startSubRecord("SCVR"); - for (std::vector::iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) + for (std::vector::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) { esm.writeHCString(*it); } diff --git a/components/esm/loadscpt.hpp b/components/esm/loadscpt.hpp index be7e83900..d5200d4c1 100644 --- a/components/esm/loadscpt.hpp +++ b/components/esm/loadscpt.hpp @@ -19,6 +19,8 @@ class ESMWriter; class Script { public: + static unsigned int sRecordId; + struct SCHDstruct { /* Script name. @@ -56,7 +58,7 @@ public: std::string mScriptText; // Uncompiled script void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadskil.cpp b/components/esm/loadskil.cpp index 676a835c3..b6724e938 100644 --- a/components/esm/loadskil.cpp +++ b/components/esm/loadskil.cpp @@ -6,6 +6,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -126,6 +127,8 @@ namespace ESM HandToHand }}; + unsigned int Skill::sRecordId = REC_SKIL; + void Skill::load(ESMReader &esm) { esm.getHNT(mIndex, "INDX"); @@ -137,7 +140,7 @@ void Skill::load(ESMReader &esm) mId = indexToId (mIndex); } -void Skill::save(ESMWriter &esm) +void Skill::save(ESMWriter &esm) const { esm.writeHNT("INDX", mIndex); esm.writeHNT("SKDT", mData, 24); diff --git a/components/esm/loadskil.hpp b/components/esm/loadskil.hpp index 384f87454..1b9db5bcf 100644 --- a/components/esm/loadskil.hpp +++ b/components/esm/loadskil.hpp @@ -19,6 +19,8 @@ class ESMWriter; struct Skill { + static unsigned int sRecordId; + std::string mId; struct SKDTstruct @@ -75,7 +77,7 @@ struct Skill static const boost::array sSkillIds; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadsndg.cpp b/components/esm/loadsndg.cpp index 42d524226..1a8ca6335 100644 --- a/components/esm/loadsndg.cpp +++ b/components/esm/loadsndg.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int SoundGenerator::sRecordId = REC_SNDG; void SoundGenerator::load(ESMReader &esm) { @@ -13,7 +15,7 @@ void SoundGenerator::load(ESMReader &esm) mCreature = esm.getHNOString("CNAM"); mSound = esm.getHNOString("SNAM"); } -void SoundGenerator::save(ESMWriter &esm) +void SoundGenerator::save(ESMWriter &esm) const { esm.writeHNT("DATA", mType, 4); esm.writeHNOCString("CNAM", mCreature); diff --git a/components/esm/loadsndg.hpp b/components/esm/loadsndg.hpp index a6226c154..5509661c1 100644 --- a/components/esm/loadsndg.hpp +++ b/components/esm/loadsndg.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct SoundGenerator { + static unsigned int sRecordId; + enum Type { LeftFoot = 0, @@ -33,7 +35,7 @@ struct SoundGenerator std::string mId, mCreature, mSound; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadsoun.cpp b/components/esm/loadsoun.cpp index 07af2b5e9..49c9eb54e 100644 --- a/components/esm/loadsoun.cpp +++ b/components/esm/loadsoun.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Sound::sRecordId = REC_SOUN; void Sound::load(ESMReader &esm) { @@ -17,7 +19,7 @@ void Sound::load(ESMReader &esm) << endl; */ } -void Sound::save(ESMWriter &esm) +void Sound::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mSound); esm.writeHNT("DATA", mData, 3); diff --git a/components/esm/loadsoun.hpp b/components/esm/loadsoun.hpp index f8e38ac09..04a0984fd 100644 --- a/components/esm/loadsoun.hpp +++ b/components/esm/loadsoun.hpp @@ -16,11 +16,13 @@ struct SOUNstruct struct Sound { + static unsigned int sRecordId; + SOUNstruct mData; std::string mId, mSound; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadspel.cpp b/components/esm/loadspel.cpp index 8149fe4ce..2c98d796d 100644 --- a/components/esm/loadspel.cpp +++ b/components/esm/loadspel.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Spell::sRecordId = REC_SPEL; void Spell::load(ESMReader &esm) { @@ -13,7 +15,7 @@ void Spell::load(ESMReader &esm) mEffects.load(esm); } -void Spell::save(ESMWriter &esm) +void Spell::save(ESMWriter &esm) const { esm.writeHNOCString("FNAM", mName); esm.writeHNT("SPDT", mData, 12); diff --git a/components/esm/loadspel.hpp b/components/esm/loadspel.hpp index 3a620962d..cbf5366c4 100644 --- a/components/esm/loadspel.hpp +++ b/components/esm/loadspel.hpp @@ -13,6 +13,8 @@ class ESMWriter; struct Spell { + static unsigned int sRecordId; + enum SpellType { ST_Spell = 0, // Normal spell, must be cast and costs mana @@ -42,7 +44,7 @@ struct Spell EffectList mEffects; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadsscr.cpp b/components/esm/loadsscr.cpp index ae50de517..69b04bb23 100644 --- a/components/esm/loadsscr.cpp +++ b/components/esm/loadsscr.cpp @@ -2,16 +2,18 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int StartScript::sRecordId = REC_SSCR; void StartScript::load(ESMReader &esm) { mData = esm.getHNString("DATA"); mScript = esm.getHNString("NAME"); } -void StartScript::save(ESMWriter &esm) +void StartScript::save(ESMWriter &esm) const { esm.writeHNString("DATA", mData); esm.writeHNString("NAME", mScript); diff --git a/components/esm/loadsscr.hpp b/components/esm/loadsscr.hpp index 713fe96b5..d09ad883e 100644 --- a/components/esm/loadsscr.hpp +++ b/components/esm/loadsscr.hpp @@ -19,12 +19,14 @@ class ESMWriter; struct StartScript { + static unsigned int sRecordId; + std::string mData; std::string mId, mScript; // Load a record and add it to the list void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/loadstat.cpp b/components/esm/loadstat.cpp index c9346dafc..a71f22dc2 100644 --- a/components/esm/loadstat.cpp +++ b/components/esm/loadstat.cpp @@ -2,15 +2,17 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Static::sRecordId = REC_STAT; void Static::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); } -void Static::save(ESMWriter &esm) +void Static::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); } diff --git a/components/esm/loadstat.hpp b/components/esm/loadstat.hpp index 1adb7d05b..d912d1058 100644 --- a/components/esm/loadstat.hpp +++ b/components/esm/loadstat.hpp @@ -22,10 +22,12 @@ class ESMWriter; struct Static { + static unsigned int sRecordId; + std::string mId, mModel; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index 74d578ba7..87a8d1d57 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -4,6 +4,7 @@ #include "esmcommon.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" void ESM::Header::blank() { @@ -13,6 +14,7 @@ void ESM::Header::blank() mData.desc.assign (""); mData.records = 0; mFormat = CurrentFormat; + mMaster.clear(); } void ESM::Header::load (ESMReader &esm) diff --git a/components/esm/loadtes3.hpp b/components/esm/loadtes3.hpp index b73a4c31e..5614d295f 100644 --- a/components/esm/loadtes3.hpp +++ b/components/esm/loadtes3.hpp @@ -24,7 +24,7 @@ namespace ESM versions are 1.2 and 1.3. These correspond to: 1.2 = 0x3f99999a and 1.3 = 0x3fa66666 */ - int version; + unsigned int version; int type; // 0=esp, 1=esm, 32=ess (unused) NAME32 author; // Author's name NAME256 desc; // File description diff --git a/components/esm/loadweap.cpp b/components/esm/loadweap.cpp index 253712396..1d0b149df 100644 --- a/components/esm/loadweap.cpp +++ b/components/esm/loadweap.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Weapon::sRecordId = REC_WEAP; void Weapon::load(ESMReader &esm) { @@ -15,7 +17,7 @@ void Weapon::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); mEnchant = esm.getHNOString("ENAM"); } -void Weapon::save(ESMWriter &esm) +void Weapon::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadweap.hpp b/components/esm/loadweap.hpp index b62179ccb..fde716b91 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Weapon { + static unsigned int sRecordId; + enum Type { ShortBladeOneHand = 0, @@ -59,7 +61,7 @@ struct Weapon std::string mId, mName, mModel, mIcon, mEnchant, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/spelllist.cpp b/components/esm/spelllist.cpp index dd886cf7f..24d3c3d0a 100644 --- a/components/esm/spelllist.cpp +++ b/components/esm/spelllist.cpp @@ -12,9 +12,9 @@ void SpellList::load(ESMReader &esm) } } -void SpellList::save(ESMWriter &esm) +void SpellList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) { + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNString("NPCS", *it, 32); } } diff --git a/components/esm/spelllist.hpp b/components/esm/spelllist.hpp index 52999270a..934bdda7a 100644 --- a/components/esm/spelllist.hpp +++ b/components/esm/spelllist.hpp @@ -17,7 +17,7 @@ namespace ESM std::vector mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/variantimp.cpp b/components/esm/variantimp.cpp index 160402aa4..1bacdc077 100644 --- a/components/esm/variantimp.cpp +++ b/components/esm/variantimp.cpp @@ -193,7 +193,7 @@ void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, Var } else if (format==Variant::Format_Gmst || format==Variant::Format_Info) { - if (type==VT_Int) + if (type!=VT_Int) { std::ostringstream stream; stream diff --git a/files/opencs/defaultfilters b/files/opencs/defaultfilters new file mode 100644 index 000000000..0ac3c8db4 Binary files /dev/null and b/files/opencs/defaultfilters differ