167 lines
6.6 KiB
Java
167 lines
6.6 KiB
Java
/*
|
|
* Copyright 2019-2020 Dmitry Isaenko
|
|
*
|
|
* This file is part of Konogonka.
|
|
*
|
|
* Konogonka is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Konogonka is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Konogonka. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package libKonogonka.Tools.RomFs;
|
|
|
|
import java.io.*;
|
|
|
|
public class RomFsDecryptedProvider implements IRomFsProvider{
|
|
|
|
private long level6Offset;
|
|
|
|
private File file;
|
|
private Level6Header header;
|
|
|
|
private FileSystemEntry rootEntry;
|
|
|
|
public RomFsDecryptedProvider(File decryptedFsImageFile, long level6Offset) throws Exception{
|
|
if (level6Offset < 0)
|
|
throw new Exception("Incorrect Level 6 Offset");
|
|
|
|
this.file = decryptedFsImageFile;
|
|
|
|
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(decryptedFsImageFile));
|
|
|
|
this.level6Offset = level6Offset;
|
|
|
|
skipBytes(bis, level6Offset);
|
|
|
|
byte[] rawDataChunk = new byte[0x50];
|
|
|
|
if (bis.read(rawDataChunk) != 0x50)
|
|
throw new Exception("Failed to read header (0x50)");
|
|
|
|
this.header = new Level6Header(rawDataChunk);
|
|
/*
|
|
// Print Dir Hash table as is:
|
|
long seekTo = header.getDirectoryHashTableOffset() - 0x50;
|
|
rawDataChunk = new byte[(int) header.getDirectoryHashTableLength()];
|
|
skipTo(bis, seekTo);
|
|
if (bis.read(rawDataChunk) != (int) header.getDirectoryHashTableLength())
|
|
throw new Exception("Failed to read Dir hash table");
|
|
RainbowDump.hexDumpUTF8(rawDataChunk);
|
|
// Print Files Hash table as is:
|
|
seekTo = header.getFileHashTableOffset() - header.getDirectoryMetadataTableOffset();
|
|
rawDataChunk = new byte[(int) header.getFileHashTableLength()];
|
|
skipTo(bis, seekTo);
|
|
if (bis.read(rawDataChunk) != (int) header.getFileHashTableLength())
|
|
throw new Exception("Failed to read Files hash table");
|
|
RainbowDump.hexDumpUTF8(rawDataChunk);
|
|
*/
|
|
// Read directories metadata
|
|
long locationInFile = header.getDirectoryMetadataTableOffset() - 0x50;
|
|
|
|
skipBytes(bis, locationInFile);
|
|
|
|
if (header.getDirectoryMetadataTableLength() < 0)
|
|
throw new Exception("Not supported operation.");
|
|
|
|
byte[] directoryMetadataTable = new byte[(int) header.getDirectoryMetadataTableLength()];
|
|
|
|
if (bis.read(directoryMetadataTable) != (int) header.getDirectoryMetadataTableLength())
|
|
throw new Exception("Failed to read "+header.getDirectoryMetadataTableLength());
|
|
// Read files metadata
|
|
locationInFile = header.getFileMetadataTableOffset() - header.getFileHashTableOffset(); // TODO: replace to 'CurrentPosition'?
|
|
|
|
skipBytes(bis, locationInFile);
|
|
|
|
if (header.getFileMetadataTableLength() < 0)
|
|
throw new Exception("Not supported operation.");
|
|
|
|
byte[] fileMetadataTable = new byte[(int) header.getFileMetadataTableLength()];
|
|
|
|
if (bis.read(fileMetadataTable) != (int) header.getFileMetadataTableLength())
|
|
throw new Exception("Failed to read "+header.getFileMetadataTableLength());
|
|
|
|
rootEntry = new FileSystemEntry(directoryMetadataTable, fileMetadataTable);
|
|
//printDebug(directoryMetadataTable, fileMetadataTable);
|
|
bis.close();
|
|
}
|
|
private void skipBytes(BufferedInputStream bis, long size) throws Exception{
|
|
long mustSkip = size;
|
|
long skipped = 0;
|
|
while (mustSkip > 0){
|
|
skipped += bis.skip(mustSkip);
|
|
mustSkip = size - skipped;
|
|
}
|
|
}
|
|
@Override
|
|
public long getLevel6Offset() { return level6Offset; }
|
|
@Override
|
|
public Level6Header getHeader() { return header; }
|
|
@Override
|
|
public FileSystemEntry getRootEntry() { return rootEntry; }
|
|
@Override
|
|
public PipedInputStream getContent(FileSystemEntry entry) throws Exception{
|
|
if (entry.isDirectory())
|
|
throw new Exception("Request of the binary stream for the folder entry doesn't make sense.");
|
|
|
|
PipedOutputStream streamOut = new PipedOutputStream();
|
|
Thread workerThread;
|
|
|
|
PipedInputStream streamIn = new PipedInputStream(streamOut);
|
|
|
|
workerThread = new Thread(() -> {
|
|
System.out.println("RomFsDecryptedProvider -> getContent(): Executing thread");
|
|
try {
|
|
long subFileRealPosition = level6Offset + header.getFileDataOffset() + entry.getFileOffset();
|
|
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
|
|
skipBytes(bis, subFileRealPosition);
|
|
|
|
int readPice = 8388608; // 8mb NOTE: consider switching to 1mb 1048576
|
|
|
|
long readFrom = 0;
|
|
long realFileSize = entry.getFileSize();
|
|
|
|
byte[] readBuf;
|
|
|
|
while (readFrom < realFileSize) {
|
|
if (realFileSize - readFrom < readPice)
|
|
readPice = Math.toIntExact(realFileSize - readFrom); // it's safe, I guarantee
|
|
readBuf = new byte[readPice];
|
|
if (bis.read(readBuf) != readPice) {
|
|
System.out.println("RomFsDecryptedProvider -> getContent(): Unable to read requested size from file.");
|
|
return;
|
|
}
|
|
streamOut.write(readBuf);
|
|
readFrom += readPice;
|
|
}
|
|
bis.close();
|
|
streamOut.close();
|
|
} catch (Exception e) {
|
|
System.out.println("RomFsDecryptedProvider -> getContent(): Unable to provide stream");
|
|
e.printStackTrace();
|
|
}
|
|
System.out.println("RomFsDecryptedProvider -> getContent(): Thread is dead");
|
|
});
|
|
workerThread.start();
|
|
return streamIn;
|
|
}
|
|
@Override
|
|
public File getFile() {
|
|
return file;
|
|
}
|
|
|
|
private void printDebug(byte[] directoryMetadataTable, byte[] fileMetadataTable){
|
|
new FolderMeta4Debug(header.getDirectoryMetadataTableLength(), directoryMetadataTable);
|
|
new FileMeta4Debug(header.getFileMetadataTableLength(), fileMetadataTable);
|
|
rootEntry.printTreeForDebug();
|
|
}
|
|
}
|