mirror of
https://github.com/OpenMW/openmw.git
synced 2025-12-01 15:34:32 +00:00
Merge branch openmw:master into master
This commit is contained in:
commit
28f8b40d9d
20 changed files with 166 additions and 64 deletions
|
|
@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...")
|
||||||
set(OPENMW_VERSION_MAJOR 0)
|
set(OPENMW_VERSION_MAJOR 0)
|
||||||
set(OPENMW_VERSION_MINOR 49)
|
set(OPENMW_VERSION_MINOR 49)
|
||||||
set(OPENMW_VERSION_RELEASE 0)
|
set(OPENMW_VERSION_RELEASE 0)
|
||||||
set(OPENMW_LUA_API_REVISION 75)
|
set(OPENMW_LUA_API_REVISION 76)
|
||||||
set(OPENMW_POSTPROCESSING_API_REVISION 2)
|
set(OPENMW_POSTPROCESSING_API_REVISION 2)
|
||||||
|
|
||||||
set(OPENMW_VERSION_COMMITHASH "")
|
set(OPENMW_VERSION_COMMITHASH "")
|
||||||
|
|
|
||||||
|
|
@ -252,7 +252,7 @@ namespace ESSImport
|
||||||
for (size_t i = 0; i < invState.mItems.size(); ++i)
|
for (size_t i = 0; i < invState.mItems.size(); ++i)
|
||||||
{
|
{
|
||||||
// FIXME: in case of conflict (multiple items with this refID) use the already equipped one?
|
// FIXME: in case of conflict (multiple items with this refID) use the already equipped one?
|
||||||
if (invState.mItems[i].mRef.mRefID == ESM::RefId::stringRefId(refr.mActorData.mSelectedEnchantItem))
|
if (invState.mItems[i].mRef.mRefID == refr.mActorData.mSelectedEnchantItem)
|
||||||
invState.mSelectedEnchantItem = i;
|
invState.mSelectedEnchantItem = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -260,12 +260,12 @@ namespace ESSImport
|
||||||
void write(ESM::ESMWriter& esm) override
|
void write(ESM::ESMWriter& esm) override
|
||||||
{
|
{
|
||||||
esm.startRecord(ESM::REC_ASPL);
|
esm.startRecord(ESM::REC_ASPL);
|
||||||
esm.writeHNString("ID__", mSelectedSpell);
|
esm.writeHNRefId("ID__", mSelectedSpell);
|
||||||
esm.endRecord(ESM::REC_ASPL);
|
esm.endRecord(ESM::REC_ASPL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mSelectedSpell;
|
ESM::RefId mSelectedSpell;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConvertPCDT : public Converter
|
class ConvertPCDT : public Converter
|
||||||
|
|
@ -374,16 +374,16 @@ namespace ESSImport
|
||||||
void write(ESM::ESMWriter& esm) override
|
void write(ESM::ESMWriter& esm) override
|
||||||
{
|
{
|
||||||
esm.startRecord(ESM::REC_DCOU);
|
esm.startRecord(ESM::REC_DCOU);
|
||||||
for (auto it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
|
for (const auto& [id, count] : mKillCounter)
|
||||||
{
|
{
|
||||||
esm.writeHNString("ID__", it->first);
|
esm.writeHNRefId("ID__", id);
|
||||||
esm.writeHNT("COUN", it->second);
|
esm.writeHNT("COUN", count);
|
||||||
}
|
}
|
||||||
esm.endRecord(ESM::REC_DCOU);
|
esm.endRecord(ESM::REC_DCOU);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<std::string, int> mKillCounter;
|
std::map<ESM::RefId, int> mKillCounter;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConvertFACT : public Converter
|
class ConvertFACT : public Converter
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,8 @@ namespace ESSImport
|
||||||
// to change them ingame
|
// to change them ingame
|
||||||
int mCombatStats[3][2];
|
int mCombatStats[3][2];
|
||||||
|
|
||||||
std::string mSelectedSpell;
|
ESM::RefId mSelectedSpell;
|
||||||
std::string mSelectedEnchantItem;
|
ESM::RefId mSelectedEnchantItem;
|
||||||
|
|
||||||
SCRI mSCRI;
|
SCRI mSCRI;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,12 +107,12 @@ namespace ESSImport
|
||||||
|
|
||||||
if (esm.isNextSub("WNAM"))
|
if (esm.isNextSub("WNAM"))
|
||||||
{
|
{
|
||||||
std::string id = esm.getHString();
|
ESM::RefId spellRefId = esm.getRefId();
|
||||||
|
|
||||||
if (esm.isNextSub("XNAM"))
|
if (esm.isNextSub("XNAM"))
|
||||||
mActorData.mSelectedEnchantItem = esm.getHString();
|
mActorData.mSelectedEnchantItem = esm.getRefId();
|
||||||
else
|
else
|
||||||
mActorData.mSelectedSpell = std::move(id);
|
mActorData.mSelectedSpell = std::move(spellRefId);
|
||||||
|
|
||||||
if (esm.isNextSub("YNAM"))
|
if (esm.isNextSub("YNAM"))
|
||||||
esm.skipHSub(); // 4 byte, 0
|
esm.skipHSub(); // 4 byte, 0
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,19 @@ namespace ESSImport
|
||||||
|
|
||||||
void INFO::load(ESM::ESMReader& esm)
|
void INFO::load(ESM::ESMReader& esm)
|
||||||
{
|
{
|
||||||
mInfo = esm.getHNString("INAM");
|
if (esm.peekNextSub("XNAM"))
|
||||||
|
{
|
||||||
|
// TODO: Support older saves by turning XNAM into a RefId.
|
||||||
|
// XNAM is probably the number of the topic response within the topic record's linked list.
|
||||||
|
// Resolving this value will likely require loading Morrowind.esm.
|
||||||
|
|
||||||
|
esm.getSubName();
|
||||||
|
esm.skipHSub();
|
||||||
|
mInfo = ESM::RefId();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mInfo = esm.getHNRefId("INAM");
|
||||||
|
|
||||||
mActorRefId = esm.getHNString("ACDT");
|
mActorRefId = esm.getHNString("ACDT");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <components/esm/refid.hpp>
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
class ESMReader;
|
class ESMReader;
|
||||||
|
|
@ -13,7 +15,7 @@ namespace ESSImport
|
||||||
|
|
||||||
struct INFO
|
struct INFO
|
||||||
{
|
{
|
||||||
std::string mInfo;
|
ESM::RefId mInfo;
|
||||||
std::string mActorRefId;
|
std::string mActorRefId;
|
||||||
|
|
||||||
void load(ESM::ESMReader& esm);
|
void load(ESM::ESMReader& esm);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace ESSImport
|
||||||
{
|
{
|
||||||
while (esm.isNextSub("KNAM"))
|
while (esm.isNextSub("KNAM"))
|
||||||
{
|
{
|
||||||
std::string refId = esm.getHString();
|
ESM::RefId refId = esm.getRefId();
|
||||||
int32_t count;
|
int32_t count;
|
||||||
esm.getHNT(count, "CNAM");
|
esm.getHNT(count, "CNAM");
|
||||||
mKillCounter[refId] = count;
|
mKillCounter[refId] = count;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
|
||||||
|
#include <components/esm/refid.hpp>
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
|
@ -18,8 +19,7 @@ namespace ESSImport
|
||||||
{
|
{
|
||||||
void load(ESM::ESMReader& esm);
|
void load(ESM::ESMReader& esm);
|
||||||
|
|
||||||
/// RefId, kill count
|
std::map<ESM::RefId, int32_t> mKillCounter;
|
||||||
std::map<std::string, int32_t> mKillCounter;
|
|
||||||
|
|
||||||
int32_t mWerewolfKills;
|
int32_t mWerewolfKills;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <components/esm3/loadligh.hpp>
|
#include <components/esm3/loadligh.hpp>
|
||||||
#include <components/lua/luastate.hpp>
|
#include <components/lua/luastate.hpp>
|
||||||
#include <components/lua/util.hpp>
|
#include <components/lua/util.hpp>
|
||||||
|
#include <components/misc/color.hpp>
|
||||||
#include <components/misc/resourcehelpers.hpp>
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
|
|
||||||
|
|
@ -62,7 +63,13 @@ namespace
|
||||||
if (rec["radius"] != sol::nil)
|
if (rec["radius"] != sol::nil)
|
||||||
light.mData.mRadius = rec["radius"];
|
light.mData.mRadius = rec["radius"];
|
||||||
if (rec["color"] != sol::nil)
|
if (rec["color"] != sol::nil)
|
||||||
light.mData.mColor = rec["color"];
|
{
|
||||||
|
sol::object color = rec["color"];
|
||||||
|
if (color.is<Misc::Color>())
|
||||||
|
light.mData.mColor = color.as<Misc::Color>().toRGBA();
|
||||||
|
else
|
||||||
|
light.mData.mColor = color.as<uint32_t>();
|
||||||
|
}
|
||||||
setRecordFlag(rec, "isCarriable", ESM::Light::Carry, light);
|
setRecordFlag(rec, "isCarriable", ESM::Light::Carry, light);
|
||||||
setRecordFlag(rec, "isDynamic", ESM::Light::Dynamic, light);
|
setRecordFlag(rec, "isDynamic", ESM::Light::Dynamic, light);
|
||||||
setRecordFlag(rec, "isFire", ESM::Light::Fire, light);
|
setRecordFlag(rec, "isFire", ESM::Light::Fire, light);
|
||||||
|
|
@ -104,7 +111,8 @@ namespace MWLua
|
||||||
record["value"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mValue; });
|
record["value"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mValue; });
|
||||||
record["duration"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mTime; });
|
record["duration"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mTime; });
|
||||||
record["radius"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mRadius; });
|
record["radius"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mRadius; });
|
||||||
record["color"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mColor; });
|
record["color"] = sol::readonly_property(
|
||||||
|
[](const ESM::Light& rec) -> Misc::Color { return Misc::Color::fromRGB(rec.mData.mColor); });
|
||||||
record["isCarriable"] = sol::readonly_property(
|
record["isCarriable"] = sol::readonly_property(
|
||||||
[](const ESM::Light& rec) -> bool { return rec.mData.mFlags & ESM::Light::Carry; });
|
[](const ESM::Light& rec) -> bool { return rec.mData.mFlags & ESM::Light::Carry; });
|
||||||
record["isDynamic"] = sol::readonly_property(
|
record["isDynamic"] = sol::readonly_property(
|
||||||
|
|
|
||||||
|
|
@ -189,19 +189,16 @@ bool Config::GameSettings::readFile(
|
||||||
if (ignoreContent && (key == QLatin1String("content") || key == QLatin1String("data")))
|
if (ignoreContent && (key == QLatin1String("content") || key == QLatin1String("data")))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
QList<SettingValue> values = cache.values(key);
|
auto containsValue = [&](const QMultiMap<QString, SettingValue>& map) {
|
||||||
values.append(settings.values(key));
|
for (auto [itr, end] = map.equal_range(key); itr != end; ++itr)
|
||||||
|
{
|
||||||
|
if (itr->value == value.value)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
bool exists = false;
|
if (!containsValue(cache) && !containsValue(settings))
|
||||||
for (const auto& existingValue : values)
|
|
||||||
{
|
|
||||||
if (existingValue.value == value.value)
|
|
||||||
{
|
|
||||||
exists = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!exists)
|
|
||||||
{
|
{
|
||||||
cache.insert(key, value);
|
cache.insert(key, value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,8 @@ namespace Crash
|
||||||
if (mShm == nullptr)
|
if (mShm == nullptr)
|
||||||
throw std::runtime_error("Failed to map crash catcher shared memory");
|
throw std::runtime_error("Failed to map crash catcher shared memory");
|
||||||
|
|
||||||
|
mShm->mMonitorStatus = CrashSHM::Status::Uninitialised;
|
||||||
|
|
||||||
mShmMutex = CreateMutexW(&attributes, FALSE, NULL);
|
mShmMutex = CreateMutexW(&attributes, FALSE, NULL);
|
||||||
if (mShmMutex == nullptr)
|
if (mShmMutex == nullptr)
|
||||||
throw std::runtime_error("Failed to create crash catcher shared memory mutex");
|
throw std::runtime_error("Failed to create crash catcher shared memory mutex");
|
||||||
|
|
@ -147,10 +149,15 @@ namespace Crash
|
||||||
|
|
||||||
void CrashCatcher::waitMonitor()
|
void CrashCatcher::waitMonitor()
|
||||||
{
|
{
|
||||||
if (WaitForSingleObject(mSignalAppEvent, CrashCatcherTimeout) != WAIT_OBJECT_0)
|
if (!waitMonitorNoThrow())
|
||||||
throw std::runtime_error("Waiting for monitor failed");
|
throw std::runtime_error("Waiting for monitor failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CrashCatcher::waitMonitorNoThrow()
|
||||||
|
{
|
||||||
|
return WaitForSingleObject(mSignalAppEvent, CrashCatcherTimeout) == WAIT_OBJECT_0;
|
||||||
|
}
|
||||||
|
|
||||||
void CrashCatcher::signalMonitor()
|
void CrashCatcher::signalMonitor()
|
||||||
{
|
{
|
||||||
SetEvent(mSignalMonitorEvent);
|
SetEvent(mSignalMonitorEvent);
|
||||||
|
|
@ -234,13 +241,23 @@ namespace Crash
|
||||||
|
|
||||||
signalMonitor();
|
signalMonitor();
|
||||||
|
|
||||||
// must remain until monitor has finished
|
// give monitor a chance to start dumping
|
||||||
waitMonitor();
|
// as we're suspended, this might time out even if it's successful, so mMonitorStatus is the source of truth
|
||||||
|
waitMonitorNoThrow();
|
||||||
|
|
||||||
|
shmLock();
|
||||||
|
CrashSHM::Status monitorStatus = mShm->mMonitorStatus;
|
||||||
|
shmUnlock();
|
||||||
|
|
||||||
|
if (monitorStatus == CrashSHM::Status::DumpedSuccessfully)
|
||||||
|
{
|
||||||
std::string message = "OpenMW has encountered a fatal error.\nCrash dump saved to '"
|
std::string message = "OpenMW has encountered a fatal error.\nCrash dump saved to '"
|
||||||
+ Misc::StringUtils::u8StringToString(getCrashDumpPath(*mShm).u8string())
|
+ Misc::StringUtils::u8StringToString(getCrashDumpPath(*mShm).u8string())
|
||||||
+ "'.\nPlease report this to https://gitlab.com/OpenMW/openmw/issues !";
|
+ "'.\nPlease report this to https://gitlab.com/OpenMW/openmw/issues !";
|
||||||
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr);
|
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr);
|
||||||
}
|
}
|
||||||
|
else if (monitorStatus == CrashSHM::Status::Dumping)
|
||||||
|
SDL_ShowSimpleMessageBox(0, "Fatal Error", "Timed out while creating crash dump", nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Crash
|
} // namespace Crash
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ namespace Crash
|
||||||
const std::filesystem::path& freezeDumpName);
|
const std::filesystem::path& freezeDumpName);
|
||||||
|
|
||||||
void waitMonitor();
|
void waitMonitor();
|
||||||
|
bool waitMonitorNoThrow();
|
||||||
|
|
||||||
void signalMonitor();
|
void signalMonitor();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ namespace Crash
|
||||||
|
|
||||||
void CrashMonitor::run()
|
void CrashMonitor::run()
|
||||||
{
|
{
|
||||||
|
mShm->mMonitorStatus = CrashSHM::Status::Monitoring;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// app waits for monitor start up, let it continue
|
// app waits for monitor start up, let it continue
|
||||||
|
|
@ -231,6 +232,18 @@ namespace Crash
|
||||||
|
|
||||||
void CrashMonitor::handleCrash(bool isFreeze)
|
void CrashMonitor::handleCrash(bool isFreeze)
|
||||||
{
|
{
|
||||||
|
shmLock();
|
||||||
|
mShm->mMonitorStatus = CrashSHM::Status::Dumping;
|
||||||
|
shmUnlock();
|
||||||
|
|
||||||
|
auto failedDumping = [this](const std::string& message) {
|
||||||
|
Log(Debug::Error) << "CrashMonitor: " << message;
|
||||||
|
shmLock();
|
||||||
|
mShm->mMonitorStatus = CrashSHM::Status::FailedDumping;
|
||||||
|
shmUnlock();
|
||||||
|
SDL_ShowSimpleMessageBox(0, "Failed to create crash dump", message.c_str(), nullptr);
|
||||||
|
};
|
||||||
|
|
||||||
DWORD processId = GetProcessId(mAppProcessHandle);
|
DWORD processId = GetProcessId(mAppProcessHandle);
|
||||||
const char* env = getenv("OPENMW_FULL_MEMDUMP");
|
const char* env = getenv("OPENMW_FULL_MEMDUMP");
|
||||||
|
|
||||||
|
|
@ -238,7 +251,10 @@ namespace Crash
|
||||||
{
|
{
|
||||||
HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
|
HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
|
||||||
if (dbghelp == NULL)
|
if (dbghelp == NULL)
|
||||||
|
{
|
||||||
|
failedDumping("Could not load dbghelp.dll");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
using MiniDumpWirteDumpFn = BOOL(WINAPI*)(HANDLE hProcess, DWORD ProcessId, HANDLE hFile,
|
using MiniDumpWirteDumpFn = BOOL(WINAPI*)(HANDLE hProcess, DWORD ProcessId, HANDLE hFile,
|
||||||
MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
||||||
|
|
@ -246,11 +262,17 @@ namespace Crash
|
||||||
|
|
||||||
MiniDumpWirteDumpFn miniDumpWriteDump = (MiniDumpWirteDumpFn)GetProcAddress(dbghelp, "MiniDumpWriteDump");
|
MiniDumpWirteDumpFn miniDumpWriteDump = (MiniDumpWirteDumpFn)GetProcAddress(dbghelp, "MiniDumpWriteDump");
|
||||||
if (miniDumpWriteDump == NULL)
|
if (miniDumpWriteDump == NULL)
|
||||||
|
{
|
||||||
|
failedDumping("Could not get MiniDumpWriteDump address");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::wstring utf16Path = (isFreeze ? getFreezeDumpPath(*mShm) : getCrashDumpPath(*mShm)).native();
|
std::wstring utf16Path = (isFreeze ? getFreezeDumpPath(*mShm) : getCrashDumpPath(*mShm)).native();
|
||||||
if (utf16Path.empty())
|
if (utf16Path.empty())
|
||||||
|
{
|
||||||
|
failedDumping("Empty dump path");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (utf16Path.length() > MAX_PATH)
|
if (utf16Path.length() > MAX_PATH)
|
||||||
utf16Path = LR"(\\?\)" + utf16Path;
|
utf16Path = LR"(\\?\)" + utf16Path;
|
||||||
|
|
@ -258,9 +280,17 @@ namespace Crash
|
||||||
HANDLE hCrashLog = CreateFileW(utf16Path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
|
HANDLE hCrashLog = CreateFileW(utf16Path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
|
||||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
if (hCrashLog == NULL || hCrashLog == INVALID_HANDLE_VALUE)
|
if (hCrashLog == NULL || hCrashLog == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
failedDumping("Bad dump file handle");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if (auto err = GetLastError(); err != ERROR_ALREADY_EXISTS && err != 0)
|
if (auto err = GetLastError(); err != ERROR_ALREADY_EXISTS && err != 0)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Error opening dump file: " << std::hex << err;
|
||||||
|
failedDumping(ss.str());
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
EXCEPTION_POINTERS exp;
|
EXCEPTION_POINTERS exp;
|
||||||
exp.ContextRecord = &mShm->mCrashed.mContext;
|
exp.ContextRecord = &mShm->mCrashed.mContext;
|
||||||
|
|
@ -274,15 +304,26 @@ namespace Crash
|
||||||
if (env)
|
if (env)
|
||||||
type = static_cast<MINIDUMP_TYPE>(type | MiniDumpWithFullMemory);
|
type = static_cast<MINIDUMP_TYPE>(type | MiniDumpWithFullMemory);
|
||||||
|
|
||||||
miniDumpWriteDump(mAppProcessHandle, processId, hCrashLog, type, &infos, 0, 0);
|
if (!miniDumpWriteDump(mAppProcessHandle, processId, hCrashLog, type, &infos, 0, 0))
|
||||||
|
{
|
||||||
|
auto err = GetLastError();
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Error while writing dump: " << std::hex << err;
|
||||||
|
failedDumping(ss.str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shmLock();
|
||||||
|
mShm->mMonitorStatus = CrashSHM::Status::DumpedSuccessfully;
|
||||||
|
shmUnlock();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "CrashMonitor: " << e.what();
|
failedDumping(e.what());
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "CrashMonitor: unknown exception";
|
failedDumping("Unknown exception");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,17 @@ namespace Crash
|
||||||
|
|
||||||
Event mEvent;
|
Event mEvent;
|
||||||
|
|
||||||
|
enum class Status
|
||||||
|
{
|
||||||
|
Uninitialised,
|
||||||
|
Monitoring,
|
||||||
|
Dumping,
|
||||||
|
DumpedSuccessfully,
|
||||||
|
FailedDumping
|
||||||
|
};
|
||||||
|
|
||||||
|
Status mMonitorStatus;
|
||||||
|
|
||||||
struct Startup
|
struct Startup
|
||||||
{
|
{
|
||||||
HANDLE mAppProcessHandle;
|
HANDLE mAppProcessHandle;
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,7 @@ namespace LuaUtil
|
||||||
colorType["asRgba"] = [](const Misc::Color& c) { return Vec4(c.r(), c.g(), c.b(), c.a()); };
|
colorType["asRgba"] = [](const Misc::Color& c) { return Vec4(c.r(), c.g(), c.b(), c.a()); };
|
||||||
colorType["asRgb"] = [](const Misc::Color& c) { return Vec3(c.r(), c.g(), c.b()); };
|
colorType["asRgb"] = [](const Misc::Color& c) { return Vec3(c.r(), c.g(), c.b()); };
|
||||||
colorType["asHex"] = [](const Misc::Color& c) { return c.toHex(); };
|
colorType["asHex"] = [](const Misc::Color& c) { return c.toHex(); };
|
||||||
|
colorType[sol::meta_function::equal_to] = [](const Misc::Color& a, const Misc::Color& b) { return a == b; };
|
||||||
|
|
||||||
sol::table color(lua, sol::create);
|
sol::table color(lua, sol::create);
|
||||||
color["rgba"] = [](float r, float g, float b, float a) { return Misc::Color(r, g, b, a); };
|
color["rgba"] = [](float r, float g, float b, float a) { return Misc::Color(r, g, b, a); };
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,12 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
|
#include <components/sceneutil/util.hpp>
|
||||||
|
|
||||||
namespace Misc
|
namespace Misc
|
||||||
{
|
{
|
||||||
Color::Color(float r, float g, float b, float a)
|
Color::Color(float r, float g, float b, float a)
|
||||||
: mR(std::clamp(r, 0.f, 1.f))
|
: mValue(std::clamp(r, 0.f, 1.f), std::clamp(g, 0.f, 1.f), std::clamp(b, 0.f, 1.f), std::clamp(a, 0.f, 1.f))
|
||||||
, mG(std::clamp(g, 0.f, 1.f))
|
|
||||||
, mB(std::clamp(b, 0.f, 1.f))
|
|
||||||
, mA(std::clamp(a, 0.f, 1.f))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,26 +26,31 @@ namespace Misc
|
||||||
{
|
{
|
||||||
if (hex.size() != 6)
|
if (hex.size() != 6)
|
||||||
throw std::logic_error(std::string("Invalid hex color: ") += hex);
|
throw std::logic_error(std::string("Invalid hex color: ") += hex);
|
||||||
std::array<float, 3> rgb;
|
Color col;
|
||||||
for (size_t i = 0; i < rgb.size(); i++)
|
col.mValue.a() = 1;
|
||||||
|
for (size_t i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
auto sub = hex.substr(i * 2, 2);
|
auto sub = hex.substr(i * 2, 2);
|
||||||
int v = 0;
|
int v = 0;
|
||||||
auto [_, ec] = std::from_chars(sub.data(), sub.data() + sub.size(), v, 16);
|
auto [_, ec] = std::from_chars(sub.data(), sub.data() + sub.size(), v, 16);
|
||||||
if (ec != std::errc())
|
if (ec != std::errc())
|
||||||
throw std::logic_error(std::string("Invalid hex color: ") += hex);
|
throw std::logic_error(std::string("Invalid hex color: ") += hex);
|
||||||
rgb[i] = v / 255.0f;
|
col.mValue[i] = v / 255.0f;
|
||||||
}
|
}
|
||||||
return Color(rgb[0], rgb[1], rgb[2], 1);
|
return col;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color Color::fromRGB(unsigned int value)
|
||||||
|
{
|
||||||
|
return Color(SceneUtil::colourFromRGB(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Color::toHex() const
|
std::string Color::toHex() const
|
||||||
{
|
{
|
||||||
std::string result(6, '0');
|
std::string result(6, '0');
|
||||||
std::array<float, 3> rgb = { mR, mG, mB };
|
for (size_t i = 0; i < 3; i++)
|
||||||
for (size_t i = 0; i < rgb.size(); i++)
|
|
||||||
{
|
{
|
||||||
int b = static_cast<int>(rgb[i] * 255.0f);
|
int b = static_cast<int>(mValue[i] * 255.0f);
|
||||||
char* start = result.data() + i * 2;
|
char* start = result.data() + i * 2;
|
||||||
if (b < 16)
|
if (b < 16)
|
||||||
start++;
|
start++;
|
||||||
|
|
@ -59,6 +63,6 @@ namespace Misc
|
||||||
|
|
||||||
bool operator==(const Color& l, const Color& r)
|
bool operator==(const Color& l, const Color& r)
|
||||||
{
|
{
|
||||||
return l.mR == r.mR && l.mG == r.mG && l.mB == r.mB && l.mA == r.mA;
|
return l.mValue == r.mValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,31 +3,38 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <osg/Vec4>
|
||||||
|
|
||||||
namespace Misc
|
namespace Misc
|
||||||
{
|
{
|
||||||
class Color
|
class Color
|
||||||
{
|
{
|
||||||
|
explicit Color(osg::Vec4&& value)
|
||||||
|
: mValue(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Color() = default;
|
||||||
Color(float r, float g, float b, float a);
|
Color(float r, float g, float b, float a);
|
||||||
|
|
||||||
float r() const { return mR; }
|
float r() const { return mValue.r(); }
|
||||||
float g() const { return mG; }
|
float g() const { return mValue.g(); }
|
||||||
float b() const { return mB; }
|
float b() const { return mValue.b(); }
|
||||||
float a() const { return mA; }
|
float a() const { return mValue.a(); }
|
||||||
|
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
|
|
||||||
static Color fromHex(std::string_view hex);
|
static Color fromHex(std::string_view hex);
|
||||||
|
static Color fromRGB(unsigned int value);
|
||||||
|
|
||||||
std::string toHex() const;
|
std::string toHex() const;
|
||||||
|
unsigned int toRGBA() const { return mValue.asRGBA(); }
|
||||||
|
|
||||||
friend bool operator==(const Color& l, const Color& r);
|
friend bool operator==(const Color& l, const Color& r);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float mR;
|
osg::Vec4 mValue;
|
||||||
float mG;
|
|
||||||
float mB;
|
|
||||||
float mA;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ Configuration sources
|
||||||
|
|
||||||
Configuration for OpenMW is composed from several sources.
|
Configuration for OpenMW is composed from several sources.
|
||||||
From lowest to highest priority, these are:
|
From lowest to highest priority, these are:
|
||||||
|
|
||||||
* The local or global ``openmw.cfg``.
|
* The local or global ``openmw.cfg``.
|
||||||
* Any ``openmw.cfg`` files in any other directories specified with the ``config`` option.
|
* Any ``openmw.cfg`` files in any other directories specified with the ``config`` option.
|
||||||
* Options specified on the command line.
|
* Options specified on the command line.
|
||||||
|
|
|
||||||
|
|
@ -1673,7 +1673,7 @@
|
||||||
-- @field #number value
|
-- @field #number value
|
||||||
-- @field #number duration
|
-- @field #number duration
|
||||||
-- @field #number radius
|
-- @field #number radius
|
||||||
-- @field #number color
|
-- @field openmw.util#Color color
|
||||||
-- @field #boolean isCarriable True if the light can be carried by actors and appears up in their inventory.
|
-- @field #boolean isCarriable True if the light can be carried by actors and appears up in their inventory.
|
||||||
-- @field #boolean isDynamic If true, the light will apply to actors and other moving objects
|
-- @field #boolean isDynamic If true, the light will apply to actors and other moving objects
|
||||||
-- @field #boolean isFire True if the light acts like a fire.
|
-- @field #boolean isFire True if the light acts like a fire.
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ testing.registerGlobalTest('record creation', function()
|
||||||
value = 10,
|
value = 10,
|
||||||
duration = 12,
|
duration = 12,
|
||||||
radius = 30,
|
radius = 30,
|
||||||
color = 5,
|
color = util.color.hex('123456'),
|
||||||
name = "TestLight",
|
name = "TestLight",
|
||||||
model = "meshes/marker_door.dae"
|
model = "meshes/marker_door.dae"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue