handle unicode filenames in import/export

This commit is contained in:
avitasia
2026-03-17 09:00:21 -07:00
parent 0ecf1b2346
commit 6d18fbdbbb
5 changed files with 9650 additions and 9569 deletions
+9593 -9545
View File
File diff suppressed because it is too large Load Diff
+36 -8
View File
@@ -132,8 +132,34 @@ namespace Nampower {
return true;
}
static std::wstring Utf8ToWide(const std::string &utf8) {
if (utf8.empty()) {
return {};
}
const int len = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), static_cast<int>(utf8.size()), nullptr, 0);
if (len <= 0) {
return {};
}
std::wstring wide(len, L'\0');
MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), static_cast<int>(utf8.size()), &wide[0], len);
return wide;
}
static std::string WideToUtf8(const std::wstring &wide) {
if (wide.empty()) {
return {};
}
const int len = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), static_cast<int>(wide.size()), nullptr, 0, nullptr, nullptr);
if (len <= 0) {
return {};
}
std::string utf8(len, '\0');
WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), static_cast<int>(wide.size()), &utf8[0], len, nullptr, nullptr);
return utf8;
}
static bool EnsureDirectoryExists(const char *baseDir) {
if (CreateDirectoryA(baseDir, nullptr) != 0) {
if (CreateDirectoryW(Utf8ToWide(baseDir).c_str(), nullptr) != 0) {
return true;
}
@@ -155,13 +181,14 @@ namespace Nampower {
}
static bool TryGetFullPath(const std::string &inputPath, std::string &outFullPath) {
char buffer[MAX_PATH] = {};
const DWORD result = GetFullPathNameA(inputPath.c_str(), MAX_PATH, buffer, nullptr);
const std::wstring wideInput = Utf8ToWide(inputPath);
wchar_t buffer[MAX_PATH] = {};
const DWORD result = GetFullPathNameW(wideInput.c_str(), MAX_PATH, buffer, nullptr);
if (result == 0 || result >= MAX_PATH) {
return false;
}
outFullPath.assign(buffer, result);
outFullPath = WideToUtf8(std::wstring(buffer, result));
return true;
}
@@ -213,7 +240,8 @@ namespace Nampower {
DEBUG_LOG("Attempting to read file from " << baseDir << ": " << fullPath);
const DWORD attributes = GetFileAttributesA(fullPath.c_str());
const std::wstring wideFullPath = Utf8ToWide(fullPath);
const DWORD attributes = GetFileAttributesW(wideFullPath.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES) {
const DWORD errorCode = GetLastError();
if (returnNilIfMissing && (errorCode == ERROR_FILE_NOT_FOUND || errorCode == ERROR_PATH_NOT_FOUND)) {
@@ -229,7 +257,7 @@ namespace Nampower {
return 0;
}
std::ifstream in(fullPath);
std::ifstream in(wideFullPath);
if (!in) {
lua_error(luaState, "Failed to open file for reading.");
return 0;
@@ -273,7 +301,7 @@ namespace Nampower {
return 0;
}
std::ofstream out(fullPath, openMode);
std::ofstream out(Utf8ToWide(fullPath), openMode);
if (!out) {
lua_error(luaState, "Failed to open file for writing.");
return 0;
@@ -362,7 +390,7 @@ namespace Nampower {
return 0;
}
const DWORD attributes = GetFileAttributesA(fullPath.c_str());
const DWORD attributes = GetFileAttributesW(Utf8ToWide(fullPath).c_str());
if (attributes == INVALID_FILE_ATTRIBUTES) {
const DWORD errorCode = GetLastError();
if (errorCode == ERROR_FILE_NOT_FOUND || errorCode == ERROR_PATH_NOT_FOUND) {
+18 -15
View File
@@ -72,7 +72,7 @@ namespace Nampower {
uint32_t gBufferTimeMs; // adjusts dynamically depending on errors
uint32_t gDisenchantItemId = 0;
uint32_t gDisenchantQuality = 0; // 0 = unset
uint32_t gDisenchantQuality = 0; // 0 = unset
bool gDisenchantIncludeSoulbound = false;
uint32_t gNextDisenchantTimeMs = 0;
@@ -182,6 +182,7 @@ namespace Nampower {
// Forward declarations for hook functions
void Player_LoadScriptFunctionsHook(hadesmem::PatchDetourBase *detour);
void Glue_LoadScriptFunctionsHook(hadesmem::PatchDetourBase *detour);
void FrameScript_CreateEventsHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t maxEventId);
@@ -1424,7 +1425,7 @@ namespace Nampower {
}
return detour;
} catch (const hadesmem::Error& e) {
} catch (const hadesmem::Error &e) {
lastError = e.what();
if (attempt == 0) {
@@ -1433,7 +1434,7 @@ namespace Nampower {
}
if (attempt < maxRetries - 1) {
Sleep(10); // Wait 10ms before retry
Sleep(10); // Wait 10ms before retry
continue;
} else {
// Out of retries
@@ -1442,7 +1443,7 @@ namespace Nampower {
<< "): " << lastError);
throw;
}
} catch (const std::exception& e) {
} catch (const std::exception &e) {
lastError = e.what();
if (attempt == 0) {
@@ -1452,7 +1453,7 @@ namespace Nampower {
}
if (attempt < maxRetries - 1) {
Sleep(10); // Wait 10ms before retry
Sleep(10); // Wait 10ms before retry
continue;
} else {
DEBUG_LOG(" FAILED after " << maxRetries << " attempts (offset 0x"
@@ -1479,7 +1480,7 @@ namespace Nampower {
}
if (attempt < maxRetries - 1) {
Sleep(10); // Wait 10ms before retry
Sleep(10); // Wait 10ms before retry
continue;
} else {
DEBUG_LOG(" FAILED after " << maxRetries << " attempts (offset 0x"
@@ -1526,13 +1527,13 @@ namespace Nampower {
gSpellEnergizeLogHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellEnergizeLogHandler,
&SpellEnergizeLogHandlerHook);
gProcResistHandlerDetour = createHook<PacketHandlerT>(process, Offsets::ProcResistHandler,
&ProcResistHandlerHook);
&ProcResistHandlerHook);
gSpellLogMissHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellLogMissHandler,
&SpellLogMissHandlerHook);
&SpellLogMissHandlerHook);
gSpellOrDamageImmuneHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellOrDamageImmuneHandler,
&SpellOrDamageImmuneHandlerHook);
&SpellOrDamageImmuneHandlerHook);
gUnitCombatLogDispelledDetour = createHook<UnitCombatLogDispelledT>(process, Offsets::UnitCombatLogDispelled,
&UnitCombatLogDispelledHook);
&UnitCombatLogDispelledHook);
gSpellFailedOtherHandlerDetour = createHook<PacketHandlerT>(process, Offsets::SpellFailedOtherHandler,
&SpellFailedOtherHandlerHook);
gSpellFailedDetour = createHook<Spell_C_SpellFailedT>(process, Offsets::Spell_C_SpellFailed,
@@ -1571,12 +1572,14 @@ namespace Nampower {
&CGUnit_C_OnAuraStacksChangedHook);
gUnitCombatLogUnitDeadDetour = createHook<UnitCombatLogUnitDeadT>(process, Offsets::UnitCombatLogUnitDead,
&UnitCombatLogUnitDeadHook);
gCGBuffBar_UpdateDurationDetour = createHook<CGBuffBar_UpdateDurationT>(process, Offsets::CGBuffBar_UpdateDuration,
&CGBuffBar_UpdateDurationHook);
gAttackRoundInfo_ReadPacketDetour = createHook<AttackRoundInfo_ReadPacketT>(process, Offsets::AttackRoundInfo_ReadPacket,
&AttackRoundInfo_ReadPacketHook);
gCGBuffBar_UpdateDurationDetour = createHook<CGBuffBar_UpdateDurationT>(
process, Offsets::CGBuffBar_UpdateDuration,
&CGBuffBar_UpdateDurationHook);
gAttackRoundInfo_ReadPacketDetour = createHook<AttackRoundInfo_ReadPacketT>(
process, Offsets::AttackRoundInfo_ReadPacket,
&AttackRoundInfo_ReadPacketHook);
gCSimpleFrame_GetNameDetour = createHook<LuaScriptT>(process, Offsets::CSimpleFrame_GetName,
&CSimpleFrame_GetNameHook);
&CSimpleFrame_GetNameHook);
gCGUnit_C_AddChatBubbleDetour = createHook<CGUnit_C_AddChatBubbleT>(
process, Offsets::CGUnit_C_AddChatBubble, &CGUnit_C_AddChatBubbleHook);
gCSimpleTop_OnKeyDownDetour = createHook<CSimpleTop_OnKeyDownT>(
+1 -1
View File
@@ -27,7 +27,7 @@ namespace Nampower {
constexpr uint32_t MAJOR_VERSION = 4;
constexpr uint32_t MINOR_VERSION = 1;
constexpr uint32_t PATCH_VERSION = 0;
constexpr uint32_t PATCH_VERSION = 1;
constexpr int32_t LUA_REGISTRYINDEX = -10000;
constexpr int32_t LUA_GLOBALSINDEX = -10001;
+2
View File
@@ -46,4 +46,6 @@ namespace Nampower {
float Spell_C_GetSpellRadiusHook(hadesmem::PatchDetourBase *detour);
void SendCastHook(hadesmem::PatchDetourBase *detour, game::SpellCast *cast, char unk);
void TriggerQuickcast();
}