mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-07-04 03:12:48 -04:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a85f8c203d | |||
| dc0c868bc1 | |||
| 2afc52772c | |||
| 0dc475108b | |||
| a7790d7323 |
@@ -22,16 +22,11 @@ It aims to be as accurate as possible to the original while also providing new o
|
|||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> At a minimum, Dusklight requires a GPU with support for either D3D12, Vulkan, or Metal. Your experience with specific hardware, operating systems, and drivers may vary. In particular, older Intel iGPUs have a high likelihood of incompatibility. We are also aware of a number of issues on devices with Adreno GPUs and are working to resolve them.
|
> At a minimum, Dusklight requires a GPU with support for either D3D12, Vulkan, or Metal. Your experience with specific hardware, operating systems, and drivers may vary. In particular, older Intel iGPUs have a high likelihood of incompatibility. We are also aware of a number of issues on devices with Adreno GPUs and are working to resolve them.
|
||||||
|
|
||||||
### 1. Verify your dump
|
### 1. Dump your game
|
||||||
|
|
||||||
First, make sure your dump of the game is clean and supported by Dusklight. You can do this by checking the SHA-1 hash of your dump against this list of supported versions:
|
You must dump your own copy of the game, please see [this article](https://wiki.dolphin-emu.org/index.php?title=Ripping_Games) for instructions. After dumping, you can use a program like [Dolphin](https://dolphin-emu.org/) or [nodtool](https://github.com/encounter/nod/releases) to convert the `.iso` to a `.rvz` to save space.
|
||||||
|
|
||||||
| Version | SHA-1 hash |
|
Currently, only the GameCube USA and EUR releases are supported. Support for other versions of the game is planned in the future.
|
||||||
|--------------| ------------------------------------------ |
|
|
||||||
| GameCube USA | `75edd3ddff41f125d1b4ce1a40378f1b565519e7` |
|
|
||||||
| GameCube EUR | `2601822a488eeb86fb89db16ca8f29c2c953e1ca` |
|
|
||||||
|
|
||||||
*Support for other versions of the game is planned in the future.
|
|
||||||
|
|
||||||
### 2. Download [Dusklight](https://github.com/TwilitRealm/dusklight/releases)
|
### 2. Download [Dusklight](https://github.com/TwilitRealm/dusklight/releases)
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ public:
|
|||||||
|
|
||||||
~FileStream();
|
~FileStream();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Flush buffered writes and throw if the flush fails.
|
||||||
|
*/
|
||||||
|
void Flush();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Open a file for reading at the given path.
|
* \brief Open a file for reading at the given path.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -45,6 +45,20 @@ void dMdl_c::create(J3DModelData* i_modelData, u16 i_materialId, dKy_tevstr_c* i
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dMdl_c::entryObj(dMdl_obj_c* i_obj) {
|
void dMdl_c::entryObj(dMdl_obj_c* i_obj) {
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
// if field_0x1a is false, this dMdl_c is not in the drawlist
|
||||||
|
// if true, we need to make sure with interp enabled
|
||||||
|
if (dusk::frame_interp::is_enabled() && field_0x1a) {
|
||||||
|
auto pkt = dComIfGd_getListPacket()->mpBuffer[0];
|
||||||
|
while (pkt && pkt != this) {
|
||||||
|
pkt = pkt->getNextPacket();
|
||||||
|
}
|
||||||
|
if (!pkt) {
|
||||||
|
field_0x1a = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!field_0x1a) {
|
if (!field_0x1a) {
|
||||||
dComIfGd_getListPacket()->entryImm(this, 0);
|
dComIfGd_getListPacket()->entryImm(this, 0);
|
||||||
field_0x1a = true;
|
field_0x1a = true;
|
||||||
|
|||||||
+35
-5
@@ -8,6 +8,8 @@
|
|||||||
#include "dusk/settings.h"
|
#include "dusk/settings.h"
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <system_error>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "dusk/main.h"
|
#include "dusk/main.h"
|
||||||
@@ -24,8 +26,24 @@ aurora::Module DuskConfigLog("dusk::config");
|
|||||||
static absl::flat_hash_map<std::string_view, ConfigVarBase*> RegisteredConfigVars;
|
static absl::flat_hash_map<std::string_view, ConfigVarBase*> RegisteredConfigVars;
|
||||||
static bool RegistrationDone = false;
|
static bool RegistrationDone = false;
|
||||||
|
|
||||||
static std::u8string GetConfigJsonPath() {
|
static std::filesystem::path GetConfigJsonPath() {
|
||||||
return (dusk::ConfigPath / ConfigFileName).u8string();
|
return dusk::ConfigPath / ConfigFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::filesystem::path GetTempConfigJsonPath(const std::filesystem::path& configJsonPath) {
|
||||||
|
auto tempPath = configJsonPath;
|
||||||
|
tempPath.replace_filename(fmt::format(".{}.tmp", configJsonPath.filename().string()));
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReplaceFile(const std::filesystem::path& source, const std::filesystem::path& target) {
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::rename(source, target, ec);
|
||||||
|
if (ec) {
|
||||||
|
const auto renameError = ec;
|
||||||
|
std::filesystem::remove(source, ec);
|
||||||
|
throw std::system_error(renameError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigVarBase::ConfigVarBase(const char* name, const ConfigImplBase* impl) : name(name), registered(false), layer(ConfigVarLayer::Default), impl(impl) {
|
ConfigVarBase::ConfigVarBase(const char* name, const ConfigImplBase* impl) : name(name), registered(false), layer(ConfigVarLayer::Default), impl(impl) {
|
||||||
@@ -211,7 +229,8 @@ void dusk::config::LoadFromUserPreferences() {
|
|||||||
if (configJsonPath.empty()) {
|
if (configJsonPath.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LoadFromFileName(reinterpret_cast<const char*>(configJsonPath.c_str()));
|
const auto configPathString = io::fs_path_to_string(configJsonPath);
|
||||||
|
LoadFromFileName(configPathString.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LoadFromPath(const char* path) {
|
static void LoadFromPath(const char* path) {
|
||||||
@@ -254,6 +273,10 @@ void dusk::config::LoadFromFileName(const char* path) {
|
|||||||
} else {
|
} else {
|
||||||
DuskConfigLog.error("Failed to load from config! {}", e.what());
|
DuskConfigLog.error("Failed to load from config! {}", e.what());
|
||||||
}
|
}
|
||||||
|
} catch (const nlohmann::json::parse_error& e) {
|
||||||
|
DuskConfigLog.error("Failed to parse config JSON, staying with defaults: {}", e.what());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
DuskConfigLog.error("Failed to load from config, staying with defaults: {}", e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,10 +285,11 @@ void dusk::config::Save() {
|
|||||||
if (configJsonPath.empty()) {
|
if (configJsonPath.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto configPathString = io::fs_path_to_string(configJsonPath);
|
||||||
|
|
||||||
DuskConfigLog.info(
|
DuskConfigLog.info(
|
||||||
"Saving config to '{}'",
|
"Saving config to '{}'",
|
||||||
reinterpret_cast<const char*>(configJsonPath.c_str()));
|
configPathString);
|
||||||
|
|
||||||
json j;
|
json j;
|
||||||
|
|
||||||
@@ -276,7 +300,13 @@ void dusk::config::Save() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
io::FileStream::WriteAllText(reinterpret_cast<const char*>(configJsonPath.c_str()), j.dump(4));
|
try {
|
||||||
|
const auto tempConfigJsonPath = GetTempConfigJsonPath(configJsonPath);
|
||||||
|
io::FileStream::WriteAllText(tempConfigJsonPath, j.dump(4));
|
||||||
|
ReplaceFile(tempConfigJsonPath, configJsonPath);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
DuskConfigLog.error("Failed to save config to '{}': {}", configPathString, e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dusk::config::ClearAllActionBindings(int port) {
|
void dusk::config::ClearAllActionBindings(int port) {
|
||||||
|
|||||||
+254
-44
@@ -64,6 +64,15 @@ struct CrashContext {
|
|||||||
};
|
};
|
||||||
CrashContext g_ctx;
|
CrashContext g_ctx;
|
||||||
|
|
||||||
|
struct ModuleInfo {
|
||||||
|
uintptr_t base = 0;
|
||||||
|
uintptr_t size = 0;
|
||||||
|
char path[1024] = {};
|
||||||
|
uint8_t buildId[64] = {};
|
||||||
|
unsigned buildIdLen = 0;
|
||||||
|
unsigned pdbAge = 0;
|
||||||
|
};
|
||||||
|
|
||||||
void rawWrite(int fd, const char* data, size_t len) {
|
void rawWrite(int fd, const char* data, size_t len) {
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
return;
|
return;
|
||||||
@@ -119,9 +128,29 @@ void writeHexBytes(int fd, const uint8_t* data, unsigned len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* moduleName() {
|
void writeHexByte(int fd, uint8_t value) {
|
||||||
const char* name = g_ctx.modulePath;
|
char buf[2];
|
||||||
for (const char* p = g_ctx.modulePath; *p != '\0'; ++p) {
|
buf[0] = kHexDigits[value >> 4];
|
||||||
|
buf[1] = kHexDigits[value & 0xF];
|
||||||
|
rawWrite(fd, buf, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeQuoted(int fd, const char* s) {
|
||||||
|
writeStr(fd, "\"");
|
||||||
|
if (s != nullptr) {
|
||||||
|
for (const char* p = s; *p != '\0'; ++p) {
|
||||||
|
if (*p == '"' || *p == '\\') {
|
||||||
|
rawWrite(fd, "\\", 1);
|
||||||
|
}
|
||||||
|
rawWrite(fd, p, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeStr(fd, "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* baseName(const char* path) {
|
||||||
|
const char* name = path;
|
||||||
|
for (const char* p = path; p != nullptr && *p != '\0'; ++p) {
|
||||||
if (*p == '/' || *p == '\\') {
|
if (*p == '/' || *p == '\\') {
|
||||||
name = p + 1;
|
name = p + 1;
|
||||||
}
|
}
|
||||||
@@ -129,6 +158,40 @@ const char* moduleName() {
|
|||||||
return name[0] != '\0' ? name : "(unknown)";
|
return name[0] != '\0' ? name : "(unknown)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void writeBuildId(int fd, const uint8_t* buildId, unsigned buildIdLen, unsigned pdbAge) {
|
||||||
|
if (buildIdLen == 0) {
|
||||||
|
writeStr(fd, "(unavailable)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if defined(_WIN32)
|
||||||
|
if (buildIdLen == 16) {
|
||||||
|
writeHexByte(fd, buildId[3]);
|
||||||
|
writeHexByte(fd, buildId[2]);
|
||||||
|
writeHexByte(fd, buildId[1]);
|
||||||
|
writeHexByte(fd, buildId[0]);
|
||||||
|
writeStr(fd, "-");
|
||||||
|
writeHexByte(fd, buildId[5]);
|
||||||
|
writeHexByte(fd, buildId[4]);
|
||||||
|
writeStr(fd, "-");
|
||||||
|
writeHexByte(fd, buildId[7]);
|
||||||
|
writeHexByte(fd, buildId[6]);
|
||||||
|
writeStr(fd, "-");
|
||||||
|
writeHexByte(fd, buildId[8]);
|
||||||
|
writeHexByte(fd, buildId[9]);
|
||||||
|
writeStr(fd, "-");
|
||||||
|
writeHexBytes(fd, buildId + 10, 6);
|
||||||
|
if (pdbAge != 0) {
|
||||||
|
writeStr(fd, "-");
|
||||||
|
writeDec(fd, pdbAge);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)pdbAge;
|
||||||
|
#endif
|
||||||
|
writeHexBytes(fd, buildId, buildIdLen);
|
||||||
|
}
|
||||||
|
|
||||||
const char* symbolFor(uintptr_t pc, unsigned long long* disp) {
|
const char* symbolFor(uintptr_t pc, unsigned long long* disp) {
|
||||||
#if defined(_WIN32) && defined(DUSK_CRASH_DBGHELP)
|
#if defined(_WIN32) && defined(DUSK_CRASH_DBGHELP)
|
||||||
alignas(SYMBOL_INFO) static char storage[sizeof(SYMBOL_INFO) + 512];
|
alignas(SYMBOL_INFO) static char storage[sizeof(SYMBOL_INFO) + 512];
|
||||||
@@ -156,7 +219,47 @@ const char* symbolFor(uintptr_t pc, unsigned long long* disp) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fallbackModuleInfo(ModuleInfo& info) {
|
||||||
|
info = {};
|
||||||
|
info.base = g_ctx.moduleBase;
|
||||||
|
std::strncpy(info.path, g_ctx.modulePath, sizeof(info.path) - 1);
|
||||||
|
if (g_ctx.buildIdLen > sizeof(info.buildId)) {
|
||||||
|
info.buildIdLen = sizeof(info.buildId);
|
||||||
|
} else {
|
||||||
|
info.buildIdLen = g_ctx.buildIdLen;
|
||||||
|
}
|
||||||
|
if (info.buildIdLen != 0) {
|
||||||
|
std::memcpy(info.buildId, g_ctx.buildId, info.buildIdLen);
|
||||||
|
}
|
||||||
|
info.pdbAge = g_ctx.pdbAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool findModuleInfo(uintptr_t pc, ModuleInfo& info);
|
||||||
|
|
||||||
|
void emitAddressDetail(int fd, uintptr_t pc) {
|
||||||
|
ModuleInfo info;
|
||||||
|
findModuleInfo(pc, info);
|
||||||
|
const uintptr_t rva = pc >= info.base ? pc - info.base : 0ull;
|
||||||
|
writeHex(fd, pc);
|
||||||
|
writeStr(fd, " module_base=");
|
||||||
|
writeHex(fd, info.base);
|
||||||
|
if (info.size != 0) {
|
||||||
|
writeStr(fd, " image_size=");
|
||||||
|
writeHex(fd, info.size);
|
||||||
|
}
|
||||||
|
writeStr(fd, " rva=");
|
||||||
|
writeHex(fd, rva);
|
||||||
|
writeStr(fd, " module=");
|
||||||
|
writeQuoted(fd, info.path[0] != '\0' ? info.path : baseName(g_ctx.modulePath));
|
||||||
|
writeStr(fd, " build_id=");
|
||||||
|
writeBuildId(fd, info.buildId, info.buildIdLen, info.pdbAge);
|
||||||
|
}
|
||||||
|
|
||||||
void emitFrame(int fd, int index, uintptr_t pc) {
|
void emitFrame(int fd, int index, uintptr_t pc) {
|
||||||
|
ModuleInfo info;
|
||||||
|
findModuleInfo(pc, info);
|
||||||
|
const uintptr_t rva = pc >= info.base ? pc - info.base : 0ull;
|
||||||
|
|
||||||
writeStr(fd, "#");
|
writeStr(fd, "#");
|
||||||
if (index < 10) {
|
if (index < 10) {
|
||||||
writeStr(fd, "0");
|
writeStr(fd, "0");
|
||||||
@@ -164,10 +267,18 @@ void emitFrame(int fd, int index, uintptr_t pc) {
|
|||||||
writeDec(fd, static_cast<unsigned int>(index));
|
writeDec(fd, static_cast<unsigned int>(index));
|
||||||
writeStr(fd, " abs=");
|
writeStr(fd, " abs=");
|
||||||
writeHex(fd, pc);
|
writeHex(fd, pc);
|
||||||
|
writeStr(fd, " module_base=");
|
||||||
|
writeHex(fd, info.base);
|
||||||
|
if (info.size != 0) {
|
||||||
|
writeStr(fd, " image_size=");
|
||||||
|
writeHex(fd, info.size);
|
||||||
|
}
|
||||||
writeStr(fd, " rva=");
|
writeStr(fd, " rva=");
|
||||||
writeHex(fd, pc >= g_ctx.moduleBase ? pc - g_ctx.moduleBase : 0ull);
|
writeHex(fd, rva);
|
||||||
writeStr(fd, " ");
|
writeStr(fd, " module=");
|
||||||
writeStr(fd, moduleName());
|
writeQuoted(fd, info.path[0] != '\0' ? info.path : baseName(g_ctx.modulePath));
|
||||||
|
writeStr(fd, " build_id=");
|
||||||
|
writeBuildId(fd, info.buildId, info.buildIdLen, info.pdbAge);
|
||||||
unsigned long long disp = 0;
|
unsigned long long disp = 0;
|
||||||
const char* sym = symbolFor(pc, &disp);
|
const char* sym = symbolFor(pc, &disp);
|
||||||
if (sym != nullptr && sym[0] != '\0') {
|
if (sym != nullptr && sym[0] != '\0') {
|
||||||
@@ -191,18 +302,7 @@ void emitHeader(int fd, const char* reason, unsigned long long code, bool hasCod
|
|||||||
writeStr(fd, "\nModule base: ");
|
writeStr(fd, "\nModule base: ");
|
||||||
writeHex(fd, g_ctx.moduleBase);
|
writeHex(fd, g_ctx.moduleBase);
|
||||||
writeStr(fd, "\nBuild-ID: ");
|
writeStr(fd, "\nBuild-ID: ");
|
||||||
if (g_ctx.buildIdLen != 0) {
|
writeBuildId(fd, g_ctx.buildId, g_ctx.buildIdLen, g_ctx.pdbAge);
|
||||||
writeHexBytes(fd, g_ctx.buildId, g_ctx.buildIdLen);
|
|
||||||
#if defined(_WIN32)
|
|
||||||
if (g_ctx.pdbAge != 0) {
|
|
||||||
writeStr(fd, " (Age=");
|
|
||||||
writeDec(fd, g_ctx.pdbAge);
|
|
||||||
writeStr(fd, ")");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
writeStr(fd, "(unavailable)");
|
|
||||||
}
|
|
||||||
writeStr(fd, "\nReason: ");
|
writeStr(fd, "\nReason: ");
|
||||||
writeStr(fd, reason);
|
writeStr(fd, reason);
|
||||||
if (hasCode) {
|
if (hasCode) {
|
||||||
@@ -214,9 +314,7 @@ void emitHeader(int fd, const char* reason, unsigned long long code, bool hasCod
|
|||||||
writeHex(fd, faultAddr);
|
writeHex(fd, faultAddr);
|
||||||
writeStr(fd, "\nCrash PC: ");
|
writeStr(fd, "\nCrash PC: ");
|
||||||
if (crashPcKnown) {
|
if (crashPcKnown) {
|
||||||
writeHex(fd, crashPc);
|
emitAddressDetail(fd, crashPc);
|
||||||
writeStr(fd, " rva=");
|
|
||||||
writeHex(fd, crashPc >= g_ctx.moduleBase ? crashPc - g_ctx.moduleBase : 0ull);
|
|
||||||
} else {
|
} else {
|
||||||
writeStr(fd, "(unavailable on this platform)");
|
writeStr(fd, "(unavailable on this platform)");
|
||||||
}
|
}
|
||||||
@@ -233,23 +331,25 @@ void emitFooter(int fd) {
|
|||||||
LONG g_inHandler = 0;
|
LONG g_inHandler = 0;
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER g_prevFilter = nullptr;
|
LPTOP_LEVEL_EXCEPTION_FILTER g_prevFilter = nullptr;
|
||||||
|
|
||||||
void captureBuildId() {
|
bool readPeModuleInfo(uintptr_t moduleBase, ModuleInfo& info) {
|
||||||
const auto* base = reinterpret_cast<const uint8_t*>(g_ctx.moduleBase);
|
const auto* base = reinterpret_cast<const uint8_t*>(moduleBase);
|
||||||
if (base == nullptr) {
|
if (base == nullptr) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
const auto* dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(base);
|
const auto* dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(base);
|
||||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
|
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
const auto* nt = reinterpret_cast<const IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
|
const auto* nt = reinterpret_cast<const IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
|
||||||
if (nt->Signature != IMAGE_NT_SIGNATURE) {
|
if (nt->Signature != IMAGE_NT_SIGNATURE) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
info.base = moduleBase;
|
||||||
|
info.size = nt->OptionalHeader.SizeOfImage;
|
||||||
const IMAGE_DATA_DIRECTORY& dir =
|
const IMAGE_DATA_DIRECTORY& dir =
|
||||||
nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
|
nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
|
||||||
if (dir.VirtualAddress == 0 || dir.Size == 0) {
|
if (dir.VirtualAddress == 0 || dir.Size == 0) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
const auto* dbg = reinterpret_cast<const IMAGE_DEBUG_DIRECTORY*>(base + dir.VirtualAddress);
|
const auto* dbg = reinterpret_cast<const IMAGE_DEBUG_DIRECTORY*>(base + dir.VirtualAddress);
|
||||||
const unsigned count = dir.Size / sizeof(IMAGE_DEBUG_DIRECTORY);
|
const unsigned count = dir.Size / sizeof(IMAGE_DEBUG_DIRECTORY);
|
||||||
@@ -261,11 +361,40 @@ void captureBuildId() {
|
|||||||
if (std::memcmp(cv, "RSDS", 4) != 0) {
|
if (std::memcmp(cv, "RSDS", 4) != 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::memcpy(g_ctx.buildId, cv + 4, sizeof(GUID));
|
std::memcpy(info.buildId, cv + 4, sizeof(GUID));
|
||||||
g_ctx.buildIdLen = sizeof(GUID);
|
info.buildIdLen = sizeof(GUID);
|
||||||
std::memcpy(&g_ctx.pdbAge, cv + 4 + sizeof(GUID), sizeof(g_ctx.pdbAge));
|
std::memcpy(&info.pdbAge, cv + 4 + sizeof(GUID), sizeof(info.pdbAge));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void captureBuildId() {
|
||||||
|
ModuleInfo info;
|
||||||
|
if (!readPeModuleInfo(g_ctx.moduleBase, info)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_ctx.buildIdLen = info.buildIdLen;
|
||||||
|
if (g_ctx.buildIdLen != 0) {
|
||||||
|
std::memcpy(g_ctx.buildId, info.buildId, g_ctx.buildIdLen);
|
||||||
|
}
|
||||||
|
g_ctx.pdbAge = info.pdbAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool findModuleInfo(uintptr_t pc, ModuleInfo& info) {
|
||||||
|
fallbackModuleInfo(info);
|
||||||
|
MEMORY_BASIC_INFORMATION mbi;
|
||||||
|
if (VirtualQuery(reinterpret_cast<LPCVOID>(pc), &mbi, sizeof(mbi)) == 0 ||
|
||||||
|
mbi.AllocationBase == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto moduleBase = reinterpret_cast<uintptr_t>(mbi.AllocationBase);
|
||||||
|
info = {};
|
||||||
|
info.base = moduleBase;
|
||||||
|
GetModuleFileNameA(reinterpret_cast<HMODULE>(moduleBase), info.path,
|
||||||
|
static_cast<DWORD>(sizeof(info.path) - 1));
|
||||||
|
readPeModuleInfo(moduleBase, info);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* exceptionName(DWORD code) {
|
const char* exceptionName(DWORD code) {
|
||||||
@@ -512,23 +641,35 @@ void prewarmUnwinder() {
|
|||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
|
|
||||||
void captureBuildId() {
|
bool readMachBuildId(uintptr_t moduleBase, ModuleInfo& info) {
|
||||||
const auto* header = reinterpret_cast<const struct mach_header_64*>(g_ctx.moduleBase);
|
const auto* header = reinterpret_cast<const struct mach_header_64*>(moduleBase);
|
||||||
if (header == nullptr || header->magic != MH_MAGIC_64) {
|
if (header == nullptr || header->magic != MH_MAGIC_64) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
const auto* lc = reinterpret_cast<const struct load_command*>(
|
const auto* lc = reinterpret_cast<const struct load_command*>(
|
||||||
reinterpret_cast<const char*>(header) + sizeof(struct mach_header_64));
|
reinterpret_cast<const char*>(header) + sizeof(struct mach_header_64));
|
||||||
for (uint32_t i = 0; i < header->ncmds; ++i) {
|
for (uint32_t i = 0; i < header->ncmds; ++i) {
|
||||||
if (lc->cmd == LC_UUID) {
|
if (lc->cmd == LC_UUID) {
|
||||||
const auto* uuid = reinterpret_cast<const struct uuid_command*>(lc);
|
const auto* uuid = reinterpret_cast<const struct uuid_command*>(lc);
|
||||||
std::memcpy(g_ctx.buildId, uuid->uuid, sizeof(uuid->uuid));
|
std::memcpy(info.buildId, uuid->uuid, sizeof(uuid->uuid));
|
||||||
g_ctx.buildIdLen = sizeof(uuid->uuid);
|
info.buildIdLen = sizeof(uuid->uuid);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
lc = reinterpret_cast<const struct load_command*>(
|
lc = reinterpret_cast<const struct load_command*>(
|
||||||
reinterpret_cast<const char*>(lc) + lc->cmdsize);
|
reinterpret_cast<const char*>(lc) + lc->cmdsize);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void captureBuildId() {
|
||||||
|
ModuleInfo info;
|
||||||
|
if (!readMachBuildId(g_ctx.moduleBase, info)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_ctx.buildIdLen = info.buildIdLen;
|
||||||
|
if (g_ctx.buildIdLen != 0) {
|
||||||
|
std::memcpy(g_ctx.buildId, info.buildId, g_ctx.buildIdLen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@@ -547,7 +688,28 @@ bool segmentContains(const dl_phdr_info* info, uintptr_t addr) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool readGnuBuildId(const dl_phdr_info* info) {
|
void readElfModuleInfo(const dl_phdr_info* info, ModuleInfo& module) {
|
||||||
|
uintptr_t minAddr = ~static_cast<uintptr_t>(0);
|
||||||
|
uintptr_t maxAddr = 0;
|
||||||
|
for (int i = 0; i < info->dlpi_phnum; ++i) {
|
||||||
|
const ElfW(Phdr)& ph = info->dlpi_phdr[i];
|
||||||
|
if (ph.p_type != PT_LOAD) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const uintptr_t start = info->dlpi_addr + ph.p_vaddr;
|
||||||
|
const uintptr_t end = start + ph.p_memsz;
|
||||||
|
if (start < minAddr) {
|
||||||
|
minAddr = start;
|
||||||
|
}
|
||||||
|
if (end > maxAddr) {
|
||||||
|
maxAddr = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (minAddr <= maxAddr && maxAddr != 0) {
|
||||||
|
module.base = minAddr;
|
||||||
|
module.size = maxAddr - minAddr;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < info->dlpi_phnum; ++i) {
|
for (int i = 0; i < info->dlpi_phnum; ++i) {
|
||||||
const ElfW(Phdr)& ph = info->dlpi_phdr[i];
|
const ElfW(Phdr)& ph = info->dlpi_phdr[i];
|
||||||
if (ph.p_type != PT_NOTE) {
|
if (ph.p_type != PT_NOTE) {
|
||||||
@@ -563,17 +725,16 @@ bool readGnuBuildId(const dl_phdr_info* info) {
|
|||||||
if (nh->n_type == NT_GNU_BUILD_ID && nh->n_namesz == 4 &&
|
if (nh->n_type == NT_GNU_BUILD_ID && nh->n_namesz == 4 &&
|
||||||
std::memcmp(name, "GNU", 4) == 0) {
|
std::memcmp(name, "GNU", 4) == 0) {
|
||||||
unsigned n = nh->n_descsz;
|
unsigned n = nh->n_descsz;
|
||||||
if (n > sizeof(g_ctx.buildId)) {
|
if (n > sizeof(module.buildId)) {
|
||||||
n = sizeof(g_ctx.buildId);
|
n = sizeof(module.buildId);
|
||||||
}
|
}
|
||||||
std::memcpy(g_ctx.buildId, desc, n);
|
std::memcpy(module.buildId, desc, n);
|
||||||
g_ctx.buildIdLen = n;
|
module.buildIdLen = n;
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
p = desc + ((nh->n_descsz + 3) & ~3u);
|
p = desc + ((nh->n_descsz + 3) & ~3u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int elfBuildIdCallback(dl_phdr_info* info, size_t, void* arg) {
|
int elfBuildIdCallback(dl_phdr_info* info, size_t, void* arg) {
|
||||||
@@ -581,7 +742,12 @@ int elfBuildIdCallback(dl_phdr_info* info, size_t, void* arg) {
|
|||||||
if (!segmentContains(info, self)) {
|
if (!segmentContains(info, self)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
readGnuBuildId(info);
|
ModuleInfo module;
|
||||||
|
readElfModuleInfo(info, module);
|
||||||
|
g_ctx.buildIdLen = module.buildIdLen;
|
||||||
|
if (g_ctx.buildIdLen != 0) {
|
||||||
|
std::memcpy(g_ctx.buildId, module.buildId, g_ctx.buildIdLen);
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -592,6 +758,50 @@ void captureBuildId() {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(__APPLE__)
|
||||||
|
struct ElfModuleSearch {
|
||||||
|
uintptr_t pc;
|
||||||
|
ModuleInfo* module;
|
||||||
|
};
|
||||||
|
|
||||||
|
int elfModuleInfoCallback(dl_phdr_info* info, size_t, void* arg) {
|
||||||
|
auto* search = static_cast<ElfModuleSearch*>(arg);
|
||||||
|
if (!segmentContains(info, search->pc)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (info->dlpi_name != nullptr && info->dlpi_name[0] != '\0') {
|
||||||
|
std::strncpy(search->module->path, info->dlpi_name,
|
||||||
|
sizeof(search->module->path) - 1);
|
||||||
|
}
|
||||||
|
readElfModuleInfo(info, *search->module);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool findModuleInfo(uintptr_t pc, ModuleInfo& info) {
|
||||||
|
fallbackModuleInfo(info);
|
||||||
|
Dl_info moduleInfo;
|
||||||
|
if (dladdr(reinterpret_cast<void*>(pc), &moduleInfo) == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (moduleInfo.dli_fbase != nullptr) {
|
||||||
|
info.base = reinterpret_cast<uintptr_t>(moduleInfo.dli_fbase);
|
||||||
|
}
|
||||||
|
if (moduleInfo.dli_fname != nullptr && moduleInfo.dli_fname[0] != '\0') {
|
||||||
|
info.path[0] = '\0';
|
||||||
|
std::strncpy(info.path, moduleInfo.dli_fname, sizeof(info.path) - 1);
|
||||||
|
}
|
||||||
|
info.buildIdLen = 0;
|
||||||
|
info.pdbAge = 0;
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
readMachBuildId(info.base, info);
|
||||||
|
#else
|
||||||
|
ElfModuleSearch search{pc, &info};
|
||||||
|
dl_iterate_phdr(&elfModuleInfoCallback, &search);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const char* signalName(int sig) {
|
const char* signalName(int sig) {
|
||||||
switch (sig) {
|
switch (sig) {
|
||||||
case SIGSEGV:
|
case SIGSEGV:
|
||||||
|
|||||||
+15
-1
@@ -1,5 +1,7 @@
|
|||||||
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
#include "dusk/io.hpp"
|
#include "dusk/io.hpp"
|
||||||
|
|
||||||
@@ -30,6 +32,9 @@ static FILE* ThrowIfNotOpen(const FileStream& file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] static void ThrowForError(int code) {
|
[[noreturn]] static void ThrowForError(int code) {
|
||||||
|
if (code == 0) {
|
||||||
|
throw std::system_error(std::make_error_code(std::errc::io_error));
|
||||||
|
}
|
||||||
throw std::system_error(std::make_error_code(static_cast<std::errc>(code)));
|
throw std::system_error(std::make_error_code(static_cast<std::errc>(code)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +82,14 @@ FileStream::~FileStream() {
|
|||||||
fclose(static_cast<FILE*>(file));
|
fclose(static_cast<FILE*>(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileStream::Flush() {
|
||||||
|
FILE* fileHandle = ThrowIfNotOpen(*this);
|
||||||
|
|
||||||
|
if (fflush(fileHandle) != 0) {
|
||||||
|
ThrowForError(errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FileStream FileStream::OpenRead(const char* utf8Path) {
|
FileStream FileStream::OpenRead(const char* utf8Path) {
|
||||||
return FileStream(OpenCore(utf8Path, MODE("rb"), _SH_DENYWR));
|
return FileStream(OpenCore(utf8Path, MODE("rb"), _SH_DENYWR));
|
||||||
}
|
}
|
||||||
@@ -163,10 +176,11 @@ void FileStream::WriteAllText(const char* utf8Path, const std::string_view text)
|
|||||||
void FileStream::WriteAllText(const std::filesystem::path& path, const std::string_view text) {
|
void FileStream::WriteAllText(const std::filesystem::path& path, const std::string_view text) {
|
||||||
auto handle = Create(path);
|
auto handle = Create(path);
|
||||||
handle.Write(text.data(), text.size());
|
handle.Write(text.data(), text.size());
|
||||||
|
handle.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE* FileStream::ToInner() {
|
FILE* FileStream::ToInner() {
|
||||||
auto handle = file;
|
auto handle = file;
|
||||||
file = nullptr;
|
file = nullptr;
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ void set_value(GraphicsOption option, int value) {
|
|||||||
getSettings().game.bloomMultiplier.setValue(std::clamp(value, 0, 100) / 100.0f);
|
getSettings().game.bloomMultiplier.setValue(std::clamp(value, 0, 100) / 100.0f);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
config::Save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rml::Element* create_stepped_carousel_root(Rml::Element* parent) {
|
Rml::Element* create_stepped_carousel_root(Rml::Element* parent) {
|
||||||
@@ -292,6 +291,7 @@ void GraphicsTuner::show() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsTuner::hide(bool close) {
|
void GraphicsTuner::hide(bool close) {
|
||||||
|
config::Save();
|
||||||
mRoot->RemoveAttribute("open");
|
mRoot->RemoveAttribute("open");
|
||||||
if (close) {
|
if (close) {
|
||||||
mPendingClose = true;
|
mPendingClose = true;
|
||||||
|
|||||||
@@ -1501,4 +1501,9 @@ void SettingsWindow::update() {
|
|||||||
Window::update();
|
Window::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsWindow::hide(bool close) {
|
||||||
|
config::Save();
|
||||||
|
Window::hide(close);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dusk::ui
|
} // namespace dusk::ui
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public:
|
|||||||
SettingsWindow(bool prelaunch = false);
|
SettingsWindow(bool prelaunch = false);
|
||||||
|
|
||||||
void update() override;
|
void update() override;
|
||||||
|
void hide(bool close) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool mPrelaunch;
|
bool mPrelaunch;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace dusk::ui {
|
|||||||
BaseStringButton::BaseStringButton(Rml::Element* parent, Props props)
|
BaseStringButton::BaseStringButton(Rml::Element* parent, Props props)
|
||||||
: BaseControlledSelectButton(parent, {std::move(props.key)}), mType(std::move(props.type)),
|
: BaseControlledSelectButton(parent, {std::move(props.key)}), mType(std::move(props.type)),
|
||||||
mMaxLength(props.maxLength) {
|
mMaxLength(props.maxLength) {
|
||||||
mInputListeners.reserve(3);
|
mInputListeners.reserve(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseStringButton::update() {
|
void BaseStringButton::update() {
|
||||||
@@ -54,6 +54,15 @@ void BaseStringButton::start_editing() {
|
|||||||
mRoot->DispatchEvent(Rml::EventId::Submit, {{"handled", Rml::Variant{true}}});
|
mRoot->DispatchEvent(Rml::EventId::Submit, {{"handled", Rml::Variant{true}}});
|
||||||
|
|
||||||
// Register input listeners
|
// Register input listeners
|
||||||
|
mInputListeners.emplace_back(std::make_unique<ScopedEventListener>(
|
||||||
|
mInputElem, Rml::EventId::Textinput, [this](Rml::Event& event) {
|
||||||
|
if (event.GetTargetElement() == mInputElem) {
|
||||||
|
const Rml::String text = event.GetParameter("text", Rml::String{});
|
||||||
|
if (!text.empty() && std::ranges::all_of(text, [](const char c) { return c == '\r' || c == '\n' || c == '\t'; })) {
|
||||||
|
event.StopImmediatePropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
mInputListeners.emplace_back(std::make_unique<ScopedEventListener>(
|
mInputListeners.emplace_back(std::make_unique<ScopedEventListener>(
|
||||||
mInputElem, Rml::EventId::Keydown, [this](Rml::Event& event) {
|
mInputElem, Rml::EventId::Keydown, [this](Rml::Event& event) {
|
||||||
const auto cmd = map_nav_event(event);
|
const auto cmd = map_nav_event(event);
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ bool initialize() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void shutdown() noexcept {
|
void shutdown() noexcept {
|
||||||
config::Save();
|
|
||||||
sDocumentStack.clear();
|
sDocumentStack.clear();
|
||||||
sPassiveDocuments.clear();
|
sPassiveDocuments.clear();
|
||||||
sConnectedGamepads.clear();
|
sConnectedGamepads.clear();
|
||||||
|
|||||||
Reference in New Issue
Block a user