mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-15 00:43:52 +00:00
refactor path mapper
This commit is contained in:
parent
5542cbaa02
commit
1e464dd513
3 changed files with 13 additions and 194 deletions
|
|
@ -448,11 +448,12 @@ static std::vector<std::string> discoverArchives(const std::string& mpqDir,
|
|||
if (actualLocaleDir.empty()) {
|
||||
actualLocaleDir = locale;
|
||||
}
|
||||
std::string localeDir = mpqDir + "/" + actualLocaleDir;
|
||||
std::cout << "Locale directory: " << localeDir << "\n";
|
||||
fs::path localeDirPath = fs::path(mpqDir) / actualLocaleDir;
|
||||
std::string localeDir = localeDirPath.string();
|
||||
auto localeMap = buildCaseMap(localeDir);
|
||||
for (auto& [name, realName] : localeMap) {
|
||||
caseMap[lowerLocale + "/" + name] = actualLocaleDir + "/" + realName;
|
||||
fs::path fullPath = fs::path(actualLocaleDir) / realName;
|
||||
caseMap[lowerLocale + "/" + name] = fullPath.string();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -520,7 +521,8 @@ static std::vector<std::string> discoverArchives(const std::string& mpqDir,
|
|||
auto addIfPresent = [&](const std::string& expected) {
|
||||
auto it = caseMap.find(toLowerStr(expected));
|
||||
if (it != caseMap.end()) {
|
||||
result.push_back(mpqDir + "/" + it->second);
|
||||
fs::path fullPath = fs::path(mpqDir) / it->second;
|
||||
result.push_back(fullPath.string());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -733,7 +735,7 @@ bool Extractor::run(const Options& opts) {
|
|||
|
||||
// Map to new filesystem path
|
||||
std::string mappedPath = PathMapper::mapPath(wowPath);
|
||||
std::string fullOutputPath = effectiveOutputDir + "/" + mappedPath;
|
||||
fs::path fullOutputPath = fs::path(effectiveOutputDir) / mappedPath;
|
||||
|
||||
// Read file data from MPQ under lock
|
||||
std::vector<uint8_t> data;
|
||||
|
|
@ -862,7 +864,7 @@ bool Extractor::run(const Options& opts) {
|
|||
}
|
||||
|
||||
// Merge with existing manifest so partial extractions don't nuke prior entries
|
||||
std::string manifestPath = effectiveOutputDir + "/manifest.json";
|
||||
fs::path manifestPath = fs::path(effectiveOutputDir) / "manifest.json";
|
||||
if (fs::exists(manifestPath)) {
|
||||
auto existing = loadManifestEntries(manifestPath);
|
||||
if (!existing.empty()) {
|
||||
|
|
@ -899,7 +901,7 @@ bool Extractor::run(const Options& opts) {
|
|||
std::cout << "Verifying extracted files...\n";
|
||||
uint64_t verified = 0, verifyFailed = 0;
|
||||
for (const auto& entry : manifestEntries) {
|
||||
std::string fsPath = effectiveOutputDir + "/" + entry.filesystemPath;
|
||||
fs::path fsPath = fs::path(effectiveOutputDir) / entry.filesystemPath;
|
||||
std::ifstream f(fsPath, std::ios::binary | std::ios::ate);
|
||||
if (!f.is_open()) {
|
||||
std::cerr << " MISSING: " << fsPath << "\n";
|
||||
|
|
|
|||
|
|
@ -18,189 +18,10 @@ std::string PathMapper::toForwardSlash(const std::string& str) {
|
|||
return result;
|
||||
}
|
||||
|
||||
bool PathMapper::startsWithCI(const std::string& str, const std::string& prefix) {
|
||||
if (str.size() < prefix.size()) return false;
|
||||
for (size_t i = 0; i < prefix.size(); ++i) {
|
||||
if (std::tolower(static_cast<unsigned char>(str[i])) !=
|
||||
std::tolower(static_cast<unsigned char>(prefix[i]))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string PathMapper::extractAfterPrefix(const std::string& path, size_t prefixLen) {
|
||||
if (prefixLen >= path.size()) return {};
|
||||
return path.substr(prefixLen);
|
||||
}
|
||||
|
||||
std::string PathMapper::mapPath(const std::string& wowPath) {
|
||||
// Lowercase entire output path — WoW archives contain mixed-case variants
|
||||
// of the same path which create duplicate directories on case-sensitive filesystems.
|
||||
return toLower(mapPathImpl(wowPath));
|
||||
}
|
||||
|
||||
std::string PathMapper::mapPathImpl(const std::string& wowPath) {
|
||||
std::string rest;
|
||||
|
||||
// DBFilesClient\ → db/
|
||||
if (startsWithCI(wowPath, "DBFilesClient\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 14);
|
||||
return "db/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Character\{Race}\{Gender}\ → character/{race}/{gender}/
|
||||
if (startsWithCI(wowPath, "Character\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 10);
|
||||
std::string lowered = toLower(rest);
|
||||
return "character/" + toForwardSlash(lowered);
|
||||
}
|
||||
|
||||
// Creature\{Name}\ → creature/{name}/
|
||||
if (startsWithCI(wowPath, "Creature\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 9);
|
||||
// Keep first component lowercase for directory, preserve filename case
|
||||
std::string fwd = toForwardSlash(rest);
|
||||
auto slash = fwd.find('/');
|
||||
if (slash != std::string::npos) {
|
||||
return "creature/" + toLower(fwd.substr(0, slash)) + "/" + fwd.substr(slash + 1);
|
||||
}
|
||||
return "creature/" + fwd;
|
||||
}
|
||||
|
||||
// Item\ObjectComponents\ → item/objectcomponents/
|
||||
if (startsWithCI(wowPath, "Item\\ObjectComponents\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 22);
|
||||
return "item/objectcomponents/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Item\TextureComponents\ → item/texturecomponents/
|
||||
if (startsWithCI(wowPath, "Item\\TextureComponents\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 23);
|
||||
return "item/texturecomponents/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Interface\Icons\ → interface/icons/
|
||||
if (startsWithCI(wowPath, "Interface\\Icons\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 16);
|
||||
return "interface/icons/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Interface\GossipFrame\ → interface/gossip/
|
||||
if (startsWithCI(wowPath, "Interface\\GossipFrame\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 21);
|
||||
return "interface/gossip/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Interface\{rest} → interface/{rest}/
|
||||
if (startsWithCI(wowPath, "Interface\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 10);
|
||||
return "interface/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Textures\Minimap\ → terrain/minimap/
|
||||
if (startsWithCI(wowPath, "Textures\\Minimap\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 17);
|
||||
return "terrain/minimap/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Textures\BakedNpcTextures\ → creature/baked/
|
||||
if (startsWithCI(wowPath, "Textures\\BakedNpcTextures\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 25);
|
||||
return "creature/baked/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Textures\{rest} → terrain/textures/{rest}
|
||||
if (startsWithCI(wowPath, "Textures\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 9);
|
||||
return "terrain/textures/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// World\Maps\{Map}\ → terrain/maps/{map}/
|
||||
if (startsWithCI(wowPath, "World\\Maps\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 11);
|
||||
std::string fwd = toForwardSlash(rest);
|
||||
auto slash = fwd.find('/');
|
||||
if (slash != std::string::npos) {
|
||||
return "terrain/maps/" + toLower(fwd.substr(0, slash)) + "/" + fwd.substr(slash + 1);
|
||||
}
|
||||
return "terrain/maps/" + fwd;
|
||||
}
|
||||
|
||||
// World\wmo\ → world/wmo/ (preserve subpath)
|
||||
if (startsWithCI(wowPath, "World\\wmo\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 10);
|
||||
return "world/wmo/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// World\Doodads\ → world/doodads/
|
||||
if (startsWithCI(wowPath, "World\\Doodads\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 14);
|
||||
return "world/doodads/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// World\{rest} → world/{rest}/
|
||||
if (startsWithCI(wowPath, "World\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 6);
|
||||
return "world/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Environments\ → environment/
|
||||
if (startsWithCI(wowPath, "Environments\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 13);
|
||||
return "environment/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Sound\Ambience\ → sound/ambient/
|
||||
if (startsWithCI(wowPath, "Sound\\Ambience\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 15);
|
||||
return "sound/ambient/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Sound\Character\ → sound/character/
|
||||
if (startsWithCI(wowPath, "Sound\\Character\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 16);
|
||||
return "sound/character/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Sound\Doodad\ → sound/doodad/
|
||||
if (startsWithCI(wowPath, "Sound\\Doodad\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 13);
|
||||
return "sound/doodad/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Sound\Creature\ → sound/creature/
|
||||
if (startsWithCI(wowPath, "Sound\\Creature\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 15);
|
||||
return "sound/creature/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Sound\Spells\ → sound/spell/
|
||||
if (startsWithCI(wowPath, "Sound\\Spells\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 13);
|
||||
return "sound/spell/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Sound\Music\ → sound/music/
|
||||
if (startsWithCI(wowPath, "Sound\\Music\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 12);
|
||||
return "sound/music/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Sound\{rest} → sound/{rest}/
|
||||
if (startsWithCI(wowPath, "Sound\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 6);
|
||||
return "sound/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Spells\ → spell/
|
||||
if (startsWithCI(wowPath, "Spells\\")) {
|
||||
rest = extractAfterPrefix(wowPath, 7);
|
||||
return "spell/" + toForwardSlash(rest);
|
||||
}
|
||||
|
||||
// Everything else → misc/{original_path}
|
||||
return "misc/" + toForwardSlash(wowPath);
|
||||
return toLower(toForwardSlash(wowPath));
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace wowee {
|
|||
namespace tools {
|
||||
|
||||
/**
|
||||
* Maps WoW virtual paths to reorganized filesystem categories.
|
||||
* Maps WoW virtual paths to organized filesystem categories.
|
||||
*
|
||||
* Input: WoW virtual path (e.g., "Creature\\Bear\\BearSkin.blp")
|
||||
* Output: Category-based relative path (e.g., "creature/bear/BearSkin.blp")
|
||||
|
|
@ -14,19 +14,15 @@ namespace tools {
|
|||
class PathMapper {
|
||||
public:
|
||||
/**
|
||||
* Map a WoW virtual path to a reorganized filesystem path.
|
||||
* Map a WoW virtual path to a organized filesystem path.
|
||||
* @param wowPath Original WoW virtual path (backslash-separated)
|
||||
* @return Reorganized relative path (forward-slash separated, fully lowercased)
|
||||
* @return Organized relative path (forward-slash separated, fully lowercased)
|
||||
*/
|
||||
static std::string mapPath(const std::string& wowPath);
|
||||
|
||||
private:
|
||||
static std::string mapPathImpl(const std::string& wowPath);
|
||||
// Helpers for prefix matching (case-insensitive)
|
||||
static bool startsWithCI(const std::string& str, const std::string& prefix);
|
||||
static std::string toLower(const std::string& str);
|
||||
static std::string toForwardSlash(const std::string& str);
|
||||
static std::string extractAfterPrefix(const std::string& path, size_t prefixLen);
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue