diff --git a/README.md b/README.md index 0852d85..4e8aa69 100644 --- a/README.md +++ b/README.md @@ -71,10 +71,10 @@ The `mpqcli` program has the following subcommands: - `version`: Print the tool version number - `about`: Print information about the tool -- `info`: Print information about an MPQ archive properties +- `info`: Print information about MPQ archive properties - `create`: Create an MPQ archive from a target directory -- `add`: Add a file to an existing MPQ archive (not implemented) -- `remove`: Remove a file from an existing MPQ archive (not implemented) +- `add`: Add a file to an existing MPQ archive +- `remove`: Remove a file from an existing MPQ archive - `list`: List files in a target MPQ archive - `extract`: Extract one/all files from a target MPQ archive - `read`: Read a specific file to stdout @@ -144,22 +144,42 @@ Use the `-s` or `--sign` argument to cryptographically sign an MPQ archive with mpqcli create --version 1 --sign ``` +### Create an MPQ archive with a given locale + +Use the `--locale` argument to specify the locale that all added files will have in the archive. Note that subsequent added files will have the default locale unless the `--locale` argument is specified again. + +``` +mpqcli create --locale koKR +``` + ### Add a file to an existing archive Add a local file to an already existing MPQ archive. ``` -echo "For The Horde!" > fth.txt -mpqcli add fth.txt wow-patch.mpq +$ echo "For The Horde" > fth.txt +$ mpqcli add fth.txt wow-patch.mpq +[+] Adding file for locale 0: fth.txt ``` Alternatively, you can add a file to a specific subdirectory using the `-p` or `--path` argument. ``` -echo "For The Alliance!" > fta.txt -mpqcli add fta.txt wow-patch.mpq --path +$ echo "For The Alliance" > fta.txt +$ mpqcli add fta.txt wow-patch.mpq --path texts +[+] Adding file for locale 0: texts\fta.txt ``` +### Add files to an MPQ archive with a given locale + +Use the `--locale` argument to specify the locale that the added file will have in the archive. Note that subsequent added files will have the default locale unless the `--locale` argument is specified again. + +``` +$ mpqcli add allianz.txt --locale deDE +[+] Adding file for locale 1031: allianz.txt +``` + + ### Remove a file from an existing archive Remove a file from an existing MPQ archive. @@ -325,6 +345,15 @@ mpqcli read "WoW.exe" wow-patch.mpq | xxd 00000030: 4504 8080 0280 9002 8098 0281 d801 0585 E............... ``` +### Read one specific file with locale + +Use the `--locale` argument to specify the locale of the file to read. If there is no file with the requested name and locale, the default locale will be used instead. + +``` +mpqcli read "rez\stat_txt.tbl" Patch_rt.mpq --locale ptPT +``` + + ### Verify an MPQ archive Check the digital signature of an MPQ archive, by verifying the signature in the archive with a collection of known Blizzard public keys (bundled in Stormlib library). The tool will print if verification is successful or fails, as well as return `0` for success and any other value for failure. diff --git a/src/main.cpp b/src/main.cpp index 53e7acc..cf67d4b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -92,6 +92,8 @@ int main(int argc, char **argv) { create->add_flag("-s,--sign", createSignArchive, "Sign the MPQ archive (default false)"); create->add_option("-v,--version", createMpqVersion, "Set the MPQ archive version (default 1)") ->check(CLI::Range(1, 2)); + create->add_option("--locale", baseLocale, "Locale to use for added files") + ->check(LocaleValid); // Subcommand: Add CLI::App *add = app.add_subcommand("add", "Add a file to an existing MPQ archive"); @@ -102,6 +104,8 @@ 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") + ->check(LocaleValid); // Subcommand: Remove CLI::App *remove = app.add_subcommand("remove", "Remove file from an existing MPQ archive"); @@ -141,6 +145,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"); @@ -208,7 +213,9 @@ 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); } @@ -242,7 +249,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); } @@ -302,8 +311,13 @@ int main(int argc, char **argv) { return 1; } + LCID locale = LangToLocale(baseLocale); + if (baseLocale != "default" && locale == defaultLocale) { + std::cout << "[!] Warning: The locale '" << baseLocale << "' is unknown. Will use default locale instead." << std::endl; + } + 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; } diff --git a/src/mpq.cpp b/src/mpq.cpp index b470862..f1d577a 100644 --- a/src/mpq.cpp +++ b/src/mpq.cpp @@ -153,7 +153,7 @@ HANDLE CreateMpqArchive(std::string outputArchiveName, int32_t fileCount, int32_ return hMpq; } -int AddFiles(HANDLE hArchive, const std::string& target) { +int AddFiles(HANDLE hArchive, const std::string& target, LCID locale) { // We need to "clean" the target path to ensure it is a valid directory // and to strip any directory structure from the files we add fs::path targetPath = fs::path(target); @@ -166,14 +166,13 @@ int AddFiles(HANDLE hArchive, const std::string& target) { // Normalise path for MPQ std::string archiveFilePath = WindowsifyFilePath(inputFilePath.u8string()); - AddFile(hArchive, entry.path().u8string(), archiveFilePath); + AddFile(hArchive, entry.path().u8string(), archiveFilePath, locale); } } return 0; } -int AddFile(HANDLE hArchive, const fs::path& localFile, const std::string& archiveFilePath) { - std::cout << "[+] Adding file: " << archiveFilePath << std::endl; +int AddFile(HANDLE hArchive, const fs::path& localFile, const std::string& archiveFilePath, LCID locale) { // Return if file doesn't exist on disk if (!fs::exists(localFile)) { @@ -182,11 +181,18 @@ int AddFile(HANDLE hArchive, const fs::path& localFile, const std::string& archi } // Check if file exists in MPQ archive - bool hasFile = SFileHasFile(hArchive, archiveFilePath.c_str()); - if (hasFile) { - std::cerr << "[!] File already exists in MPQ archive: " << archiveFilePath << " Skipping..." << std::endl; - return -1; + SFileSetLocale(locale); + HANDLE hFile; + if (SFileOpenFileEx(hArchive, archiveFilePath.c_str(), SFILE_OPEN_FROM_MPQ, &hFile)) { + int32_t fileLocale = GetFileInfo(hFile, SFileInfoLocale); + if (fileLocale == locale) { + std::cerr << "[!] File for locale " << locale << " already exists in MPQ archive: " << archiveFilePath + << " - Skipping..." << std::endl; + return -1; + } } + SFileCloseFile(hFile); + std::cout << "[+] Adding file for locale " << locale << ": " << archiveFilePath << std::endl; // Verify that we are not exceeding maxFile size of the archive, and if we do, increase it int32_t numberOfFiles = GetFileInfo(hArchive, SFileMpqNumberOfFiles); @@ -202,7 +208,7 @@ int AddFile(HANDLE hArchive, const fs::path& localFile, const std::string& archi } } - // Set file attributes in MPQ archive (compression and encryption) + // Set file attributes in the MPQ archive (compression and encryption) DWORD dwFlags = MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED; DWORD dwCompression = MPQ_COMPRESSION_ZLIB; @@ -415,7 +421,8 @@ int ListFiles(HANDLE hArchive, const std::string& listfileName, bool listAll, bo return 0; } -char* ReadFile(HANDLE hArchive, const char *szFileName, unsigned int *fileSize) { +char* ReadFile(HANDLE hArchive, const char *szFileName, unsigned int *fileSize, LCID preferredLocale) { + SFileSetLocale(preferredLocale); if (!SFileHasFile(hArchive, szFileName)) { std::cerr << "[!] Failed: File doesn't exist: " << szFileName << std::endl; return NULL; @@ -549,7 +556,7 @@ int32_t PrintMpqSignature(HANDLE hArchive, std::string target) { } else if (signatureType == SIGNATURE_TYPE_WEAK) { const char* szFileName = "(signature)"; uint32_t fileSize; - char* fileContent = ReadFile(hArchive, szFileName, &fileSize); + char* fileContent = ReadFile(hArchive, szFileName, &fileSize, defaultLocale); if (fileContent == NULL) { std::cerr << "[!] Failed to read weak signature file." << std::endl; diff --git a/src/mpq.h b/src/mpq.h index c86f302..3acd9a7 100644 --- a/src/mpq.h +++ b/src/mpq.h @@ -14,11 +14,11 @@ int SignMpqArchive(HANDLE hArchive); int ExtractFiles(HANDLE hArchive, const std::string& output, const std::string &listfileName); int ExtractFile(HANDLE hArchive, const std::string& output, const std::string& fileName, bool keepFolderStructure); HANDLE CreateMpqArchive(std::string outputArchiveName, int32_t fileCount, int32_t mpqVersion); -int AddFiles(HANDLE hArchive, const std::string& inputPath); -int AddFile(HANDLE hArchive, const fs::path& localFile, const std::string& archiveFilePath); +int AddFiles(HANDLE hArchive, const std::string& inputPath, LCID locale); +int AddFile(HANDLE hArchive, const fs::path& localFile, const std::string& archiveFilePath, LCID locale); int RemoveFile(HANDLE hArchive, const std::string& archiveFilePath); int ListFiles(HANDLE hHandle, const std::string &listfileName, bool listAll, bool listDetailed, std::vector& propertiesToPrint); -char* ReadFile(HANDLE hArchive, const char *szFileName, unsigned int *fileSize); +char* ReadFile(HANDLE hArchive, const char *szFileName, unsigned int *fileSize, LCID preferredLocale); void PrintMpqInfo(HANDLE hArchive, const std::string& infoProperty); uint32_t VerifyMpqArchive(HANDLE hArchive); int32_t PrintMpqSignature(HANDLE hArchive, std::string target); diff --git a/test/test_add.py b/test/test_add.py index b18f2d7..20ad165 100644 --- a/test/test_add.py +++ b/test/test_add.py @@ -11,11 +11,11 @@ def test_add_target_mpq_does_not_exist(binary_path, generate_test_files): """ _ = generate_test_files script_dir = Path(__file__).parent - target_dir = script_dir / "does" / "not" / "exist" - target_file = script_dir / "data" / "files" / "cats.txt" + target_mpq = script_dir / "does" / "not" / "exist" + target_file = script_dir / "data" / "files" / "cats.txt" result = subprocess.run( - [str(binary_path), "add", str(target_file), str(target_dir)], + [str(binary_path), "add", str(target_file), str(target_mpq)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True @@ -33,11 +33,11 @@ def test_add_target_file_does_not_exist(binary_path, generate_test_files): """ _ = generate_test_files script_dir = Path(__file__).parent - target_dir = script_dir / "does" / "not" / "exist" - target_file = script_dir / "data" / "files" / "cats.txt" + target_mpq = script_dir / "data" / "files.mpq" + target_file = script_dir / "does" / "not" / "exist" result = subprocess.run( - [str(binary_path), "add", str(target_file), str(target_dir)], + [str(binary_path), "add", str(target_file), str(target_mpq)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True @@ -55,8 +55,138 @@ def test_add_file_to_mpq_archive(binary_path, generate_test_files): """ _ = generate_test_files script_dir = Path(__file__).parent + target_file = script_dir / "data" / "files.mpq" + + # Start by creating an MPQ archive for this test + create_mpq_archive_for_test(binary_path, script_dir) + + # Create a new test file on the fly + test_file = script_dir / "data" / "test.txt" + test_file.write_text("This is a test file for MPQ addition.") + + + result = subprocess.run( + [str(binary_path), "add", str(test_file), str(target_file)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + output_lines = set(result.stdout.splitlines()) + expected_stdout_output = { + "[+] Adding file for locale 0: test.txt", + } + assert output_lines == expected_stdout_output, f"Unexpected output: {output_lines}" + + output_lines = set(result.stderr.splitlines()) + expected_stderr_output = set() + assert output_lines == expected_stderr_output, f"Unexpected output: {output_lines}" + + assert result.returncode == 0, f"mpqcli failed with error: {result.stderr}" + + expected_content = { + "enUS bytes", + "enUS dogs.txt", + "enUS cats.txt", + "enUS test.txt", + } + verify_archive_file_content(binary_path, target_file, expected_content) + + +def test_create_mpq_with_illegal_locale(binary_path, generate_test_files): + """ + Test MPQ file addition with illegal locale. + + This test checks: + - That when an illegal locale is provided, no file is added. + """ + _ = generate_test_files + script_dir = Path(__file__).parent + target_file = script_dir / "data" / "files.mpq" + + # Start by creating an MPQ archive for this test + create_mpq_archive_for_test(binary_path, script_dir) + + # Verify that the archive has the expected content + expected_content = { + "enUS bytes", + "enUS dogs.txt", + "enUS cats.txt", + } + verify_archive_file_content(binary_path, target_file, expected_content) + + + # Create a new test file on the fly + test_file = script_dir / "data" / "horses.txt" + test_file.write_text("This is a file about horses.") + + result = subprocess.run( + [str(binary_path), "add", str(test_file), str(target_file), "--locale", "illegal_locale"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + assert result.returncode == 105, f"mpqcli failed with error: {result.stderr}" + + + # Verify that the archive still has the same content + expected_content = { + "enUS bytes", + "enUS dogs.txt", + "enUS cats.txt", + } + verify_archive_file_content(binary_path, target_file, expected_content) + + +def test_create_mpq_with_locale(binary_path, generate_test_files): + """ + Test MPQ file addition. + + This test checks: + - If the application correctly handles adding a file to an MPQ archive. + """ + _ = generate_test_files + script_dir = Path(__file__).parent + target_file = script_dir / "data" / "files.mpq" # Start by creating an MPQ archive for this test + create_mpq_archive_for_test(binary_path, script_dir) + + # Create a new test file on the fly + test_file = script_dir / "data" / "cats.txt" + test_file.write_text("Este es un archivo sobre gatos.") + + result = subprocess.run( + [str(binary_path), "add", str(test_file), str(target_file), "--locale", "esES"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + output_lines = set(result.stdout.splitlines()) + expected_stdout_output = { + "[+] Adding file for locale 1034: cats.txt", + } + assert output_lines == expected_stdout_output, f"Unexpected output: {output_lines}" + + output_lines = set(result.stderr.splitlines()) + expected_stderr_output = set() + assert output_lines == expected_stderr_output, f"Unexpected output: {output_lines}" + + assert result.returncode == 0, f"mpqcli failed with error: {result.stderr}" + + + # Verify that the archive has the expected content + expected_content = { + "enUS bytes", + "enUS dogs.txt", + "enUS cats.txt", + "esES cats.txt", + } + verify_archive_file_content(binary_path, target_file, expected_content) + + +def create_mpq_archive_for_test(binary_path, script_dir): target_dir = script_dir / "data" / "files" target_file = target_dir.with_suffix(".mpq") # Remove the target file if it exists @@ -74,17 +204,16 @@ def test_add_file_to_mpq_archive(binary_path, generate_test_files): assert target_file.exists(), "MPQ file was not created" assert target_file.stat().st_size > 0, "MPQ file is empty" - # Create a new test file on the fly - test_file = script_dir / "data" / "test.txt" - test_file.write_text("This is a test file for MPQ addition.") - - target_file = script_dir / "data" / "files.mpq" +def verify_archive_file_content(binary_path, test_file, expected_output): result = subprocess.run( - [str(binary_path), "add", str(test_file), str(target_file)], + [str(binary_path), "list", str(test_file), "-d", "-p", "locale"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) + output_lines = set(result.stdout.splitlines()) + assert result.returncode == 0, f"mpqcli failed with error: {result.stderr}" + assert output_lines == expected_output, f"Unexpected output: {output_lines}" diff --git a/test/test_create.py b/test/test_create.py index e4c3718..a8fe6bf 100644 --- a/test/test_create.py +++ b/test/test_create.py @@ -139,3 +139,57 @@ def test_create_mpq_already_exists(binary_path, generate_test_files): ) assert result.returncode == 1, f"mpqcli failed with error: {result.stderr}" + + +def test_create_mpq_with_illegal_locale(binary_path, generate_test_files): + """ + Test MPQ file creation with illegal locale. + + This test checks: + - That the MPQ archive is not created + """ + _ = generate_test_files + script_dir = Path(__file__).parent + target_dir = script_dir / "data" / "files" + + output_file = script_dir / "data" / "new_mpq.mpq" + + result = subprocess.run( + [str(binary_path), "create", "-v", "1", "-o", str(output_file), str(target_dir), "--locale", "illegal_locale"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + assert result.returncode == 105, f"mpqcli failed with error: {result.stderr}" + assert not output_file.exists(), "MPQ file was created" + + +def test_create_mpq_with_locale(binary_path, generate_locales_mpq_test_files): + """ + Test MPQ file creation with a given locale. + + This test checks: + - That the MPQ archive contains the expected file with the expected locale. + """ + + # Creating an MPQ with a given locale happens in conftest.py. This test only checks the output. + _ = generate_locales_mpq_test_files + script_dir = Path(__file__).parent + output_file = script_dir / "data" / "mpq_with_one_locale.mpq" + + verify_archive_file_content(binary_path, output_file, {"esES cats.txt"}) + + +def verify_archive_file_content(binary_path, test_file, expected_output): + result = subprocess.run( + [str(binary_path), "list", str(test_file), "-d", "-p", "locale"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + output_lines = set(result.stdout.splitlines()) + + assert result.returncode == 0, f"mpqcli failed with error: {result.stderr}" + assert output_lines == expected_output, f"Unexpected output: {output_lines}" diff --git a/test/test_read.py b/test/test_read.py index 0725144..2ef7443 100644 --- a/test/test_read.py +++ b/test/test_read.py @@ -43,3 +43,194 @@ def test_read_mpq_v1_hex(binary_path): assert result.returncode == 0, f"mpqcli failed with error: {result.stderr}" assert output_lines == expected_output, f"Unexpected output: {output_lines}" + + +def test_read_file_from_mpq_with_locale(binary_path, generate_locales_mpq_test_files): + """ + Test MPQ archive file reading with a specified locale. + + This test checks: + - If the file with the given locale is read correctly. + - That the file content is correct. + """ + _ = generate_locales_mpq_test_files + script_dir = Path(__file__).parent + test_file = script_dir / "data" / "mpq_with_many_locales.mpq" + file_to_read = "cats.txt" + locale = "deDE" + + result = subprocess.run( + [str(binary_path), "read", file_to_read, str(test_file), "--locale", locale], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + expected_content = { + "Dies ist eine Datei über Katzen.", + } + + output_lines = set(result.stdout.splitlines()) + assert result.returncode == 0, f"mpqcli failed with error: {result.stderr}" + assert output_lines == expected_content, f"Unexpected output: {output_lines}" + + +def test_read_file_from_mpq_with_default_locale(binary_path, generate_locales_mpq_test_files): + """ + Test MPQ archive file reading with no specified locale but many matching file names. + + This test checks: + - If the file with the default locale is read correctly. + - That the file content is correct. + """ + _ = generate_locales_mpq_test_files + script_dir = Path(__file__).parent + test_file = script_dir / "data" / "mpq_with_many_locales.mpq" + file_to_read = "cats.txt" + + + result = subprocess.run( + [str(binary_path), "read", file_to_read, str(test_file)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + expected_content = { + "This is a file about cats.", + } + + output_lines = set(result.stdout.splitlines()) + assert result.returncode == 0, f"mpqcli failed with error: {result.stderr}" + assert output_lines == expected_content, f"Unexpected output: {output_lines}" + + +def test_read_file_from_mpq_with_illegal_locale(binary_path, generate_locales_mpq_test_files): + """ + Test MPQ archive file reading with an illegal locale. + + This test checks: + - When a locale is given that does not exist in the file, the file with the default locale is read. + - That the file content is correct. + """ + _ = generate_locales_mpq_test_files + script_dir = Path(__file__).parent + test_file = script_dir / "data" / "mpq_with_many_locales.mpq" + file_to_read = "cats.txt" + locale = "nosuchlocale" + + + result = subprocess.run( + [str(binary_path), "read", file_to_read, str(test_file), "--locale", locale], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + expected_content = { + "[!] Warning: The locale 'nosuchlocale' is unknown. Will use default locale instead.", + "This is a file about cats.", + } + + output_lines = set(result.stdout.splitlines()) + assert result.returncode == 0, f"mpqcli failed with error: {result.stderr}" + assert output_lines == expected_content, f"Unexpected output: {output_lines}" + + +def test_read_file_from_mpq_with_locale_not_in_file(binary_path, generate_locales_mpq_test_files): + """ + Test MPQ archive file reading with a specified locale that is not in the file. + + This test checks: + - When a locale is given that does not exist in the file, the file with the default locale is read. + - That the file content is correct. + """ + _ = generate_locales_mpq_test_files + script_dir = Path(__file__).parent + test_file = script_dir / "data" / "mpq_with_many_locales.mpq" + file_to_read = "cats.txt" + locale = "ptPT" # There is no file for this locale + + result = subprocess.run( + [str(binary_path), "read", file_to_read, str(test_file), "--locale", locale], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + expected_content = { + "This is a file about cats.", + } + + output_lines = set(result.stdout.splitlines()) + assert result.returncode == 0, f"mpqcli failed with error: {result.stderr}" + assert output_lines == expected_content, f"Unexpected output: {output_lines}" + + +def test_read_file_from_mpq_with_no_locale_argument_and_no_default_locale(binary_path, generate_locales_mpq_test_files): + """ + Test MPQ archive file reading without a specified locale, and no file with the default locale. + + This test checks: + - When no locale is given, and no file by that name exists for the default locale, + but one does for a different one, no file is read. + """ + _ = generate_locales_mpq_test_files + script_dir = Path(__file__).parent + test_file = script_dir / "data" / "mpq_with_one_locale.mpq" + file_to_read = "cats.txt" # There is a file by this name, but for locale esES + + + result = subprocess.run( + [str(binary_path), "read", file_to_read, str(test_file)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + expected_stdout_output = set() + expected_stderr_output = { + "[!] Failed: File doesn't exist: " + file_to_read, + } + + stdout_output_lines = set(result.stdout.splitlines()) + stderr_output_lines = set(result.stderr.splitlines()) + + assert result.returncode == 1, f"mpqcli failed with error: {result.stderr}" + assert stdout_output_lines == expected_stdout_output, f"Unexpected output: {stdout_output_lines}" + assert stderr_output_lines == expected_stderr_output, f"Unexpected output: {stderr_output_lines}" + + +def test_read_file_from_mpq_with_wrong_locale_argument_and_no_default_locale(binary_path, generate_locales_mpq_test_files): + """ + Test MPQ archive file reading with a specified locale, and no file with the default locale. + + This test checks: + - When no locale is given, and no file by that name exists for the default locale, + but one does for a different one, no file is read. + """ + _ = generate_locales_mpq_test_files + script_dir = Path(__file__).parent + test_file = script_dir / "data" / "mpq_with_one_locale.mpq" + file_to_read = "cats.txt" # There is a file by this name, but for locale esES + locale = "deDE" + + + result = subprocess.run( + [str(binary_path), "read", file_to_read, str(test_file), "--locale", locale], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + expected_stdout_output = set() + expected_stderr_output = { + "[!] Failed: File doesn't exist: " + file_to_read, + } + + stdout_output_lines = set(result.stdout.splitlines()) + stderr_output_lines = set(result.stderr.splitlines()) + + assert result.returncode == 1, f"mpqcli failed with error: {result.stderr}" + assert stdout_output_lines == expected_stdout_output, f"Unexpected output: {stdout_output_lines}" + assert stderr_output_lines == expected_stderr_output, f"Unexpected output: {stderr_output_lines}"