Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 49 additions & 18 deletions src/helpers.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#include <algorithm>
#include <cctype>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>
#include <map>
#include <ctime>

#ifdef _WIN32
Expand All @@ -15,7 +14,6 @@
#include <StormLib.h>

#include "helpers.h"
#include "mpq.h"

namespace fs = std::filesystem;

Expand All @@ -36,22 +34,55 @@ std::string FileTimeToLsTime(int64_t fileTime) {
return std::string(buf);
}

namespace {
// Files in MPQs have locales with which they are associated.
// Multiple files can have the same file name if they have different locales.
// This function maps locales to language names.
//
// The mappings are from the Windows Language Code Identifier (LCID).
// They can be found, for example, here:
// https://winprotocoldoc.z19.web.core.windows.net/MS-LCID/%5bMS-LCID%5d.pdf

// Define a bidirectional map for locale-language mappings
const std::map<uint16_t, std::string> localeToLangMap = {
{0x000, "enUS"}, // Default - English (US)
{0x409, "enUS"}, // English (US)
{0x404, "zhTW"}, // Chinese (Taiwan)
{0x405, "csCZ"}, // Czech
{0x407, "deDE"}, // German
{0x40a, "esES"}, // Spanish (Spain)
{0x40c, "frFR"}, // French
{0x410, "itIT"}, // Italian
{0x411, "jaJP"}, // Japanese
{0x412, "koKR"}, // Korean
{0x415, "plPL"}, // Polish
{0x416, "ptPT"}, // Portuguese (Portugal)
{0x419, "ruRU"}, // Russian
{0x804, "zhCN"}, // Chinese (Simplified)
{0x809, "enGB"}, // English (UK)
{0x80A, "esMX"} // Spanish (Mexico)
};

// Create a reverse map for language to locale lookups
const std::map<std::string, uint16_t> langToLocaleMap = []() {
std::map<std::string, uint16_t> reverseMap;
for (const auto& [locale, lang] : localeToLangMap) {
if (locale != 0x000) { // Skip the default entry to avoid duplication
reverseMap[lang] = locale;
}
}
return reverseMap;
}();
}

std::string LocaleToLang(uint16_t locale) {
switch (locale) {
case 0: return "enUS"; // English (US/GB)
case 1: return "koKR"; // Korean
case 2: return "frFR"; // French
case 3: return "deDE"; // German
case 4: return "zhCN"; // Chinese (Simplified)
case 5: return "zhTW"; // Chinese (Taiwan)
case 6: return "esES"; // Spanish (Spain)
case 7: return "esMX"; // Spanish (Mexico)
case 8: return "ruRU"; // Russian
case 9: return "jaJP"; // Japanese
case 10: return "ptPT"; // Portuguese (Portugal)
case 11: return "itIT"; // Italian
default: return ""; // Unknown locale
}
auto it = localeToLangMap.find(locale);
return it != localeToLangMap.end() ? it->second : "";
}

LCID LangToLocale(const std::string& lang) {
auto it = langToLocaleMap.find(lang);
return it != langToLocaleMap.end() ? it->second : 0;
}

std::string NormalizeFilePath(const fs::path &path) {
Expand Down
1 change: 1 addition & 0 deletions src/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace fs = std::filesystem;

std::string FileTimeToLsTime(int64_t fileTime);
std::string LocaleToLang(uint16_t locale);
LCID LangToLocale(const std::string &lang);
std::string NormalizeFilePath(const fs::path &path);
std::string WindowsifyFilePath(const fs::path &path);
int32_t CalculateMpqMaxFileValue(const std::string &directory);
Expand Down
24 changes: 18 additions & 6 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ int main(int argc, char **argv) {
std::string baseTarget = "default"; // all subcommands
std::string baseFile = "default"; // add, remove, extract, read
std::string basePath = "default"; // add
std::string baseLocale = "default"; // add, remove, extract, read
std::string baseOutput = "default"; // create, extract
std::string baseListfileName = "default"; // list, extract
// CLI: info
Expand Down Expand Up @@ -83,6 +84,7 @@ int main(int argc, char **argv) {
->required()
->check(CLI::ExistingFile);
add->add_option("-p,--path", basePath, "Path within MPQ archive");
add->add_option("--locale", baseLocale, "Locale to use for added file");

// Subcommand: Remove
CLI::App *remove = app.add_subcommand("remove", "Remove file from an existing MPQ archive");
Expand All @@ -91,6 +93,7 @@ int main(int argc, char **argv) {
remove->add_option("target", baseTarget, "Target MPQ archive")
->required()
->check(CLI::ExistingFile);
remove->add_option("--locale", baseLocale, "Locale of file to remove");

// Subcommand: List
CLI::App *list = app.add_subcommand("list", "List files from the MPQ archive");
Expand All @@ -112,6 +115,7 @@ int main(int argc, char **argv) {
extract->add_flag("-k,--keep", extractKeepFolderStructure, "Keep folder structure (default false)");
extract->add_option("-l,--listfile", baseListfileName, "File listing content of an MPQ archive")
->check(CLI::ExistingFile);
extract->add_option("--locale", baseLocale, "Preferred locale for extracted file");

// Subcommand: Read
CLI::App* read = app.add_subcommand("read", "Read a file from an MPQ archive");
Expand All @@ -120,6 +124,7 @@ int main(int argc, char **argv) {
read->add_option("target", baseTarget, "Target MPQ archive")
->required()
->check(CLI::ExistingFile);
read->add_option("--locale", baseLocale, "Preferred locale for read file");

// Subcommand: Verify
CLI::App *verify = app.add_subcommand("verify", "Verify the MPQ archive");
Expand Down Expand Up @@ -187,7 +192,8 @@ int main(int argc, char **argv) {
// Create the MPQ archive and add files
HANDLE hArchive = CreateMpqArchive(outputFile, fileCount, createMpqVersion);
if (hArchive) {
AddFiles(hArchive, baseTarget);
LCID locale = LangToLocale(baseLocale);
AddFiles(hArchive, baseTarget, locale);
if (createSignArchive) {
SignMpqArchive(hArchive);
}
Expand Down Expand Up @@ -221,7 +227,9 @@ int main(int argc, char **argv) {
archivePath = WindowsifyFilePath(archiveFullPath.u8string());
}

AddFile(hArchive, baseFile, archivePath);
LCID locale = LangToLocale(baseLocale);

AddFile(hArchive, baseFile, archivePath, locale);
CloseMpqArchive(hArchive);
}

Expand All @@ -234,7 +242,9 @@ int main(int argc, char **argv) {
return 1;
}

RemoveFile(hArchive, baseFile);
LCID locale = LangToLocale(baseLocale);

RemoveFile(hArchive, baseFile, locale);
CloseMpqArchive(hArchive);
}

Expand Down Expand Up @@ -266,10 +276,11 @@ int main(int argc, char **argv) {
return 1;
}

LCID locale = LangToLocale(baseLocale);
if (baseFile != "default") {
ExtractFile(hArchive, baseOutput, baseFile, extractKeepFolderStructure);
ExtractFile(hArchive, baseOutput, baseFile, extractKeepFolderStructure, locale);
} else {
ExtractFiles(hArchive, baseOutput, baseListfileName);
ExtractFiles(hArchive, baseOutput, baseListfileName, locale);
}
}

Expand All @@ -281,8 +292,9 @@ int main(int argc, char **argv) {
return 1;
}

LCID locale = LangToLocale(baseLocale);
uint32_t fileSize;
char* fileContent = ReadFile(hArchive, baseFile.c_str(), &fileSize);
char* fileContent = ReadFile(hArchive, baseFile.c_str(), &fileSize, locale);
if (fileContent == NULL) {
return 1;
}
Expand Down
Loading
Loading