Unify export functionality
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
3262fb2360
commit
e47d977779
9 changed files with 159 additions and 278 deletions
47
src/main/java/libKonogonka/Tools/ExportAble.java
Normal file
47
src/main/java/libKonogonka/Tools/ExportAble.java
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package libKonogonka.Tools;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class ExportAble {
|
||||||
|
|
||||||
|
protected BufferedInputStream stream;
|
||||||
|
|
||||||
|
protected boolean export(String saveTo, String fileName, long skip, long size) throws Exception{
|
||||||
|
if (skip != stream.skip(skip))
|
||||||
|
throw new Exception("Can't seek to start position: "+skip);
|
||||||
|
|
||||||
|
File location = new File(saveTo);
|
||||||
|
location.mkdirs();
|
||||||
|
|
||||||
|
try (BufferedOutputStream extractedFileBOS = new BufferedOutputStream(
|
||||||
|
Files.newOutputStream(Paths.get(saveTo+File.separator+fileName)))){
|
||||||
|
|
||||||
|
int blockSize = 0x200;
|
||||||
|
if (size < 0x200)
|
||||||
|
blockSize = (int) size;
|
||||||
|
|
||||||
|
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) > size) {
|
||||||
|
blockSize = (int) (size - i);
|
||||||
|
if (blockSize == 0)
|
||||||
|
break;
|
||||||
|
block = new byte[blockSize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -203,6 +203,8 @@ public class KernelAccessControlProvider {
|
||||||
canDebugOthers = (block >> 18 & 1) != 0;
|
canDebugOthers = (block >> 18 & 1) != 0;
|
||||||
log.trace("DEBUGFLAGS "+canBeDebugged+" "+canDebugOthers);
|
log.trace("DEBUGFLAGS "+canBeDebugged+" "+canDebugOthers);
|
||||||
break;
|
break;
|
||||||
|
case 0x20:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
log.warn("INVALID ind:0b"+Integer.toBinaryString(block));
|
log.warn("INVALID ind:0b"+Integer.toBinaryString(block));
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package libKonogonka.Tools.PFS0;
|
package libKonogonka.Tools.PFS0;
|
||||||
|
|
||||||
import libKonogonka.RainbowDump;
|
import libKonogonka.RainbowDump;
|
||||||
|
import libKonogonka.Tools.ExportAble;
|
||||||
import libKonogonka.Tools.ISuperProvider;
|
import libKonogonka.Tools.ISuperProvider;
|
||||||
import libKonogonka.Tools.NCA.NCASectionTableBlock.SuperBlockPFS0;
|
import libKonogonka.Tools.NCA.NCASectionTableBlock.SuperBlockPFS0;
|
||||||
import libKonogonka.ctraes.InFileStreamProducer;
|
import libKonogonka.ctraes.InFileStreamProducer;
|
||||||
|
@ -30,14 +31,13 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
public class PFS0Provider implements ISuperProvider {
|
public class PFS0Provider extends ExportAble implements ISuperProvider {
|
||||||
private final static Logger log = LogManager.getLogger(PFS0Provider.class);
|
private final static Logger log = LogManager.getLogger(PFS0Provider.class);
|
||||||
|
|
||||||
private final long rawBlockDataStart;
|
private final long rawBlockDataStart;
|
||||||
|
|
||||||
private long offsetPositionInFile;
|
private long offsetPositionInFile;
|
||||||
private final InFileStreamProducer producer;
|
private final InFileStreamProducer producer;
|
||||||
private BufferedInputStream stream;
|
|
||||||
private SuperBlockPFS0 superBlockPFS0;
|
private SuperBlockPFS0 superBlockPFS0;
|
||||||
|
|
||||||
private long mediaStartOffset;
|
private long mediaStartOffset;
|
||||||
|
@ -108,47 +108,16 @@ public class PFS0Provider implements ISuperProvider {
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean exportContent(String saveToLocation, int subFileNumber){
|
public boolean exportContent(String saveToLocation, int subFileNumber){
|
||||||
|
try {
|
||||||
PFS0subFile subFile = header.getPfs0subFiles()[subFileNumber];
|
PFS0subFile subFile = header.getPfs0subFiles()[subFileNumber];
|
||||||
File location = new File(saveToLocation);
|
|
||||||
location.mkdirs();
|
|
||||||
|
|
||||||
try (BufferedOutputStream extractedFileBOS = new BufferedOutputStream(
|
|
||||||
Files.newOutputStream(Paths.get(saveToLocation+File.separator+subFile.getName())))){
|
|
||||||
|
|
||||||
this.stream = producer.produce();
|
this.stream = producer.produce();
|
||||||
|
|
||||||
long subFileSize = subFile.getSize();
|
|
||||||
|
|
||||||
long toSkip = subFile.getOffset() + mediaStartOffset * 0x200 + rawBlockDataStart;
|
long toSkip = subFile.getOffset() + mediaStartOffset * 0x200 + rawBlockDataStart;
|
||||||
if (toSkip != stream.skip(toSkip))
|
return export(saveToLocation, subFile.getName(), toSkip, subFile.getSize());
|
||||||
throw new Exception("Unable to skip offset: "+toSkip);
|
|
||||||
|
|
||||||
int blockSize = 0x200;
|
|
||||||
if (subFileSize < 0x200)
|
|
||||||
blockSize = (int) subFileSize;
|
|
||||||
|
|
||||||
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) > subFileSize) {
|
|
||||||
blockSize = (int) (subFileSize - i);
|
|
||||||
if (blockSize == 0)
|
|
||||||
break;
|
|
||||||
block = new byte[blockSize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception e){
|
catch (Exception e){
|
||||||
log.error("File export failure", e);
|
log.error("File export failure", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package libKonogonka.Tools.RomFs;
|
package libKonogonka.Tools.RomFs;
|
||||||
|
|
||||||
import libKonogonka.Tools.PFS0.PFS0subFile;
|
import libKonogonka.Tools.ExportAble;
|
||||||
import libKonogonka.Tools.RomFs.view.DirectoryMetaTablePlainView;
|
import libKonogonka.Tools.RomFs.view.DirectoryMetaTablePlainView;
|
||||||
import libKonogonka.Tools.RomFs.view.FileMetaTablePlainView;
|
import libKonogonka.Tools.RomFs.view.FileMetaTablePlainView;
|
||||||
import libKonogonka.ctraes.InFileStreamProducer;
|
import libKonogonka.ctraes.InFileStreamProducer;
|
||||||
|
@ -26,9 +26,8 @@ import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
|
||||||
|
|
||||||
public class RomFsProvider{
|
public class RomFsProvider extends ExportAble {
|
||||||
private final static Logger log = LogManager.getLogger(RomFsProvider.class);
|
private final static Logger log = LogManager.getLogger(RomFsProvider.class);
|
||||||
|
|
||||||
private final InFileStreamProducer producer;
|
private final InFileStreamProducer producer;
|
||||||
|
@ -99,36 +98,9 @@ public class RomFsProvider{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exportSingleFile(FileSystemEntry entry, String saveToLocation) throws Exception {
|
private void exportSingleFile(FileSystemEntry entry, String saveToLocation) throws Exception {
|
||||||
File contentFile = new File(saveToLocation + entry.getName());
|
stream = producer.produce();
|
||||||
try(BufferedOutputStream extractedFileBOS = new BufferedOutputStream(Files.newOutputStream(contentFile.toPath()));
|
|
||||||
BufferedInputStream stream = producer.produce()) {
|
|
||||||
long skipBytes = entry.getOffset() + mediaStartOffset * 0x200 + level6Header.getFileDataOffset() + level6Offset;
|
long skipBytes = entry.getOffset() + mediaStartOffset * 0x200 + level6Header.getFileDataOffset() + level6Offset;
|
||||||
|
export(saveToLocation, entry.getName(), skipBytes, entry.getSize());
|
||||||
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{
|
public InFileStreamProducer getStreamProducer(FileSystemEntry entry) throws Exception{
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package libKonogonka.Tools.XCI;
|
package libKonogonka.Tools.XCI;
|
||||||
|
|
||||||
|
import libKonogonka.Tools.ExportAble;
|
||||||
import libKonogonka.Tools.ISuperProvider;
|
import libKonogonka.Tools.ISuperProvider;
|
||||||
import libKonogonka.ctraes.InFileStreamProducer;
|
import libKonogonka.ctraes.InFileStreamProducer;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
@ -34,7 +35,7 @@ import static libKonogonka.Converter.*;
|
||||||
/**
|
/**
|
||||||
* HFS0
|
* HFS0
|
||||||
* */
|
* */
|
||||||
public class HFS0Provider implements ISuperProvider {
|
public class HFS0Provider extends ExportAble implements ISuperProvider {
|
||||||
private final static Logger log = LogManager.getLogger(HFS0Provider.class);
|
private final static Logger log = LogManager.getLogger(HFS0Provider.class);
|
||||||
|
|
||||||
private final String magic;
|
private final String magic;
|
||||||
|
@ -140,41 +141,8 @@ public class HFS0Provider implements ISuperProvider {
|
||||||
@Override
|
@Override
|
||||||
public boolean exportContent(String saveToLocation, int subFileNumber) throws Exception {
|
public boolean exportContent(String saveToLocation, int subFileNumber) throws Exception {
|
||||||
HFS0File subFile = hfs0Files[subFileNumber];
|
HFS0File subFile = hfs0Files[subFileNumber];
|
||||||
File location = new File(saveToLocation);
|
stream = getStreamProducer(subFileNumber).produce();
|
||||||
location.mkdirs();
|
return export(saveToLocation, subFile.getName(), 0, subFile.getSize());
|
||||||
|
|
||||||
try (BufferedOutputStream extractedFileBOS = new BufferedOutputStream(
|
|
||||||
Files.newOutputStream(Paths.get(saveToLocation+File.separator+subFile.getName())));
|
|
||||||
BufferedInputStream stream = getStreamProducer(subFileNumber).produce()){
|
|
||||||
|
|
||||||
long subFileSize = subFile.getSize();
|
|
||||||
|
|
||||||
int blockSize = 0x200;
|
|
||||||
if (subFileSize < 0x200)
|
|
||||||
blockSize = (int) subFileSize;
|
|
||||||
|
|
||||||
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) > subFileSize) {
|
|
||||||
blockSize = (int) (subFileSize - i);
|
|
||||||
if (blockSize == 0)
|
|
||||||
break;
|
|
||||||
block = new byte[blockSize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e){
|
|
||||||
log.error("File export failure", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public InFileStreamProducer getStreamProducer(String subFileName) throws FileNotFoundException{
|
public InFileStreamProducer getStreamProducer(String subFileName) throws FileNotFoundException{
|
||||||
|
|
|
@ -19,21 +19,21 @@
|
||||||
package libKonogonka.Tools.other.System2;
|
package libKonogonka.Tools.other.System2;
|
||||||
|
|
||||||
import libKonogonka.KeyChainHolder;
|
import libKonogonka.KeyChainHolder;
|
||||||
|
import libKonogonka.Tools.ExportAble;
|
||||||
import libKonogonka.Tools.other.System2.ini1.Ini1Provider;
|
import libKonogonka.Tools.other.System2.ini1.Ini1Provider;
|
||||||
|
import libKonogonka.ctraesclassic.AesCtrClassicBufferedInputStream;
|
||||||
|
import libKonogonka.ctraesclassic.AesCtrDecryptClassic;
|
||||||
import libKonogonka.ctraesclassic.AesCtrStream;
|
import libKonogonka.ctraesclassic.AesCtrStream;
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import javax.crypto.CipherInputStream;
|
import javax.crypto.CipherInputStream;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
|
||||||
public class System2Provider {
|
public class System2Provider extends ExportAble {
|
||||||
private final static Logger log = LogManager.getLogger(System2Provider.class);
|
|
||||||
|
|
||||||
private byte[] rsa2048signature;
|
private byte[] rsa2048signature;
|
||||||
private System2Header header;
|
private System2Header header;
|
||||||
private KernelMap kernelMap;
|
private KernelMap kernelMap;
|
||||||
|
@ -46,20 +46,20 @@ public class System2Provider {
|
||||||
this.pathToFile = pathToFile;
|
this.pathToFile = pathToFile;
|
||||||
this.keyChainHolder = keyChainHolder;
|
this.keyChainHolder = keyChainHolder;
|
||||||
|
|
||||||
try (BufferedInputStream stream = new BufferedInputStream(Files.newInputStream(Paths.get(pathToFile)))) {
|
this.stream = new BufferedInputStream(Files.newInputStream(Paths.get(pathToFile)));
|
||||||
readSignatures(stream);
|
readSignatures();
|
||||||
readHeader(stream);
|
readHeader();
|
||||||
findIni1KernelMap();
|
findIni1KernelMap();
|
||||||
}
|
this.stream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readSignatures(BufferedInputStream stream) throws Exception{
|
private void readSignatures() throws Exception{
|
||||||
rsa2048signature = new byte[0x100];
|
rsa2048signature = new byte[0x100];
|
||||||
if (0x100 != stream.read(rsa2048signature))
|
if (0x100 != stream.read(rsa2048signature))
|
||||||
throw new Exception("Unable to read System2 RSA-2048 signature bytes");
|
throw new Exception("Unable to read System2 RSA-2048 signature bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readHeader(BufferedInputStream stream) throws Exception{
|
private void readHeader() throws Exception{
|
||||||
byte[] headerBytes = new byte[0x100];
|
byte[] headerBytes = new byte[0x100];
|
||||||
if (0x100 != stream.read(headerBytes))
|
if (0x100 != stream.read(headerBytes))
|
||||||
throw new Exception("Unable to read System2 header bytes");
|
throw new Exception("Unable to read System2 header bytes");
|
||||||
|
@ -74,11 +74,11 @@ public class System2Provider {
|
||||||
throw new Exception("Unable to skip offset: " + toSkip);
|
throw new Exception("Unable to skip offset: " + toSkip);
|
||||||
|
|
||||||
ByteBuffer byteBuffer = ByteBuffer.allocate(0x1000);
|
ByteBuffer byteBuffer = ByteBuffer.allocate(0x1000);
|
||||||
try (CipherInputStream stream = AesCtrStream.getStream(header.getKey(), header.getSection0Ctr(), fis);) {
|
try (CipherInputStream cipherInputStream = AesCtrStream.getStream(header.getKey(), header.getSection0Ctr(), fis);) {
|
||||||
for (int j = 0; j < 8; j++) {
|
for (int j = 0; j < 8; j++) {
|
||||||
byte[] block = new byte[0x200];
|
byte[] block = new byte[0x200];
|
||||||
int actuallyRead;
|
int actuallyRead;
|
||||||
if ((actuallyRead = stream.read(block)) != 0x200)
|
if ((actuallyRead = cipherInputStream.read(block)) != 0x200)
|
||||||
throw new Exception("Read failure " + actuallyRead);
|
throw new Exception("Read failure " + actuallyRead);
|
||||||
byteBuffer.put(block);
|
byteBuffer.put(block);
|
||||||
}
|
}
|
||||||
|
@ -95,47 +95,15 @@ public class System2Provider {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean exportKernel(String saveTo) throws Exception{
|
public boolean exportKernel(String saveTo) throws Exception{
|
||||||
File location = new File(saveTo);
|
Path filePath = Paths.get(pathToFile);
|
||||||
location.mkdirs();
|
AesCtrDecryptClassic decryptor = new AesCtrDecryptClassic(header.getKey(), header.getSection0Ctr());
|
||||||
|
stream = new AesCtrClassicBufferedInputStream(decryptor,
|
||||||
|
0x200,
|
||||||
|
Files.size(filePath), // size of system2
|
||||||
|
Files.newInputStream(filePath),
|
||||||
|
Files.size(filePath));
|
||||||
|
|
||||||
InputStream fis = Files.newInputStream(Paths.get(pathToFile));
|
return export(saveTo, "Kernel.bin", 0x200, header.getSection0size());
|
||||||
// Encrypted section comes next
|
|
||||||
long toSkip = 0x200;
|
|
||||||
if (toSkip != fis.skip(toSkip))
|
|
||||||
throw new Exception("Unable to skip offset: "+toSkip);
|
|
||||||
|
|
||||||
try (CipherInputStream stream = AesCtrStream.getStream(header.getKey(), header.getSection0Ctr(), fis);
|
|
||||||
BufferedOutputStream extractedFileBOS = new BufferedOutputStream(
|
|
||||||
Files.newOutputStream(Paths.get(saveTo+File.separator+"Kernel.bin")))){
|
|
||||||
|
|
||||||
long kernelSize = header.getSection0size();
|
|
||||||
|
|
||||||
int blockSize = 0x200;
|
|
||||||
if (kernelSize < 0x200)
|
|
||||||
blockSize = (int) kernelSize;
|
|
||||||
|
|
||||||
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) > kernelSize) {
|
|
||||||
blockSize = (int) (kernelSize - i);
|
|
||||||
if (blockSize == 0)
|
|
||||||
break;
|
|
||||||
block = new byte[blockSize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e){
|
|
||||||
log.error("File export failure", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getRsa2048signature() { return rsa2048signature; }
|
public byte[] getRsa2048signature() { return rsa2048signature; }
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package libKonogonka.Tools.other.System2.ini1;
|
package libKonogonka.Tools.other.System2.ini1;
|
||||||
|
|
||||||
|
import libKonogonka.Tools.ExportAble;
|
||||||
import libKonogonka.Tools.other.System2.KernelMap;
|
import libKonogonka.Tools.other.System2.KernelMap;
|
||||||
import libKonogonka.Tools.other.System2.System2Header;
|
import libKonogonka.Tools.other.System2.System2Header;
|
||||||
import libKonogonka.ctraesclassic.AesCtrClassicBufferedInputStream;
|
import libKonogonka.ctraesclassic.AesCtrClassicBufferedInputStream;
|
||||||
|
@ -25,25 +26,19 @@ import libKonogonka.ctraesclassic.AesCtrDecryptClassic;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Ini1Provider {
|
public class Ini1Provider extends ExportAble {
|
||||||
private final static Logger log = LogManager.getLogger(Ini1Provider.class);
|
|
||||||
|
|
||||||
private final System2Header system2Header;
|
private final System2Header system2Header;
|
||||||
private final String pathToFile;
|
private final String pathToFile;
|
||||||
private final KernelMap kernelMap;
|
private final KernelMap kernelMap;
|
||||||
private Ini1Header ini1Header;
|
private Ini1Header ini1Header;
|
||||||
private List<Kip1> kip1List;
|
private List<Kip1> kip1List;
|
||||||
|
|
||||||
private AesCtrClassicBufferedInputStream stream;
|
|
||||||
|
|
||||||
public Ini1Provider(System2Header system2Header, String pathToFile, KernelMap kernelMap) throws Exception{
|
public Ini1Provider(System2Header system2Header, String pathToFile, KernelMap kernelMap) throws Exception{
|
||||||
this.system2Header = system2Header;
|
this.system2Header = system2Header;
|
||||||
this.pathToFile = pathToFile;
|
this.pathToFile = pathToFile;
|
||||||
|
@ -100,80 +95,14 @@ public class Ini1Provider {
|
||||||
|
|
||||||
public boolean exportIni1(String saveTo) throws Exception{
|
public boolean exportIni1(String saveTo) throws Exception{
|
||||||
makeStream();
|
makeStream();
|
||||||
File location = new File(saveTo);
|
return export(saveTo, "INI1.bin", 0, ini1Header.getSize());
|
||||||
location.mkdirs();
|
|
||||||
|
|
||||||
try (BufferedOutputStream extractedFileBOS = new BufferedOutputStream(
|
|
||||||
Files.newOutputStream(Paths.get(saveTo+File.separator+"INI1.bin")))){
|
|
||||||
|
|
||||||
long iniSize = ini1Header.getSize();
|
|
||||||
|
|
||||||
int blockSize = 0x200;
|
|
||||||
if (iniSize < 0x200)
|
|
||||||
blockSize = (int) iniSize;
|
|
||||||
|
|
||||||
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) > iniSize) {
|
|
||||||
blockSize = (int) (iniSize - i);
|
|
||||||
if (blockSize == 0)
|
|
||||||
break;
|
|
||||||
block = new byte[blockSize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e){
|
|
||||||
log.error("File export failure", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean exportKip1(String saveTo, Kip1 kip1) throws Exception{
|
public boolean exportKip1(String saveTo, Kip1 kip1) throws Exception{
|
||||||
makeStream();
|
makeStream();
|
||||||
long startOffset = 0x10 + kip1.getStartOffset();
|
return export(saveTo,
|
||||||
if (startOffset != stream.skip(startOffset))
|
kip1.getName()+".kip1",
|
||||||
throw new Exception("Can't seek to start position of KIP1: "+startOffset);
|
0x10 + kip1.getStartOffset(),
|
||||||
File location = new File(saveTo);
|
kip1.getEndOffset()-kip1.getStartOffset());
|
||||||
location.mkdirs();
|
|
||||||
|
|
||||||
try (BufferedOutputStream extractedFileBOS = new BufferedOutputStream(
|
|
||||||
Files.newOutputStream(Paths.get(saveTo+File.separator+kip1.getName()+".kip1")))){
|
|
||||||
|
|
||||||
long size = kip1.getEndOffset()-kip1.getStartOffset();
|
|
||||||
|
|
||||||
int blockSize = 0x200;
|
|
||||||
if (size < 0x200)
|
|
||||||
blockSize = (int) size;
|
|
||||||
|
|
||||||
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) > size) {
|
|
||||||
blockSize = (int) (size - i);
|
|
||||||
if (blockSize == 0)
|
|
||||||
break;
|
|
||||||
block = new byte[blockSize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e){
|
|
||||||
log.error("File export failure", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,55 +25,70 @@ import libKonogonka.Tools.NSO.SegmentHeader;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class Kip1 {
|
public class Kip1 {
|
||||||
private final static Logger log = LogManager.getLogger(Kip1.class);
|
private final static Logger log = LogManager.getLogger(Kip1.class);
|
||||||
|
|
||||||
private final String magic;
|
private String magic;
|
||||||
private final String name;
|
private String name;
|
||||||
private final byte[] programId;
|
private byte[] programId;
|
||||||
private final int version;
|
private int version;
|
||||||
private final byte mainThreadPriority;
|
private byte mainThreadPriority;
|
||||||
private final byte mainThreadCoreNumber;
|
private byte mainThreadCoreNumber;
|
||||||
private final byte reserved1;
|
private byte reserved1;
|
||||||
private final byte flags; // bit0=TextCompress, bit1=RoCompress, bit2=DataCompress, bit3=Is64BitInstruction, bit4=ProcessAddressSpace64Bit, bit5=[2.0.0+] UseSecureMemory
|
private byte flags;
|
||||||
private final SegmentHeader textSegmentHeader;
|
private SegmentHeader textSegmentHeader;
|
||||||
private final int threadAffinityMask;
|
private int threadAffinityMask;
|
||||||
private final SegmentHeader roDataSegmentHeader;
|
private SegmentHeader roDataSegmentHeader;
|
||||||
private final int mainThreadStackSize ;
|
private int mainThreadStackSize ;
|
||||||
private final SegmentHeader rwDataSegmentHeader;
|
private SegmentHeader rwDataSegmentHeader;
|
||||||
private final byte[] reserved2;
|
private byte[] reserved2;
|
||||||
private final SegmentHeader bssSegmentHeader;
|
private SegmentHeader bssSegmentHeader;
|
||||||
private final byte[] reserved3;
|
private byte[] reserved3;
|
||||||
private final KernelAccessControlProvider kernelCapabilityData;
|
private KernelAccessControlProvider kernelCapabilityData;
|
||||||
|
|
||||||
private final long startOffset;
|
private long startOffset;
|
||||||
private final long endOffset;
|
private long endOffset;
|
||||||
|
|
||||||
public Kip1(byte[] kip1Bytes, long kip1StartOffset) throws Exception{
|
public Kip1(String fileLocation) throws Exception{
|
||||||
this.magic = new String(kip1Bytes, 0, 0x4);
|
try (BufferedInputStream stream = new BufferedInputStream(Files.newInputStream(Paths.get(fileLocation)));) {
|
||||||
this.name = new String(kip1Bytes, 0x4, 0xC).trim();
|
byte[] kip1HeaderBytes = new byte[0x100];
|
||||||
this.programId = Arrays.copyOfRange(kip1Bytes, 0x10, 0x18);
|
if (0x100 != stream.read(kip1HeaderBytes))
|
||||||
this.version = Converter.getLEint(kip1Bytes, 0x18);
|
throw new Exception("Unable to read KIP1 file header");
|
||||||
this.mainThreadPriority = kip1Bytes[0x1c];
|
makeHeader(kip1HeaderBytes, 0);
|
||||||
this.mainThreadCoreNumber = kip1Bytes[0x1d];
|
}
|
||||||
this.reserved1 = kip1Bytes[0x1e];
|
}
|
||||||
this.flags = kip1Bytes[0x1f];
|
|
||||||
this.textSegmentHeader = new SegmentHeader(kip1Bytes, 0x20);
|
public Kip1(byte[] kip1HeaderBytes, long kip1StartOffset) throws Exception{
|
||||||
this.threadAffinityMask = Converter.getLEint(kip1Bytes, 0x2c);
|
makeHeader(kip1HeaderBytes, kip1StartOffset);
|
||||||
this.roDataSegmentHeader = new SegmentHeader(kip1Bytes, 0x30);
|
}
|
||||||
this.mainThreadStackSize = Converter.getLEint(kip1Bytes, 0x3c);
|
|
||||||
this.rwDataSegmentHeader = new SegmentHeader(kip1Bytes, 0x40);
|
private void makeHeader(byte[] kip1HeaderBytes, long kip1StartOffset) throws Exception{
|
||||||
this.reserved2 = Arrays.copyOfRange(kip1Bytes, 0x4c, 0x50);
|
this.magic = new String(kip1HeaderBytes, 0, 0x4);
|
||||||
this.bssSegmentHeader = new SegmentHeader(kip1Bytes, 0x50);
|
this.name = new String(kip1HeaderBytes, 0x4, 0xC).trim();
|
||||||
this.reserved3 = Arrays.copyOfRange(kip1Bytes, 0x5c, 0x80);
|
this.programId = Arrays.copyOfRange(kip1HeaderBytes, 0x10, 0x18);
|
||||||
this.kernelCapabilityData = new KernelAccessControlProvider(Arrays.copyOfRange(kip1Bytes, 0x80, 0x100));
|
this.version = Converter.getLEint(kip1HeaderBytes, 0x18);
|
||||||
|
this.mainThreadPriority = kip1HeaderBytes[0x1c];
|
||||||
|
this.mainThreadCoreNumber = kip1HeaderBytes[0x1d];
|
||||||
|
this.reserved1 = kip1HeaderBytes[0x1e];
|
||||||
|
this.flags = kip1HeaderBytes[0x1f];
|
||||||
|
this.textSegmentHeader = new SegmentHeader(kip1HeaderBytes, 0x20);
|
||||||
|
this.threadAffinityMask = Converter.getLEint(kip1HeaderBytes, 0x2c);
|
||||||
|
this.roDataSegmentHeader = new SegmentHeader(kip1HeaderBytes, 0x30);
|
||||||
|
this.mainThreadStackSize = Converter.getLEint(kip1HeaderBytes, 0x3c);
|
||||||
|
this.rwDataSegmentHeader = new SegmentHeader(kip1HeaderBytes, 0x40);
|
||||||
|
this.reserved2 = Arrays.copyOfRange(kip1HeaderBytes, 0x4c, 0x50);
|
||||||
|
this.bssSegmentHeader = new SegmentHeader(kip1HeaderBytes, 0x50);
|
||||||
|
this.reserved3 = Arrays.copyOfRange(kip1HeaderBytes, 0x5c, 0x80);
|
||||||
|
this.kernelCapabilityData = new KernelAccessControlProvider(Arrays.copyOfRange(kip1HeaderBytes, 0x80, 0x100));
|
||||||
|
|
||||||
this.startOffset = kip1StartOffset;
|
this.startOffset = kip1StartOffset;
|
||||||
this.endOffset = 0x100 + kip1StartOffset + textSegmentHeader.getSizeAsDecompressed() + roDataSegmentHeader.getSizeAsDecompressed() +
|
this.endOffset = 0x100 + kip1StartOffset + textSegmentHeader.getSizeAsDecompressed() + roDataSegmentHeader.getSizeAsDecompressed() +
|
||||||
rwDataSegmentHeader.getSizeAsDecompressed() + bssSegmentHeader.getSizeAsDecompressed();
|
rwDataSegmentHeader.getSizeAsDecompressed() + bssSegmentHeader.getSizeAsDecompressed();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMagic() { return magic; }
|
public String getMagic() { return magic; }
|
||||||
|
@ -134,7 +149,13 @@ public class Kip1 {
|
||||||
"Main thread priority : " + String.format("0x%x", mainThreadPriority) + "\n" +
|
"Main thread priority : " + String.format("0x%x", mainThreadPriority) + "\n" +
|
||||||
"Main thread core number : " + String.format("0x%x", mainThreadCoreNumber) + "\n" +
|
"Main thread core number : " + String.format("0x%x", mainThreadCoreNumber) + "\n" +
|
||||||
"Reserved 1 : " + String.format("0x%x", reserved1) + "\n" +
|
"Reserved 1 : " + String.format("0x%x", reserved1) + "\n" +
|
||||||
"Flags : " + flags + "\n" +
|
"Flags : " + Converter.intToBinaryString(flags) + "\n" +
|
||||||
|
" 0| .text compress : " + ((flags & 1) == 1 ? "YES" : "NO") + "\n" +
|
||||||
|
" 1| .ro compress : " + ((flags >> 1 & 1) == 1 ? "YES" : "NO") + "\n" +
|
||||||
|
" 2| .rw compress : " + ((flags >> 2 & 1) == 1 ? "YES" : "NO") + "\n" +
|
||||||
|
" 3| Is 64-bit instruction : " + ((flags >> 3 & 1) == 1 ? "YES" : "NO") + "\n" +
|
||||||
|
" 4| Process addr. space 64-bit : " + ((flags >> 4 & 1) == 1 ? "YES" : "NO") + "\n" +
|
||||||
|
" 5| Use secure memory : " + ((flags >> 5 & 1) == 1 ? "YES" : "NO") + "\n" +
|
||||||
".text segment header\n" +
|
".text segment header\n" +
|
||||||
" Segment offset : " + RainbowDump.formatDecHexString(textSegmentHeader.getSegmentOffset()) + "\n" +
|
" Segment offset : " + RainbowDump.formatDecHexString(textSegmentHeader.getSegmentOffset()) + "\n" +
|
||||||
" Memory offset : " + RainbowDump.formatDecHexString(textSegmentHeader.getMemoryOffset()) + "\n" +
|
" Memory offset : " + RainbowDump.formatDecHexString(textSegmentHeader.getMemoryOffset()) + "\n" +
|
||||||
|
@ -145,7 +166,7 @@ public class Kip1 {
|
||||||
" Memory offset : " + RainbowDump.formatDecHexString(roDataSegmentHeader.getMemoryOffset()) + "\n" +
|
" Memory offset : " + RainbowDump.formatDecHexString(roDataSegmentHeader.getMemoryOffset()) + "\n" +
|
||||||
" Size : " + RainbowDump.formatDecHexString(roDataSegmentHeader.getSizeAsDecompressed()) + "\n" +
|
" Size : " + RainbowDump.formatDecHexString(roDataSegmentHeader.getSizeAsDecompressed()) + "\n" +
|
||||||
"Main thread stack size : " + RainbowDump.formatDecHexString(mainThreadStackSize) + "\n" +
|
"Main thread stack size : " + RainbowDump.formatDecHexString(mainThreadStackSize) + "\n" +
|
||||||
".data segment header\n" +
|
".rw segment header\n" +
|
||||||
" Segment offset : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getSegmentOffset()) + "\n" +
|
" Segment offset : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getSegmentOffset()) + "\n" +
|
||||||
" Memory offset : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getMemoryOffset()) + "\n" +
|
" Memory offset : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getMemoryOffset()) + "\n" +
|
||||||
" Size : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getSizeAsDecompressed()) + "\n" +
|
" Size : " + RainbowDump.formatDecHexString(rwDataSegmentHeader.getSizeAsDecompressed()) + "\n" +
|
||||||
|
|
|
@ -103,7 +103,6 @@ public class Package2UnpackedTest {
|
||||||
ini1Provider.getIni1Header().printDebug();
|
ini1Provider.getIni1Header().printDebug();
|
||||||
for (Kip1 kip1 : ini1Provider.getKip1List())
|
for (Kip1 kip1 : ini1Provider.getKip1List())
|
||||||
kip1.printDebug();
|
kip1.printDebug();
|
||||||
|
|
||||||
boolean exported = provider.exportKernel("/home/loper/Projects/libKonogonka/FilesForTests/own/");
|
boolean exported = provider.exportKernel("/home/loper/Projects/libKonogonka/FilesForTests/own/");
|
||||||
System.out.println("Exported = "+exported);
|
System.out.println("Exported = "+exported);
|
||||||
|
|
||||||
|
@ -115,4 +114,10 @@ public class Package2UnpackedTest {
|
||||||
System.out.println("Exported KIP1s "+ kip1.getName() +" = " + exported + String.format(" Size 0x%x", Files.size(Paths.get("/home/loper/Projects/libKonogonka/FilesForTests/own/KIP1s/"+kip1.getName()+".kip1"))));
|
System.out.println("Exported KIP1s "+ kip1.getName() +" = " + exported + String.format(" Size 0x%x", Files.size(Paths.get("/home/loper/Projects/libKonogonka/FilesForTests/own/KIP1s/"+kip1.getName()+".kip1"))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@DisplayName("KIP1 read reference")
|
||||||
|
@Test
|
||||||
|
void checkReference() throws Exception{
|
||||||
|
Kip1 kip1 = new Kip1("/home/loper/Projects/libKonogonka/FilesForTests/");
|
||||||
|
kip1.printDebug();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue