Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Windows build workflow #23

Merged
merged 5 commits into from
Jan 15, 2024
Merged
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
21 changes: 19 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,27 @@ jobs:
submodules: true

- name: Run cmake
run: cmake -Bbuild
run: cmake -B build

- name: Compile project
run: make
run: cmake --build build

- name: Check mpqcli version
run: ./build/bin/mpqcli version

build_windows:
runs-on: windows-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
with:
submodules: true

- name: Run cmake
run: cmake -B build

- name: Compile project
run: cmake --build build --config "Release"

- name: Check mpqcli version
run: .\build\bin\Release\mpqcli version
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.10)

# Set project version
set(CMAKE_PROJECT_VERSION_MAJOR 0)
set(CMAKE_PROJECT_VERSION_MINOR 1)
set(CMAKE_PROJECT_VERSION_MINOR 2)
set(CMAKE_PROJECT_VERSION_PATCH 0)
set(FULL_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})

Expand Down
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
build_mpqcli:
cd build; \
cmake ..; \
make
cmake -B build; \
cmake --build build

clean_mpqcli:
rm -rf build
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
# mpqcli

![Project Status Badge](https://img.shields.io/badge/Status-Alpha-red)

![C++ Standard Version](https://img.shields.io/badge/Version-17-blue.svg?style=flat&logo=c%2B%2B)
![Build Status](https://img.shields.io/github/actions/workflow/status/TheGrayDot/mpqcli/build.yml?branch=main&style=flat)

A command line tool to read, extract, search, create and verify MPQ files using the StormLib library

## Overview

**This is a command line tool, designed for automation**. For example, run one command to create an MPQ file from a directory of files. If you require an MPQ tool with a graphical interface (GUI) - I would recommend using [Ladik's MPQ Editor](http://www.zezula.net/en/mpq/download.html).

**This project is for original World of Warcraft MPQ archives**. This means is has been primarily authored for MPQ files used in the following World of Warcraft (WoW) versions: Vanilla (1.12.1), TBC (2.4.3) and WoTLK (3.3.5). It has only been tested and for MPQ versions 1 and 2, and only tested on WoW MPQ archives/patches. No testing has been performed on other MPQ versions or archives.
**This project is for original World of Warcraft MPQ archives**. This means is has been primarily authored for MPQ files used in the following World of Warcraft (WoW) versions: Vanilla (1.12.1), TBC (2.4.3) and WoTLK (3.3.5). It has only been tested on WoW MPQ archives/patches which use MPQ versions 1 or 2. No testing has been performed on other MPQ versions or archives from other games.

## Download

Expand All @@ -29,9 +27,9 @@ TODO.

```
git clone --recursive https://github.com/TheGrayDot/mpqcli.git
cd mpqcli && mkdir build && cd build
cmake ..
make
cd mpqcli
cmake -B build
cmake --build build
```

The `mpqcli` binary will be available in: `./build/bin/mpqcli`
Expand Down Expand Up @@ -83,7 +81,7 @@ Pretty simple, list files in an MPQ archive. Useful to "pipe" to other tools, su
mpqcli list <target_mpq_file>
```

### Search and extract files
### Search and extract files on Linux

The `mpqcli` tool has no native search feature - instead it is designed to be integrated with other, extenal operating system tools. For example, `mpqcli list` can be "piped" to `grep` in Linux or `Select-String` in Windows Powershell to perform searching.

Expand All @@ -93,6 +91,14 @@ The following command lists all files in an MPQ archive, and each filename is fi
mpqcli list <target_mpq_file> | grep ".exe" | xargs -I@ mpqcli extract -f "@" <target_mpq_file>
```

### Search and extract files on Windows

The following command lists all files in an MPQ archive, and each filename is filtered using `grep` - selecting files with `exe` in their name, which is then passed back to `mpqcli extract`. The result: search and extract all `exe` files.

```
mpqcli.exe list <target_mpq_file> | Select-String -Pattern ".exe" | ForEach-Object { mpqcli.exe extract -f $_ <target_mpq_file> }
```

### Create archive from a target directory

The `create` subcommand has not yet been added.
Expand Down
6 changes: 4 additions & 2 deletions src/helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ int ExtractMpqAndBinFromExe(HANDLE hArchive, bool extractBin) {
}

if (extractBin) {
std::string outputBinFile = archivePath.parent_path() / archivePath.stem();
fs::path outputBinFilePath = archivePath.parent_path() / archivePath.stem();
std::string outputBinFile{outputBinFilePath.u8string()};
outputBinFile = outputBinFile + ".bin";
std::cout << "[+] Output location: " << outputBinFile << std::endl;
std::ifstream file_bin(archiveName, std::ios::binary);
Expand All @@ -52,7 +53,8 @@ int ExtractMpqAndBinFromExe(HANDLE hArchive, bool extractBin) {
output_bin.write(buffer_bin.data(), buffer_bin.size());
}

std::string outputMpqFile = archivePath.parent_path() / archivePath.stem();
fs::path outputMpqFilePath = archivePath.parent_path() / archivePath.stem();
std::string outputMpqFile{outputMpqFilePath.u8string()};
outputMpqFile = outputMpqFile + ".mpq";
std::cout << "[+] Output location: " << outputMpqFile << std::endl;
std::ifstream file_mpq(archiveName, std::ios::binary);
Expand Down
6 changes: 4 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ int main(int argc, char **argv) {
// If no output directory specified, use MPQ path without extension
// If output directory specified, create it if it doesn't exist
if (output == "default") {
const fs::path outputPath = fs::canonical(target);
output = outputPath.parent_path() / outputPath.stem();
fs::path outputPathAbsolute = fs::canonical(target);
fs::path outputPath = outputPathAbsolute.parent_path() / outputPathAbsolute.stem();
std::string outputString{outputPath.u8string()};
output = outputString;
}
fs::create_directory(output);

Expand Down
19 changes: 10 additions & 9 deletions src/mpq.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,14 @@ int ExtractFile(HANDLE hArchive, const std::string& output, const std::string& f
fileNameString = fileNamePath.string();
#endif

fs::path outputPath = fs::canonical(output);
std::string outputFilePath = outputPath / fileNameString;
std::filesystem::create_directories(fs::path(outputFilePath).parent_path());
fs::path outputPathAbsolute = fs::canonical(output);
fs::path outputPathBase = outputPathAbsolute.parent_path() / outputPathAbsolute.filename();
std::filesystem::create_directories(fs::path(outputPathBase).parent_path());

if (SFileExtractFile(hArchive, szFileName, outputFilePath.c_str(), 0)) {
fs::path outputFilePathName = outputPathBase / szFileName;
std::string outputFileName{outputFilePathName.u8string()};

if (SFileExtractFile(hArchive, szFileName, outputFileName.c_str(), 0)) {
std::cout << "[+] Extracted: " << szFileName << std::endl;
} else {
int32_t error = GetLastError();
Expand Down Expand Up @@ -110,7 +113,7 @@ char* ReadFile(HANDLE hArchive, const char *szFileName, unsigned int *fileSize)
*fileSize = SFileGetFileSize(hFile, NULL);

char* fileContent = new char[*fileSize + 1];
unsigned int dwBytes;
DWORD dwBytes;
if (!SFileReadFile(hFile, fileContent, *fileSize, &dwBytes, NULL)) {
std::cerr << "[+] Failed: Cannot read file contents..." << std::endl;
int32_t error = GetLastError();
Expand Down Expand Up @@ -164,7 +167,7 @@ TMPQHeader GetMpqHeader(HANDLE hArchive) {
std::cerr << "[+] Failed: " << "(" << error << ") " << std::endl;
return header;
}
// USHORT wFormatVersion

unsigned short formatVersion = header.wFormatVersion;
std::cout << "[+] Format version: " << formatVersion << std::endl;
return header;
Expand Down Expand Up @@ -285,9 +288,7 @@ int PrintMpqSignature(HANDLE hArchive, int signatureType) {
int32_t archiveSize = GetMpqArchiveSize(hArchive);
int64_t archiveOffset = GetMpqArchiveHeaderOffset(hArchive);
std::uintmax_t fileSize = fs::file_size(archivePath);

int signatureStart = archiveOffset + archiveSize;
int signatureLength = fileSize - archiveOffset - archiveSize;
int64_t signatureLength = fileSize - archiveOffset - archiveSize;

std::ifstream file_mpq(archivePath, std::ios::binary);
file_mpq.seekg(archiveOffset + archiveSize, std::ios::beg);
Expand Down