mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-02 13:15:36 +00:00
20da0892ef
Slowly moving through the open-cs errors Good progress in openCS Very good progress on openCS Getting closer with openCS OpenCS compiles and runs! Didn't have time to test it all though ix openMW everything compiles on windows?? Fix gcc Fix Clang
553 lines
15 KiB
C++
553 lines
15 KiB
C++
#include "document.hpp"
|
|
|
|
#include "state.hpp"
|
|
|
|
#include <apps/opencs/model/doc/blacklist.hpp>
|
|
#include <apps/opencs/model/doc/messages.hpp>
|
|
#include <apps/opencs/model/doc/operationholder.hpp>
|
|
#include <apps/opencs/model/doc/runner.hpp>
|
|
#include <apps/opencs/model/doc/saving.hpp>
|
|
#include <apps/opencs/model/tools/tools.hpp>
|
|
#include <apps/opencs/model/world/data.hpp>
|
|
#include <apps/opencs/model/world/idcollection.hpp>
|
|
#include <apps/opencs/model/world/record.hpp>
|
|
|
|
#include <components/debug/debuglog.hpp>
|
|
#include <components/esm3/loaddial.hpp>
|
|
#include <components/esm3/loadglob.hpp>
|
|
#include <components/esm3/loadgmst.hpp>
|
|
#include <components/esm3/loadmgef.hpp>
|
|
#include <components/esm3/loadskil.hpp>
|
|
#include <components/esm3/variant.hpp>
|
|
#include <components/files/conversion.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <memory>
|
|
#include <stddef.h>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
#include "../world/defaultgmsts.hpp"
|
|
|
|
#ifndef Q_MOC_RUN
|
|
#include <components/files/configurationmanager.hpp>
|
|
#endif
|
|
|
|
namespace CSMWorld
|
|
{
|
|
class IdCompletionManager;
|
|
}
|
|
|
|
void CSMDoc::Document::addGmsts()
|
|
{
|
|
for (size_t i = 0; i < CSMWorld::DefaultGmsts::FloatCount; ++i)
|
|
{
|
|
ESM::GameSetting gmst;
|
|
gmst.mId = ESM::RefId::stringRefId(CSMWorld::DefaultGmsts::Floats[i]);
|
|
gmst.mValue.setType(ESM::VT_Float);
|
|
gmst.mRecordFlags = 0;
|
|
gmst.mValue.setFloat(CSMWorld::DefaultGmsts::FloatsDefaultValues[i]);
|
|
getData().getGmsts().add(gmst);
|
|
}
|
|
|
|
for (size_t i = 0; i < CSMWorld::DefaultGmsts::IntCount; ++i)
|
|
{
|
|
ESM::GameSetting gmst;
|
|
gmst.mId = ESM::RefId::stringRefId(CSMWorld::DefaultGmsts::Ints[i]);
|
|
gmst.mValue.setType(ESM::VT_Int);
|
|
gmst.mRecordFlags = 0;
|
|
gmst.mValue.setInteger(CSMWorld::DefaultGmsts::IntsDefaultValues[i]);
|
|
getData().getGmsts().add(gmst);
|
|
}
|
|
|
|
for (size_t i = 0; i < CSMWorld::DefaultGmsts::StringCount; ++i)
|
|
{
|
|
ESM::GameSetting gmst;
|
|
gmst.mId = ESM::RefId::stringRefId(CSMWorld::DefaultGmsts::Strings[i]);
|
|
gmst.mValue.setType(ESM::VT_String);
|
|
gmst.mRecordFlags = 0;
|
|
gmst.mValue.setString("");
|
|
getData().getGmsts().add(gmst);
|
|
}
|
|
}
|
|
|
|
void CSMDoc::Document::addOptionalGmsts()
|
|
{
|
|
for (size_t i = 0; i < CSMWorld::DefaultGmsts::OptionalFloatCount; ++i)
|
|
{
|
|
ESM::GameSetting gmst;
|
|
gmst.mId = ESM::RefId::stringRefId(CSMWorld::DefaultGmsts::OptionalFloats[i]);
|
|
gmst.blank();
|
|
gmst.mValue.setType(ESM::VT_Float);
|
|
addOptionalGmst(gmst);
|
|
}
|
|
|
|
for (size_t i = 0; i < CSMWorld::DefaultGmsts::OptionalIntCount; ++i)
|
|
{
|
|
ESM::GameSetting gmst;
|
|
gmst.mId = ESM::RefId::stringRefId(CSMWorld::DefaultGmsts::OptionalInts[i]);
|
|
gmst.blank();
|
|
gmst.mValue.setType(ESM::VT_Int);
|
|
addOptionalGmst(gmst);
|
|
}
|
|
|
|
for (size_t i = 0; i < CSMWorld::DefaultGmsts::OptionalStringCount; ++i)
|
|
{
|
|
ESM::GameSetting gmst;
|
|
gmst.mId = ESM::RefId::stringRefId(CSMWorld::DefaultGmsts::OptionalStrings[i]);
|
|
gmst.blank();
|
|
gmst.mValue.setType(ESM::VT_String);
|
|
gmst.mValue.setString("<no text>");
|
|
addOptionalGmst(gmst);
|
|
}
|
|
}
|
|
|
|
void CSMDoc::Document::addOptionalGlobals()
|
|
{
|
|
static const char* sGlobals[] = {
|
|
"DaysPassed",
|
|
"PCWerewolf",
|
|
"PCYear",
|
|
nullptr,
|
|
};
|
|
|
|
for (int i = 0; sGlobals[i]; ++i)
|
|
{
|
|
ESM::Global global;
|
|
global.mId = ESM::RefId::stringRefId(sGlobals[i]);
|
|
global.blank();
|
|
global.mValue.setType(ESM::VT_Long);
|
|
|
|
if (i == 0)
|
|
global.mValue.setInteger(1); // dayspassed starts counting at 1
|
|
|
|
addOptionalGlobal(global);
|
|
}
|
|
}
|
|
|
|
void CSMDoc::Document::addOptionalMagicEffects()
|
|
{
|
|
for (int i = ESM::MagicEffect::SummonFabricant; i <= ESM::MagicEffect::SummonCreature05; ++i)
|
|
{
|
|
ESM::MagicEffect effect;
|
|
effect.mIndex = i;
|
|
effect.mId = ESM::RefId::stringRefId(ESM::MagicEffect::indexToId(i));
|
|
effect.blank();
|
|
|
|
addOptionalMagicEffect(effect);
|
|
}
|
|
}
|
|
|
|
void CSMDoc::Document::addOptionalGmst(const ESM::GameSetting& gmst)
|
|
{
|
|
if (getData().getGmsts().searchId(gmst.mId) == -1)
|
|
{
|
|
auto record = std::make_unique<CSMWorld::Record<ESM::GameSetting>>();
|
|
record->mBase = gmst;
|
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
|
getData().getGmsts().appendRecord(std::move(record));
|
|
}
|
|
}
|
|
|
|
void CSMDoc::Document::addOptionalGlobal(const ESM::Global& global)
|
|
{
|
|
if (getData().getGlobals().searchId(global.mId) == -1)
|
|
{
|
|
auto record = std::make_unique<CSMWorld::Record<ESM::Global>>();
|
|
record->mBase = global;
|
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
|
getData().getGlobals().appendRecord(std::move(record));
|
|
}
|
|
}
|
|
|
|
void CSMDoc::Document::addOptionalMagicEffect(const ESM::MagicEffect& magicEffect)
|
|
{
|
|
if (getData().getMagicEffects().searchId(magicEffect.mId) == -1)
|
|
{
|
|
auto record = std::make_unique<CSMWorld::Record<ESM::MagicEffect>>();
|
|
record->mBase = magicEffect;
|
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
|
getData().getMagicEffects().appendRecord(std::move(record));
|
|
}
|
|
}
|
|
|
|
void CSMDoc::Document::createBase()
|
|
{
|
|
static const char* sGlobals[] = {
|
|
"Day",
|
|
"DaysPassed",
|
|
"GameHour",
|
|
"Month",
|
|
"PCRace",
|
|
"PCVampire",
|
|
"PCWerewolf",
|
|
"PCYear",
|
|
nullptr,
|
|
};
|
|
|
|
for (int i = 0; sGlobals[i]; ++i)
|
|
{
|
|
ESM::Global record;
|
|
record.mId = ESM::RefId::stringRefId(sGlobals[i]);
|
|
record.mRecordFlags = 0;
|
|
record.mValue.setType(i == 2 ? ESM::VT_Float : ESM::VT_Long);
|
|
|
|
if (i == 0 || i == 1)
|
|
record.mValue.setInteger(1);
|
|
|
|
getData().getGlobals().add(record);
|
|
}
|
|
|
|
addGmsts();
|
|
|
|
for (int i = 0; i < 27; ++i)
|
|
{
|
|
ESM::Skill record;
|
|
record.mIndex = i;
|
|
record.mId = ESM::RefId::stringRefId(ESM::Skill::indexToId(record.mIndex));
|
|
record.blank();
|
|
|
|
getData().getSkills().add(record);
|
|
}
|
|
|
|
static const char* sVoice[] = {
|
|
"Intruder",
|
|
"Attack",
|
|
"Hello",
|
|
"Thief",
|
|
"Alarm",
|
|
"Idle",
|
|
"Flee",
|
|
"Hit",
|
|
nullptr,
|
|
};
|
|
|
|
for (int i = 0; sVoice[i]; ++i)
|
|
{
|
|
ESM::Dialogue record;
|
|
record.mId = ESM::RefId::stringRefId(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",
|
|
nullptr,
|
|
};
|
|
|
|
for (int i = 0; sGreetings[i]; ++i)
|
|
{
|
|
ESM::Dialogue record;
|
|
record.mId = ESM::RefId::stringRefId(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",
|
|
nullptr,
|
|
};
|
|
|
|
for (int i = 0; sPersuasion[i]; ++i)
|
|
{
|
|
ESM::Dialogue record;
|
|
record.mId = ESM::RefId::stringRefId(sPersuasion[i]);
|
|
record.mType = ESM::Dialogue::Persuasion;
|
|
record.blank();
|
|
|
|
getData().getTopics().add(record);
|
|
}
|
|
|
|
for (int i = 0; i < ESM::MagicEffect::Length; ++i)
|
|
{
|
|
ESM::MagicEffect record;
|
|
|
|
record.mIndex = i;
|
|
record.mId = ESM::RefId::stringRefId(ESM::MagicEffect::indexToId(i));
|
|
|
|
record.blank();
|
|
|
|
getData().getMagicEffects().add(record);
|
|
}
|
|
}
|
|
|
|
CSMDoc::Document::Document(const Files::ConfigurationManager& configuration, std::vector<std::filesystem::path> files,
|
|
bool new_, const std::filesystem::path& savePath, const std::filesystem::path& resDir, ToUTF8::FromType encoding,
|
|
const std::vector<std::string>& blacklistedScripts, bool fsStrict, const Files::PathContainer& dataPaths,
|
|
const std::vector<std::string>& archives)
|
|
: mSavePath(savePath)
|
|
, mContentFiles(std::move(files))
|
|
, mNew(new_)
|
|
, mData(encoding, fsStrict, dataPaths, archives, resDir)
|
|
, mTools(*this, encoding)
|
|
, mProjectPath((configuration.getUserDataPath() / "projects") / (savePath.filename().u8string() + u8".project"))
|
|
, mSavingOperation(*this, mProjectPath, encoding)
|
|
, mSaving(&mSavingOperation)
|
|
, mResDir(resDir)
|
|
, mRunner(mProjectPath)
|
|
, mDirty(false)
|
|
, mIdCompletionManager(mData)
|
|
{
|
|
if (mContentFiles.empty())
|
|
throw std::runtime_error("Empty content file sequence");
|
|
|
|
if (mNew || !std::filesystem::exists(mProjectPath))
|
|
{
|
|
auto filtersPath = configuration.getUserDataPath() / "defaultfilters";
|
|
|
|
std::ofstream destination(mProjectPath, std::ios::out | std::ios::binary);
|
|
if (!destination.is_open())
|
|
throw std::runtime_error("Can not create project file: " + Files::pathToUnicodeString(mProjectPath));
|
|
destination.exceptions(std::ios::failbit | std::ios::badbit);
|
|
|
|
if (!std::filesystem::exists(filtersPath))
|
|
filtersPath = mResDir / "defaultfilters";
|
|
|
|
std::ifstream source(filtersPath, std::ios::in | std::ios::binary);
|
|
if (!source.is_open())
|
|
throw std::runtime_error("Can not read filters file: " + Files::pathToUnicodeString(filtersPath));
|
|
source.exceptions(std::ios::failbit | std::ios::badbit);
|
|
|
|
destination << source.rdbuf();
|
|
}
|
|
|
|
if (mNew)
|
|
{
|
|
if (mContentFiles.size() == 1)
|
|
createBase();
|
|
}
|
|
|
|
mBlacklist.add(CSMWorld::UniversalId::Type_Script, blacklistedScripts);
|
|
|
|
addOptionalGmsts();
|
|
addOptionalGlobals();
|
|
addOptionalMagicEffects();
|
|
|
|
connect(&mUndoStack, &QUndoStack::cleanChanged, this, &Document::modificationStateChanged);
|
|
|
|
connect(&mTools, &CSMTools::Tools::progress, this, qOverload<int, int, int>(&Document::progress));
|
|
connect(&mTools, &CSMTools::Tools::done, this, &Document::operationDone);
|
|
connect(&mTools, &CSMTools::Tools::done, this, &Document::operationDone2);
|
|
connect(&mTools, &CSMTools::Tools::mergeDone, this, &Document::mergeDone);
|
|
|
|
connect(&mSaving, &OperationHolder::progress, this, qOverload<int, int, int>(&Document::progress));
|
|
connect(&mSaving, &OperationHolder::done, this, &Document::operationDone2);
|
|
|
|
connect(&mSaving, &OperationHolder::reportMessage, this, &Document::reportMessage);
|
|
|
|
connect(&mRunner, &Runner::runStateChanged, this, &Document::runStateChanged);
|
|
}
|
|
|
|
CSMDoc::Document::~Document() {}
|
|
|
|
QUndoStack& CSMDoc::Document::getUndoStack()
|
|
{
|
|
return mUndoStack;
|
|
}
|
|
|
|
int CSMDoc::Document::getState() const
|
|
{
|
|
int state = 0;
|
|
|
|
if (!mUndoStack.isClean() || mDirty)
|
|
state |= State_Modified;
|
|
|
|
if (mSaving.isRunning())
|
|
state |= State_Locked | State_Saving | State_Operation;
|
|
|
|
if (mRunner.isRunning())
|
|
state |= State_Locked | State_Running;
|
|
|
|
if (int operations = mTools.getRunningOperations())
|
|
state |= State_Locked | State_Operation | operations;
|
|
|
|
return state;
|
|
}
|
|
|
|
const std::filesystem::path& CSMDoc::Document::getResourceDir() const
|
|
{
|
|
return mResDir;
|
|
}
|
|
|
|
const std::filesystem::path& CSMDoc::Document::getSavePath() const
|
|
{
|
|
return mSavePath;
|
|
}
|
|
|
|
const std::filesystem::path& CSMDoc::Document::getProjectPath() const
|
|
{
|
|
return mProjectPath;
|
|
}
|
|
|
|
const std::vector<std::filesystem::path>& CSMDoc::Document::getContentFiles() const
|
|
{
|
|
return mContentFiles;
|
|
}
|
|
|
|
bool CSMDoc::Document::isNew() const
|
|
{
|
|
return mNew;
|
|
}
|
|
|
|
void CSMDoc::Document::save()
|
|
{
|
|
if (mSaving.isRunning())
|
|
throw std::logic_error("Failed to initiate save, because a save operation is already running.");
|
|
|
|
mSaving.start();
|
|
|
|
emit stateChanged(getState(), this);
|
|
}
|
|
|
|
CSMWorld::UniversalId CSMDoc::Document::verify(const CSMWorld::UniversalId& reportId)
|
|
{
|
|
CSMWorld::UniversalId id = mTools.runVerifier(reportId);
|
|
emit stateChanged(getState(), this);
|
|
return id;
|
|
}
|
|
|
|
CSMWorld::UniversalId CSMDoc::Document::newSearch()
|
|
{
|
|
return mTools.newSearch();
|
|
}
|
|
|
|
void CSMDoc::Document::runSearch(const CSMWorld::UniversalId& searchId, const CSMTools::Search& search)
|
|
{
|
|
mTools.runSearch(searchId, search);
|
|
emit stateChanged(getState(), this);
|
|
}
|
|
|
|
void CSMDoc::Document::runMerge(std::unique_ptr<CSMDoc::Document> target)
|
|
{
|
|
mTools.runMerge(std::move(target));
|
|
emit stateChanged(getState(), this);
|
|
}
|
|
|
|
void CSMDoc::Document::abortOperation(int type)
|
|
{
|
|
if (type == State_Saving)
|
|
mSaving.abort();
|
|
else
|
|
mTools.abortOperation(type);
|
|
}
|
|
|
|
void CSMDoc::Document::modificationStateChanged(bool clean)
|
|
{
|
|
emit stateChanged(getState(), this);
|
|
}
|
|
|
|
void CSMDoc::Document::reportMessage(const CSMDoc::Message& message, int type)
|
|
{
|
|
/// \todo find a better way to get these messages to the user.
|
|
Log(Debug::Info) << message.mMessage;
|
|
}
|
|
|
|
void CSMDoc::Document::operationDone2(int type, bool failed)
|
|
{
|
|
if (type == CSMDoc::State_Saving && !failed)
|
|
mDirty = false;
|
|
|
|
emit stateChanged(getState(), this);
|
|
}
|
|
|
|
const CSMWorld::Data& CSMDoc::Document::getData() const
|
|
{
|
|
return mData;
|
|
}
|
|
|
|
CSMWorld::Data& CSMDoc::Document::getData()
|
|
{
|
|
return mData;
|
|
}
|
|
|
|
CSMTools::ReportModel* CSMDoc::Document::getReport(const CSMWorld::UniversalId& id)
|
|
{
|
|
return mTools.getReport(id);
|
|
}
|
|
|
|
bool CSMDoc::Document::isBlacklisted(const CSMWorld::UniversalId& id) const
|
|
{
|
|
return mBlacklist.isBlacklisted(id);
|
|
}
|
|
|
|
void CSMDoc::Document::startRunning(const std::string& profile, const std::string& startupInstruction)
|
|
{
|
|
std::vector<std::filesystem::path> contentFiles;
|
|
|
|
for (const auto& mContentFile : mContentFiles)
|
|
{
|
|
contentFiles.emplace_back(mContentFile.filename());
|
|
}
|
|
|
|
mRunner.configure(getData().getDebugProfiles().getRecord(ESM::RefId::stringRefId(profile)).get(), contentFiles, startupInstruction);
|
|
|
|
int state = getState();
|
|
|
|
if (state & State_Modified)
|
|
{
|
|
// need to save first
|
|
mRunner.start(true);
|
|
|
|
new SaveWatcher(&mRunner, &mSaving); // no, that is not a memory leak. Qt is weird.
|
|
|
|
if (!(state & State_Saving))
|
|
save();
|
|
}
|
|
else
|
|
mRunner.start();
|
|
}
|
|
|
|
void CSMDoc::Document::stopRunning()
|
|
{
|
|
mRunner.stop();
|
|
}
|
|
|
|
QTextDocument* CSMDoc::Document::getRunLog()
|
|
{
|
|
return mRunner.getLog();
|
|
}
|
|
|
|
void CSMDoc::Document::runStateChanged()
|
|
{
|
|
emit stateChanged(getState(), this);
|
|
}
|
|
|
|
void CSMDoc::Document::progress(int current, int max, int type)
|
|
{
|
|
emit progress(current, max, type, 1, this);
|
|
}
|
|
|
|
CSMWorld::IdCompletionManager& CSMDoc::Document::getIdCompletionManager()
|
|
{
|
|
return mIdCompletionManager;
|
|
}
|
|
|
|
void CSMDoc::Document::flagAsDirty()
|
|
{
|
|
mDirty = true;
|
|
}
|