I already wrote
one several years ago. I was still learning about Java so it is not very neat.
It did not support HET and BET tables but it could open SC2 map and mod archives since they still used HashTable and BlockTable mechanics in parallel. It also did not support all types of decompression but did support the common ones. Main flaw was a lack of writing support, however the way MPQ is constructed makes this difficult (why every patch had to rebuild the archives when patching). I could argue the lack of file writing support is similar to your lack of MPQ1+ support in that it was designed with other priorities at hand and never finished.
My Java MPQ solution provided you with SeekableByteChannel to the file contents when reading. The chunk mechanics of MPQ files meant that the channel was equivalently buffered so even small reads would be reasonably fast. No temporary files were created at any time when opening a MPQ archive and reading files from it. Double buffering was used to minimize chunk buffer allocation when decrypting/decompressing to at most 2 per channel opened.
Performance measurements showed that it was entirely I/O bound. Full extracting (reading and writing out to file, each part done on separate disks, files extracted in order presented by listfile) a main MPQ archive (war3.mpq) would take <60 seconds when not inside the file cache. Extracting the same MPQ archive when inside the OS file cache took <3 seconds. When not inside the file cache the slow performance is the result of random access I/O on mechanical drives and mirrors the "initial loading performance" Warcraft III has.
After I have finished some more work on my BLP ImageReader I am considering revisiting the MPQ library and updating it to be a full FileSystem.
with the unconfirmed argument that IO would be better than NIO in this usecase
As I have said multiple times already both
io and
nio are meant to work together. Nowhere did I say one is better than the other, just what you are doing with them does not make much sense. Usual file input/output in Java is still done using the standard
io package and there is no reason to migrate from that as it fulfils its purpose perfectly. File systems and other low-level input/output providers should be using the
nio package and channels, even if ultimately the channel they provide ends up wrapped in an
io package stream to be used.
Edit:
Because you copy the file when opening an archive you probably force a very fast sequential read of the entire archive. This means that both source and destination files are now in the OS file cache so no underlying I/O will occur. This will give you performance similar to my library after forcing the entire archive into the file cache. Forcing the entire archive into the file cache requires 500 MB of memory odd for a main archive. If such memory is not available or the OS is forced to purge the file cache then performance should degrade closer to the random access performance my library presents. Also your argument about memory usage is not entirely true since your process virtual address space could spike by 500 MB when opening a MPQ archive due to the memory mapping of the entire archive file.