mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-19 04:39:44 +00:00
merge from saving
Merge branch 'saving' of https://github.com/zinnschlag/openmw into esxSelector Conflicts: apps/launcher/datafilespage.cpp apps/opencs/editor.cpp apps/opencs/view/doc/filedialog.cpp apps/opencs/view/doc/filedialog.hpp
This commit is contained in:
commit
9ce4a04a2d
176 changed files with 2335 additions and 726 deletions
|
@ -319,6 +319,9 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
|
|||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg
|
||||
"${OpenMW_BINARY_DIR}/opencs.cfg")
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
|
||||
"${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY)
|
||||
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
|
||||
|
|
|
@ -161,8 +161,42 @@ void Launcher::DataFilesPage::slotProfileDeleted (const QString &item)
|
|||
|
||||
void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤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)
|
||||
|
|
|
@ -163,12 +163,12 @@ bool Launcher::GameSettings::writeFile(QTextStream &stream)
|
|||
|
||||
QStringList masters = mSettings.values(QString("master"));
|
||||
for (int i = masters.count(); i--;) {
|
||||
stream << "master=" << masters.at(i) << "\n";
|
||||
stream << "content=" << masters.at(i) << "\n";
|
||||
}
|
||||
|
||||
QStringList plugins = mSettings.values(QString("plugin"));
|
||||
for (int i = plugins.count(); i--;) {
|
||||
stream << "plugin=" << plugins.at(i) << "\n";
|
||||
stream << "content=" << plugins.at(i) << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -813,8 +813,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
|
|||
}
|
||||
|
||||
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const {
|
||||
std::vector<std::string> esmFiles;
|
||||
std::vector<std::string> espFiles;
|
||||
std::vector<std::string> contentFiles;
|
||||
std::string baseGameFile("Game Files:GameFile");
|
||||
std::string gameFile("");
|
||||
|
||||
|
@ -832,29 +831,19 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) co
|
|||
std::string filetype(entry->substr(entry->length()-3));
|
||||
Misc::StringUtils::toLower(filetype);
|
||||
|
||||
if(filetype.compare("esm") == 0) {
|
||||
esmFiles.push_back(*entry);
|
||||
}
|
||||
else if(filetype.compare("esp") == 0) {
|
||||
espFiles.push_back(*entry);
|
||||
if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) {
|
||||
contentFiles.push_back(*entry);
|
||||
}
|
||||
}
|
||||
|
||||
gameFile = "";
|
||||
}
|
||||
|
||||
cfg.erase("master");
|
||||
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("master", std::vector<std::string>() ) );
|
||||
cfg.erase("content");
|
||||
cfg.insert( std::make_pair("content", std::vector<std::string>() ) );
|
||||
|
||||
for(std::vector<std::string>::const_iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) {
|
||||
cfg["master"].push_back(*it);
|
||||
}
|
||||
|
||||
cfg.erase("plugin");
|
||||
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("plugin", std::vector<std::string>() ) );
|
||||
|
||||
for(std::vector<std::string>::const_iterator it=espFiles.begin(); it!=espFiles.end(); ++it) {
|
||||
cfg["plugin"].push_back(*it);
|
||||
for(std::vector<std::string>::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) {
|
||||
cfg["content"].push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ opencs_units (. editor)
|
|||
set (CMAKE_BUILD_TYPE DEBUG)
|
||||
|
||||
opencs_units (model/doc
|
||||
document
|
||||
document operation saving
|
||||
)
|
||||
|
||||
opencs_units_noqt (model/doc
|
||||
documentmanager
|
||||
documentmanager stage savingstate savingstages
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (model/doc
|
||||
|
@ -33,11 +33,11 @@ opencs_hdrs_noqt (model/world
|
|||
|
||||
|
||||
opencs_units (model/tools
|
||||
tools operation reportmodel
|
||||
tools reportmodel
|
||||
)
|
||||
|
||||
opencs_units_noqt (model/tools
|
||||
stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
|
||||
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
|
||||
birthsigncheck spellcheck
|
||||
)
|
||||
|
||||
|
@ -66,7 +66,7 @@ opencs_units (view/world
|
|||
opencs_units_noqt (view/world
|
||||
dialoguesubview subviews
|
||||
enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
|
||||
scripthighlighter idvalidator
|
||||
scripthighlighter idvalidator dialoguecreator
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -8,15 +8,18 @@
|
|||
|
||||
#include "model/doc/document.hpp"
|
||||
#include "model/world/data.hpp"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
CS::Editor::Editor() : mViewManager (mDocumentManager)
|
||||
CS::Editor::Editor()
|
||||
: mDocumentManager (mCfgMgr), mViewManager (mDocumentManager)
|
||||
{
|
||||
mIpcServerName = "org.openmw.OpenCS";
|
||||
|
||||
setupDataFiles();
|
||||
|
||||
mNewGame.setLocalData (mLocal);
|
||||
mFileDialog.setLocalData (mLocal);
|
||||
|
||||
connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ()));
|
||||
connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
|
||||
|
@ -29,22 +32,24 @@ CS::Editor::Editor() : mViewManager (mDocumentManager)
|
|||
connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ()));
|
||||
|
||||
connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles()));
|
||||
connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile()));
|
||||
connect (&mFileDialog, SIGNAL(createNewFile (const boost::filesystem::path&)),
|
||||
this, SLOT(createNewFile (const boost::filesystem::path&)));
|
||||
|
||||
connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)),
|
||||
this, SLOT (createNewGame (const boost::filesystem::path&)));
|
||||
this, SLOT (createNewGame (const boost::filesystem::path&)));
|
||||
}
|
||||
|
||||
void CS::Editor::setupDataFiles()
|
||||
{
|
||||
boost::program_options::variables_map variables;
|
||||
boost::program_options::options_description desc;
|
||||
boost::program_options::options_description desc("Syntax: opencs <options>\nAllowed options");
|
||||
|
||||
desc.add_options()
|
||||
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
|
||||
("data-local", boost::program_options::value<std::string>()->default_value(""))
|
||||
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
|
||||
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
|
||||
("encoding", boost::program_options::value<std::string>()->default_value("win1252"))
|
||||
("resources", boost::program_options::value<std::string>()->default_value("resources"));
|
||||
|
||||
boost::program_options::notify(variables);
|
||||
|
||||
|
@ -79,11 +84,13 @@ void CS::Editor::setupDataFiles()
|
|||
}
|
||||
|
||||
// Set the charset for reading the esm/esp files
|
||||
// QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
|
||||
// QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
|
||||
//mFileDialog.setEncoding(encoding);
|
||||
|
||||
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
|
||||
|
||||
mDocumentManager.setResourceDir (variables["resources"].as<std::string>());
|
||||
|
||||
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
|
||||
{
|
||||
QString path = QString::fromStdString(iter->string());
|
||||
|
@ -134,7 +141,7 @@ void CS::Editor::openFiles()
|
|||
mFileDialog.hide();
|
||||
}
|
||||
|
||||
void CS::Editor::createNewFile()
|
||||
void CS::Editor::createNewFile (const boost::filesystem::path& savePath)
|
||||
{
|
||||
std::vector<boost::filesystem::path> files;
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace CS
|
|||
{
|
||||
Q_OBJECT
|
||||
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
CSMSettings::UserSettings mUserSettings;
|
||||
CSMDoc::DocumentManager mDocumentManager;
|
||||
CSVDoc::ViewManager mViewManager;
|
||||
|
@ -34,7 +35,6 @@ namespace CS
|
|||
CSVSettings::UserSettingsDialog mSettings;
|
||||
CSVDoc::FileDialog mFileDialog;
|
||||
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
boost::filesystem::path mLocal;
|
||||
|
||||
void setupDataFiles();
|
||||
|
@ -60,7 +60,7 @@ namespace CS
|
|||
|
||||
void loadDocument();
|
||||
void openFiles();
|
||||
void createNewFile();
|
||||
void createNewFile (const boost::filesystem::path& savePath);
|
||||
void createNewGame (const boost::filesystem::path& file);
|
||||
|
||||
void showStartup();
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
#include "document.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifndef Q_MOC_RUN
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#endif
|
||||
|
||||
void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_iterator& begin,
|
||||
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified)
|
||||
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified)
|
||||
{
|
||||
assert (begin!=end);
|
||||
|
||||
|
@ -12,10 +19,10 @@ void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_i
|
|||
--end2;
|
||||
|
||||
for (std::vector<boost::filesystem::path>::const_iterator iter (begin); iter!=end2; ++iter)
|
||||
getData().loadFile (*iter, true);
|
||||
getData().loadFile (*iter, true, false);
|
||||
|
||||
if (lastAsModified)
|
||||
getData().loadFile (*end2, false);
|
||||
getData().loadFile (*end2, false, false);
|
||||
}
|
||||
|
||||
void CSMDoc::Document::addGmsts()
|
||||
|
@ -2058,9 +2065,9 @@ void CSMDoc::Document::addOptionalGlobals()
|
|||
{
|
||||
static const char *sGlobals[] =
|
||||
{
|
||||
"dayspassed",
|
||||
"pcwerewolf",
|
||||
"pcyear",
|
||||
"DaysPassed",
|
||||
"PCWerewolf",
|
||||
"PCYear",
|
||||
0
|
||||
};
|
||||
|
||||
|
@ -2137,11 +2144,86 @@ void CSMDoc::Document::createBase()
|
|||
|
||||
getData().getSkills().add (record);
|
||||
}
|
||||
|
||||
static const char *sVoice[] =
|
||||
{
|
||||
"Intruder",
|
||||
"Attack",
|
||||
"Hello",
|
||||
"Thief",
|
||||
"Alarm",
|
||||
"Idle",
|
||||
"Flee",
|
||||
"Hit",
|
||||
0
|
||||
};
|
||||
|
||||
for (int i=0; sVoice[i]; ++i)
|
||||
{
|
||||
ESM::Dialogue record;
|
||||
record.mId = sVoice[i];
|
||||
record.mType = ESM::Dialogue::Voice;
|
||||
record.blank();
|
||||
|
||||
getData().getTopics().add (record);
|
||||
}
|
||||
|
||||
static const char *sGreetings[] =
|
||||
{
|
||||
"Greeting 0",
|
||||
"Greeting 1",
|
||||
"Greeting 2",
|
||||
"Greeting 3",
|
||||
"Greeting 4",
|
||||
"Greeting 5",
|
||||
"Greeting 6",
|
||||
"Greeting 7",
|
||||
"Greeting 8",
|
||||
"Greeting 9",
|
||||
0
|
||||
};
|
||||
|
||||
for (int i=0; sGreetings[i]; ++i)
|
||||
{
|
||||
ESM::Dialogue record;
|
||||
record.mId = sGreetings[i];
|
||||
record.mType = ESM::Dialogue::Greeting;
|
||||
record.blank();
|
||||
|
||||
getData().getTopics().add (record);
|
||||
}
|
||||
|
||||
static const char *sPersuasion[] =
|
||||
{
|
||||
"Intimidate Success",
|
||||
"Intimidate Fail",
|
||||
"Service Refusal",
|
||||
"Admire Success",
|
||||
"Taunt Success",
|
||||
"Bribe Success",
|
||||
"Info Refusal",
|
||||
"Admire Fail",
|
||||
"Taunt Fail",
|
||||
"Bribe Fail",
|
||||
0
|
||||
};
|
||||
|
||||
for (int i=0; sPersuasion[i]; ++i)
|
||||
{
|
||||
ESM::Dialogue record;
|
||||
record.mId = sPersuasion[i];
|
||||
record.mType = ESM::Dialogue::Persuasion;
|
||||
record.blank();
|
||||
|
||||
getData().getTopics().add (record);
|
||||
}
|
||||
}
|
||||
|
||||
CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
|
||||
const boost::filesystem::path& savePath, bool new_)
|
||||
: mSavePath (savePath), mTools (mData)
|
||||
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_)
|
||||
: mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir),
|
||||
mProjectPath ((configuration.getUserPath() / "projects") /
|
||||
(savePath.filename().string() + ".project")),
|
||||
mSaving (*this, mProjectPath)
|
||||
{
|
||||
if (files.empty())
|
||||
throw std::runtime_error ("Empty content file sequence");
|
||||
|
@ -2158,6 +2240,34 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
|
|||
load (files.begin(), end, !new_);
|
||||
}
|
||||
|
||||
if (new_)
|
||||
{
|
||||
mData.setDescription ("");
|
||||
mData.setAuthor ("");
|
||||
}
|
||||
/// \todo un-outcomment the else, once loading an existing content file works properly again.
|
||||
// else
|
||||
{
|
||||
if (boost::filesystem::exists (mProjectPath))
|
||||
{
|
||||
getData().loadFile (mProjectPath, false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::filesystem::path locCustomFiltersPath (configuration.getUserPath());
|
||||
locCustomFiltersPath /= "defaultfilters";
|
||||
if (boost::filesystem::exists(locCustomFiltersPath))
|
||||
{
|
||||
boost::filesystem::copy_file (locCustomFiltersPath, mProjectPath);
|
||||
} else {
|
||||
boost::filesystem::path filters(mResDir);
|
||||
filters /= "defaultfilters";
|
||||
boost::filesystem::copy_file(filters, mProjectPath);
|
||||
}
|
||||
getData().loadFile (mProjectPath, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
addOptionalGmsts();
|
||||
addOptionalGlobals();
|
||||
|
||||
|
@ -2166,9 +2276,10 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
|
|||
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
|
||||
connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int)));
|
||||
|
||||
// dummy implementation -> remove when proper save is implemented.
|
||||
mSaveCount = 0;
|
||||
connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving()));
|
||||
connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
|
||||
connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int)));
|
||||
connect (&mSaving, SIGNAL (reportMessage (const QString&, int)),
|
||||
this, SLOT (reportMessage (const QString&, int)));
|
||||
}
|
||||
|
||||
CSMDoc::Document::~Document()
|
||||
|
@ -2187,7 +2298,7 @@ int CSMDoc::Document::getState() const
|
|||
if (!mUndoStack.isClean())
|
||||
state |= State_Modified;
|
||||
|
||||
if (mSaveCount)
|
||||
if (mSaving.isRunning())
|
||||
state |= State_Locked | State_Saving | State_Operation;
|
||||
|
||||
if (int operations = mTools.getRunningOperations())
|
||||
|
@ -2201,12 +2312,20 @@ const boost::filesystem::path& CSMDoc::Document::getSavePath() const
|
|||
return mSavePath;
|
||||
}
|
||||
|
||||
const std::vector<boost::filesystem::path>& CSMDoc::Document::getContentFiles() const
|
||||
{
|
||||
return mContentFiles;
|
||||
}
|
||||
|
||||
void CSMDoc::Document::save()
|
||||
{
|
||||
mSaveCount = 1;
|
||||
mSaveTimer.start (500);
|
||||
if (mSaving.isRunning())
|
||||
throw std::logic_error (
|
||||
"Failed to initiate save, because a save operation is already running.");
|
||||
|
||||
mSaving.start();
|
||||
|
||||
emit stateChanged (getState(), this);
|
||||
emit progress (1, 16, State_Saving, 1, this);
|
||||
}
|
||||
|
||||
CSMWorld::UniversalId CSMDoc::Document::verify()
|
||||
|
@ -2218,46 +2337,28 @@ 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::reportMessage (const QString& message, int type)
|
||||
{
|
||||
/// \todo find a better way to get these messages to the user.
|
||||
std::cout << message.toUtf8().constData() << std::endl;
|
||||
}
|
||||
|
||||
void CSMDoc::Document::operationDone (int type)
|
||||
{
|
||||
emit stateChanged (getState(), this);
|
||||
}
|
||||
|
||||
void CSMDoc::Document::saving()
|
||||
{
|
||||
++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);
|
||||
}
|
||||
}
|
||||
|
||||
const CSMWorld::Data& CSMDoc::Document::getData() const
|
||||
{
|
||||
return mData;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "../tools/tools.hpp"
|
||||
|
||||
#include "state.hpp"
|
||||
#include "saving.hpp"
|
||||
|
||||
class QAbstractItemModel;
|
||||
|
||||
|
@ -23,6 +24,11 @@ namespace ESM
|
|||
struct Global;
|
||||
}
|
||||
|
||||
namespace Files
|
||||
{
|
||||
class ConfigurationManager;
|
||||
}
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document : public QObject
|
||||
|
@ -32,16 +38,17 @@ namespace CSMDoc
|
|||
private:
|
||||
|
||||
boost::filesystem::path mSavePath;
|
||||
std::vector<boost::filesystem::path> mContentFiles;
|
||||
CSMWorld::Data mData;
|
||||
CSMTools::Tools mTools;
|
||||
boost::filesystem::path mProjectPath;
|
||||
Saving mSaving;
|
||||
boost::filesystem::path mResDir;
|
||||
|
||||
// It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is
|
||||
// using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late.
|
||||
QUndoStack mUndoStack;
|
||||
|
||||
int mSaveCount; ///< dummy implementation -> remove when proper save is implemented.
|
||||
QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented.
|
||||
|
||||
// not implemented
|
||||
Document (const Document&);
|
||||
Document& operator= (const Document&);
|
||||
|
@ -64,8 +71,7 @@ namespace CSMDoc
|
|||
|
||||
public:
|
||||
|
||||
Document (const std::vector<boost::filesystem::path>& files,
|
||||
const boost::filesystem::path& savePath, bool new_);
|
||||
Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_);
|
||||
|
||||
~Document();
|
||||
|
||||
|
@ -75,6 +81,10 @@ namespace CSMDoc
|
|||
|
||||
const boost::filesystem::path& getSavePath() const;
|
||||
|
||||
const std::vector<boost::filesystem::path>& getContentFiles() const;
|
||||
///< \attention The last element in this collection is the file that is being edited,
|
||||
/// but with its original path instead of the save path.
|
||||
|
||||
void save();
|
||||
|
||||
CSMWorld::UniversalId verify();
|
||||
|
@ -98,10 +108,9 @@ namespace CSMDoc
|
|||
|
||||
void modificationStateChanged (bool clean);
|
||||
|
||||
void operationDone (int type);
|
||||
void reportMessage (const QString& message, int type);
|
||||
|
||||
void saving();
|
||||
///< dummy implementation -> remove when proper save is implemented.
|
||||
void operationDone (int type);
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -110,3 +119,4 @@ namespace CSMDoc
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -4,9 +4,22 @@
|
|||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifndef Q_MOC_RUN
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#endif
|
||||
|
||||
#include "document.hpp"
|
||||
|
||||
CSMDoc::DocumentManager::DocumentManager() {}
|
||||
CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
|
||||
: mConfiguration (configuration)
|
||||
{
|
||||
boost::filesystem::path projectPath = configuration.getUserPath() / "projects";
|
||||
|
||||
if (!boost::filesystem::is_directory (projectPath))
|
||||
boost::filesystem::create_directories (projectPath);
|
||||
}
|
||||
|
||||
CSMDoc::DocumentManager::~DocumentManager()
|
||||
{
|
||||
|
@ -17,7 +30,7 @@ CSMDoc::DocumentManager::~DocumentManager()
|
|||
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
|
||||
bool new_)
|
||||
{
|
||||
Document *document = new Document (files, savePath, new_);
|
||||
Document *document = new Document (mConfiguration, files, savePath, mResDir, new_);
|
||||
|
||||
mDocuments.push_back (document);
|
||||
|
||||
|
@ -35,4 +48,9 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document)
|
|||
delete document;
|
||||
|
||||
return mDocuments.empty();
|
||||
}
|
||||
|
||||
void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir)
|
||||
{
|
||||
mResDir = boost::filesystem::system_complete(parResDir);
|
||||
}
|
|
@ -6,6 +6,11 @@
|
|||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace Files
|
||||
{
|
||||
class ConfigurationManager;
|
||||
}
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document;
|
||||
|
@ -13,18 +18,18 @@ namespace CSMDoc
|
|||
class DocumentManager
|
||||
{
|
||||
std::vector<Document *> mDocuments;
|
||||
const Files::ConfigurationManager& mConfiguration;
|
||||
|
||||
DocumentManager (const DocumentManager&);
|
||||
DocumentManager& operator= (const DocumentManager&);
|
||||
|
||||
public:
|
||||
|
||||
DocumentManager();
|
||||
DocumentManager (const Files::ConfigurationManager& configuration);
|
||||
|
||||
~DocumentManager();
|
||||
|
||||
Document *addDocument (const std::vector<boost::filesystem::path>& files,
|
||||
const boost::filesystem::path& savePath, bool new_);
|
||||
Document *addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_);
|
||||
///< The ownership of the returned document is not transferred to the caller.
|
||||
///
|
||||
/// \param new_ Do not load the last content file in \a files and instead create in an
|
||||
|
@ -32,6 +37,10 @@ namespace CSMDoc
|
|||
|
||||
bool removeDocument (Document *document);
|
||||
///< \return last document removed?
|
||||
void setResourceDir (const boost::filesystem::path& parResDir);
|
||||
|
||||
private:
|
||||
boost::filesystem::path mResDir;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,16 +6,16 @@
|
|||
|
||||
#include <QTimer>
|
||||
|
||||
#include "../doc/state.hpp"
|
||||
|
||||
#include "state.hpp"
|
||||
#include "stage.hpp"
|
||||
|
||||
void CSMTools::Operation::prepareStages()
|
||||
void CSMDoc::Operation::prepareStages()
|
||||
{
|
||||
mCurrentStage = mStages.begin();
|
||||
mCurrentStep = 0;
|
||||
mCurrentStepTotal = 0;
|
||||
mTotalSteps = 0;
|
||||
mError = false;
|
||||
|
||||
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
|
||||
{
|
||||
|
@ -24,38 +24,61 @@ void CSMTools::Operation::prepareStages()
|
|||
}
|
||||
}
|
||||
|
||||
CSMTools::Operation::Operation (int type) : mType (type) {}
|
||||
CSMDoc::Operation::Operation (int type, bool ordered, bool finalAlways)
|
||||
: mType (type), mOrdered (ordered), mFinalAlways (finalAlways)
|
||||
{
|
||||
connect (this, SIGNAL (finished()), this, SLOT (operationDone()));
|
||||
}
|
||||
|
||||
CSMTools::Operation::~Operation()
|
||||
CSMDoc::Operation::~Operation()
|
||||
{
|
||||
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
|
||||
delete iter->first;
|
||||
}
|
||||
|
||||
void CSMTools::Operation::run()
|
||||
void CSMDoc::Operation::run()
|
||||
{
|
||||
prepareStages();
|
||||
|
||||
QTimer timer;
|
||||
|
||||
timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify()));
|
||||
timer.connect (&timer, SIGNAL (timeout()), this, SLOT (executeStage()));
|
||||
|
||||
timer.start (0);
|
||||
|
||||
exec();
|
||||
}
|
||||
|
||||
void CSMTools::Operation::appendStage (Stage *stage)
|
||||
void CSMDoc::Operation::appendStage (Stage *stage)
|
||||
{
|
||||
mStages.push_back (std::make_pair (stage, 0));
|
||||
}
|
||||
|
||||
void CSMTools::Operation::abort()
|
||||
bool CSMDoc::Operation::hasError() const
|
||||
{
|
||||
exit();
|
||||
return mError;
|
||||
}
|
||||
|
||||
void CSMTools::Operation::verify()
|
||||
void CSMDoc::Operation::abort()
|
||||
{
|
||||
if (!isRunning())
|
||||
return;
|
||||
|
||||
mError = true;
|
||||
|
||||
if (mFinalAlways)
|
||||
{
|
||||
if (mStages.begin()!=mStages.end() && mCurrentStage!=--mStages.end())
|
||||
{
|
||||
mCurrentStep = 0;
|
||||
mCurrentStage = --mStages.end();
|
||||
}
|
||||
}
|
||||
else
|
||||
mCurrentStage = mStages.end();
|
||||
}
|
||||
|
||||
void CSMDoc::Operation::executeStage()
|
||||
{
|
||||
std::vector<std::string> messages;
|
||||
|
||||
|
@ -68,7 +91,16 @@ void CSMTools::Operation::verify()
|
|||
}
|
||||
else
|
||||
{
|
||||
mCurrentStage->first->perform (mCurrentStep++, messages);
|
||||
try
|
||||
{
|
||||
mCurrentStage->first->perform (mCurrentStep++, messages);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
emit reportMessage (e.what(), mType);
|
||||
abort();
|
||||
}
|
||||
|
||||
++mCurrentStepTotal;
|
||||
break;
|
||||
}
|
||||
|
@ -81,4 +113,9 @@ void CSMTools::Operation::verify()
|
|||
|
||||
if (mCurrentStage==mStages.end())
|
||||
exit();
|
||||
}
|
||||
|
||||
void CSMDoc::Operation::operationDone()
|
||||
{
|
||||
emit done (mType);
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
#ifndef CSM_TOOLS_OPERATION_H
|
||||
#define CSM_TOOLS_OPERATION_H
|
||||
#ifndef CSM_DOC_OPERATION_H
|
||||
#define CSM_DOC_OPERATION_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QThread>
|
||||
|
||||
namespace CSMTools
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Stage;
|
||||
|
||||
|
@ -19,12 +19,17 @@ namespace CSMTools
|
|||
int mCurrentStep;
|
||||
int mCurrentStepTotal;
|
||||
int mTotalSteps;
|
||||
int mOrdered;
|
||||
bool mFinalAlways;
|
||||
bool mError;
|
||||
|
||||
void prepareStages();
|
||||
|
||||
public:
|
||||
|
||||
Operation (int type);
|
||||
Operation (int type, bool ordered, bool finalAlways = false);
|
||||
///< \param ordered Stages must be executed in the given order.
|
||||
/// \param finalAlways Execute last stage even if an error occurred during earlier stages.
|
||||
|
||||
virtual ~Operation();
|
||||
|
||||
|
@ -35,19 +40,25 @@ namespace CSMTools
|
|||
///
|
||||
/// \attention Do no call this function while this Operation is running.
|
||||
|
||||
bool hasError() const;
|
||||
|
||||
signals:
|
||||
|
||||
void progress (int current, int max, int type);
|
||||
|
||||
void reportMessage (const QString& message, int type);
|
||||
|
||||
void done (int type);
|
||||
|
||||
public slots:
|
||||
|
||||
void abort();
|
||||
|
||||
private slots:
|
||||
|
||||
void verify();
|
||||
void executeStage();
|
||||
|
||||
void operationDone();
|
||||
};
|
||||
}
|
||||
|
74
apps/opencs/model/doc/saving.cpp
Normal file
74
apps/opencs/model/doc/saving.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
|
||||
#include "saving.hpp"
|
||||
|
||||
#include "../world/data.hpp"
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "state.hpp"
|
||||
#include "savingstages.hpp"
|
||||
#include "document.hpp"
|
||||
|
||||
CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& projectPath)
|
||||
: Operation (State_Saving, true, true), mDocument (document), mState (*this, projectPath)
|
||||
{
|
||||
// save project file
|
||||
appendStage (new OpenSaveStage (mDocument, mState, true));
|
||||
|
||||
appendStage (new WriteHeaderStage (mDocument, mState, true));
|
||||
|
||||
appendStage (new WriteFilterStage (mDocument, mState, CSMFilter::Filter::Scope_Project));
|
||||
|
||||
appendStage (new CloseSaveStage (mState));
|
||||
|
||||
// save content file
|
||||
appendStage (new OpenSaveStage (mDocument, mState, false));
|
||||
|
||||
appendStage (new WriteHeaderStage (mDocument, mState, false));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Global> >
|
||||
(mDocument.getData().getGlobals(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::GameSetting> >
|
||||
(mDocument.getData().getGmsts(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Skill> >
|
||||
(mDocument.getData().getSkills(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Class> >
|
||||
(mDocument.getData().getClasses(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Faction> >
|
||||
(mDocument.getData().getFactions(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Race> >
|
||||
(mDocument.getData().getRaces(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Sound> >
|
||||
(mDocument.getData().getSounds(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Script> >
|
||||
(mDocument.getData().getScripts(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Region> >
|
||||
(mDocument.getData().getRegions(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::BirthSign> >
|
||||
(mDocument.getData().getBirthsigns(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Spell> >
|
||||
(mDocument.getData().getSpells(), mState));
|
||||
|
||||
/// \todo deal with info records for topcis and journals
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Dialogue> >
|
||||
(mDocument.getData().getTopics(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Dialogue> >
|
||||
(mDocument.getData().getJournals(), mState));
|
||||
|
||||
appendStage (new WriteRefIdCollectionStage (mDocument, mState));
|
||||
|
||||
|
||||
appendStage (new CloseSaveStage (mState));
|
||||
|
||||
appendStage (new FinalSavingStage (mDocument, mState));
|
||||
}
|
27
apps/opencs/model/doc/saving.hpp
Normal file
27
apps/opencs/model/doc/saving.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef CSM_DOC_SAVING_H
|
||||
#define CSM_DOC_SAVING_H
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include "operation.hpp"
|
||||
#include "savingstate.hpp"
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document;
|
||||
|
||||
class Saving : public Operation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Document& mDocument;
|
||||
SavingState mState;
|
||||
|
||||
public:
|
||||
|
||||
Saving (Document& document, const boost::filesystem::path& projectPath);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
161
apps/opencs/model/doc/savingstages.cpp
Normal file
161
apps/opencs/model/doc/savingstages.cpp
Normal file
|
@ -0,0 +1,161 @@
|
|||
|
||||
#include "savingstages.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <QUndoStack>
|
||||
|
||||
#include "document.hpp"
|
||||
#include "savingstate.hpp"
|
||||
|
||||
CSMDoc::OpenSaveStage::OpenSaveStage (Document& document, SavingState& state, bool projectFile)
|
||||
: mDocument (document), mState (state), mProjectFile (projectFile)
|
||||
{}
|
||||
|
||||
int CSMDoc::OpenSaveStage::setup()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSMDoc::OpenSaveStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
mState.start (mDocument, mProjectFile);
|
||||
|
||||
mState.getStream().open ((mProjectFile ? mState.getPath() : mState.getTmpPath()).string().c_str());
|
||||
|
||||
if (!mState.getStream().is_open())
|
||||
throw std::runtime_error ("failed to open stream for saving");
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::WriteHeaderStage::WriteHeaderStage (Document& document, SavingState& state, bool simple)
|
||||
: mDocument (document), mState (state), mSimple (simple)
|
||||
{}
|
||||
|
||||
int CSMDoc::WriteHeaderStage::setup()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSMDoc::WriteHeaderStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
mState.getWriter().setVersion();
|
||||
|
||||
mState.getWriter().clearMaster();
|
||||
|
||||
mState.getWriter().setFormat (0);
|
||||
|
||||
if (mSimple)
|
||||
{
|
||||
mState.getWriter().setAuthor ("");
|
||||
mState.getWriter().setDescription ("");
|
||||
mState.getWriter().setRecordCount (0);
|
||||
}
|
||||
else
|
||||
{
|
||||
mState.getWriter().setAuthor (mDocument.getData().getAuthor());
|
||||
mState.getWriter().setDescription (mDocument.getData().getDescription());
|
||||
mState.getWriter().setRecordCount (
|
||||
mDocument.getData().count (CSMWorld::RecordBase::State_Modified) +
|
||||
mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) +
|
||||
mDocument.getData().count (CSMWorld::RecordBase::State_Deleted));
|
||||
|
||||
/// \todo refine dependency list (at least remove redundant dependencies)
|
||||
std::vector<boost::filesystem::path> dependencies = mDocument.getContentFiles();
|
||||
std::vector<boost::filesystem::path>::const_iterator end (--dependencies.end());
|
||||
|
||||
for (std::vector<boost::filesystem::path>::const_iterator iter (dependencies.begin());
|
||||
iter!=end; ++iter)
|
||||
{
|
||||
std::string name = iter->filename().string();
|
||||
uint64_t size = boost::filesystem::file_size (*iter);
|
||||
|
||||
mState.getWriter().addMaster (name, size);
|
||||
}
|
||||
}
|
||||
|
||||
mState.getWriter().save (mState.getStream());
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::WriteRefIdCollectionStage::WriteRefIdCollectionStage (Document& document, SavingState& state)
|
||||
: mDocument (document), mState (state)
|
||||
{}
|
||||
|
||||
int CSMDoc::WriteRefIdCollectionStage::setup()
|
||||
{
|
||||
return mDocument.getData().getReferenceables().getSize();
|
||||
}
|
||||
|
||||
void CSMDoc::WriteRefIdCollectionStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
mDocument.getData().getReferenceables().save (stage, mState.getWriter());
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::WriteFilterStage::WriteFilterStage (Document& document, SavingState& state,
|
||||
CSMFilter::Filter::Scope scope)
|
||||
: WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> > (document.getData().getFilters(),
|
||||
state),
|
||||
mDocument (document), mScope (scope)
|
||||
{}
|
||||
|
||||
void CSMDoc::WriteFilterStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const CSMWorld::Record<CSMFilter::Filter>& record =
|
||||
mDocument.getData().getFilters().getRecord (stage);
|
||||
|
||||
if (record.get().mScope==mScope)
|
||||
WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> >::perform (stage, messages);
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state)
|
||||
: mState (state)
|
||||
{}
|
||||
|
||||
int CSMDoc::CloseSaveStage::setup()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSMDoc::CloseSaveStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
mState.getStream().close();
|
||||
|
||||
if (!mState.getStream())
|
||||
throw std::runtime_error ("saving failed");
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::FinalSavingStage::FinalSavingStage (Document& document, SavingState& state)
|
||||
: mDocument (document), mState (state)
|
||||
{}
|
||||
|
||||
int CSMDoc::FinalSavingStage::setup()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSMDoc::FinalSavingStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
if (mState.hasError())
|
||||
{
|
||||
mState.getWriter().close();
|
||||
mState.getStream().close();
|
||||
|
||||
if (boost::filesystem::exists (mState.getTmpPath()))
|
||||
boost::filesystem::remove (mState.getTmpPath());
|
||||
}
|
||||
else if (!mState.isProjectFile())
|
||||
{
|
||||
if (boost::filesystem::exists (mState.getPath()))
|
||||
boost::filesystem::remove (mState.getPath());
|
||||
|
||||
boost::filesystem::rename (mState.getTmpPath(), mState.getPath());
|
||||
|
||||
mDocument.getUndoStack().setClean();
|
||||
}
|
||||
}
|
172
apps/opencs/model/doc/savingstages.hpp
Normal file
172
apps/opencs/model/doc/savingstages.hpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
#ifndef CSM_DOC_SAVINGSTAGES_H
|
||||
#define CSM_DOC_SAVINGSTAGES_H
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
#include "savingstate.hpp"
|
||||
|
||||
#include "../world/record.hpp"
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "../filter/filter.hpp"
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document;
|
||||
class SavingState;
|
||||
|
||||
class OpenSaveStage : public Stage
|
||||
{
|
||||
Document& mDocument;
|
||||
SavingState& mState;
|
||||
bool mProjectFile;
|
||||
|
||||
public:
|
||||
|
||||
OpenSaveStage (Document& document, SavingState& state, bool projectFile);
|
||||
///< \param projectFile Saving the project file instead of the content file.
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
class WriteHeaderStage : public Stage
|
||||
{
|
||||
Document& mDocument;
|
||||
SavingState& mState;
|
||||
bool mSimple;
|
||||
|
||||
public:
|
||||
|
||||
WriteHeaderStage (Document& document, SavingState& state, bool simple);
|
||||
///< \param simple Simplified header (used for project files).
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
|
||||
template<class CollectionT>
|
||||
class WriteCollectionStage : public Stage
|
||||
{
|
||||
const CollectionT& mCollection;
|
||||
SavingState& mState;
|
||||
|
||||
public:
|
||||
|
||||
WriteCollectionStage (const CollectionT& collection, SavingState& state);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
template<class CollectionT>
|
||||
WriteCollectionStage<CollectionT>::WriteCollectionStage (const CollectionT& collection,
|
||||
SavingState& state)
|
||||
: mCollection (collection), mState (state)
|
||||
{}
|
||||
|
||||
template<class CollectionT>
|
||||
int WriteCollectionStage<CollectionT>::setup()
|
||||
{
|
||||
return mCollection.getSize();
|
||||
}
|
||||
|
||||
template<class CollectionT>
|
||||
void WriteCollectionStage<CollectionT>::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState;
|
||||
|
||||
if (state==CSMWorld::RecordBase::State_Modified ||
|
||||
state==CSMWorld::RecordBase::State_ModifiedOnly)
|
||||
{
|
||||
std::string type;
|
||||
for (int i=0; i<4; ++i)
|
||||
/// \todo make endianess agnostic (change ESMWriter interface?)
|
||||
type += reinterpret_cast<const char *> (&mCollection.getRecord (stage).mModified.sRecordId)[i];
|
||||
|
||||
mState.getWriter().startRecord (type);
|
||||
mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage));
|
||||
mCollection.getRecord (stage).mModified.save (mState.getWriter());
|
||||
mState.getWriter().endRecord (type);
|
||||
}
|
||||
else if (state==CSMWorld::RecordBase::State_Deleted)
|
||||
{
|
||||
/// \todo write record with delete flag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class WriteRefIdCollectionStage : public Stage
|
||||
{
|
||||
Document& mDocument;
|
||||
SavingState& mState;
|
||||
|
||||
public:
|
||||
|
||||
WriteRefIdCollectionStage (Document& document, SavingState& state);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
|
||||
class WriteFilterStage : public WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> >
|
||||
{
|
||||
Document& mDocument;
|
||||
CSMFilter::Filter::Scope mScope;
|
||||
|
||||
public:
|
||||
|
||||
WriteFilterStage (Document& document, SavingState& state, CSMFilter::Filter::Scope scope);
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
|
||||
class CloseSaveStage : public Stage
|
||||
{
|
||||
SavingState& mState;
|
||||
|
||||
public:
|
||||
|
||||
CloseSaveStage (SavingState& state);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
class FinalSavingStage : public Stage
|
||||
{
|
||||
Document& mDocument;
|
||||
SavingState& mState;
|
||||
|
||||
public:
|
||||
|
||||
FinalSavingStage (Document& document, SavingState& state);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
65
apps/opencs/model/doc/savingstate.cpp
Normal file
65
apps/opencs/model/doc/savingstate.cpp
Normal file
|
@ -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;
|
||||
}
|
50
apps/opencs/model/doc/savingstate.hpp
Normal file
50
apps/opencs/model/doc/savingstate.hpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#ifndef CSM_DOC_SAVINGSTATE_H
|
||||
#define CSM_DOC_SAVINGSTATE_H
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Operation;
|
||||
class Document;
|
||||
|
||||
class SavingState
|
||||
{
|
||||
Operation& mOperation;
|
||||
boost::filesystem::path mPath;
|
||||
boost::filesystem::path mTmpPath;
|
||||
ToUTF8::Utf8Encoder mEncoder;
|
||||
std::ofstream mStream;
|
||||
ESM::ESMWriter mWriter;
|
||||
boost::filesystem::path mProjectPath;
|
||||
bool mProjectFile;
|
||||
|
||||
public:
|
||||
|
||||
SavingState (Operation& operation, const boost::filesystem::path& projectPath);
|
||||
|
||||
bool hasError() const;
|
||||
|
||||
void start (Document& document, bool project);
|
||||
///< \param project Save project file instead of content file.
|
||||
|
||||
const boost::filesystem::path& getPath() const;
|
||||
|
||||
const boost::filesystem::path& getTmpPath() const;
|
||||
|
||||
std::ofstream& getStream();
|
||||
|
||||
ESM::ESMWriter& getWriter();
|
||||
|
||||
bool isProjectFile() const;
|
||||
///< Currently saving project file? (instead of content file)
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
4
apps/opencs/model/doc/stage.cpp
Normal file
4
apps/opencs/model/doc/stage.cpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
#include "stage.hpp"
|
||||
|
||||
CSMDoc::Stage::~Stage() {}
|
|
@ -1,10 +1,10 @@
|
|||
#ifndef CSM_TOOLS_STAGE_H
|
||||
#define CSM_TOOLS_STAGE_H
|
||||
#ifndef CSM_DOC_STAGE_H
|
||||
#define CSM_DOC_STAGE_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace CSMTools
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Stage
|
||||
{
|
||||
|
@ -16,7 +16,7 @@ namespace CSMTools
|
|||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages) = 0;
|
||||
///< Messages resulting from this tage will be appended to \a messages.
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
}
|
||||
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that birthsign records are internally consistent
|
||||
class BirthsignCheckStage : public Stage
|
||||
class BirthsignCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns;
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that class records are internally consistent
|
||||
class ClassCheckStage : public Stage
|
||||
class ClassCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Class>& mClasses;
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that faction records are internally consistent
|
||||
class FactionCheckStage : public Stage
|
||||
class FactionCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
@ -16,7 +16,7 @@ namespace CSMWorld
|
|||
namespace CSMTools
|
||||
{
|
||||
/// \brief Verify stage: make sure that records with specific IDs exist.
|
||||
class MandatoryIdStage : public Stage
|
||||
class MandatoryIdStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::CollectionBase& mIdCollection;
|
||||
CSMWorld::UniversalId mCollectionId;
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that race records are internally consistent
|
||||
class RaceCheckStage : public Stage
|
||||
class RaceCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Race>& mRaces;
|
||||
bool mPlayable;
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that region records are internally consistent
|
||||
class RegionCheckStage : public Stage
|
||||
class RegionCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Region>& mRegions;
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that skill records are internally consistent
|
||||
class SkillCheckStage : public Stage
|
||||
class SkillCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Skill>& mSkills;
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that sound records are internally consistent
|
||||
class SoundCheckStage : public Stage
|
||||
class SoundCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Sound>& mSounds;
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that spell records are internally consistent
|
||||
class SpellCheckStage : public Stage
|
||||
class SpellCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Spell>& mSpells;
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
|
||||
#include "stage.hpp"
|
||||
|
||||
CSMTools::Stage::~Stage() {}
|
|
@ -3,9 +3,8 @@
|
|||
|
||||
#include <QThreadPool>
|
||||
|
||||
#include "verifier.hpp"
|
||||
|
||||
#include "../doc/state.hpp"
|
||||
#include "../doc/operation.hpp"
|
||||
|
||||
#include "../world/data.hpp"
|
||||
#include "../world/universalid.hpp"
|
||||
|
@ -21,7 +20,7 @@
|
|||
#include "birthsigncheck.hpp"
|
||||
#include "spellcheck.hpp"
|
||||
|
||||
CSMTools::Operation *CSMTools::Tools::get (int type)
|
||||
CSMDoc::Operation *CSMTools::Tools::get (int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
|
@ -31,19 +30,19 @@ CSMTools::Operation *CSMTools::Tools::get (int type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
const CSMTools::Operation *CSMTools::Tools::get (int type) const
|
||||
const CSMDoc::Operation *CSMTools::Tools::get (int type) const
|
||||
{
|
||||
return const_cast<Tools *> (this)->get (type);
|
||||
}
|
||||
|
||||
CSMTools::Verifier *CSMTools::Tools::getVerifier()
|
||||
CSMDoc::Operation *CSMTools::Tools::getVerifier()
|
||||
{
|
||||
if (!mVerifier)
|
||||
{
|
||||
mVerifier = new Verifier;
|
||||
mVerifier = new CSMDoc::Operation (CSMDoc::State_Verifying, false);
|
||||
|
||||
connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
|
||||
connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone()));
|
||||
connect (mVerifier, SIGNAL (done (int)), this, SIGNAL (done (int)));
|
||||
connect (mVerifier, SIGNAL (reportMessage (const QString&, int)),
|
||||
this, SLOT (verifierMessage (const QString&, int)));
|
||||
|
||||
|
@ -103,7 +102,7 @@ CSMWorld::UniversalId CSMTools::Tools::runVerifier()
|
|||
|
||||
void CSMTools::Tools::abortOperation (int type)
|
||||
{
|
||||
if (Operation *operation = get (type))
|
||||
if (CSMDoc::Operation *operation = get (type))
|
||||
operation->abort();
|
||||
}
|
||||
|
||||
|
@ -118,7 +117,7 @@ int CSMTools::Tools::getRunningOperations() const
|
|||
int result = 0;
|
||||
|
||||
for (int i=0; sOperations[i]!=-1; ++i)
|
||||
if (const Operation *operation = get (sOperations[i]))
|
||||
if (const CSMDoc::Operation *operation = get (sOperations[i]))
|
||||
if (operation->isRunning())
|
||||
result |= sOperations[i];
|
||||
|
||||
|
@ -133,11 +132,6 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId&
|
|||
return mReports.at (id.getIndex());
|
||||
}
|
||||
|
||||
void CSMTools::Tools::verifierDone()
|
||||
{
|
||||
emit done (CSMDoc::State_Verifying);
|
||||
}
|
||||
|
||||
void CSMTools::Tools::verifierMessage (const QString& message, int type)
|
||||
{
|
||||
std::map<int, int>::iterator iter = mActiveReports.find (type);
|
||||
|
|
|
@ -11,10 +11,13 @@ namespace CSMWorld
|
|||
class UniversalId;
|
||||
}
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Operation;
|
||||
}
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
class Verifier;
|
||||
class Operation;
|
||||
class ReportModel;
|
||||
|
||||
class Tools : public QObject
|
||||
|
@ -22,7 +25,7 @@ namespace CSMTools
|
|||
Q_OBJECT
|
||||
|
||||
CSMWorld::Data& mData;
|
||||
Verifier *mVerifier;
|
||||
CSMDoc::Operation *mVerifier;
|
||||
std::map<int, ReportModel *> mReports;
|
||||
int mNextReportNumber;
|
||||
std::map<int, int> mActiveReports; // type, report number
|
||||
|
@ -31,12 +34,12 @@ namespace CSMTools
|
|||
Tools (const Tools&);
|
||||
Tools& operator= (const Tools&);
|
||||
|
||||
Verifier *getVerifier();
|
||||
CSMDoc::Operation *getVerifier();
|
||||
|
||||
Operation *get (int type);
|
||||
CSMDoc::Operation *get (int type);
|
||||
///< Returns a 0-pointer, if operation hasn't been used yet.
|
||||
|
||||
const Operation *get (int type) const;
|
||||
const CSMDoc::Operation *get (int type) const;
|
||||
///< Returns a 0-pointer, if operation hasn't been used yet.
|
||||
|
||||
public:
|
||||
|
@ -58,8 +61,6 @@ namespace CSMTools
|
|||
|
||||
private slots:
|
||||
|
||||
void verifierDone();
|
||||
|
||||
void verifierMessage (const QString& message, int type);
|
||||
|
||||
signals:
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
#include "verifier.hpp"
|
||||
|
||||
#include "../doc/state.hpp"
|
||||
|
||||
CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying)
|
||||
{}
|
|
@ -1,17 +0,0 @@
|
|||
#ifndef CSM_TOOLS_VERIFIER_H
|
||||
#define CSM_TOOLS_VERIFIER_H
|
||||
|
||||
#include "operation.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
class Verifier : public Operation
|
||||
{
|
||||
public:
|
||||
|
||||
Verifier();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,6 +1,31 @@
|
|||
|
||||
#include "collectionbase.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "columnbase.hpp"
|
||||
|
||||
CSMWorld::CollectionBase::CollectionBase() {}
|
||||
|
||||
CSMWorld::CollectionBase::~CollectionBase() {}
|
||||
|
||||
int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const
|
||||
{
|
||||
int columns = getColumns();
|
||||
|
||||
for (int i=0; i<columns; ++i)
|
||||
if (getColumn (i).mColumnId==id)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CSMWorld::CollectionBase::findColumnIndex (Columns::ColumnId id) const
|
||||
{
|
||||
int index = searchColumnIndex (id);
|
||||
|
||||
if (index==-1)
|
||||
throw std::logic_error ("invalid column index");
|
||||
|
||||
return index;
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "universalid.hpp"
|
||||
#include "columns.hpp"
|
||||
|
||||
class QVariant;
|
||||
|
||||
|
@ -83,6 +84,13 @@ namespace CSMWorld
|
|||
///< Return a sorted collection of all IDs
|
||||
///
|
||||
/// \param listDeleted include deleted record in the list
|
||||
|
||||
int searchColumnIndex (Columns::ColumnId id) const;
|
||||
///< Return index of column with the given \a id. If no such column exists, -1 is returned.
|
||||
|
||||
int findColumnIndex (Columns::ColumnId id) const;
|
||||
///< Return index of column with the given \a id. If no such column exists, an exception is
|
||||
/// thrown.
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,8 @@ namespace CSMWorld
|
|||
Display_CreatureType,
|
||||
Display_WeaponType,
|
||||
Display_RecordState,
|
||||
Display_RefRecordType
|
||||
Display_RefRecordType,
|
||||
Display_DialogueType
|
||||
};
|
||||
|
||||
int mColumnId;
|
||||
|
|
|
@ -1217,6 +1217,37 @@ namespace CSMWorld
|
|||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct ScopeColumn : public Column<ESXRecordT>
|
||||
{
|
||||
ScopeColumn()
|
||||
: Column<ESXRecordT> (Columns::ColumnId_Scope, ColumnBase::Display_Integer, 0)
|
||||
{}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return static_cast<int> (record.get().mScope);
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
record2.mScope = static_cast<CSMFilter::Filter::Scope> (data.toInt());
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool isUserEditable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct PosColumn : public Column<ESXRecordT>
|
||||
{
|
||||
|
@ -1284,6 +1315,39 @@ namespace CSMWorld
|
|||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct DialogueTypeColumn : public Column<ESXRecordT>
|
||||
{
|
||||
DialogueTypeColumn (bool hidden = false)
|
||||
: Column<ESXRecordT> (Columns::ColumnId_DialogueType, ColumnBase::Display_DialogueType,
|
||||
hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue)
|
||||
{}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return static_cast<int> (record.get().mType);
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mType = data.toInt();
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool isUserEditable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -159,6 +159,8 @@ namespace CSMWorld
|
|||
{ ColumnId_DoorPositionXRot, "Teleport Rot X" },
|
||||
{ ColumnId_DoorPositionYRot, "Teleport Rot Y" },
|
||||
{ ColumnId_DoorPositionZRot, "Teleport Rot Z" },
|
||||
{ ColumnId_DialogueType, "Dialogue Type" },
|
||||
{ ColumnId_Scope, "Scope", },
|
||||
|
||||
{ ColumnId_UseValue1, "Use value 1" },
|
||||
{ ColumnId_UseValue2, "Use value 2" },
|
||||
|
@ -269,6 +271,11 @@ namespace
|
|||
"unknown", "none", "short", "integer", "long", "float", "string", 0
|
||||
};
|
||||
|
||||
static const char *sDialogueTypeEnums[] =
|
||||
{
|
||||
"Topic", "Voice", "Greeting", "Persuasion", 0
|
||||
};
|
||||
|
||||
const char **getEnumNames (CSMWorld::Columns::ColumnId column)
|
||||
{
|
||||
switch (column)
|
||||
|
@ -283,6 +290,7 @@ namespace
|
|||
case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes;
|
||||
case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums;
|
||||
case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums;
|
||||
case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums;
|
||||
|
||||
default: return 0;
|
||||
}
|
||||
|
|
|
@ -152,6 +152,8 @@ namespace CSMWorld
|
|||
ColumnId_DoorPositionXRot = 139,
|
||||
ColumnId_DoorPositionYRot = 140,
|
||||
ColumnId_DoorPositionZRot = 141,
|
||||
ColumnId_DialogueType = 142,
|
||||
ColumnId_Scope = 143,
|
||||
|
||||
// Allocated to a separate value range, so we don't get a collision should we ever need
|
||||
// to extend the number of use values.
|
||||
|
|
|
@ -44,6 +44,17 @@ void CSMWorld::Data::appendIds (std::vector<std::string>& ids, const CollectionB
|
|||
ids.insert (ids.end(), ids2.begin(), ids2.end());
|
||||
}
|
||||
|
||||
int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collection)
|
||||
{
|
||||
int number = 0;
|
||||
|
||||
for (int i=0; i<collection.getSize(); ++i)
|
||||
if (collection.getRecord (i).mState==state)
|
||||
++number;
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
CSMWorld::Data::Data() : mRefs (mCells)
|
||||
{
|
||||
mGlobals.addColumn (new StringIdColumn<ESM::Global>);
|
||||
|
@ -141,6 +152,14 @@ CSMWorld::Data::Data() : mRefs (mCells)
|
|||
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_StarterSpell, 0x2));
|
||||
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AlwaysSucceeds, 0x4));
|
||||
|
||||
mTopics.addColumn (new StringIdColumn<ESM::Dialogue>);
|
||||
mTopics.addColumn (new RecordStateColumn<ESM::Dialogue>);
|
||||
mTopics.addColumn (new DialogueTypeColumn<ESM::Dialogue>);
|
||||
|
||||
mJournals.addColumn (new StringIdColumn<ESM::Dialogue>);
|
||||
mJournals.addColumn (new RecordStateColumn<ESM::Dialogue>);
|
||||
mJournals.addColumn (new DialogueTypeColumn<ESM::Dialogue> (true));
|
||||
|
||||
mCells.addColumn (new StringIdColumn<Cell>);
|
||||
mCells.addColumn (new RecordStateColumn<Cell>);
|
||||
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));
|
||||
|
@ -184,6 +203,7 @@ CSMWorld::Data::Data() : mRefs (mCells)
|
|||
mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>);
|
||||
mFilters.addColumn (new FilterColumn<CSMFilter::Filter>);
|
||||
mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>);
|
||||
mFilters.addColumn (new ScopeColumn<CSMFilter::Filter>);
|
||||
|
||||
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
|
||||
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst);
|
||||
|
@ -196,6 +216,8 @@ CSMWorld::Data::Data() : mRefs (mCells)
|
|||
addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region);
|
||||
addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign);
|
||||
addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell);
|
||||
addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic);
|
||||
addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal);
|
||||
addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell);
|
||||
addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables,
|
||||
UniversalId::Type_Referenceable);
|
||||
|
@ -319,6 +341,28 @@ CSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells()
|
|||
return mSpells;
|
||||
}
|
||||
|
||||
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getTopics() const
|
||||
{
|
||||
return mTopics;
|
||||
}
|
||||
|
||||
CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getTopics()
|
||||
{
|
||||
return mTopics;
|
||||
}
|
||||
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals() const
|
||||
{
|
||||
return mJournals;
|
||||
}
|
||||
|
||||
CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals()
|
||||
{
|
||||
return mJournals;
|
||||
}
|
||||
|
||||
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells() const
|
||||
{
|
||||
return mCells;
|
||||
|
@ -387,7 +431,7 @@ void CSMWorld::Data::merge()
|
|||
mGlobals.merge();
|
||||
}
|
||||
|
||||
void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
|
||||
void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, bool project)
|
||||
{
|
||||
ESM::ESMReader reader;
|
||||
|
||||
|
@ -397,6 +441,9 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
|
|||
|
||||
reader.open (path.string());
|
||||
|
||||
mAuthor = reader.getAuthor();
|
||||
mDescription = reader.getDesc();
|
||||
|
||||
// Note: We do not need to send update signals here, because at this point the model is not connected
|
||||
// to any view.
|
||||
while (reader.hasMoreRecs())
|
||||
|
@ -447,6 +494,54 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
|
|||
case ESM::REC_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break;
|
||||
case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break;
|
||||
|
||||
case ESM::REC_DIAL:
|
||||
{
|
||||
std::string id = reader.getHNOString ("NAME");
|
||||
|
||||
ESM::Dialogue record;
|
||||
record.mId = id;
|
||||
record.load (reader);
|
||||
|
||||
if (record.mType==ESM::Dialogue::Journal)
|
||||
{
|
||||
mJournals.load (record, base);
|
||||
}
|
||||
else if (record.mType==ESM::Dialogue::Deleted)
|
||||
{
|
||||
if (mJournals.tryDelete (id))
|
||||
{
|
||||
/// \todo handle info records
|
||||
}
|
||||
else if (mTopics.tryDelete (id))
|
||||
{
|
||||
/// \todo handle info records
|
||||
}
|
||||
else
|
||||
{
|
||||
/// \todo report deletion of non-existing record
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mTopics.load (record, base);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::REC_FILT:
|
||||
|
||||
if (project)
|
||||
{
|
||||
mFilters.load (reader, base);
|
||||
mFilters.setData (mFilters.getSize()-1,
|
||||
mFilters.findColumnIndex (CSMWorld::Columns::ColumnId_Scope),
|
||||
static_cast<int> (CSMFilter::Filter::Scope_Project));
|
||||
break;
|
||||
}
|
||||
|
||||
// fall through (filter record in a content file is an error with format 0)
|
||||
|
||||
default:
|
||||
|
||||
/// \todo throw an exception instead, once all records are implemented
|
||||
|
@ -469,10 +564,50 @@ bool CSMWorld::Data::hasId (const std::string& id) const
|
|||
getRegions().searchId (id)!=-1 ||
|
||||
getBirthsigns().searchId (id)!=-1 ||
|
||||
getSpells().searchId (id)!=-1 ||
|
||||
getTopics().searchId (id)!=-1 ||
|
||||
getJournals().searchId (id)!=-1 ||
|
||||
getCells().searchId (id)!=-1 ||
|
||||
getReferenceables().searchId (id)!=-1;
|
||||
}
|
||||
|
||||
int CSMWorld::Data::count (RecordBase::State state) const
|
||||
{
|
||||
return
|
||||
count (state, mGlobals) +
|
||||
count (state, mGmsts) +
|
||||
count (state, mSkills) +
|
||||
count (state, mClasses) +
|
||||
count (state, mFactions) +
|
||||
count (state, mRaces) +
|
||||
count (state, mSounds) +
|
||||
count (state, mScripts) +
|
||||
count (state, mRegions) +
|
||||
count (state, mBirthsigns) +
|
||||
count (state, mSpells) +
|
||||
count (state, mCells) +
|
||||
count (state, mReferenceables);
|
||||
}
|
||||
|
||||
void CSMWorld::Data::setDescription (const std::string& description)
|
||||
{
|
||||
mDescription = description;
|
||||
}
|
||||
|
||||
std::string CSMWorld::Data::getDescription() const
|
||||
{
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
void CSMWorld::Data::setAuthor (const std::string& author)
|
||||
{
|
||||
mAuthor = author;
|
||||
}
|
||||
|
||||
std::string CSMWorld::Data::getAuthor() const
|
||||
{
|
||||
return mAuthor;
|
||||
}
|
||||
|
||||
std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
|
||||
{
|
||||
std::vector<std::string> ids;
|
||||
|
@ -487,6 +622,8 @@ std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
|
|||
appendIds (ids, mRegions, listDeleted);
|
||||
appendIds (ids, mBirthsigns, listDeleted);
|
||||
appendIds (ids, mSpells, listDeleted);
|
||||
appendIds (ids, mTopics, listDeleted);
|
||||
appendIds (ids, mJournals, listDeleted);
|
||||
appendIds (ids, mCells, listDeleted);
|
||||
appendIds (ids, mReferenceables, listDeleted);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <components/esm/loadregn.hpp>
|
||||
#include <components/esm/loadbsgn.hpp>
|
||||
#include <components/esm/loadspel.hpp>
|
||||
#include <components/esm/loaddial.hpp>
|
||||
|
||||
#include "../filter/filter.hpp"
|
||||
|
||||
|
@ -48,12 +49,16 @@ namespace CSMWorld
|
|||
IdCollection<ESM::Region> mRegions;
|
||||
IdCollection<ESM::BirthSign> mBirthsigns;
|
||||
IdCollection<ESM::Spell> mSpells;
|
||||
IdCollection<ESM::Dialogue> mTopics;
|
||||
IdCollection<ESM::Dialogue> mJournals;
|
||||
IdCollection<Cell> mCells;
|
||||
RefIdCollection mReferenceables;
|
||||
RefCollection mRefs;
|
||||
IdCollection<CSMFilter::Filter> mFilters;
|
||||
std::vector<QAbstractItemModel *> mModels;
|
||||
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
|
||||
std::string mAuthor;
|
||||
std::string mDescription;
|
||||
|
||||
// not implemented
|
||||
Data (const Data&);
|
||||
|
@ -66,6 +71,8 @@ namespace CSMWorld
|
|||
bool listDeleted);
|
||||
///< Append all IDs from collection to \a ids.
|
||||
|
||||
static int count (RecordBase::State state, const CollectionBase& collection);
|
||||
|
||||
public:
|
||||
|
||||
Data();
|
||||
|
@ -116,6 +123,14 @@ namespace CSMWorld
|
|||
|
||||
IdCollection<ESM::Spell>& getSpells();
|
||||
|
||||
const IdCollection<ESM::Dialogue>& getTopics() const;
|
||||
|
||||
IdCollection<ESM::Dialogue>& getTopics();
|
||||
|
||||
const IdCollection<ESM::Dialogue>& getJournals() const;
|
||||
|
||||
IdCollection<ESM::Dialogue>& getJournals();
|
||||
|
||||
const IdCollection<Cell>& getCells() const;
|
||||
|
||||
IdCollection<Cell>& getCells();
|
||||
|
@ -141,8 +156,10 @@ namespace CSMWorld
|
|||
void merge();
|
||||
///< Merge modified into base.
|
||||
|
||||
void loadFile (const boost::filesystem::path& path, bool base);
|
||||
void loadFile (const boost::filesystem::path& path, bool base, bool project);
|
||||
///< Merging content of a file into base or modified.
|
||||
///
|
||||
/// \param project load project file instead of content file
|
||||
|
||||
bool hasId (const std::string& id) const;
|
||||
|
||||
|
@ -151,6 +168,17 @@ namespace CSMWorld
|
|||
///
|
||||
/// \param listDeleted include deleted record in the list
|
||||
|
||||
int count (RecordBase::State state) const;
|
||||
///< Return number of top-level records with the given \a state.
|
||||
|
||||
void setDescription (const std::string& description);
|
||||
|
||||
std::string getDescription() const;
|
||||
|
||||
void setAuthor (const std::string& author);
|
||||
|
||||
std::string getAuthor() const;
|
||||
|
||||
signals:
|
||||
|
||||
void idListChanged();
|
||||
|
|
|
@ -7,21 +7,24 @@
|
|||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
||||
/// \brief Single type collection of top level records
|
||||
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
|
||||
class IdCollection : public Collection<ESXRecordT, IdAccessorT>
|
||||
{
|
||||
public:
|
||||
|
||||
void load (ESM::ESMReader& reader, bool base,
|
||||
UniversalId::Type type = UniversalId::Type_None);
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
void load (ESM::ESMReader& reader, bool base);
|
||||
|
||||
void load (const ESXRecordT& record, bool base);
|
||||
|
||||
bool tryDelete (const std::string& id);
|
||||
///< Try deleting \a id. If the id does not exist or can't be deleted the call is ignored.
|
||||
///
|
||||
/// \return Has the ID been deleted?
|
||||
};
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base,
|
||||
UniversalId::Type type)
|
||||
void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
|
||||
{
|
||||
std::string id = reader.getHNOString ("NAME");
|
||||
|
||||
|
@ -56,31 +59,63 @@ namespace CSMWorld
|
|||
IdAccessorT().getId (record) = id;
|
||||
record.load (reader);
|
||||
|
||||
int index = this->searchId (IdAccessorT().getId (record));
|
||||
|
||||
if (index==-1)
|
||||
{
|
||||
// new record
|
||||
Record<ESXRecordT> record2;
|
||||
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||
(base ? record2.mBase : record2.mModified) = record;
|
||||
|
||||
this->appendRecord (record2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// old record
|
||||
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
||||
|
||||
if (base)
|
||||
record2.mBase = record;
|
||||
else
|
||||
record2.setModified (record);
|
||||
|
||||
this->setRecord (index, record2);
|
||||
}
|
||||
load (record, base);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base)
|
||||
{
|
||||
int index = this->searchId (IdAccessorT().getId (record));
|
||||
|
||||
if (index==-1)
|
||||
{
|
||||
// new record
|
||||
Record<ESXRecordT> record2;
|
||||
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||
(base ? record2.mBase : record2.mModified) = record;
|
||||
|
||||
this->appendRecord (record2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// old record
|
||||
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
||||
|
||||
if (base)
|
||||
record2.mBase = record;
|
||||
else
|
||||
record2.setModified (record);
|
||||
|
||||
this->setRecord (index, record2);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
bool IdCollection<ESXRecordT, IdAccessorT>::tryDelete (const std::string& id)
|
||||
{
|
||||
int index = this->searchId (id);
|
||||
|
||||
if (index==-1)
|
||||
return false;
|
||||
|
||||
Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
||||
|
||||
if (record.isDeleted())
|
||||
return false;
|
||||
|
||||
if (record.mState==RecordBase::State_ModifiedOnly)
|
||||
{
|
||||
Collection<ESXRecordT, IdAccessorT>::removeRows (index, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
record.mState = RecordBase::State_Deleted;
|
||||
setRecord (index, record);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -161,21 +161,10 @@ const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id)
|
|||
|
||||
int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const
|
||||
{
|
||||
int columns = mIdCollection->getColumns();
|
||||
|
||||
for (int i=0; i<columns; ++i)
|
||||
if (mIdCollection->getColumn (i).mColumnId==id)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
return mIdCollection->searchColumnIndex (id);
|
||||
}
|
||||
|
||||
int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const
|
||||
{
|
||||
int index = searchColumnIndex (id);
|
||||
|
||||
if (index==-1)
|
||||
throw std::logic_error ("invalid column index");
|
||||
|
||||
return index;
|
||||
return mIdCollection->findColumnIndex (id);
|
||||
}
|
|
@ -539,3 +539,8 @@ std::vector<std::string> CSMWorld::RefIdCollection::getIds (bool listDeleted) co
|
|||
{
|
||||
return mData.getIds (listDeleted);
|
||||
}
|
||||
|
||||
void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const
|
||||
{
|
||||
mData.save (index, writer);
|
||||
}
|
|
@ -9,6 +9,11 @@
|
|||
#include "collectionbase.hpp"
|
||||
#include "refiddata.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMWriter;
|
||||
}
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class RefIdAdapter;
|
||||
|
@ -94,6 +99,8 @@ namespace CSMWorld
|
|||
///< Return a sorted collection of all IDs
|
||||
///
|
||||
/// \param listDeleted include deleted record in the list
|
||||
|
||||
void save (int index, ESM::ESMWriter& writer) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -218,3 +218,16 @@ std::vector<std::string> CSMWorld::RefIdData::getIds (bool listDeleted) const
|
|||
|
||||
return ids;
|
||||
}
|
||||
|
||||
void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const
|
||||
{
|
||||
LocalIndex localIndex = globalToLocalIndex (index);
|
||||
|
||||
std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter =
|
||||
mRecordContainers.find (localIndex.second);
|
||||
|
||||
if (iter==mRecordContainers.end())
|
||||
throw std::logic_error ("invalid local index type");
|
||||
|
||||
iter->second->save (localIndex.first, writer);
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
#include <components/esm/loadweap.hpp>
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/loadmisc.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
|
||||
#include "record.hpp"
|
||||
#include "universalid.hpp"
|
||||
|
@ -51,6 +52,8 @@ namespace CSMWorld
|
|||
virtual void erase (int index, int count) = 0;
|
||||
|
||||
virtual std::string getId (int index) const = 0;
|
||||
|
||||
virtual void save (int index, ESM::ESMWriter& writer) const = 0;
|
||||
};
|
||||
|
||||
template<typename RecordT>
|
||||
|
@ -71,6 +74,8 @@ namespace CSMWorld
|
|||
virtual void erase (int index, int count);
|
||||
|
||||
virtual std::string getId (int index) const;
|
||||
|
||||
virtual void save (int index, ESM::ESMWriter& writer) const;
|
||||
};
|
||||
|
||||
template<typename RecordT>
|
||||
|
@ -123,6 +128,31 @@ namespace CSMWorld
|
|||
return mContainer.at (index).get().mId;
|
||||
}
|
||||
|
||||
template<typename RecordT>
|
||||
void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
|
||||
{
|
||||
CSMWorld::RecordBase::State state = mContainer.at (index).mState;
|
||||
|
||||
if (state==CSMWorld::RecordBase::State_Modified ||
|
||||
state==CSMWorld::RecordBase::State_ModifiedOnly)
|
||||
{
|
||||
std::string type;
|
||||
for (int i=0; i<4; ++i)
|
||||
/// \todo make endianess agnostic (change ESMWriter interface?)
|
||||
type += reinterpret_cast<const char *> (&mContainer.at (index).mModified.sRecordId)[i];
|
||||
|
||||
writer.startRecord (type);
|
||||
writer.writeHNCString ("NAME", getId (index));
|
||||
mContainer.at (index).mModified.save (writer);
|
||||
writer.endRecord (type);
|
||||
}
|
||||
else if (state==CSMWorld::RecordBase::State_Deleted)
|
||||
{
|
||||
/// \todo write record with delete flag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RefIdData
|
||||
{
|
||||
public:
|
||||
|
@ -187,6 +217,8 @@ namespace CSMWorld
|
|||
///< Return a sorted collection of all IDs
|
||||
///
|
||||
/// \param listDeleted include deleted record in the list
|
||||
|
||||
void save (int index, ESM::ESMWriter& writer) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace
|
|||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
|
||||
"Referenceables", 0 },
|
||||
|
@ -54,6 +56,8 @@ namespace
|
|||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":./land.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":./birthsign.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },
|
||||
|
|
|
@ -87,6 +87,10 @@ namespace CSMWorld
|
|||
Type_RegionMap,
|
||||
Type_Filter,
|
||||
Type_Filters,
|
||||
Type_Topics,
|
||||
Type_Topic,
|
||||
Type_Journals,
|
||||
Type_Journal,
|
||||
Type_Scene
|
||||
};
|
||||
|
||||
|
|
|
@ -128,3 +128,8 @@ void CSVDoc::FileDialog::slotRejected()
|
|||
emit rejected();
|
||||
close();
|
||||
}
|
||||
|
||||
void CSVDoc::FileDialog::createNewFile()
|
||||
{
|
||||
emit createNewFile (mAdjusterWidget->getPath());
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace CSVDoc
|
|||
signals:
|
||||
|
||||
void openFiles();
|
||||
void createNewFile();
|
||||
void createNewFile (const boost::filesystem::path& savePath);
|
||||
|
||||
void signalUpdateCreateButton (bool, int);
|
||||
void signalUpdateCreateButtonFlags(int);
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
#include <QDialog>
|
||||
#include <QMetaType>
|
||||
|
||||
#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED
|
||||
#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED
|
||||
Q_DECLARE_METATYPE (boost::filesystem::path)
|
||||
#endif
|
||||
|
||||
class QPushButton;
|
||||
|
||||
|
|
|
@ -104,6 +104,17 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
|
|||
layout->addWidget (createButtons());
|
||||
layout->addWidget (createTools());
|
||||
|
||||
/// \todo remove this label once loading and saving are fully implemented
|
||||
QLabel *warning = new QLabel ("<font color=Red>WARNING:<p>OpenCS is in alpha stage.<br>The code for loading and saving is incomplete.<br>This version of OpenCS is only a preview.<br>Do NOT use it for real editing!<br>You will lose records both on loading and on saving.<p>Please note:<br>If you lose data and come to the OpenMW forum to complain,<br>we will mock you.</font color>");
|
||||
|
||||
QFont font;
|
||||
font.setPointSize (12);
|
||||
font.setBold (true);
|
||||
|
||||
warning->setFont (font);
|
||||
|
||||
layout->addWidget (warning, 1);
|
||||
|
||||
setLayout (layout);
|
||||
|
||||
QRect scr = QApplication::desktop()->screenGeometry();
|
||||
|
|
|
@ -163,6 +163,14 @@ void CSVDoc::View::setupMechanicsMenu()
|
|||
QAction *spells = new QAction (tr ("Spells"), this);
|
||||
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
|
||||
mechanics->addAction (spells);
|
||||
|
||||
QAction *topics = new QAction (tr ("Topics"), this);
|
||||
connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView()));
|
||||
mechanics->addAction (topics);
|
||||
|
||||
QAction *journals = new QAction (tr ("Journals"), this);
|
||||
connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView()));
|
||||
mechanics->addAction (journals);
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupAssetsMenu()
|
||||
|
@ -412,6 +420,16 @@ void CSVDoc::View::addSceneSubView()
|
|||
addSubView (CSMWorld::UniversalId::Type_Scene);
|
||||
}
|
||||
|
||||
void CSVDoc::View::addTopicsSubView()
|
||||
{
|
||||
addSubView (CSMWorld::UniversalId::Type_Topics);
|
||||
}
|
||||
|
||||
void CSVDoc::View::addJournalsSubView()
|
||||
{
|
||||
addSubView (CSMWorld::UniversalId::Type_Journals);
|
||||
}
|
||||
|
||||
void CSVDoc::View::abortOperation (int type)
|
||||
{
|
||||
mDocument->abortOperation (type);
|
||||
|
|
|
@ -166,6 +166,10 @@ namespace CSVDoc
|
|||
|
||||
void addSceneSubView();
|
||||
|
||||
void addTopicsSubView();
|
||||
|
||||
void addJournalsSubView();
|
||||
|
||||
void toggleShowStatusBar (bool show);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
|
|||
{ CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false },
|
||||
{ CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false },
|
||||
{ CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false },
|
||||
{ CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false }
|
||||
{ CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false },
|
||||
{ CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false }
|
||||
};
|
||||
|
||||
for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
|
||||
#include "../../model/filter/filter.hpp"
|
||||
|
||||
#include "../../model/world/data.hpp"
|
||||
#include "../../model/world/commands.hpp"
|
||||
#include "../../model/world/columns.hpp"
|
||||
#include "../../model/world/idtable.hpp"
|
||||
|
||||
std::string CSVFilter::FilterCreator::getNamespace() const
|
||||
{
|
||||
switch (mScope->currentIndex())
|
||||
|
@ -28,6 +33,15 @@ std::string CSVFilter::FilterCreator::getId() const
|
|||
return getNamespace() + GenericCreator::getId();
|
||||
}
|
||||
|
||||
void CSVFilter::FilterCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
|
||||
{
|
||||
int index =
|
||||
dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).
|
||||
findColumnIndex (CSMWorld::Columns::ColumnId_Scope);
|
||||
|
||||
command.addValue (index, mScope->currentIndex());
|
||||
}
|
||||
|
||||
CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id)
|
||||
: GenericCreator (data, undoStack, id)
|
||||
|
@ -39,7 +53,7 @@ CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoS
|
|||
|
||||
mScope->addItem ("Project");
|
||||
mScope->addItem ("Session");
|
||||
/// \ŧodo re-enable for OpenMW 1.1
|
||||
/// \todo re-enable for OpenMW 1.1
|
||||
// mScope->addItem ("Content");
|
||||
|
||||
connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int)));
|
||||
|
|
|
@ -25,6 +25,8 @@ namespace CSVFilter
|
|||
|
||||
virtual std::string getId() const;
|
||||
|
||||
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
|
||||
|
||||
public:
|
||||
|
||||
FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
|
|
35
apps/opencs/view/world/dialoguecreator.cpp
Normal file
35
apps/opencs/view/world/dialoguecreator.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
#include "dialoguecreator.hpp"
|
||||
|
||||
#include <components/esm/loaddial.hpp>
|
||||
|
||||
#include "../../model/world/data.hpp"
|
||||
#include "../../model/world/commands.hpp"
|
||||
#include "../../model/world/columns.hpp"
|
||||
#include "../../model/world/idtable.hpp"
|
||||
|
||||
void CSVWorld::DialogueCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
|
||||
{
|
||||
int index =
|
||||
dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).
|
||||
findColumnIndex (CSMWorld::Columns::ColumnId_DialogueType);
|
||||
|
||||
command.addValue (index, mType);
|
||||
}
|
||||
|
||||
CSVWorld::DialogueCreator::DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id, int type)
|
||||
: GenericCreator (data, undoStack, id), mType (type)
|
||||
{}
|
||||
|
||||
CSVWorld::Creator *CSVWorld::TopicCreatorFactory::makeCreator (CSMWorld::Data& data,
|
||||
QUndoStack& undoStack, const CSMWorld::UniversalId& id) const
|
||||
{
|
||||
return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Topic);
|
||||
}
|
||||
|
||||
CSVWorld::Creator *CSVWorld::JournalCreatorFactory::makeCreator (CSMWorld::Data& data,
|
||||
QUndoStack& undoStack, const CSMWorld::UniversalId& id) const
|
||||
{
|
||||
return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Journal);
|
||||
}
|
41
apps/opencs/view/world/dialoguecreator.hpp
Normal file
41
apps/opencs/view/world/dialoguecreator.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#ifndef CSV_WORLD_DIALOGUECREATOR_H
|
||||
#define CSV_WORLD_DIALOGUECREATOR_H
|
||||
|
||||
#include "genericcreator.hpp"
|
||||
|
||||
namespace CSVWorld
|
||||
{
|
||||
class DialogueCreator : public GenericCreator
|
||||
{
|
||||
int mType;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
|
||||
|
||||
public:
|
||||
|
||||
DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id, int type);
|
||||
};
|
||||
|
||||
class TopicCreatorFactory : public CreatorFactoryBase
|
||||
{
|
||||
public:
|
||||
|
||||
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id) const;
|
||||
///< The ownership of the returned Creator is transferred to the caller.
|
||||
};
|
||||
|
||||
class JournalCreatorFactory : public CreatorFactoryBase
|
||||
{
|
||||
public:
|
||||
|
||||
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id) const;
|
||||
///< The ownership of the returned Creator is transferred to the caller.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -14,6 +14,7 @@
|
|||
#include "referenceablecreator.hpp"
|
||||
#include "referencecreator.hpp"
|
||||
#include "scenesubview.hpp"
|
||||
#include "dialoguecreator.hpp"
|
||||
|
||||
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
|
||||
{
|
||||
|
@ -53,6 +54,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
|
|||
manager.add (CSMWorld::UniversalId::Type_References,
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<ReferenceCreator> >);
|
||||
|
||||
manager.add (CSMWorld::UniversalId::Type_Topics,
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, TopicCreatorFactory>);
|
||||
|
||||
manager.add (CSMWorld::UniversalId::Type_Journal,
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, JournalCreatorFactory>);
|
||||
|
||||
// Subviews for editing/viewing individual records
|
||||
manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>);
|
||||
|
||||
|
|
|
@ -87,19 +87,33 @@ std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
|
|||
{
|
||||
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
|
||||
|
||||
// check record state
|
||||
CSMWorld::RecordBase::State state =
|
||||
static_cast<CSMWorld::RecordBase::State> (
|
||||
mModel->data (mModel->index (index.row(), 1)).toInt());
|
||||
|
||||
if (state!=CSMWorld::RecordBase::State_Deleted)
|
||||
if (state==CSMWorld::RecordBase::State_Deleted)
|
||||
continue;
|
||||
|
||||
// check other columns (only relevant for a subset of the tables)
|
||||
int dialogueTypeIndex =
|
||||
mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType);
|
||||
|
||||
if (dialogueTypeIndex!=-1)
|
||||
{
|
||||
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
|
||||
int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt();
|
||||
|
||||
std::string id = mModel->data (mModel->index (index.row(), columnIndex)).
|
||||
toString().toUtf8().constData();
|
||||
|
||||
deletableIds.push_back (id);
|
||||
if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal)
|
||||
continue;
|
||||
}
|
||||
|
||||
// add the id to the collection
|
||||
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
|
||||
|
||||
std::string id = mModel->data (mModel->index (index.row(), columnIndex)).
|
||||
toString().toUtf8().constData();
|
||||
|
||||
deletableIds.push_back (id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ add_openmw_dir (mwworld
|
|||
cells localscripts customdata weather inventorystore ptr actionopen actionread
|
||||
actionequip timestamp actionalchemy cellstore actionapply actioneat
|
||||
esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
|
||||
contentloader esmloader omwloader
|
||||
)
|
||||
|
||||
add_openmw_dir (mwclass
|
||||
|
|
|
@ -261,34 +261,14 @@ void OMW::Engine::setCell (const std::string& cellName)
|
|||
mCellName = cellName;
|
||||
}
|
||||
|
||||
// Set master file (esm)
|
||||
// - If the given name does not have an extension, ".esm" is added automatically
|
||||
|
||||
void OMW::Engine::addMaster (const std::string& master)
|
||||
void OMW::Engine::addContentFile(const std::string& file)
|
||||
{
|
||||
mMaster.push_back(master);
|
||||
std::string &str = mMaster.back();
|
||||
if (file.find_last_of(".") == std::string::npos)
|
||||
{
|
||||
throw std::runtime_error("Missing extension in content file!");
|
||||
}
|
||||
|
||||
// Append .esm if not already there
|
||||
std::string::size_type sep = str.find_last_of (".");
|
||||
if (sep == std::string::npos)
|
||||
{
|
||||
str += ".esm";
|
||||
}
|
||||
}
|
||||
|
||||
// Add plugin file (esp)
|
||||
void OMW::Engine::addPlugin (const std::string& plugin)
|
||||
{
|
||||
mPlugins.push_back(plugin);
|
||||
std::string &str = mPlugins.back();
|
||||
|
||||
// Append .esp if not already there
|
||||
std::string::size_type sep = str.find_last_of (".");
|
||||
if (sep == std::string::npos)
|
||||
{
|
||||
str += ".esp";
|
||||
}
|
||||
mContentFiles.push_back(file);
|
||||
}
|
||||
|
||||
void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
|
||||
|
@ -401,7 +381,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
mEnvironment.setWindowManager (window);
|
||||
|
||||
// Create the world
|
||||
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins,
|
||||
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles,
|
||||
mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap,
|
||||
mActivationDistanceOverride));
|
||||
MWBase::Environment::get().getWorld()->setupPlayer();
|
||||
|
@ -416,8 +396,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
|
||||
//Load translation data
|
||||
mTranslationDataStorage.setEncoder(mEncoder);
|
||||
for (size_t i = 0; i < mMaster.size(); i++)
|
||||
mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster[i]);
|
||||
for (size_t i = 0; i < mContentFiles.size(); i++)
|
||||
mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]);
|
||||
|
||||
Compiler::registerExtensions (mExtensions);
|
||||
|
||||
|
@ -482,7 +462,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
void OMW::Engine::go()
|
||||
{
|
||||
assert (!mCellName.empty());
|
||||
assert (!mMaster.empty());
|
||||
assert (!mContentFiles.empty());
|
||||
assert (!mOgre);
|
||||
|
||||
Settings::Manager settings;
|
||||
|
|
|
@ -68,8 +68,7 @@ namespace OMW
|
|||
boost::filesystem::path mResDir;
|
||||
OEngine::Render::OgreRenderer *mOgre;
|
||||
std::string mCellName;
|
||||
std::vector<std::string> mMaster;
|
||||
std::vector<std::string> mPlugins;
|
||||
std::vector<std::string> mContentFiles;
|
||||
int mFpsLevel;
|
||||
bool mVerboseScripts;
|
||||
bool mNewGame;
|
||||
|
@ -135,13 +134,11 @@ namespace OMW
|
|||
/// Set start cell name (only interiors for now)
|
||||
void setCell(const std::string& cellName);
|
||||
|
||||
/// Set master file (esm)
|
||||
/// - If the given name does not have an extension, ".esm" is added automatically
|
||||
void addMaster(const std::string& master);
|
||||
|
||||
/// Same as "addMaster", but for plugin files (esp)
|
||||
/// - If the given name does not have an extension, ".esp" is added automatically
|
||||
void addPlugin(const std::string& plugin);
|
||||
/**
|
||||
* @brief addContentFile - Adds content file (ie. esm/esp, or omwgame/omwaddon) to the content files container.
|
||||
* @param file - filename (extension is required)
|
||||
*/
|
||||
void addContentFile(const std::string& file);
|
||||
|
||||
/// Enable fps counter
|
||||
void showFPS(int level);
|
||||
|
|
|
@ -110,11 +110,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
|||
("start", bpo::value<std::string>()->default_value("Beshara"),
|
||||
"set initial cell")
|
||||
|
||||
("master", bpo::value<StringsVector>()->default_value(StringsVector(), "")
|
||||
->multitoken(), "master file(s)")
|
||||
|
||||
("plugin", bpo::value<StringsVector>()->default_value(StringsVector(), "")
|
||||
->multitoken(), "plugin file(s)")
|
||||
("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
|
||||
->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon")
|
||||
|
||||
("anim-verbose", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "output animation indices files")
|
||||
|
@ -152,8 +149,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
|||
|
||||
("activate-dist", bpo::value <int> ()->default_value (-1), "activation distance override");
|
||||
|
||||
;
|
||||
|
||||
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)
|
||||
.options(desc).allow_unregistered().run();
|
||||
|
||||
|
@ -211,29 +206,18 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
|||
|
||||
engine.setResourceDir(variables["resources"].as<std::string>());
|
||||
|
||||
// master and plugin
|
||||
StringsVector master = variables["master"].as<StringsVector>();
|
||||
if (master.empty())
|
||||
StringsVector content = variables["content"].as<StringsVector>();
|
||||
if (content.empty())
|
||||
{
|
||||
std::cout << "No master file given. Aborting...\n";
|
||||
return false;
|
||||
std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
StringsVector plugin = variables["plugin"].as<StringsVector>();
|
||||
// Removed check for 255 files, which would be the hard-coded limit in Morrowind.
|
||||
// I'll keep the following variable in, maybe we can use it for something different.
|
||||
// Say, a feedback like "loading file x/cnt".
|
||||
// Commenting this out for now to silence compiler warning.
|
||||
//int cnt = master.size() + plugin.size();
|
||||
|
||||
// Prepare loading master/plugin files (i.e. send filenames to engine)
|
||||
for (std::vector<std::string>::size_type i = 0; i < master.size(); i++)
|
||||
StringsVector::const_iterator it(content.begin());
|
||||
StringsVector::const_iterator end(content.end());
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
engine.addMaster(master[i]);
|
||||
}
|
||||
for (std::vector<std::string>::size_type i = 0; i < plugin.size(); i++)
|
||||
{
|
||||
engine.addPlugin(plugin[i]);
|
||||
engine.addContentFile(*it);
|
||||
}
|
||||
|
||||
// startup-settings
|
||||
|
|
35
apps/openmw/mwworld/contentloader.hpp
Normal file
35
apps/openmw/mwworld/contentloader.hpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#ifndef CONTENTLOADER_HPP
|
||||
#define CONTENTLOADER_HPP
|
||||
|
||||
#include <iosfwd>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include "components/loadinglistener/loadinglistener.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
struct ContentLoader
|
||||
{
|
||||
ContentLoader(Loading::Listener& listener)
|
||||
: mListener(listener)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ContentLoader()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void load(const boost::filesystem::path& filepath, int& index)
|
||||
{
|
||||
std::cout << "Loading content file " << filepath.string() << std::endl;
|
||||
mListener.setLabel(filepath.string());
|
||||
}
|
||||
|
||||
protected:
|
||||
Loading::Listener& mListener;
|
||||
};
|
||||
|
||||
} /* namespace MWWorld */
|
||||
|
||||
#endif /* CONTENTLOADER_HPP */
|
31
apps/openmw/mwworld/esmloader.cpp
Normal file
31
apps/openmw/mwworld/esmloader.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "esmloader.hpp"
|
||||
#include "esmstore.hpp"
|
||||
|
||||
#include "components/to_utf8/to_utf8.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,
|
||||
ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener)
|
||||
: ContentLoader(listener)
|
||||
, mStore(store)
|
||||
, mEsm(readers)
|
||||
, mEncoder(encoder)
|
||||
{
|
||||
}
|
||||
|
||||
void EsmLoader::load(const boost::filesystem::path& filepath, int& index)
|
||||
{
|
||||
ContentLoader::load(filepath.filename(), index);
|
||||
|
||||
ESM::ESMReader lEsm;
|
||||
lEsm.setEncoder(mEncoder);
|
||||
lEsm.setIndex(index);
|
||||
lEsm.setGlobalReaderList(&mEsm);
|
||||
lEsm.open(filepath.string());
|
||||
mEsm[index] = lEsm;
|
||||
mStore.load(mEsm[index], &mListener);
|
||||
}
|
||||
|
||||
} /* namespace MWWorld */
|
34
apps/openmw/mwworld/esmloader.hpp
Normal file
34
apps/openmw/mwworld/esmloader.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef ESMLOADER_HPP
|
||||
#define ESMLOADER_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "contentloader.hpp"
|
||||
#include "components/esm/esmreader.hpp"
|
||||
|
||||
namespace ToUTF8
|
||||
{
|
||||
class Utf8Encoder;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
class ESMStore;
|
||||
|
||||
struct EsmLoader : public ContentLoader
|
||||
{
|
||||
EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,
|
||||
ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener);
|
||||
|
||||
void load(const boost::filesystem::path& filepath, int& index);
|
||||
|
||||
private:
|
||||
std::vector<ESM::ESMReader>& mEsm;
|
||||
MWWorld::ESMStore& mStore;
|
||||
ToUTF8::Utf8Encoder* mEncoder;
|
||||
};
|
||||
|
||||
} /* namespace MWWorld */
|
||||
|
||||
#endif // ESMLOADER_HPP
|
17
apps/openmw/mwworld/omwloader.cpp
Normal file
17
apps/openmw/mwworld/omwloader.cpp
Normal file
|
@ -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 */
|
||||
|
21
apps/openmw/mwworld/omwloader.hpp
Normal file
21
apps/openmw/mwworld/omwloader.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef OMWLOADER_HPP
|
||||
#define OMWLOADER_HPP
|
||||
|
||||
#include "contentloader.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Placeholder for real OpenMW content loader
|
||||
*/
|
||||
struct OmwLoader : public ContentLoader
|
||||
{
|
||||
OmwLoader(Loading::Listener& listener);
|
||||
|
||||
void load(const boost::filesystem::path& filepath, int& index);
|
||||
};
|
||||
|
||||
} /* namespace MWWorld */
|
||||
|
||||
#endif /* OMWLOADER_HPP */
|
|
@ -1,4 +1,11 @@
|
|||
#include "worldimp.hpp"
|
||||
#ifdef _WIN32
|
||||
#include <boost/tr1/tr1/unordered_map>
|
||||
#elif defined HAVE_UNORDERED_MAP
|
||||
#include <unordered_map>
|
||||
#else
|
||||
#include <tr1/unordered_map>
|
||||
#endif
|
||||
|
||||
#include <OgreSceneNode.h>
|
||||
|
||||
|
@ -31,6 +38,10 @@
|
|||
#include "containerstore.hpp"
|
||||
#include "inventorystore.hpp"
|
||||
|
||||
#include "contentloader.hpp"
|
||||
#include "esmloader.hpp"
|
||||
#include "omwloader.hpp"
|
||||
|
||||
using namespace Ogre;
|
||||
|
||||
namespace
|
||||
|
@ -80,6 +91,38 @@ namespace
|
|||
|
||||
namespace MWWorld
|
||||
{
|
||||
struct GameContentLoader : public ContentLoader
|
||||
{
|
||||
GameContentLoader(Loading::Listener& listener)
|
||||
: ContentLoader(listener)
|
||||
{
|
||||
}
|
||||
|
||||
bool addLoader(const std::string& extension, ContentLoader* loader)
|
||||
{
|
||||
return mLoaders.insert(std::make_pair(extension, loader)).second;
|
||||
}
|
||||
|
||||
void load(const boost::filesystem::path& filepath, int& index)
|
||||
{
|
||||
LoadersContainer::iterator it(mLoaders.find(filepath.extension().string()));
|
||||
if (it != mLoaders.end())
|
||||
{
|
||||
it->second->load(filepath, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string msg("Cannot load file: ");
|
||||
msg += filepath.string();
|
||||
throw std::runtime_error(msg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::tr1::unordered_map<std::string, ContentLoader*> LoadersContainer;
|
||||
LoadersContainer mLoaders;
|
||||
};
|
||||
|
||||
Ptr World::getPtrViaHandle (const std::string& handle, Ptr::CellStore& cell)
|
||||
{
|
||||
if (MWWorld::LiveCellRef<ESM::Activator> *ref =
|
||||
|
@ -163,7 +206,7 @@ namespace MWWorld
|
|||
|
||||
World::World (OEngine::Render::OgreRenderer& renderer,
|
||||
const Files::Collections& fileCollections,
|
||||
const std::vector<std::string>& master, const std::vector<std::string>& plugins,
|
||||
const std::vector<std::string>& contentFiles,
|
||||
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir,
|
||||
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride)
|
||||
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
|
||||
|
@ -181,44 +224,22 @@ namespace MWWorld
|
|||
|
||||
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback);
|
||||
|
||||
int idx = 0;
|
||||
// NOTE: We might need to reserve one more for the running game / save.
|
||||
mEsm.resize(master.size() + plugins.size());
|
||||
mEsm.resize(contentFiles.size());
|
||||
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
||||
listener->loadingOn();
|
||||
for (std::vector<std::string>::size_type i = 0; i < master.size(); i++, idx++)
|
||||
{
|
||||
boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master[i]));
|
||||
|
||||
std::cout << "Loading ESM " << masterPath.string() << "\n";
|
||||
listener->setLabel(masterPath.filename().string());
|
||||
GameContentLoader gameContentLoader(*listener);
|
||||
EsmLoader esmLoader(mStore, mEsm, encoder, *listener);
|
||||
OmwLoader omwLoader(*listener);
|
||||
|
||||
// This parses the ESM file
|
||||
ESM::ESMReader lEsm;
|
||||
lEsm.setEncoder(encoder);
|
||||
lEsm.setIndex(idx);
|
||||
lEsm.setGlobalReaderList(&mEsm);
|
||||
lEsm.open (masterPath.string());
|
||||
mEsm[idx] = lEsm;
|
||||
mStore.load (mEsm[idx], listener);
|
||||
}
|
||||
gameContentLoader.addLoader(".esm", &esmLoader);
|
||||
gameContentLoader.addLoader(".esp", &esmLoader);
|
||||
gameContentLoader.addLoader(".omwgame", &omwLoader);
|
||||
gameContentLoader.addLoader(".omwaddon", &omwLoader);
|
||||
|
||||
for (std::vector<std::string>::size_type i = 0; i < plugins.size(); i++, idx++)
|
||||
{
|
||||
boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i]));
|
||||
loadContentFiles(fileCollections, contentFiles, gameContentLoader);
|
||||
|
||||
std::cout << "Loading ESP " << pluginPath.string() << "\n";
|
||||
listener->setLabel(pluginPath.filename().string());
|
||||
|
||||
// This parses the ESP file
|
||||
ESM::ESMReader lEsm;
|
||||
lEsm.setEncoder(encoder);
|
||||
lEsm.setIndex(idx);
|
||||
lEsm.setGlobalReaderList(&mEsm);
|
||||
lEsm.open (pluginPath.string());
|
||||
mEsm[idx] = lEsm;
|
||||
mStore.load (mEsm[idx], listener);
|
||||
}
|
||||
listener->loadingOff();
|
||||
|
||||
// insert records that may not be present in all versions of MW
|
||||
|
@ -1983,4 +2004,19 @@ namespace MWWorld
|
|||
return mGodMode;
|
||||
}
|
||||
|
||||
void World::loadContentFiles(const Files::Collections& fileCollections,
|
||||
const std::vector<std::string>& content, ContentLoader& contentLoader)
|
||||
{
|
||||
std::vector<std::string>::const_iterator it(content.begin());
|
||||
std::vector<std::string>::const_iterator end(content.end());
|
||||
for (int idx = 0; it != end; ++it, ++idx)
|
||||
{
|
||||
boost::filesystem::path filename(*it);
|
||||
const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string());
|
||||
if (col.doesExist(*it))
|
||||
{
|
||||
contentLoader.load(col.getPath(*it), idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "contentloader.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class Vector3;
|
||||
|
@ -41,6 +43,8 @@ namespace MWRender
|
|||
class Animation;
|
||||
}
|
||||
|
||||
struct ContentLoader;
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class WeatherManager;
|
||||
|
@ -113,6 +117,15 @@ namespace MWWorld
|
|||
|
||||
void ensureNeededRecords();
|
||||
|
||||
/**
|
||||
* @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon)
|
||||
* @param fileCollections- Container which holds content file names and their paths
|
||||
* @param content - Container which holds content file names
|
||||
* @param contentLoader -
|
||||
*/
|
||||
void loadContentFiles(const Files::Collections& fileCollections,
|
||||
const std::vector<std::string>& content, ContentLoader& contentLoader);
|
||||
|
||||
int mPlayIntro;
|
||||
|
||||
bool mTeleportEnabled;
|
||||
|
@ -122,7 +135,7 @@ namespace MWWorld
|
|||
|
||||
World (OEngine::Render::OgreRenderer& renderer,
|
||||
const Files::Collections& fileCollections,
|
||||
const std::vector<std::string>& master, const std::vector<std::string>& plugins,
|
||||
const std::vector<std::string>& contentFiles,
|
||||
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir,
|
||||
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride);
|
||||
|
||||
|
|
|
@ -44,9 +44,9 @@ namespace ESM
|
|||
}
|
||||
}
|
||||
|
||||
void AIPackageList::save(ESMWriter &esm)
|
||||
void AIPackageList::save(ESMWriter &esm) const
|
||||
{
|
||||
typedef std::vector<AIPackage>::iterator PackageIter;
|
||||
typedef std::vector<AIPackage>::const_iterator PackageIter;
|
||||
for (PackageIter it = mList.begin(); it != mList.end(); ++it) {
|
||||
switch (it->mType) {
|
||||
case AI_Wander:
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace ESM
|
|||
/// it needs to use retSubName() if needed. But, hey, there
|
||||
/// is only one field left (XSCL) and only two records uses AI
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
void ESM::CellRef::save(ESMWriter &esm)
|
||||
void ESM::CellRef::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNT("FRMR", mRefnum);
|
||||
esm.writeHNCString("NAME", mRefID);
|
||||
|
|
|
@ -83,7 +83,7 @@ namespace ESM
|
|||
// Position and rotation of this object within the cell
|
||||
Position mPos;
|
||||
|
||||
void save(ESMWriter &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
|
||||
void blank();
|
||||
};
|
||||
|
|
|
@ -36,6 +36,7 @@ struct Position
|
|||
|
||||
enum RecNameInts
|
||||
{
|
||||
// format 0 / legacy
|
||||
REC_ACTI = 0x49544341,
|
||||
REC_ALCH = 0x48434c41,
|
||||
REC_APPA = 0x41505041,
|
||||
|
@ -80,7 +81,10 @@ enum RecNameInts
|
|||
REC_SPEL = 0x4c455053,
|
||||
REC_SSCR = 0x52435353,
|
||||
REC_STAT = 0x54415453,
|
||||
REC_WEAP = 0x50414557
|
||||
REC_WEAP = 0x50414557,
|
||||
|
||||
// format 1
|
||||
REC_FILT = 0x544C4946
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -14,9 +14,9 @@ void EffectList::load(ESMReader &esm)
|
|||
}
|
||||
}
|
||||
|
||||
void EffectList::save(ESMWriter &esm)
|
||||
void EffectList::save(ESMWriter &esm) const
|
||||
{
|
||||
for (std::vector<ENAMstruct>::iterator it = mList.begin(); it != mList.end(); ++it) {
|
||||
for (std::vector<ENAMstruct>::const_iterator it = mList.begin(); it != mList.end(); ++it) {
|
||||
esm.writeHNT<ENAMstruct>("ENAM", *it, 24);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,9 @@ namespace ESM
|
|||
std::vector<ENAMstruct> mList;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,185 +2,188 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
bool count = true;
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
ESMWriter::ESMWriter() : mRecordCount (0), mCounting (true) {}
|
||||
|
||||
int ESMWriter::getVersion()
|
||||
{
|
||||
return mHeader.mData.version;
|
||||
}
|
||||
|
||||
void ESMWriter::setVersion(int ver)
|
||||
{
|
||||
mHeader.mData.version = ver;
|
||||
}
|
||||
|
||||
void ESMWriter::setAuthor(const std::string& auth)
|
||||
{
|
||||
mHeader.mData.author.assign (auth);
|
||||
}
|
||||
|
||||
void ESMWriter::setDescription(const std::string& desc)
|
||||
{
|
||||
mHeader.mData.desc.assign (desc);
|
||||
}
|
||||
|
||||
void ESMWriter::setRecordCount (int count)
|
||||
{
|
||||
mHeader.mData.records = count;
|
||||
}
|
||||
|
||||
void ESMWriter::setFormat (int format)
|
||||
{
|
||||
mHeader.mFormat = format;
|
||||
}
|
||||
|
||||
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::save(const std::string& file)
|
||||
{
|
||||
std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc);
|
||||
save(fs);
|
||||
}
|
||||
|
||||
void ESMWriter::save(std::ostream& file)
|
||||
{
|
||||
m_recordCount = 0;
|
||||
m_stream = &file;
|
||||
|
||||
startRecord("TES3", 0);
|
||||
|
||||
mHeader.save (*this);
|
||||
|
||||
endRecord("TES3");
|
||||
}
|
||||
|
||||
void ESMWriter::close()
|
||||
{
|
||||
m_stream->flush();
|
||||
|
||||
if (!m_records.empty())
|
||||
throw "Unclosed record remaining";
|
||||
}
|
||||
|
||||
void ESMWriter::startRecord(const std::string& name, uint32_t flags)
|
||||
{
|
||||
m_recordCount++;
|
||||
|
||||
writeName(name);
|
||||
RecordData rec;
|
||||
rec.name = name;
|
||||
rec.position = m_stream->tellp();
|
||||
rec.size = 0;
|
||||
writeT<int>(0); // Size goes here
|
||||
writeT<int>(0); // Unused header?
|
||||
writeT(flags);
|
||||
m_records.push_back(rec);
|
||||
|
||||
assert(m_records.back().size == 0);
|
||||
}
|
||||
|
||||
void ESMWriter::startSubRecord(const std::string& name)
|
||||
{
|
||||
writeName(name);
|
||||
RecordData rec;
|
||||
rec.name = name;
|
||||
rec.position = m_stream->tellp();
|
||||
rec.size = 0;
|
||||
writeT<int>(0); // Size goes here
|
||||
m_records.push_back(rec);
|
||||
|
||||
assert(m_records.back().size == 0);
|
||||
}
|
||||
|
||||
void ESMWriter::endRecord(const std::string& name)
|
||||
{
|
||||
RecordData rec = m_records.back();
|
||||
assert(rec.name == name);
|
||||
m_records.pop_back();
|
||||
|
||||
m_stream->seekp(rec.position);
|
||||
|
||||
count = false;
|
||||
write((char*)&rec.size, sizeof(int));
|
||||
count = true;
|
||||
|
||||
m_stream->seekp(0, std::ios::end);
|
||||
|
||||
}
|
||||
|
||||
void ESMWriter::writeHNString(const std::string& name, const std::string& data)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeHString(data);
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size)
|
||||
{
|
||||
assert(data.size() <= size);
|
||||
startSubRecord(name);
|
||||
writeHString(data);
|
||||
|
||||
if (data.size() < size)
|
||||
unsigned int ESMWriter::getVersion() const
|
||||
{
|
||||
for (size_t i = data.size(); i < size; ++i)
|
||||
write("\0",1);
|
||||
return mHeader.mData.version;
|
||||
}
|
||||
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
void ESMWriter::writeHString(const std::string& data)
|
||||
{
|
||||
if (data.size() == 0)
|
||||
write("\0", 1);
|
||||
else
|
||||
void ESMWriter::setVersion(unsigned int ver)
|
||||
{
|
||||
// Convert to UTF8 and return
|
||||
std::string ascii = m_encoder->getLegacyEnc(data);
|
||||
|
||||
write(ascii.c_str(), ascii.size());
|
||||
}
|
||||
}
|
||||
|
||||
void ESMWriter::writeHCString(const std::string& data)
|
||||
{
|
||||
writeHString(data);
|
||||
if (data.size() > 0 && data[data.size()-1] != '\0')
|
||||
write("\0", 1);
|
||||
}
|
||||
|
||||
void ESMWriter::writeName(const std::string& name)
|
||||
{
|
||||
assert((name.size() == 4 && name[3] != '\0'));
|
||||
write(name.c_str(), name.size());
|
||||
}
|
||||
|
||||
void ESMWriter::write(const char* data, size_t size)
|
||||
{
|
||||
if (count && !m_records.empty())
|
||||
{
|
||||
for (std::list<RecordData>::iterator it = m_records.begin(); it != m_records.end(); ++it)
|
||||
it->size += size;
|
||||
mHeader.mData.version = ver;
|
||||
}
|
||||
|
||||
m_stream->write(data, size);
|
||||
}
|
||||
void ESMWriter::setAuthor(const std::string& auth)
|
||||
{
|
||||
mHeader.mData.author.assign (auth);
|
||||
}
|
||||
|
||||
void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder)
|
||||
{
|
||||
m_encoder = encoder;
|
||||
}
|
||||
void ESMWriter::setDescription(const std::string& desc)
|
||||
{
|
||||
mHeader.mData.desc.assign (desc);
|
||||
}
|
||||
|
||||
void ESMWriter::setRecordCount (int count)
|
||||
{
|
||||
mHeader.mData.records = count;
|
||||
}
|
||||
|
||||
void ESMWriter::setFormat (int format)
|
||||
{
|
||||
mHeader.mFormat = format;
|
||||
}
|
||||
|
||||
void ESMWriter::clearMaster()
|
||||
{
|
||||
mHeader.mMaster.clear();
|
||||
}
|
||||
|
||||
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::save(const std::string& file)
|
||||
{
|
||||
std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc);
|
||||
save(fs);
|
||||
}
|
||||
|
||||
void ESMWriter::save(std::ostream& file)
|
||||
{
|
||||
mRecordCount = 0;
|
||||
mRecords.clear();
|
||||
mCounting = true;
|
||||
mStream = &file;
|
||||
|
||||
startRecord("TES3", 0);
|
||||
|
||||
mHeader.save (*this);
|
||||
|
||||
endRecord("TES3");
|
||||
}
|
||||
|
||||
void ESMWriter::close()
|
||||
{
|
||||
if (!mRecords.empty())
|
||||
throw std::runtime_error ("Unclosed record remaining");
|
||||
}
|
||||
|
||||
void ESMWriter::startRecord(const std::string& name, uint32_t flags)
|
||||
{
|
||||
mRecordCount++;
|
||||
|
||||
writeName(name);
|
||||
RecordData rec;
|
||||
rec.name = name;
|
||||
rec.position = mStream->tellp();
|
||||
rec.size = 0;
|
||||
writeT<int>(0); // Size goes here
|
||||
writeT<int>(0); // Unused header?
|
||||
writeT(flags);
|
||||
mRecords.push_back(rec);
|
||||
|
||||
assert(mRecords.back().size == 0);
|
||||
}
|
||||
|
||||
void ESMWriter::startSubRecord(const std::string& name)
|
||||
{
|
||||
writeName(name);
|
||||
RecordData rec;
|
||||
rec.name = name;
|
||||
rec.position = mStream->tellp();
|
||||
rec.size = 0;
|
||||
writeT<int>(0); // Size goes here
|
||||
mRecords.push_back(rec);
|
||||
|
||||
assert(mRecords.back().size == 0);
|
||||
}
|
||||
|
||||
void ESMWriter::endRecord(const std::string& name)
|
||||
{
|
||||
RecordData rec = mRecords.back();
|
||||
assert(rec.name == name);
|
||||
mRecords.pop_back();
|
||||
|
||||
mStream->seekp(rec.position);
|
||||
|
||||
mCounting = false;
|
||||
write (reinterpret_cast<const char*> (&rec.size), sizeof(int));
|
||||
mCounting = true;
|
||||
|
||||
mStream->seekp(0, std::ios::end);
|
||||
|
||||
}
|
||||
|
||||
void ESMWriter::writeHNString(const std::string& name, const std::string& data)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeHString(data);
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size)
|
||||
{
|
||||
assert(data.size() <= size);
|
||||
startSubRecord(name);
|
||||
writeHString(data);
|
||||
|
||||
if (data.size() < size)
|
||||
{
|
||||
for (size_t i = data.size(); i < size; ++i)
|
||||
write("\0",1);
|
||||
}
|
||||
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
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::writeHCString(const std::string& data)
|
||||
{
|
||||
writeHString(data);
|
||||
if (data.size() > 0 && data[data.size()-1] != '\0')
|
||||
write("\0", 1);
|
||||
}
|
||||
|
||||
void ESMWriter::writeName(const std::string& name)
|
||||
{
|
||||
assert((name.size() == 4 && name[3] != '\0'));
|
||||
write(name.c_str(), name.size());
|
||||
}
|
||||
|
||||
void ESMWriter::write(const char* data, size_t size)
|
||||
{
|
||||
if (mCounting && !mRecords.empty())
|
||||
{
|
||||
for (std::list<RecordData>::iterator it = mRecords.begin(); it != mRecords.end(); ++it)
|
||||
it->size += size;
|
||||
}
|
||||
|
||||
mStream->write(data, size);
|
||||
}
|
||||
|
||||
void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder)
|
||||
{
|
||||
mEncoder = encoder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,92 +13,103 @@ namespace ESM {
|
|||
|
||||
class ESMWriter
|
||||
{
|
||||
struct RecordData
|
||||
{
|
||||
std::string name;
|
||||
std::streampos position;
|
||||
size_t size;
|
||||
struct RecordData
|
||||
{
|
||||
std::string name;
|
||||
std::streampos position;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
ESMWriter();
|
||||
|
||||
unsigned int getVersion() const;
|
||||
void setVersion(unsigned int ver = 0x3fa66666);
|
||||
void setEncoder(ToUTF8::Utf8Encoder *encoding);
|
||||
void setAuthor(const std::string& author);
|
||||
void setDescription(const std::string& desc);
|
||||
void setRecordCount (int count);
|
||||
void setFormat (int format);
|
||||
|
||||
void clearMaster();
|
||||
|
||||
void addMaster(const std::string& name, uint64_t size);
|
||||
|
||||
void save(const std::string& file);
|
||||
///< Start saving a file by writing the TES3 header.
|
||||
|
||||
void save(std::ostream& file);
|
||||
///< Start saving a file by writing the TES3 header.
|
||||
|
||||
void close();
|
||||
///< \note Does not close the stream.
|
||||
|
||||
void writeHNString(const std::string& name, const std::string& data);
|
||||
void writeHNString(const std::string& name, const std::string& data, size_t size);
|
||||
void writeHNCString(const std::string& name, const std::string& data)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeHCString(data);
|
||||
endRecord(name);
|
||||
}
|
||||
void writeHNOString(const std::string& name, const std::string& data)
|
||||
{
|
||||
if (!data.empty())
|
||||
writeHNString(name, data);
|
||||
}
|
||||
void writeHNOCString(const std::string& name, const std::string& data)
|
||||
{
|
||||
if (!data.empty())
|
||||
writeHNCString(name, data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeHNT(const std::string& name, const T& data)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeT(data);
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeHNT(const std::string& name, const T& data, int size)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeT(data, size);
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeT(const T& data)
|
||||
{
|
||||
write((char*)&data, sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeT(const T& data, size_t size)
|
||||
{
|
||||
write((char*)&data, size);
|
||||
}
|
||||
|
||||
void startRecord(const std::string& name, uint32_t flags = 0);
|
||||
void startSubRecord(const std::string& name);
|
||||
void endRecord(const std::string& name);
|
||||
void writeHString(const std::string& data);
|
||||
void writeHCString(const std::string& data);
|
||||
void writeName(const std::string& data);
|
||||
void write(const char* data, size_t size);
|
||||
|
||||
private:
|
||||
std::list<RecordData> mRecords;
|
||||
std::ostream* mStream;
|
||||
std::streampos mHeaderPos;
|
||||
ToUTF8::Utf8Encoder* mEncoder;
|
||||
int mRecordCount;
|
||||
bool mCounting;
|
||||
|
||||
Header mHeader;
|
||||
};
|
||||
|
||||
public:
|
||||
int getVersion();
|
||||
void setVersion(int ver);
|
||||
void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8?
|
||||
void setAuthor(const std::string& author);
|
||||
void setDescription(const std::string& desc);
|
||||
void setRecordCount (int count);
|
||||
void setFormat (int format);
|
||||
|
||||
void addMaster(const std::string& name, uint64_t size);
|
||||
|
||||
void save(const std::string& file);
|
||||
void save(std::ostream& file);
|
||||
void close();
|
||||
|
||||
void writeHNString(const std::string& name, const std::string& data);
|
||||
void writeHNString(const std::string& name, const std::string& data, size_t size);
|
||||
void writeHNCString(const std::string& name, const std::string& data)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeHCString(data);
|
||||
endRecord(name);
|
||||
}
|
||||
void writeHNOString(const std::string& name, const std::string& data)
|
||||
{
|
||||
if (!data.empty())
|
||||
writeHNString(name, data);
|
||||
}
|
||||
void writeHNOCString(const std::string& name, const std::string& data)
|
||||
{
|
||||
if (!data.empty())
|
||||
writeHNCString(name, data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeHNT(const std::string& name, const T& data)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeT(data);
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeHNT(const std::string& name, const T& data, int size)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeT(data, size);
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeT(const T& data)
|
||||
{
|
||||
write((char*)&data, sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeT(const T& data, size_t size)
|
||||
{
|
||||
write((char*)&data, size);
|
||||
}
|
||||
|
||||
void startRecord(const std::string& name, uint32_t flags);
|
||||
void startSubRecord(const std::string& name);
|
||||
void endRecord(const std::string& name);
|
||||
void writeHString(const std::string& data);
|
||||
void writeHCString(const std::string& data);
|
||||
void writeName(const std::string& data);
|
||||
void write(const char* data, size_t size);
|
||||
|
||||
private:
|
||||
std::list<RecordData> m_records;
|
||||
std::ostream* m_stream;
|
||||
std::streampos m_headerPos;
|
||||
ToUTF8::Utf8Encoder* m_encoder;
|
||||
int m_recordCount;
|
||||
|
||||
Header mHeader;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
unsigned int ESM::Filter::sRecordId = REC_FILT;
|
||||
|
||||
void ESM::Filter::load (ESMReader& esm)
|
||||
{
|
||||
|
@ -10,7 +13,7 @@ void ESM::Filter::load (ESMReader& esm)
|
|||
mDescription = esm.getHNString ("DESC");
|
||||
}
|
||||
|
||||
void ESM::Filter::save (ESMWriter& esm)
|
||||
void ESM::Filter::save (ESMWriter& esm) const
|
||||
{
|
||||
esm.writeHNCString ("FILT", mFilter);
|
||||
esm.writeHNCString ("DESC", mDescription);
|
||||
|
|
|
@ -10,6 +10,8 @@ namespace ESM
|
|||
|
||||
struct Filter
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
std::string mId;
|
||||
|
||||
std::string mDescription;
|
||||
|
@ -17,7 +19,7 @@ namespace ESM
|
|||
std::string mFilter;
|
||||
|
||||
void load (ESMReader& esm);
|
||||
void save (ESMWriter& esm);
|
||||
void save (ESMWriter& esm) const;
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
|
|
|
@ -2,16 +2,19 @@
|
|||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
unsigned int Activator::sRecordId = REC_ACTI;
|
||||
|
||||
void Activator::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNString("FNAM");
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
}
|
||||
void Activator::save(ESMWriter &esm)
|
||||
void Activator::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNCString("FNAM", mName);
|
||||
|
|
|
@ -11,10 +11,12 @@ class ESMWriter;
|
|||
|
||||
struct Activator
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
std::string mId, mName, mScript, mModel;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
unsigned int Potion::sRecordId = REC_ALCH;
|
||||
|
||||
void Potion::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
|
@ -14,7 +17,7 @@ void Potion::load(ESMReader &esm)
|
|||
esm.getHNT(mData, "ALDT", 12);
|
||||
mEffects.load(esm);
|
||||
}
|
||||
void Potion::save(ESMWriter &esm)
|
||||
void Potion::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("TEXT", mIcon);
|
||||
|
|
|
@ -17,6 +17,8 @@ class ESMWriter;
|
|||
|
||||
struct Potion
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
struct ALDTstruct
|
||||
{
|
||||
float mWeight;
|
||||
|
@ -29,7 +31,7 @@ struct Potion
|
|||
EffectList mEffects;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
unsigned int Apparatus::sRecordId = REC_APPA;
|
||||
|
||||
void Apparatus::load(ESMReader &esm)
|
||||
{
|
||||
// we will not treat duplicated subrecords as errors here
|
||||
|
@ -28,7 +31,7 @@ void Apparatus::load(ESMReader &esm)
|
|||
}
|
||||
}
|
||||
|
||||
void Apparatus::save(ESMWriter &esm)
|
||||
void Apparatus::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNCString("FNAM", mName);
|
||||
|
|
|
@ -15,6 +15,8 @@ class ESMWriter;
|
|||
|
||||
struct Apparatus
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
enum AppaType
|
||||
{
|
||||
MortarPestle = 0,
|
||||
|
@ -35,7 +37,7 @@ struct Apparatus
|
|||
std::string mId, mModel, mIcon, mScript, mName;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -18,9 +19,9 @@ void PartReferenceList::load(ESMReader &esm)
|
|||
}
|
||||
}
|
||||
|
||||
void PartReferenceList::save(ESMWriter &esm)
|
||||
void PartReferenceList::save(ESMWriter &esm) const
|
||||
{
|
||||
for (std::vector<PartReference>::iterator it = mParts.begin(); it != mParts.end(); ++it)
|
||||
for (std::vector<PartReference>::const_iterator it = mParts.begin(); it != mParts.end(); ++it)
|
||||
{
|
||||
esm.writeHNT("INDX", it->mPart);
|
||||
esm.writeHNOString("BNAM", it->mMale);
|
||||
|
@ -28,6 +29,8 @@ void PartReferenceList::save(ESMWriter &esm)
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int Armor::sRecordId = REC_ARMO;
|
||||
|
||||
void Armor::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
|
@ -39,7 +42,7 @@ void Armor::load(ESMReader &esm)
|
|||
mEnchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
|
||||
void Armor::save(ESMWriter &esm)
|
||||
void Armor::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNCString("FNAM", mName);
|
||||
|
|
|
@ -56,11 +56,13 @@ struct PartReferenceList
|
|||
std::vector<PartReference> mParts;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
};
|
||||
|
||||
struct Armor
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
enum Type
|
||||
{
|
||||
Helmet = 0,
|
||||
|
@ -89,7 +91,7 @@ struct Armor
|
|||
std::string mId, mName, mModel, mIcon, mScript, mEnchant;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
unsigned int BodyPart::sRecordId = REC_BODY;
|
||||
|
||||
|
||||
void BodyPart::load(ESMReader &esm)
|
||||
{
|
||||
|
@ -12,7 +15,7 @@ void BodyPart::load(ESMReader &esm)
|
|||
mRace = esm.getHNString("FNAM");
|
||||
esm.getHNT(mData, "BYDT", 4);
|
||||
}
|
||||
void BodyPart::save(ESMWriter &esm)
|
||||
void BodyPart::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNCString("FNAM", mRace);
|
||||
|
|
|
@ -11,6 +11,8 @@ class ESMWriter;
|
|||
|
||||
struct BodyPart
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
enum MeshPart
|
||||
{
|
||||
MP_Head = 0,
|
||||
|
@ -57,7 +59,7 @@ struct BodyPart
|
|||
std::string mId, mModel, mRace;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
unsigned int Book::sRecordId = REC_BOOK;
|
||||
|
||||
void Book::load(ESMReader &esm)
|
||||
{
|
||||
|
@ -16,7 +18,7 @@ void Book::load(ESMReader &esm)
|
|||
mText = esm.getHNOString("TEXT");
|
||||
mEnchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
void Book::save(ESMWriter &esm)
|
||||
void Book::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
|
|
|
@ -14,6 +14,8 @@ class ESMWriter;
|
|||
|
||||
struct Book
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
struct BKDTstruct
|
||||
{
|
||||
float mWeight;
|
||||
|
@ -25,7 +27,7 @@ struct Book
|
|||
std::string mId;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
unsigned int BirthSign::sRecordId = REC_BSGN;
|
||||
|
||||
void BirthSign::load(ESMReader &esm)
|
||||
{
|
||||
|
@ -15,7 +17,7 @@ void BirthSign::load(ESMReader &esm)
|
|||
mPowers.load(esm);
|
||||
}
|
||||
|
||||
void BirthSign::save(ESMWriter &esm)
|
||||
void BirthSign::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("FNAM", mName);
|
||||
esm.writeHNOCString("TNAM", mTexture);
|
||||
|
|
|
@ -13,13 +13,15 @@ class ESMWriter;
|
|||
|
||||
struct BirthSign
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
std::string mId, mName, mDescription, mTexture;
|
||||
|
||||
// List of powers and abilities that come with this birth sign.
|
||||
SpellList mPowers;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID/index).
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue