JMPQ3 is a small Java library for reading and modifying MPQ (MoPaQ) archives.
Common file endings are .mpq, .w3m, and .w3x.
MoPaQ is Blizzard's older proprietary archive format for game data. It is used by Warcraft III maps and was later replaced by CASC in newer Blizzard games.
JMPQ3 is primarily tested with Warcraft III maps and MPQs. Archives from other games may work, but Warcraft III compatibility is the main target.
For MPQ format background and a graphical editor, see Ladislav Zezula's MPQ tools: http://www.zezula.net/en/mpq/main.html
JMPQ3 1.9.0 and newer requires Java 11 or newer at runtime.
The project currently builds with a Java 17 toolchain while compiling Java 11-compatible bytecode.
JMPQ3 is available through JitPack: https://jitpack.io/#inwc3/JMPQ3/
Gradle:
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.inwc3:JMPQ3:1.9.8'
}You can also depend on a Git commit or branch through JitPack while testing unreleased changes.
Use try-with-resources so the editor is closed correctly. Writable archives are rebuilt when the editor is closed.
import systems.crigges.jmpq3.JMpqEditor;
import systems.crigges.jmpq3.MPQOpenOption;
import java.io.File;
import java.nio.charset.StandardCharsets;
try (JMpqEditor mpq = new JMpqEditor(new File("MyMap.w3x"), MPQOpenOption.READ_ONLY, MPQOpenOption.FORCE_V0)) {
if (mpq.hasFile("war3map.j")) {
byte[] script = mpq.extractFileAsBytes("war3map.j");
System.out.println(new String(script, StandardCharsets.UTF_8));
}
for (String fileName : mpq.getFileNames()) {
System.out.println(fileName);
}
}MPQOpenOption.READ_ONLY opens the archive without modifying it.
MPQOpenOption.FORCE_V0 reads the archive like Warcraft III does, ignoring newer optional MPQ metadata where needed. This is useful for Warcraft III maps and some intentionally odd or damaged archives.
Open without READ_ONLY to allow writes. Changes are applied when close() runs.
import systems.crigges.jmpq3.JMpqEditor;
import systems.crigges.jmpq3.MPQOpenOption;
import java.io.File;
try (JMpqEditor mpq = new JMpqEditor(new File("MyMap.w3x"), MPQOpenOption.FORCE_V0)) {
if (mpq.hasFile("war3map.j")) {
mpq.deleteFile("war3map.j");
}
mpq.insertFile("war3map.j", new File("build/war3map.j"));
mpq.insertByteArray("war3mapImported/readme.txt", "generated by JMPQ3".getBytes());
}insertFile stores the file path and reads the file when the archive is rebuilt on close. Keep that source file alive until the editor is closed. If the data is temporary, use insertByteArray instead.
To overwrite an existing file directly:
mpq.insertFile("war3map.j", new File("build/war3map.j"), true);
mpq.insertByteArray("war3mapImported/readme.txt", bytes, true);JMPQ3 can create a minimal empty archive and then rebuild it with inserted files. This is useful for Warcraft III "folder mode" maps, where a .w3x directory contains the files that should become a real MPQ archive.
import systems.crigges.jmpq3.JMpqEditor;
import systems.crigges.jmpq3.MPQOpenOption;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
File outputMap = new File("build/MyMap.w3x");
JMpqEditor.createEmptyArchive(outputMap);
try (JMpqEditor mpq = new JMpqEditor(outputMap, MPQOpenOption.FORCE_V0)) {
mpq.insertFile("war3map.w3i", new File("folderMap/war3map.w3i"));
mpq.insertFile("war3map.j", new File("folderMap/war3map.j"));
mpq.insertFile("war3mapImported/icon.blp", new File("folderMap/war3mapImported/icon.blp"));
byte[] generatedScript = Files.readAllBytes(Path.of("build/war3map.j"));
mpq.insertByteArray("war3map.j", generatedScript, true);
}You can also get the initial empty archive bytes without writing a file:
byte[] emptyArchive = JMpqEditor.createEmptyArchive();Empty directories are not represented in MPQ archives, so only insert regular files.
MPQs do not always contain a complete list of filenames. extractAllFiles extracts known files when a usable (listfile) is available. Archives without a complete listfile may still contain files that can only be accessed if you know their exact path.
try (JMpqEditor mpq = new JMpqEditor(new File("MyMap.w3x"), MPQOpenOption.READ_ONLY, MPQOpenOption.FORCE_V0)) {
mpq.extractAllFiles(new File("extracted"));
}For writable archives with a missing or incomplete listfile, you can provide an external listfile before rebuilding:
try (JMpqEditor mpq = new JMpqEditor(new File("MyMap.w3x"), MPQOpenOption.FORCE_V0)) {
mpq.setExternalListfile(new File("listfile.txt"));
mpq.insertByteArray("war3mapImported/generated.txt", "hello".getBytes());
}- Unsupported decompression algorithms: sparse and bzip2.
- Supported compression is zlib/zopfli.
- JMPQ3 does not currently build a valid
(attributes)file. Warcraft III maps appear to work without it. - Empty directories are not stored in MPQ archives.