Skip to content
Open
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

![Release downloads](https://img.shields.io/github/downloads/thegraydot/mpqcli/total?label=release_downloads) ![Package downloads](https://img.shields.io/badge/package_downloads-996-green)

A command-line tool to create, add, remove, list, extract, read, and verify MPQ archives using the [StormLib library](https://github.com/ladislav-zezula/StormLib).
A command-line tool to create, add, remove, list, extract, read, rename, and verify MPQ archives using the [StormLib library](https://github.com/ladislav-zezula/StormLib).

> ⚠️ **Warning:** This project is under active development and will change functionality between released versions until version 1.0.0.

Expand Down
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [create](./commands/create.md)
- [add](./commands/add.md)
- [remove](./commands/remove.md)
- [rename](./commands/rename.md)
- [list](./commands/list.md)
- [extract](./commands/extract.md)
- [read](./commands/read.md)
Expand Down
19 changes: 19 additions & 0 deletions docs/commands/rename.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# rename

Rename a file in an existing MPQ archive without extracting and re-adding it.
The archive is the first positional argument, followed by the current and new archive paths.
This operation does not cause MPQ fragmentation, and thus it is not necessary to
[`compact`](compact.html) the archive.

```bash
$ mpqcli rename wow-patch.mpq old-name.txt new-name.txt
[~] Renaming file: old-name.txt -> new-name.txt
```

Use `--locale` to rename only the copy stored under a specific locale. Without `--locale`,
the file stored under the default locale is renamed.

```bash
$ mpqcli rename wow-patch.mpq old-name.txt new-name.txt --locale esES
[~] Renaming file for locale esES: old-name.txt -> new-name.txt
```
1 change: 1 addition & 0 deletions docs/commands_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The `mpqcli` program has the following subcommands:
| [`create`](./commands/create.md) | Create an MPQ archive from a target directory or a single file |
| [`add`](./commands/add.md) | Add a file to an existing MPQ archive |
| [`remove`](./commands/remove.md) | Remove a file from an existing MPQ archive |
| [`rename`](./commands/rename.md) | Rename a file in an existing MPQ archive |
| [`list`](./commands/list.md) | List files in a target MPQ archive |
| [`extract`](./commands/extract.md) | Extract one or all files from a target MPQ archive |
| [`read`](./commands/read.md) | Read a specific file to stdout |
Expand Down
13 changes: 13 additions & 0 deletions src/commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,19 @@ int HandleRemove(const std::vector<std::string> &files, const std::string &targe
return overall_result;
}

int HandleRename(const std::string &old_file, const std::string &new_file,
const std::string &target, const std::optional<std::string> &locale) {
HANDLE archive;
if (!OpenMpqArchive(target, &archive, 0)) {
return 1;
}

LCID lcid = locale.has_value() ? LangToLocale(locale.value()) : default_locale;
int result = RenameFile(archive, old_file, new_file, lcid);
CloseMpqArchive(archive);
return result;
}

int HandleList(const std::string &target, const std::optional<std::string> &listfile_name,
bool list_all, bool list_detailed, const std::vector<std::string> &properties) {
HANDLE archive;
Expand Down
2 changes: 2 additions & 0 deletions src/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ int HandleAdd(const std::vector<std::string> &files, const std::string &target,
int64_t file_compression, int64_t file_compression_next);
int HandleRemove(const std::vector<std::string> &files, const std::string &target,
const std::optional<std::string> &locale);
int HandleRename(const std::string &old_file, const std::string &new_file,
const std::string &target, const std::optional<std::string> &locale);
int HandleList(const std::string &target, const std::optional<std::string> &listfile_name,
bool list_all, bool list_detailed, const std::vector<std::string> &properties);
int HandleExtract(const std::string &target, const std::optional<std::string> &output,
Expand Down
8 changes: 5 additions & 3 deletions src/completion/mpqcli.fish
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ set -l __mpqcli_list_properties \
flags encryption-key encryption-key-raw

# Top-level subcommands (no subcommand active yet)
complete -c mpqcli -n 'not __fish_seen_subcommand_from version about info create add remove list extract read verify compact completion' \
complete -c mpqcli -n 'not __fish_seen_subcommand_from version about info create add remove rename list extract read verify compact completion' \
-a version -d 'Print program version'
complete -c mpqcli -n 'not __fish_seen_subcommand_from version about info create add remove list extract read verify compact completion' \
-a about -d 'Print program information'
Expand All @@ -50,9 +50,11 @@ complete -c mpqcli -n 'not __fish_seen_subcommand_from version about info create
-a create -d 'Create an MPQ archive from a file or directory'
complete -c mpqcli -n 'not __fish_seen_subcommand_from version about info create add remove list extract read verify compact completion' \
-a add -d 'Add files to an existing MPQ archive'
complete -c mpqcli -n 'not __fish_seen_subcommand_from version about info create add remove list extract read verify compact completion' \
complete -c mpqcli -n 'not __fish_seen_subcommand_from version about info create add remove rename list extract read verify compact completion' \
-a remove -d 'Remove files from an existing MPQ archive'
complete -c mpqcli -n 'not __fish_seen_subcommand_from version about info create add remove list extract read verify compact completion' \
complete -c mpqcli -n 'not __fish_seen_subcommand_from version about info create add remove rename list extract read verify compact completion' \
-a rename -d 'Rename a file in an existing MPQ archive'
complete -c mpqcli -n 'not __fish_seen_subcommand_from version about info create add remove rename list extract read verify compact completion' \
-a list -d 'List files in an MPQ archive'
complete -c mpqcli -n 'not __fish_seen_subcommand_from version about info create add remove list extract read verify compact completion' \
-a extract -d 'Extract files from an MPQ archive'
Expand Down
10 changes: 10 additions & 0 deletions src/completion/mpqcli.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ _mpqcli() {
create) _mpqcli_create ;;
add) _mpqcli_add ;;
remove) _mpqcli_remove ;;
rename) _mpqcli_rename ;;
list) _mpqcli_list ;;
extract) _mpqcli_extract ;;
read) _mpqcli_read ;;
Expand All @@ -68,6 +69,7 @@ _mpqcli_cmds() {
'create:Create an MPQ archive from target file or directory'
'add:Add files to an existing MPQ archive'
'remove:Remove files from an existing MPQ archive'
'rename:Rename a file in an existing MPQ archive'
'list:List files from the MPQ archive'
'extract:Extract files from the MPQ archive'
'read:Read a file from an MPQ archive'
Expand Down Expand Up @@ -127,6 +129,14 @@ _mpqcli_remove() {
'--locale[locale of file to remove]:locale:('"${_mpqcli_locales[@]}"')'
}

_mpqcli_rename() {
_arguments \
'1:archive:_files' \
'2:old archive path' \
'3:new archive path' \
'--locale[locale of file to rename]:locale:("${_mpqcli_locales[@]}")'
}

_mpqcli_list() {
local props="${_mpqcli_list_properties[*]}"
_arguments \
Expand Down
15 changes: 15 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ int main(int argc, char **argv) {
std::vector<std::string> add_files;
// CLI: remove
std::vector<std::string> remove_files;
// CLI: rename
std::string rename_new_file;
// CLI: extract
bool extract_keep_folder_structure = false;
// CLI: create
Expand Down Expand Up @@ -193,6 +195,15 @@ int main(int argc, char **argv) {
->expected(-1);
remove->add_option("--locale", base_locale, "Locale of file to remove")->check(locale_valid);

// Subcommand: Rename
CLI::App *rename = app.add_subcommand("rename", "Rename a file in an existing MPQ archive");
rename->add_option("archive", base_target, "Target MPQ archive")
->required()
->check(CLI::ExistingFile);
rename->add_option("old-file", base_file, "Current archive path of the file")->required();
rename->add_option("new-file", rename_new_file, "New archive path of the file")->required();
rename->add_option("--locale", base_locale, "Locale of file to rename")->check(locale_valid);

// Subcommand: List
CLI::App *list = app.add_subcommand("list", "List files from the MPQ archive");
list->add_option("target", base_target, "Target MPQ archive")
Expand Down Expand Up @@ -319,6 +330,10 @@ int main(int argc, char **argv) {
return HandleRemove(resolved_remove_files, base_target, base_locale);
}

if (app.got_subcommand(rename)) {
return HandleRename(base_file, rename_new_file, base_target, base_locale);
}

if (app.got_subcommand(list)) {
return HandleList(base_target, base_listfile_name, list_all, list_detailed,
list_properties);
Expand Down
23 changes: 23 additions & 0 deletions src/mpq.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,29 @@ int RemoveFile(HANDLE archive, const std::string &archive_file_path, LCID locale
return 0;
}

int RenameFile(HANDLE archive, const std::string &old_archive_file_path,
const std::string &new_archive_file_path, LCID locale) {
SFileSetLocale(locale);
std::cout << "[~] Renaming file" << PrettyPrintLocale(locale, " for locale ") << ": "
<< old_archive_file_path << " -> " << new_archive_file_path << std::endl;

if (!FileExistsInArchiveForLocale(archive, old_archive_file_path, locale)) {
std::cerr << "[!] Failed: File doesn't exist"
<< PrettyPrintLocale(locale, " for locale ", true) << ": "
<< old_archive_file_path << std::endl;
return 1;
}

if (!SFileRenameFile(archive, old_archive_file_path.c_str(), new_archive_file_path.c_str())) {
std::cerr << "[!] Failed: File cannot be renamed"
<< PrettyPrintLocale(locale, " for locale ", true) << ": "
<< old_archive_file_path << " -> " << new_archive_file_path << std::endl;
return 1;
}

return 0;
}

std::string GetFlagString(uint32_t flags) {
std::string result;

Expand Down
2 changes: 2 additions & 0 deletions src/mpq.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ int AddFile(HANDLE archive, const fs::path &local_file, const std::string &archi
const CompressionSettingsOverrides &overrides = CompressionSettingsOverrides(),
bool overwrite = false);
int RemoveFile(HANDLE archive, const std::string &archive_file_path, LCID locale);
int RenameFile(HANDLE archive, const std::string &old_archive_file_path,
const std::string &new_archive_file_path, LCID locale);
int ListFiles(HANDLE archive, const std::optional<std::string> &listfile_name, bool list_all,
bool list_detailed, const std::vector<std::string> &properties);
std::unique_ptr<char[]> ReadFile(HANDLE archive, const char *file_name, unsigned int *file_size,
Expand Down
Loading