Ass stream support into System2 related classes.
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
045d195f91
commit
7add08c196
11 changed files with 350 additions and 129 deletions
|
@ -4,9 +4,8 @@ import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
public interface IProducer {
|
public interface IProducer {
|
||||||
|
|
||||||
BufferedInputStream produce() throws Exception;
|
BufferedInputStream produce() throws Exception;
|
||||||
IProducer getSuccessor(long subOffset);
|
IProducer getSuccessor(long subOffset) throws Exception;
|
||||||
boolean isEncrypted();
|
boolean isEncrypted();
|
||||||
File getFile();
|
File getFile();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
// NOTE: Probably it would be better to make it singleton which creates along with 'library initialization'
|
||||||
|
|
||||||
public class KeyChainHolder {
|
public class KeyChainHolder {
|
||||||
|
|
||||||
|
@ -72,31 +73,11 @@ public class KeyChainHolder {
|
||||||
return tempKeySet;
|
return tempKeySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getXci_header_key() {
|
public String getXci_header_key() { return xci_header_key; }
|
||||||
return xci_header_key;
|
public String getHeader_key() { return rawKeySet.get("header_key"); }
|
||||||
}
|
public HashMap<String, String> getRawKeySet() { return rawKeySet; }
|
||||||
|
public HashMap<String, String> getKey_area_key_application() { return key_area_key_application; }
|
||||||
public String getHeader_key() {
|
public HashMap<String, String> getKey_area_key_ocean() { return key_area_key_ocean; }
|
||||||
return rawKeySet.get("header_key");
|
public HashMap<String, String> getKey_area_key_system() { return key_area_key_system; }
|
||||||
}
|
public HashMap<String, String> getTitlekek() { return titlekek; }
|
||||||
|
|
||||||
public HashMap<String, String> getRawKeySet() {
|
|
||||||
return rawKeySet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashMap<String, String> getKey_area_key_application() {
|
|
||||||
return key_area_key_application;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashMap<String, String> getKey_area_key_ocean() {
|
|
||||||
return key_area_key_ocean;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashMap<String, String> getKey_area_key_system() {
|
|
||||||
return key_area_key_system;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashMap<String, String> getTitlekek() {
|
|
||||||
return titlekek;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ public class RomFsProvider extends ExportAble {
|
||||||
exportSingleFile(entry, saveToLocation);
|
exportSingleFile(entry, saveToLocation);
|
||||||
}
|
}
|
||||||
catch (Exception e){
|
catch (Exception e){
|
||||||
log.error("File export failure", e);
|
log.error(getFile().getName()+" export failure ", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -98,10 +98,15 @@ public class RomFsProvider extends ExportAble {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exportSingleFile(FileSystemEntry entry, String saveToLocation) throws Exception {
|
private void exportSingleFile(FileSystemEntry entry, String saveToLocation) throws Exception {
|
||||||
|
try {
|
||||||
stream = producer.produce();
|
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());
|
export(saveToLocation, entry.getName(), skipBytes, entry.getSize());
|
||||||
}
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
throw new Exception(entry.getName()+": "+e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public InFileStreamProducer getStreamProducer(FileSystemEntry entry) throws Exception{
|
public InFileStreamProducer getStreamProducer(FileSystemEntry entry) throws Exception{
|
||||||
if (entry.isDirectory())
|
if (entry.isDirectory())
|
||||||
|
|
|
@ -115,6 +115,9 @@ public class System2Header {
|
||||||
sha256overEncryptedSection1 = Arrays.copyOfRange(decodedHeaderBytes, 0xa0, 0xc0);
|
sha256overEncryptedSection1 = Arrays.copyOfRange(decodedHeaderBytes, 0xa0, 0xc0);
|
||||||
sha256overEncryptedSection2 = Arrays.copyOfRange(decodedHeaderBytes, 0xc0, 0xe0);
|
sha256overEncryptedSection2 = Arrays.copyOfRange(decodedHeaderBytes, 0xc0, 0xe0);
|
||||||
sha256overEncryptedSection3 = Arrays.copyOfRange(decodedHeaderBytes, 0xe0, 0x100);
|
sha256overEncryptedSection3 = Arrays.copyOfRange(decodedHeaderBytes, 0xe0, 0x100);
|
||||||
|
|
||||||
|
if (packageSize != 0x200 + section0size)
|
||||||
|
log.error("'Package size' doesn't match 'Header Size' + 'Section 0 size'!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getHeaderCtr() { return headerCtr; }
|
public byte[] getHeaderCtr() { return headerCtr; }
|
||||||
|
|
|
@ -21,11 +21,9 @@ package libKonogonka.Tools.other.System2;
|
||||||
import libKonogonka.KeyChainHolder;
|
import libKonogonka.KeyChainHolder;
|
||||||
import libKonogonka.Tools.ExportAble;
|
import libKonogonka.Tools.ExportAble;
|
||||||
import libKonogonka.Tools.other.System2.ini1.Ini1Provider;
|
import libKonogonka.Tools.other.System2.ini1.Ini1Provider;
|
||||||
import libKonogonka.ctraesclassic.AesCtrClassicBufferedInputStream;
|
import libKonogonka.ctraes.InFileStreamProducer;
|
||||||
import libKonogonka.ctraesclassic.AesCtrDecryptClassic;
|
import libKonogonka.ctraesclassic.InFileStreamClassicProducer;
|
||||||
import libKonogonka.ctraesclassic.AesCtrStream;
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -39,16 +37,30 @@ public class System2Provider extends ExportAble {
|
||||||
private KernelMap kernelMap;
|
private KernelMap kernelMap;
|
||||||
private Ini1Provider ini1Provider;
|
private Ini1Provider ini1Provider;
|
||||||
|
|
||||||
private final String pathToFile;
|
|
||||||
private final KeyChainHolder keyChainHolder;
|
private final KeyChainHolder keyChainHolder;
|
||||||
|
private InFileStreamClassicProducer producer;
|
||||||
|
|
||||||
public System2Provider(String pathToFile, KeyChainHolder keyChainHolder) throws Exception{
|
public System2Provider(String pathToFile, KeyChainHolder keyChainHolder) throws Exception{
|
||||||
this.pathToFile = pathToFile;
|
|
||||||
this.keyChainHolder = keyChainHolder;
|
this.keyChainHolder = keyChainHolder;
|
||||||
|
|
||||||
this.stream = new BufferedInputStream(Files.newInputStream(Paths.get(pathToFile)));
|
Path filePath = Paths.get(pathToFile);
|
||||||
|
this.stream = new BufferedInputStream(Files.newInputStream(filePath));
|
||||||
readSignatures();
|
readSignatures();
|
||||||
readHeader();
|
readHeader();
|
||||||
|
this.stream.close();
|
||||||
|
createProducerOfFile(filePath);
|
||||||
|
findIni1KernelMap();
|
||||||
|
this.stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public System2Provider(InFileStreamProducer producer, KeyChainHolder keyChainHolder) throws Exception{
|
||||||
|
this.keyChainHolder = keyChainHolder;
|
||||||
|
|
||||||
|
this.stream = producer.produce();
|
||||||
|
readSignatures();
|
||||||
|
readHeader();
|
||||||
|
this.stream.close();
|
||||||
|
createProducerOfStream(producer);
|
||||||
findIni1KernelMap();
|
findIni1KernelMap();
|
||||||
this.stream.close();
|
this.stream.close();
|
||||||
}
|
}
|
||||||
|
@ -66,23 +78,40 @@ public class System2Provider extends ExportAble {
|
||||||
this.header = new System2Header(headerBytes, keyChainHolder.getRawKeySet());
|
this.header = new System2Header(headerBytes, keyChainHolder.getRawKeySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createProducerOfFile(Path filePath) throws Exception{
|
||||||
|
this.producer = new InFileStreamClassicProducer(filePath,
|
||||||
|
0,
|
||||||
|
0x200,
|
||||||
|
header.getPackageSize(),
|
||||||
|
header.getKey(),
|
||||||
|
header.getSection0Ctr());
|
||||||
|
this.stream = producer.produce();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createProducerOfStream(InFileStreamProducer parentProducer) throws Exception{
|
||||||
|
producer = new InFileStreamClassicProducer(parentProducer,
|
||||||
|
0,
|
||||||
|
0x200,
|
||||||
|
header.getPackageSize(),
|
||||||
|
header.getKey(),
|
||||||
|
header.getSection0Ctr(),
|
||||||
|
header.getPackageSize());
|
||||||
|
this.stream = producer.produce();
|
||||||
|
}
|
||||||
|
|
||||||
private void findIni1KernelMap() throws Exception{
|
private void findIni1KernelMap() throws Exception{
|
||||||
try (InputStream fis = Files.newInputStream(Paths.get(pathToFile))){
|
if (0x200 != stream.skip(0x200))
|
||||||
// Encrypted section comes next
|
throw new Exception("Unable to skip offset of 0x200");
|
||||||
long toSkip = 0x200;
|
|
||||||
if (toSkip != fis.skip(toSkip))
|
|
||||||
throw new Exception("Unable to skip offset: " + toSkip);
|
|
||||||
|
|
||||||
ByteBuffer byteBuffer = ByteBuffer.allocate(0x1000);
|
ByteBuffer byteBuffer = ByteBuffer.allocate(0x1000);
|
||||||
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 = cipherInputStream.read(block)) != 0x200)
|
if ((actuallyRead = stream.read(block)) != 0x200)
|
||||||
throw new Exception("Read failure " + actuallyRead);
|
throw new Exception("Read failure " + actuallyRead);
|
||||||
byteBuffer.put(block);
|
byteBuffer.put(block);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
byte[] searchField = byteBuffer.array();
|
byte[] searchField = byteBuffer.array();
|
||||||
for (int i = 0; i < 1024; i += 4) {
|
for (int i = 0; i < 1024; i += 4) {
|
||||||
|
@ -92,17 +121,9 @@ public class System2Provider extends ExportAble {
|
||||||
}
|
}
|
||||||
throw new Exception("Kernel map not found");
|
throw new Exception("Kernel map not found");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public boolean exportKernel(String saveTo) throws Exception{
|
public boolean exportKernel(String saveTo) throws Exception{
|
||||||
Path filePath = Paths.get(pathToFile);
|
stream = producer.produce();
|
||||||
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));
|
|
||||||
|
|
||||||
return export(saveTo, "Kernel.bin", 0x200, header.getSection0size());
|
return export(saveTo, "Kernel.bin", 0x200, header.getSection0size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +132,8 @@ public class System2Provider extends ExportAble {
|
||||||
public KernelMap getKernelMap() { return kernelMap; }
|
public KernelMap getKernelMap() { return kernelMap; }
|
||||||
public Ini1Provider getIni1Provider() throws Exception{
|
public Ini1Provider getIni1Provider() throws Exception{
|
||||||
if (ini1Provider == null)
|
if (ini1Provider == null)
|
||||||
ini1Provider = new Ini1Provider(header, pathToFile, kernelMap);
|
ini1Provider = new Ini1Provider(
|
||||||
|
producer.getSuccessor(0x200 + kernelMap.getIni1Offset(), true));
|
||||||
return ini1Provider;
|
return ini1Provider;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
package libKonogonka.Tools.other.System2.ini1;
|
package libKonogonka.Tools.other.System2.ini1;
|
||||||
|
|
||||||
import libKonogonka.Tools.ExportAble;
|
import libKonogonka.Tools.ExportAble;
|
||||||
import libKonogonka.Tools.other.System2.KernelMap;
|
|
||||||
import libKonogonka.Tools.other.System2.System2Header;
|
import libKonogonka.Tools.other.System2.System2Header;
|
||||||
import libKonogonka.ctraesclassic.InFileStreamClassicProducer;
|
import libKonogonka.ctraesclassic.InFileStreamClassicProducer;
|
||||||
|
|
||||||
|
@ -35,17 +34,26 @@ public class Ini1Provider extends ExportAble {
|
||||||
|
|
||||||
private final InFileStreamClassicProducer producer;
|
private final InFileStreamClassicProducer producer;
|
||||||
|
|
||||||
public Ini1Provider(System2Header system2Header, String pathToFile, KernelMap kernelMap) throws Exception{
|
public Ini1Provider(InFileStreamClassicProducer producer) throws Exception{
|
||||||
|
this.producer = producer;
|
||||||
|
this.stream = producer.produce();
|
||||||
|
makeHeader();
|
||||||
|
collectKips();
|
||||||
|
this.stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ini1Provider(System2Header system2Header, String pathToFile, int kernelMapIni1Offset) throws Exception{
|
||||||
Path filePath = Paths.get(pathToFile);
|
Path filePath = Paths.get(pathToFile);
|
||||||
this.producer = new InFileStreamClassicProducer(filePath,
|
this.producer = new InFileStreamClassicProducer(filePath,
|
||||||
0x200 + kernelMap.getIni1Offset(),
|
0x200 + kernelMapIni1Offset,
|
||||||
0x200,
|
0x200,
|
||||||
Files.size(filePath), // size of system2
|
Files.size(filePath), // size of system2
|
||||||
system2Header.getKey(),
|
system2Header.getKey(),
|
||||||
system2Header.getSection0Ctr());
|
system2Header.getSection0Ctr());
|
||||||
stream = producer.produce();
|
this.stream = producer.produce();
|
||||||
makeHeader();
|
makeHeader();
|
||||||
collectKips();
|
collectKips();
|
||||||
|
this.stream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void makeHeader() throws Exception{
|
private void makeHeader() throws Exception{
|
||||||
|
|
|
@ -54,23 +54,25 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
|
||||||
private int pointerInsideDecryptedSection;
|
private int pointerInsideDecryptedSection;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized int read(byte[] b) throws IOException{
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
int bytesToRead = b.length;
|
if (off != 0 || len != b.length)
|
||||||
|
throw new IOException("Not supported");
|
||||||
|
|
||||||
if (isPointerInsideEncryptedSection()){
|
if (isPointerInsideEncryptedSection()){
|
||||||
int bytesFromFirstBlock = 0x200 - pointerInsideDecryptedSection;
|
int bytesFromFirstBlock = 0x200 - pointerInsideDecryptedSection;
|
||||||
if (bytesFromFirstBlock > bytesToRead){
|
if (bytesFromFirstBlock > len){
|
||||||
log.trace("1.2. Pointer Inside + End Position Inside (Decrypted) Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
|
log.trace("1.2. Pointer Inside + End Position Inside (Decrypted) Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
|
||||||
System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesToRead);
|
System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, len);
|
||||||
|
|
||||||
pseudoPos += bytesToRead;
|
pseudoPos += len;
|
||||||
pointerInsideDecryptedSection += bytesToRead;
|
pointerInsideDecryptedSection += len;
|
||||||
return b.length;
|
return b.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEndPositionInsideEncryptedSection(b.length)) {
|
if (isEndPositionInsideEncryptedSection(b.length)) {
|
||||||
log.trace("1.1. Pointer Inside + End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
|
log.trace("1.1. Pointer Inside + End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
|
||||||
int middleBlocksCount = (bytesToRead - bytesFromFirstBlock) / 0x200;
|
int middleBlocksCount = (len - bytesFromFirstBlock) / 0x200;
|
||||||
int bytesFromLastBlock = (bytesToRead - bytesFromFirstBlock) % 0x200;
|
int bytesFromLastBlock = (len - bytesFromFirstBlock) % 0x200;
|
||||||
//1
|
//1
|
||||||
System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock);
|
System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock);
|
||||||
//2
|
//2
|
||||||
|
@ -79,17 +81,17 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
|
||||||
System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200);
|
System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200);
|
||||||
}
|
}
|
||||||
//3
|
//3
|
||||||
if(fileSize > (pseudoPos+bytesToRead)) {
|
if(fileSize > (pseudoPos+ len)) {
|
||||||
fillDecryptedCache();
|
fillDecryptedCache();
|
||||||
System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock + middleBlocksCount * 0x200, bytesFromLastBlock);
|
System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock + middleBlocksCount * 0x200, bytesFromLastBlock);
|
||||||
}
|
}
|
||||||
pseudoPos += bytesToRead;
|
pseudoPos += len;
|
||||||
pointerInsideDecryptedSection = bytesFromLastBlock;
|
pointerInsideDecryptedSection = bytesFromLastBlock;
|
||||||
return b.length;
|
return b.length;
|
||||||
}
|
}
|
||||||
log.trace("1. Pointer Inside + End Position Outside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
|
log.trace("1. Pointer Inside + End Position Outside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
|
||||||
int middleBlocksCount = (int) ((mediaOffsetPositionEnd - (pseudoPos+bytesFromFirstBlock)) / 0x200);
|
int middleBlocksCount = (int) ((mediaOffsetPositionEnd - (pseudoPos+bytesFromFirstBlock)) / 0x200);
|
||||||
int bytesFromEnd = bytesToRead - bytesFromFirstBlock - middleBlocksCount * 0x200;
|
int bytesFromEnd = len - bytesFromFirstBlock - middleBlocksCount * 0x200;
|
||||||
//1
|
//1
|
||||||
System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock);
|
System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock);
|
||||||
//2
|
//2
|
||||||
|
@ -100,15 +102,15 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
|
||||||
}
|
}
|
||||||
//3 // TODO: if it's zero?
|
//3 // TODO: if it's zero?
|
||||||
System.arraycopy(readChunk(bytesFromEnd), 0, b, bytesFromFirstBlock+middleBlocksCount*0x200, bytesFromEnd);
|
System.arraycopy(readChunk(bytesFromEnd), 0, b, bytesFromFirstBlock+middleBlocksCount*0x200, bytesFromEnd);
|
||||||
pseudoPos += bytesToRead;
|
pseudoPos += len;
|
||||||
pointerInsideDecryptedSection = 0;
|
pointerInsideDecryptedSection = 0;
|
||||||
return b.length;
|
return b.length;
|
||||||
}
|
}
|
||||||
if (isEndPositionInsideEncryptedSection(bytesToRead)) {
|
if (isEndPositionInsideEncryptedSection(len)) {
|
||||||
log.trace("2. End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
|
log.trace("2. End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
|
||||||
int bytesTillEncrypted = (int) (mediaOffsetPositionStart - pseudoPos);
|
int bytesTillEncrypted = (int) (mediaOffsetPositionStart - pseudoPos);
|
||||||
int fullEncryptedBlocks = (bytesToRead - bytesTillEncrypted) / 0x200;
|
int fullEncryptedBlocks = (len - bytesTillEncrypted) / 0x200;
|
||||||
int incompleteEncryptedBytes = (bytesToRead - bytesTillEncrypted) % 0x200;
|
int incompleteEncryptedBytes = (len - bytesTillEncrypted) % 0x200;
|
||||||
System.arraycopy(readChunk(bytesTillEncrypted), 0, b, 0, bytesTillEncrypted);
|
System.arraycopy(readChunk(bytesTillEncrypted), 0, b, 0, bytesTillEncrypted);
|
||||||
//2
|
//2
|
||||||
for (int i = 0; i < fullEncryptedBlocks; i++) {
|
for (int i = 0; i < fullEncryptedBlocks; i++) {
|
||||||
|
@ -118,12 +120,12 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
|
||||||
//3
|
//3
|
||||||
fillDecryptedCache();
|
fillDecryptedCache();
|
||||||
System.arraycopy(decryptedBytes, 0, b, bytesTillEncrypted+fullEncryptedBlocks*0x200, incompleteEncryptedBytes);
|
System.arraycopy(decryptedBytes, 0, b, bytesTillEncrypted+fullEncryptedBlocks*0x200, incompleteEncryptedBytes);
|
||||||
pseudoPos += bytesToRead;
|
pseudoPos += len;
|
||||||
pointerInsideDecryptedSection = incompleteEncryptedBytes;
|
pointerInsideDecryptedSection = incompleteEncryptedBytes;
|
||||||
return b.length;
|
return b.length;
|
||||||
}
|
}
|
||||||
log.trace("3. Not encrypted ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
|
log.trace("3. Not encrypted ("+pseudoPos+"-"+(pseudoPos+b.length)+")");
|
||||||
pseudoPos += bytesToRead;
|
pseudoPos += len;
|
||||||
pointerInsideDecryptedSection = 0;
|
pointerInsideDecryptedSection = 0;
|
||||||
return super.read(b);
|
return super.read(b);
|
||||||
}
|
}
|
||||||
|
@ -146,10 +148,9 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
|
||||||
}
|
}
|
||||||
private byte[] readChunk(int bytes) throws IOException{
|
private byte[] readChunk(int bytes) throws IOException{
|
||||||
byte[] chunkBytes = new byte[bytes];
|
byte[] chunkBytes = new byte[bytes];
|
||||||
long actuallyRead = super.read(chunkBytes);
|
long actuallyRead = super.read(chunkBytes, 0, bytes);
|
||||||
if (actuallyRead != bytes)
|
if (actuallyRead != bytes)
|
||||||
throw new IOException("Can't read. Need block of "+ bytes +" while only " +
|
throw new IOException("Can't read: " + actuallyRead + "/"+ bytes);
|
||||||
actuallyRead + " bytes.");
|
|
||||||
return chunkBytes;
|
return chunkBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,8 +138,7 @@ public class AesCtrClassicBufferedInputStream extends BufferedInputStream {
|
||||||
byte[] chunkBytes = new byte[bytes];
|
byte[] chunkBytes = new byte[bytes];
|
||||||
long actuallyRead = super.read(chunkBytes);
|
long actuallyRead = super.read(chunkBytes);
|
||||||
if (actuallyRead != bytes)
|
if (actuallyRead != bytes)
|
||||||
throw new IOException("Can't read. Need block of "+ bytes +" while only " +
|
throw new IOException("Can't read. "+ bytes +"/" + actuallyRead);
|
||||||
actuallyRead + " bytes.");
|
|
||||||
return chunkBytes;
|
return chunkBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class AesCtrStream {
|
public class AesCtrStream {
|
||||||
private static boolean BCinitialized = false;
|
private static boolean BCinitialized = false;
|
||||||
|
|
||||||
|
|
|
@ -19,21 +19,36 @@
|
||||||
package libKonogonka.ctraesclassic;
|
package libKonogonka.ctraesclassic;
|
||||||
|
|
||||||
import libKonogonka.IProducer;
|
import libKonogonka.IProducer;
|
||||||
|
import libKonogonka.RainbowDump;
|
||||||
|
import libKonogonka.ctraes.InFileStreamProducer;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
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;
|
||||||
|
|
||||||
public class InFileStreamClassicProducer implements IProducer {
|
public class InFileStreamClassicProducer implements IProducer {
|
||||||
private boolean encrypted;
|
private boolean encrypted;
|
||||||
|
|
||||||
private final Path filePath;
|
private Path filePath;
|
||||||
|
private InFileStreamProducer parentProducer;
|
||||||
private long offset;
|
private long offset;
|
||||||
private long encryptedStartOffset;
|
private long encryptedStartOffset;
|
||||||
private long encryptedEndOffset;
|
private long encryptedEndOffset;
|
||||||
private AesCtrDecryptClassic decryptor;
|
private AesCtrDecryptClassic decryptor;
|
||||||
|
private final long fileSize;
|
||||||
|
|
||||||
|
/** Reference AES-CTR stream producer.
|
||||||
|
* @param filePath Path to file-container
|
||||||
|
* @param offset Offset to skip (since file beginning).
|
||||||
|
* @param encryptedStartOffset Offset since file beginning where encrypted section starts
|
||||||
|
* @param encryptedEndOffset Offset since file beginning where encrypted section ends
|
||||||
|
* @param key AES-CTR Key
|
||||||
|
* @param iv CTR / IV (counter)
|
||||||
|
*/
|
||||||
public InFileStreamClassicProducer(Path filePath,
|
public InFileStreamClassicProducer(Path filePath,
|
||||||
long offset,
|
long offset,
|
||||||
long encryptedStartOffset,
|
long encryptedStartOffset,
|
||||||
|
@ -46,43 +61,94 @@ public class InFileStreamClassicProducer implements IProducer {
|
||||||
this.encryptedStartOffset = encryptedStartOffset;
|
this.encryptedStartOffset = encryptedStartOffset;
|
||||||
this.encryptedEndOffset = encryptedEndOffset;
|
this.encryptedEndOffset = encryptedEndOffset;
|
||||||
this.decryptor = new AesCtrDecryptClassic(key, iv);
|
this.decryptor = new AesCtrDecryptClassic(key, iv);
|
||||||
|
this.fileSize = Files.size(filePath);
|
||||||
}
|
}
|
||||||
public InFileStreamClassicProducer(Path filePath,
|
private InFileStreamClassicProducer(Path filePath,
|
||||||
long offset,
|
long offset,
|
||||||
long encryptedStartOffset,
|
long encryptedStartOffset,
|
||||||
long encryptedEndOffset, //Files.size(filePath)
|
long encryptedEndOffset, //Files.size(filePath)
|
||||||
AesCtrDecryptClassic decryptor){
|
AesCtrDecryptClassic decryptor) throws Exception{
|
||||||
this.encrypted = true;
|
this.encrypted = true;
|
||||||
this.filePath = filePath;
|
this.filePath = filePath;
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
this.encryptedStartOffset = encryptedStartOffset;
|
this.encryptedStartOffset = encryptedStartOffset;
|
||||||
this.encryptedEndOffset = encryptedEndOffset;
|
this.encryptedEndOffset = encryptedEndOffset;
|
||||||
this.decryptor = decryptor;
|
this.decryptor = decryptor;
|
||||||
|
this.fileSize = Files.size(filePath);
|
||||||
}
|
}
|
||||||
|
/** Stream producer for non-encrypted files.
|
||||||
public InFileStreamClassicProducer(Path filePath){
|
* @param filePath Path to file-container
|
||||||
|
* */
|
||||||
|
public InFileStreamClassicProducer(Path filePath) throws Exception{
|
||||||
this.filePath = filePath;
|
this.filePath = filePath;
|
||||||
|
this.fileSize = Files.size(filePath);
|
||||||
}
|
}
|
||||||
public InFileStreamClassicProducer(Path filePath, long offset){
|
/** Stream producer for non-encrypted files.
|
||||||
|
* @param filePath Path to file-container
|
||||||
|
* @param offset Offset to skip (since file beginning).
|
||||||
|
* */
|
||||||
|
public InFileStreamClassicProducer(Path filePath, long offset) throws Exception{
|
||||||
this.filePath = filePath;
|
this.filePath = filePath;
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
|
this.fileSize = Files.size(filePath);
|
||||||
|
}
|
||||||
|
/** Reference AES-CTR stream producer that utilizes InFileStreamProducer instead of file.
|
||||||
|
* @param parentProducer Producer of the stream
|
||||||
|
* @param offset Offset to skip at parent stream
|
||||||
|
* @param encryptedStartOffset Offset since parent stream start at stream where encrypted section starts
|
||||||
|
* @param encryptedEndOffset Offset since parent stream start at stream where encrypted section ends
|
||||||
|
* @param key AES-CTR Key
|
||||||
|
* @param iv CTR / IV (counter)
|
||||||
|
*/
|
||||||
|
public InFileStreamClassicProducer(InFileStreamProducer parentProducer,
|
||||||
|
long offset,
|
||||||
|
long encryptedStartOffset,
|
||||||
|
long encryptedEndOffset,
|
||||||
|
String key,
|
||||||
|
byte[] iv,
|
||||||
|
long fileSize) throws Exception{
|
||||||
|
this.parentProducer = parentProducer;
|
||||||
|
this.encrypted = true;
|
||||||
|
this.offset = offset;
|
||||||
|
this.encryptedStartOffset = encryptedStartOffset;
|
||||||
|
this.encryptedEndOffset = encryptedEndOffset;
|
||||||
|
this.decryptor = new AesCtrDecryptClassic(key, iv);
|
||||||
|
this.fileSize = fileSize;
|
||||||
|
}
|
||||||
|
private InFileStreamClassicProducer(InFileStreamProducer parentProducer,
|
||||||
|
long offset,
|
||||||
|
long encryptedStartOffset,
|
||||||
|
long encryptedEndOffset,
|
||||||
|
AesCtrDecryptClassic decryptor,
|
||||||
|
long fileSize){
|
||||||
|
this.parentProducer = parentProducer;
|
||||||
|
this.encrypted = true;
|
||||||
|
this.offset = offset;
|
||||||
|
this.encryptedStartOffset = encryptedStartOffset;
|
||||||
|
this.encryptedEndOffset = encryptedEndOffset;
|
||||||
|
this.decryptor = decryptor;
|
||||||
|
this.fileSize = fileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BufferedInputStream produce() throws Exception{
|
public BufferedInputStream produce() throws Exception{
|
||||||
if (encrypted)
|
if (encrypted)
|
||||||
return produceAesCtr();
|
return produceAesCtr();
|
||||||
else
|
|
||||||
return produceNotEncrypted();
|
return produceNotEncrypted();
|
||||||
}
|
}
|
||||||
|
|
||||||
private BufferedInputStream produceAesCtr() throws Exception{
|
private BufferedInputStream produceAesCtr() throws Exception{
|
||||||
decryptor.reset();
|
decryptor.reset();
|
||||||
AesCtrClassicBufferedInputStream stream = new AesCtrClassicBufferedInputStream(decryptor,
|
|
||||||
encryptedStartOffset,
|
InputStream is;
|
||||||
encryptedEndOffset,
|
|
||||||
Files.newInputStream(filePath),
|
if (filePath == null)
|
||||||
Files.size(filePath));
|
is = parentProducer.produce();
|
||||||
|
else
|
||||||
|
is = Files.newInputStream(filePath);
|
||||||
|
|
||||||
|
AesCtrClassicBufferedInputStream stream = new AesCtrClassicBufferedInputStream(
|
||||||
|
decryptor, encryptedStartOffset, encryptedEndOffset, is, fileSize);
|
||||||
|
|
||||||
if (offset != stream.skip(offset))
|
if (offset != stream.skip(offset))
|
||||||
throw new Exception("Unable to skip offset: "+offset);
|
throw new Exception("Unable to skip offset: "+offset);
|
||||||
|
@ -91,18 +157,29 @@ public class InFileStreamClassicProducer implements IProducer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private BufferedInputStream produceNotEncrypted() throws Exception{
|
private BufferedInputStream produceNotEncrypted() throws Exception{
|
||||||
BufferedInputStream stream = new BufferedInputStream(Files.newInputStream(filePath));
|
BufferedInputStream stream;
|
||||||
|
|
||||||
|
if (filePath == null)
|
||||||
|
stream = new BufferedInputStream(parentProducer.produce());
|
||||||
|
else
|
||||||
|
stream = new BufferedInputStream(Files.newInputStream(filePath));
|
||||||
|
|
||||||
if (offset != stream.skip(offset))
|
if (offset != stream.skip(offset))
|
||||||
throw new Exception("Unable to skip offset: "+offset);
|
throw new Exception("Unable to skip offset: "+offset);
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public InFileStreamClassicProducer getSuccessor(long offset){
|
public InFileStreamClassicProducer getSuccessor(long offset) throws Exception{
|
||||||
if (encrypted)
|
if (! encrypted)
|
||||||
return new InFileStreamClassicProducer(filePath, offset, encryptedStartOffset, encryptedEndOffset, decryptor);
|
|
||||||
return new InFileStreamClassicProducer(filePath, offset);
|
return new InFileStreamClassicProducer(filePath, offset);
|
||||||
|
|
||||||
|
if (filePath == null)
|
||||||
|
return new InFileStreamClassicProducer(parentProducer, offset, encryptedStartOffset, encryptedEndOffset, decryptor, fileSize);
|
||||||
|
return new InFileStreamClassicProducer(filePath, offset, encryptedStartOffset, encryptedEndOffset, decryptor);
|
||||||
}
|
}
|
||||||
public InFileStreamClassicProducer getSuccessor(long offset, boolean incrementExisting){
|
|
||||||
|
public InFileStreamClassicProducer getSuccessor(long offset, boolean incrementExisting) throws Exception{
|
||||||
if (incrementExisting)
|
if (incrementExisting)
|
||||||
return getSuccessor(this.offset + offset);
|
return getSuccessor(this.offset + offset);
|
||||||
return getSuccessor(offset);
|
return getSuccessor(offset);
|
||||||
|
@ -113,9 +190,15 @@ public class InFileStreamClassicProducer implements IProducer {
|
||||||
return encrypted;
|
return encrypted;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public File getFile(){ return filePath.toFile(); }
|
public File getFile(){
|
||||||
|
if (filePath == null)
|
||||||
|
return parentProducer.getFile();
|
||||||
|
return filePath.toFile();
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public String toString(){
|
public String toString(){
|
||||||
|
if (filePath == null)
|
||||||
|
return parentProducer.getFile().getAbsolutePath();
|
||||||
return filePath.toString();
|
return filePath.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,15 @@
|
||||||
*/
|
*/
|
||||||
package libKonogonka.RomFsDecrypted;
|
package libKonogonka.RomFsDecrypted;
|
||||||
|
|
||||||
|
import libKonogonka.Converter;
|
||||||
import libKonogonka.KeyChainHolder;
|
import libKonogonka.KeyChainHolder;
|
||||||
|
import libKonogonka.Tools.NCA.NCAProvider;
|
||||||
|
import libKonogonka.Tools.RomFs.FileSystemEntry;
|
||||||
|
import libKonogonka.Tools.RomFs.RomFsProvider;
|
||||||
import libKonogonka.Tools.other.System2.System2Provider;
|
import libKonogonka.Tools.other.System2.System2Provider;
|
||||||
import libKonogonka.Tools.other.System2.ini1.Ini1Provider;
|
import libKonogonka.Tools.other.System2.ini1.Ini1Provider;
|
||||||
import libKonogonka.Tools.other.System2.ini1.KIP1Provider;
|
import libKonogonka.Tools.other.System2.ini1.KIP1Provider;
|
||||||
|
import libKonogonka.ctraes.InFileStreamProducer;
|
||||||
import libKonogonka.ctraesclassic.AesCtrDecryptClassic;
|
import libKonogonka.ctraesclassic.AesCtrDecryptClassic;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
@ -32,9 +37,8 @@ import java.nio.charset.StandardCharsets;
|
||||||
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.Arrays;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
import java.util.stream.Collectors;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class Package2UnpackedTest {
|
public class Package2UnpackedTest {
|
||||||
|
|
||||||
|
@ -44,6 +48,8 @@ public class Package2UnpackedTest {
|
||||||
|
|
||||||
private static final String fileLocation = "/home/loper/Projects/libKonogonka/FilesForTests/6b7abe7efa17ad065b18e62d1c87a5cc.nca_extracted/ROOT/nx/package2";
|
private static final String fileLocation = "/home/loper/Projects/libKonogonka/FilesForTests/6b7abe7efa17ad065b18e62d1c87a5cc.nca_extracted/ROOT/nx/package2";
|
||||||
|
|
||||||
|
final String pathToFirmware = "/home/loper/Загрузки/patchesPlayground/nintendo-switch-global-firmwares/Firmware 14.1.0";
|
||||||
|
|
||||||
@DisplayName("Package2 unpacked test")
|
@DisplayName("Package2 unpacked test")
|
||||||
@Test
|
@Test
|
||||||
void discover() throws Exception{
|
void discover() throws Exception{
|
||||||
|
@ -118,17 +124,130 @@ public class Package2UnpackedTest {
|
||||||
|
|
||||||
@DisplayName("KIP1 unpack test")
|
@DisplayName("KIP1 unpack test")
|
||||||
@Test
|
@Test
|
||||||
void unpackKip1() throws Exception{
|
void unpackKip1FromNca() throws Exception{
|
||||||
keyChainHolder = new KeyChainHolder(keysFileLocation, null);
|
keyChainHolder = new KeyChainHolder(keysFileLocation, null);
|
||||||
System2Provider provider = new System2Provider(fileLocation, keyChainHolder);
|
// ------------------------------------------------------------------------------------------------------------
|
||||||
|
File firmware = new File(pathToFirmware);
|
||||||
|
|
||||||
|
if (! firmware.exists())
|
||||||
|
throw new Exception("Firmware directory does not exist " + pathToFirmware);
|
||||||
|
|
||||||
|
String[] fileNamesArray = firmware.list((File directory, String file) -> ( ! file.endsWith(".cnmt.nca") && file.endsWith(".nca")));
|
||||||
|
List<String> ncaFilesList = Arrays.asList(Objects.requireNonNull(fileNamesArray));
|
||||||
|
if (ncaFilesList.size() == 0)
|
||||||
|
throw new Exception("No NCA files found in firmware folder");
|
||||||
|
|
||||||
|
List<NCAProvider> ncaProviders = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String ncaFileName : fileNamesArray){
|
||||||
|
File nca = new File(firmware.getAbsolutePath()+File.separator+ncaFileName);
|
||||||
|
NCAProvider provider = new NCAProvider(nca, keyChainHolder.getRawKeySet());
|
||||||
|
ncaProviders.add(provider);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
NCAProvider system2FatNcaProvider = null;
|
||||||
|
NCAProvider system2ExFatNcaProvider = null;
|
||||||
|
for (NCAProvider ncaProvider : ncaProviders) {
|
||||||
|
String titleId = Converter.byteArrToHexStringAsLE(ncaProvider.getTitleId());
|
||||||
|
if (titleId.equals("0100000000000819"))
|
||||||
|
system2FatNcaProvider = ncaProvider;
|
||||||
|
if (titleId.equals("010000000000081b"))
|
||||||
|
system2ExFatNcaProvider = ncaProvider;
|
||||||
|
}
|
||||||
|
System.out.println("FAT " + (system2FatNcaProvider == null ? "NOT FOUND": "FOUND"));
|
||||||
|
System.out.println("ExFAT " + (system2ExFatNcaProvider == null ? "NOT FOUND": "FOUND"));
|
||||||
|
|
||||||
|
|
||||||
|
RomFsProvider romFsExFatProvider = null;
|
||||||
|
FileSystemEntry exFatPackage2Content = null;
|
||||||
|
InFileStreamProducer producerExFat = null;
|
||||||
|
if (system2ExFatNcaProvider != null){
|
||||||
|
romFsExFatProvider = system2ExFatNcaProvider.getNCAContentProvider(0).getRomfs();
|
||||||
|
exFatPackage2Content = romFsExFatProvider.getRootEntry().getContent()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getName().equals("nx"))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
.get(0)
|
||||||
|
.getContent()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getName().equals("package2"))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
.get(0);
|
||||||
|
producerExFat = romFsExFatProvider.getStreamProducer(exFatPackage2Content);
|
||||||
|
|
||||||
|
system2ExFatNcaProvider.getNCAContentProvider(0).getRomfs().getRootEntry().printTreeForDebug();
|
||||||
|
romFsExFatProvider.exportContent("/tmp/exported_ExFat", exFatPackage2Content);
|
||||||
|
|
||||||
|
System2Provider provider = new System2Provider(producerExFat, keyChainHolder);
|
||||||
|
provider.getKernelMap().printDebug();
|
||||||
Ini1Provider ini1Provider = provider.getIni1Provider();
|
Ini1Provider ini1Provider = provider.getIni1Provider();
|
||||||
|
KIP1Provider fsProvider = null;
|
||||||
|
|
||||||
for (KIP1Provider kip1Provider : ini1Provider.getKip1List())
|
for (KIP1Provider kip1Provider : ini1Provider.getKip1List())
|
||||||
if (kip1Provider.getHeader().getName().startsWith("FS"))
|
if (kip1Provider.getHeader().getName().startsWith("FS"))
|
||||||
kip1Provider.printDebug();
|
fsProvider = kip1Provider;
|
||||||
|
|
||||||
for (KIP1Provider kip1Provider : ini1Provider.getKip1List()) {
|
if (fsProvider != null) {
|
||||||
|
fsProvider.printDebug();
|
||||||
|
fsProvider.exportAsDecompressed("/tmp/FAT_kip1");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
System.out.println("FS KIP1 NOT FOUND");
|
||||||
|
}
|
||||||
|
|
||||||
|
RomFsProvider romFsFatProvider = null;
|
||||||
|
FileSystemEntry fatPackage2Content = null;
|
||||||
|
InFileStreamProducer producerFat;
|
||||||
|
if (system2FatNcaProvider != null){
|
||||||
|
romFsFatProvider = system2FatNcaProvider.getNCAContentProvider(0).getRomfs();
|
||||||
|
|
||||||
|
fatPackage2Content = romFsFatProvider.getRootEntry().getContent()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getName().equals("nx"))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
.get(0)
|
||||||
|
.getContent()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getName().equals("package2"))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
.get(0);
|
||||||
|
producerFat = romFsFatProvider.getStreamProducer(fatPackage2Content);
|
||||||
|
System2Provider provider = new System2Provider(producerFat, keyChainHolder);
|
||||||
|
provider.getKernelMap().printDebug();
|
||||||
|
Ini1Provider ini1Provider = provider.getIni1Provider();
|
||||||
|
KIP1Provider fsProvider = null;
|
||||||
|
|
||||||
|
for (KIP1Provider kip1Provider : ini1Provider.getKip1List())
|
||||||
if (kip1Provider.getHeader().getName().startsWith("FS"))
|
if (kip1Provider.getHeader().getName().startsWith("FS"))
|
||||||
kip1Provider.exportAsDecompressed("/tmp");
|
fsProvider = kip1Provider;
|
||||||
|
|
||||||
|
if (fsProvider != null) {
|
||||||
|
fsProvider.printDebug();
|
||||||
|
fsProvider.exportAsDecompressed("/tmp/FAT_kip1");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
System.out.println("FS KIP1 NOT FOUND");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DisplayName("KIP1 unpack test")
|
||||||
|
@Test
|
||||||
|
void unpackKip1() throws Exception{
|
||||||
|
System2Provider provider = new System2Provider(fileLocation, keyChainHolder);
|
||||||
|
provider.getKernelMap().printDebug();
|
||||||
|
Ini1Provider ini1Provider = provider.getIni1Provider();
|
||||||
|
KIP1Provider fsProvider = null;
|
||||||
|
|
||||||
|
for (KIP1Provider kip1Provider : ini1Provider.getKip1List())
|
||||||
|
if (kip1Provider.getHeader().getName().startsWith("FS"))
|
||||||
|
fsProvider = kip1Provider;
|
||||||
|
|
||||||
|
if (fsProvider != null) {
|
||||||
|
fsProvider.printDebug();
|
||||||
|
fsProvider.exportAsDecompressed("/tmp");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
System.out.println("FS KIP1 NOT FOUND");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue