-
Notifications
You must be signed in to change notification settings - Fork 17
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
Allow usage of external Listfile to rebuild MPQ. #36
Changes from 7 commits
227b387
b7906b8
65168c5
3e7fa95
52c1bb7
1a4fc49
91c39ff
76cf05c
1c314cf
5b6e5a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
|
@@ -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 { | ||
|
@@ -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() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems to be equivalent to https://github.com/inwc3/JMPQ3/pull/36/files#diff-fb7d083bb9ea8eb0c5d21c0cc0059b5bR305 ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it is. Good point, that's a much better way of doing this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it resolved? |
||
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); | ||
} | ||
|
@@ -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); | ||
|
@@ -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()); | ||
} | ||
} |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why add this superfluous variable?
It also wouldn't sound right to open the mpq as read-only and for it then to become writable.
The listfile function should probably only be available in edit mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like the "edit mode" is only available in the Constructor. If the (listfile) doesn't exist, it sets canWrite to "false" even if it was originally not opened as read-only. That's why the setExternalListfile method needs to set canWrite to "true". However I don't want to set canWrite to "true" if the archive was orignally opened as readOnly. Does this make sense? Maybe there's a better way of accomplishing this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could pass listfile in constructor i suppose to prevent setting canWrite to false.