96 lines
3.4 KiB
C++
96 lines
3.4 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// FileDataID resolver for selected retail assets shipped in MoonWell patches.
|
|
|
|
#include "Host.hpp"
|
|
#include "core/Logger.hpp"
|
|
#include "mpq/MpqStore.hpp"
|
|
|
|
#include <charconv>
|
|
#include <cstdint>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <system_error>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
namespace moonwell::host
|
|
{
|
|
namespace
|
|
{
|
|
constexpr std::string_view kFileDataMap = "WXLFileData.csv";
|
|
|
|
std::once_flag g_loadOnce;
|
|
std::unordered_map<uint32_t, std::string> g_paths;
|
|
|
|
std::string_view Trim(std::string_view value)
|
|
{
|
|
while (!value.empty() && (value.front() == ' ' || value.front() == '\t' ||
|
|
value.front() == '\r' || value.front() == '\n'))
|
|
value.remove_prefix(1);
|
|
while (!value.empty() && (value.back() == ' ' || value.back() == '\t' ||
|
|
value.back() == '\r' || value.back() == '\n'))
|
|
value.remove_suffix(1);
|
|
return value;
|
|
}
|
|
|
|
void Load()
|
|
{
|
|
const std::string root = wxl::host::ClientRoot();
|
|
wxl::host::mpq::MpqStore store;
|
|
std::vector<uint8_t> bytes;
|
|
if (root.empty() || !store.Mount(root) || !store.ReadAll(kFileDataMap, bytes))
|
|
{
|
|
WLOG_WARN("moonwell: FileDataID map '%.*s' was not found",
|
|
int(kFileDataMap.size()), kFileDataMap.data());
|
|
return;
|
|
}
|
|
|
|
const std::string_view text(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
|
size_t lineStart = 0;
|
|
while (lineStart < text.size())
|
|
{
|
|
size_t lineEnd = text.find('\n', lineStart);
|
|
if (lineEnd == std::string_view::npos) lineEnd = text.size();
|
|
std::string_view line = Trim(text.substr(lineStart, lineEnd - lineStart));
|
|
lineStart = lineEnd + 1;
|
|
|
|
if (line.empty() || line.front() == '#') continue;
|
|
const size_t comma = line.find(',');
|
|
if (comma == std::string_view::npos) continue;
|
|
|
|
const std::string_view idText = Trim(line.substr(0, comma));
|
|
std::string_view pathText = Trim(line.substr(comma + 1));
|
|
uint32_t fileDataId = 0;
|
|
const auto parsed = std::from_chars(idText.data(), idText.data() + idText.size(), fileDataId);
|
|
if (parsed.ec != std::errc{} || parsed.ptr != idText.data() + idText.size() ||
|
|
fileDataId == 0 || pathText.empty())
|
|
continue;
|
|
|
|
std::string path(pathText);
|
|
for (char& c : path) if (c == '/') c = '\\';
|
|
g_paths[fileDataId] = std::move(path);
|
|
}
|
|
|
|
WLOG_INFO("moonwell: loaded %zu FileDataID path(s) from %.*s",
|
|
g_paths.size(), int(kFileDataMap.size()), kFileDataMap.data());
|
|
}
|
|
|
|
bool Resolve(uint32_t fileDataId, std::string& outPath)
|
|
{
|
|
std::call_once(g_loadOnce, &Load);
|
|
const auto found = g_paths.find(fileDataId);
|
|
if (found == g_paths.end()) return false;
|
|
outPath = found->second;
|
|
return true;
|
|
}
|
|
|
|
struct Registrar
|
|
{
|
|
Registrar() { wxl::host::RegisterResolver("moonwell-filedata", &Resolve); }
|
|
};
|
|
|
|
Registrar g_registrar;
|
|
}
|
|
}
|