Skip to content

Commit

Permalink
Merge 76cf05c into 7e87dc9
Browse files Browse the repository at this point in the history
  • Loading branch information
zach-cloud committed May 3, 2020
2 parents 7e87dc9 + 76cf05c commit 4af4a85
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 14 deletions.
101 changes: 89 additions & 12 deletions src/main/java/systems/crigges/jmpq3/JMpqEditor.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ public class JMpqEditor implements AutoCloseable {

/** If write operations are supported on the archive. */
private boolean canWrite;
/** If the archive was originally read-only */
private boolean openedAsReadOnly;

/**
* Creates a new MPQ editor for the MPQ file at the specified path.
Expand All @@ -138,6 +140,7 @@ public class JMpqEditor implements AutoCloseable {
public JMpqEditor(Path mpqArchive, MPQOpenOption... openOptions) throws JMpqException {
// process open options
canWrite = !Arrays.asList(openOptions).contains(MPQOpenOption.READ_ONLY);
openedAsReadOnly = !canWrite;
legacyCompatibility = Arrays.asList(openOptions).contains(MPQOpenOption.FORCE_V0);
log.debug(mpqArchive.toString());
try {
Expand Down Expand Up @@ -209,25 +212,59 @@ private void readAttributesFile() {
}
}

/**
* For use when the MPQ is missing a (listfile)
* Adds this custom listfile into the MPQ and uses it
* for rebuilding purposes.
* If this is not a full listfile, the end result will be missing files.
*
* @param externalListfilePath Path to a file containing listfile entries
*/
public void setExternalListfile(File externalListfilePath) {
if(!externalListfilePath.exists()) {
log.warn("External MPQ File: " + externalListfilePath.getAbsolutePath() +
" does not exist and will not be used");
return;
}
try {
// Read and apply listfile
listFile = new Listfile(Files.readAllBytes(externalListfilePath.toPath()));
checkListfileEntries();
removeMissingFiles();
// Operation succeeded and added a listfile so we can now write properly.
// (as long as it wasn't read-only to begin with)
canWrite = !openedAsReadOnly;
} catch (Exception ex) {
log.warn("Could not apply external listfile: " + externalListfilePath.getAbsolutePath());
// The value of canWrite is not changed intentionally
}
}

/**
* Removes files from the listfile if they aren't
* actually in the map.
*/
private void removeMissingFiles() {
Iterator<String> it = listFile.getFiles().iterator();
while(it.hasNext()) {
if(!hasFile(it.next())) {
it.remove();
}
}
}

/**
* Reads an internal Listfile name called (listfile)
* and applies that as the archive's listfile.
*/
private void readListFile() {
if (hasFile("(listfile)")) {
try {
File tempFile = File.createTempFile("list", "file", JMpqEditor.tempDir);
tempFile.deleteOnExit();
extractFile("(listfile)", tempFile);
listFile = new Listfile(Files.readAllBytes(tempFile.toPath()));
int hiddenFiles = (hasFile("(attributes)") ? 2 : 1) + (hasFile("(signature)") ? 1 : 0);
if (canWrite) {
if (listFile.getFiles().size() >= blockTable.getAllVaildBlocks().size() - hiddenFiles) {
log.warn("mpq's listfile is incomplete. Blocks without listfile entry will be discarded");
}
for (String fileName : listFile.getFiles()) {
if (!hasFile(fileName)) {
log.warn("listfile entry does not exist in archive and will be discarded: " + fileName);
}
}
listFile.getFileMap().entrySet().removeIf(file -> !hasFile(file.getValue()));
}
checkListfileEntries();
} catch (Exception e) {
log.warn("Extracting the mpq's listfile failed. It cannot be rebuild.", e);
}
Expand All @@ -237,6 +274,37 @@ private void readListFile() {
}
}

/**
* Performs verification to see if we know all the blocks of this file.
* Prints warnings if we don't know all blocks.
*
* @throws JMpqException If retrieving valid blocks fails
*/
private void checkListfileEntries() throws JMpqException {
int hiddenFiles = (hasFile("(attributes)") ? 2 : 1) + (hasFile("(signature)") ? 1 : 0);
if (canWrite) {
checkListfileCompleteness(hiddenFiles);
}
}

/**
* Checks listfile for completeness against block table
*
* @param hiddenFiles Num. hidden files
* @throws JMpqException If retrieving valid blocks fails
*/
private void checkListfileCompleteness(int hiddenFiles) throws JMpqException {
if (listFile.getFiles().size() >= blockTable.getAllVaildBlocks().size() - hiddenFiles) {
log.warn("mpq's listfile is incomplete. Blocks without listfile entry will be discarded");
}
for (String fileName : listFile.getFiles()) {
if (!hasFile(fileName)) {
log.warn("listfile entry does not exist in archive and will be discarded: " + fileName);
}
}
listFile.getFileMap().entrySet().removeIf(file -> !hasFile(file.getValue()));
}

private void readBlockTable() throws IOException {
ByteBuffer blockBuffer = ByteBuffer.allocate(blockSize * 16).order(ByteOrder.LITTLE_ENDIAN);
fc.position(headerOffset + blockPos);
Expand Down Expand Up @@ -1046,4 +1114,13 @@ public String toString() {
return "JMpqEditor [headerSize=" + headerSize + ", archiveSize=" + archiveSize + ", formatVersion=" + formatVersion + ", discBlockSize=" + discBlockSize
+ ", hashPos=" + hashPos + ", blockPos=" + blockPos + ", hashSize=" + hashSize + ", blockSize=" + blockSize + ", hashMap=" + hashTable + "]";
}

/**
* Returns an unmodifiable collection of all Listfile entries
*
* @return Listfile entries
*/
public Collection<String> getListfileEntries() {
return Collections.unmodifiableCollection(listFile.getFiles());
}
}
14 changes: 12 additions & 2 deletions src/test/java/systems/crigges/jmpq3test/MpqTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
import java.nio.file.StandardCopyOption;
import java.util.*;

import static systems.crigges.jmpq3.HashTable.calculateFileKey;

/**
* Created by Frotty on 06.03.2017.
*/
Expand Down Expand Up @@ -136,6 +134,18 @@ public void testRebuild() throws IOException {
}
}

@Test
public void testExternalListfile() throws Exception {
File mpq = getFile("mpqs/normalMap.w3x");
File listFile = getFile("listfile.txt");
JMpqEditor mpqEditor = new JMpqEditor(mpq, MPQOpenOption.FORCE_V0);
if(mpqEditor.isCanWrite()) {
mpqEditor.deleteFile("(listfile)");
}
mpqEditor.setExternalListfile(listFile);
Assert.assertTrue(mpqEditor.getListfileEntries().contains("war3map.w3a"));
}

@Test
public void testRecompressBuild() throws IOException {
File[] mpqs = getMpqs();
Expand Down
6 changes: 6 additions & 0 deletions src/test/resources/listfile.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
scripts\war3map.j
war3map.j
war3map.w3u
war3map.w3t
war3map.w3a
customFile.j

0 comments on commit 4af4a85

Please sign in to comment.