libKonogonka/src/main/java/libKonogonka/Tools/RomFs/RomFsProvider.java

152 lines
6.1 KiB
Java

/*
* Copyright 2019-2022 Dmitry Isaenko
*
* This file is part of libKonogonka.
*
* libKonogonka 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.
*
* libKonogonka 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 libKonogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package libKonogonka.Tools.RomFs;
import libKonogonka.Tools.PFS0.PFS0subFile;
import libKonogonka.Tools.RomFs.view.DirectoryMetaTablePlainView;
import libKonogonka.Tools.RomFs.view.FileMetaTablePlainView;
import libKonogonka.ctraes.InFileStreamProducer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.*;
import java.nio.file.Files;
public class RomFsProvider{
private final static Logger log = LogManager.getLogger(RomFsProvider.class);
private final InFileStreamProducer producer;
private final long level6Offset;
private final Level6Header level6Header;
private final FileSystemEntry rootEntry;
private long mediaStartOffset;
// Used only for debug
private final byte[] directoryMetadataTable;
private final byte[] fileMetadataTable;
public RomFsProvider(File decryptedFsImageFile, long level6offset) throws Exception{
this.producer = new InFileStreamProducer(decryptedFsImageFile);
RomFsConstruct construct = new RomFsConstruct(producer, level6offset);
this.level6Offset = level6offset;
this.level6Header = construct.getHeader();
this.rootEntry = construct.getRootEntry();
this.directoryMetadataTable = construct.getDirectoryMetadataTable();
this.fileMetadataTable = construct.getFileMetadataTable();
}
public RomFsProvider(InFileStreamProducer producer,
long level6Offset,
long offsetPositionInFile,
long mediaStartOffset
) throws Exception{
this.producer = producer;
this.mediaStartOffset = mediaStartOffset;
RomFsConstruct construct = new RomFsConstruct(producer, level6Offset, offsetPositionInFile);
this.level6Offset = level6Offset;
this.level6Header = construct.getHeader();
this.rootEntry = construct.getRootEntry();
this.directoryMetadataTable = construct.getDirectoryMetadataTable();
this.fileMetadataTable = construct.getFileMetadataTable();
}
public long getLevel6Offset() { return level6Offset; }
public Level6Header getHeader() {return level6Header;}
public FileSystemEntry getRootEntry() { return rootEntry; }
public boolean exportContent(String saveToLocation, FileSystemEntry entry){
try{
if (! saveToLocation.endsWith(File.separator))
saveToLocation += File.separator;
if (entry.isDirectory())
exportFolderContent(entry, saveToLocation);
else
exportSingleFile(entry, saveToLocation);
}
catch (Exception e){
log.error("File export failure", e);
return false;
}
return true;
}
private void exportFolderContent(FileSystemEntry entry, String saveToLocation) throws Exception{
File contentFile = new File(saveToLocation + entry.getName());
contentFile.mkdirs();
String currentDirPath = saveToLocation + entry.getName() + File.separator;
for (FileSystemEntry fileEntry : entry.getContent()){
if (fileEntry.isDirectory())
exportFolderContent(fileEntry, currentDirPath);
else
exportSingleFile(fileEntry, currentDirPath);
}
}
private void exportSingleFile(FileSystemEntry entry, String saveToLocation) throws Exception {
File contentFile = new File(saveToLocation + entry.getName());
try(BufferedOutputStream extractedFileBOS = new BufferedOutputStream(Files.newOutputStream(contentFile.toPath()));
BufferedInputStream stream = producer.produce()) {
long skipBytes = entry.getOffset() + mediaStartOffset * 0x200 + level6Header.getFileDataOffset() + level6Offset;
if (skipBytes != stream.skip(skipBytes))
throw new Exception("Can't skip");
int blockSize = 0x200;
if (entry.getSize() < 0x200)
blockSize = (int) entry.getSize();
long i = 0;
byte[] block = new byte[blockSize];
int actuallyRead;
while (true) {
if ((actuallyRead = stream.read(block)) != blockSize)
throw new Exception("Read failure. Block Size: "+blockSize+", actuallyRead: "+actuallyRead);
extractedFileBOS.write(block);
i += blockSize;
if ((i + blockSize) >= entry.getSize()) {
blockSize = (int) (entry.getSize() - i);
if (blockSize == 0)
break;
block = new byte[blockSize];
}
}
}
}
public InFileStreamProducer getStreamProducer(FileSystemEntry entry) throws Exception{
if (entry.isDirectory())
throw new Exception("Directory entries are not supported");
return producer.getSuccessor(
entry.getOffset() + mediaStartOffset * 0x200 + level6Header.getFileDataOffset() + level6Offset);
}
public File getFile(){
return producer.getFile();
}
public void printDebug(){
level6Header.printDebugInfo();
new DirectoryMetaTablePlainView(level6Header.getDirectoryMetadataTableLength(), directoryMetadataTable);
new FileMetaTablePlainView(level6Header.getFileMetadataTableLength(), fileMetadataTable);
rootEntry.printTreeForDebug();
}
}