diff --git a/src/main/java/libKonogonka/fs/NCA/NCAProvider.java b/src/main/java/libKonogonka/fs/NCA/NCAProvider.java index 176c7bf..2d6bef5 100644 --- a/src/main/java/libKonogonka/fs/NCA/NCAProvider.java +++ b/src/main/java/libKonogonka/fs/NCA/NCAProvider.java @@ -92,24 +92,23 @@ public class NCAProvider { //------------------------------------------------------------------------------------------------------------------------- byte[] decryptedHeader = new byte[0xC00]; - RandomAccessFile raf = new RandomAccessFile(file, "r"); - byte[] encryptedSequence = new byte[0x200]; - byte[] decryptedSequence; + try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { + byte[] encryptedSequence = new byte[0x200]; + byte[] decryptedSequence; - raf.seek(offsetPosition); + raf.seek(offsetPosition); - for (int i = 0; i < 6; i++){ - if (raf.read(encryptedSequence) != 0x200) - throw new Exception("Read error "+i); - decryptedSequence = new byte[0x200]; - xtsaesCipher.processDataUnit(encryptedSequence, 0, 0x200, decryptedSequence, 0, i); - System.arraycopy(decryptedSequence, 0, decryptedHeader, i * 0x200, 0x200); + for (int i = 0; i < 6; i++){ + if (raf.read(encryptedSequence) != 0x200) + throw new Exception("Read error "+i); + decryptedSequence = new byte[0x200]; + xtsaesCipher.processDataUnit(encryptedSequence, 0, 0x200, decryptedSequence, 0, i); + System.arraycopy(decryptedSequence, 0, decryptedHeader, i * 0x200, 0x200); + } + + setupHeader(decryptedHeader); } - setupHeader(decryptedHeader); - - raf.close(); - setupNCAContent(); /*//--------------------------------------------------------------------- FileInputStream fis = new FileInputStream(file); diff --git a/src/main/java/libKonogonka/fs/other/System2/ini1/KIP1Provider.java b/src/main/java/libKonogonka/fs/other/System2/ini1/KIP1Provider.java index 69b1481..e4ca3e2 100644 --- a/src/main/java/libKonogonka/fs/other/System2/ini1/KIP1Provider.java +++ b/src/main/java/libKonogonka/fs/other/System2/ini1/KIP1Provider.java @@ -34,14 +34,20 @@ public class KIP1Provider extends ExportAble { private long size; public KIP1Provider(String fileLocation) throws Exception{ + this(fileLocation, 0); + } + + public KIP1Provider(String fileLocation, long kip1StartOffset) throws Exception{ this.producer = new InFileStreamClassicProducer(Paths.get(fileLocation)); this.stream = producer.produce(); + if (kip1StartOffset != stream.skip(kip1StartOffset)) + throw new Exception("Failed to skip declared starting offset "+kip1StartOffset); byte[] kip1HeaderBytes = new byte[HEADER_SIZE]; if (HEADER_SIZE != stream.read(kip1HeaderBytes)) throw new Exception("Unable to read KIP1 file header"); makeHeader(kip1HeaderBytes); - calculateOffsets(0); + calculateOffsets(kip1StartOffset); } public KIP1Provider(byte[] kip1HeaderBytes, long kip1StartOffset, InFileStreamClassicProducer producer) throws Exception{ diff --git a/src/test/java/libKonogonka/examples/ExtractDecompressedKip1Test.java b/src/test/java/libKonogonka/examples/ExtractDecompressedKip1Test.java new file mode 100644 index 0000000..1233012 --- /dev/null +++ b/src/test/java/libKonogonka/examples/ExtractDecompressedKip1Test.java @@ -0,0 +1,126 @@ +package libKonogonka.examples; + +import libKonogonka.Converter; +import libKonogonka.KeyChainHolder; +import libKonogonka.fs.NCA.NCAProvider; +import libKonogonka.fs.RomFs.FileSystemEntry; +import libKonogonka.fs.RomFs.RomFsProvider; +import libKonogonka.fs.other.System2.System2Provider; +import libKonogonka.fs.other.System2.ini1.KIP1Provider; +import libKonogonka.aesctr.InFileStreamProducer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.nio.file.*; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class ExtractDecompressedKip1Test { + final String KEYS_FILE_LOCATION = "FilesForTests"+ File.separator+"prod.keys"; + final String XCI_HEADER_KEYS_FILE_LOCATION = "FilesForTests"+File.separator+"xci_header_key.txt"; + + final String pathToFirmwares = ". . . nintendo-switch-global-firmwares"; + + private static KeyChainHolder keyChainHolder; + + @DisplayName("Extract FS.kip1") + @Test + void testSystem2() throws Exception{ + makeKeys(); + File firmwaresDir = new File(pathToFirmwares); + Assertions.assertNotNull(firmwaresDir); + //File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) -> (s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*") && ! s.endsWith(".zip"))); + File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) -> s.equals("Firmware 14.1.2")); + + for (File fw : fwDirs) { + if (fw.isFile()) + continue; + System.out.println("\t\t\t==== "+fw.getName()+" ===="); + String fwAbsolutePath = fw.getAbsolutePath(); + iterate(fwAbsolutePath, + System.getProperty("java.io.tmpdir")+File.separator+fw.getName()+File.separator+"FAT", + System.getProperty("java.io.tmpdir")+File.separator+fw.getName()+File.separator+"ExFAT"); + } + } + void iterate(String pathToFirmware, String exportFat, String exportExFat){ + try { + String[] ncaFileNames = collectNcaFileNames(pathToFirmware); + List ncaProviders = makeNcaProviders(ncaFileNames, pathToFirmware); + + NCAProvider system2FatNcaProvider = null; + NCAProvider system2ExFatNcaProvider = null; + + for (NCAProvider ncaProvider : ncaProviders) { + String titleId = Converter.byteArrToHexStringAsLE(ncaProvider.getTitleId()); + if (titleId.equals("0100000000000819")) + system2FatNcaProvider = ncaProvider; + else if (titleId.equals("010000000000081b")) + system2ExFatNcaProvider = ncaProvider; + } + + Assertions.assertNotNull(system2FatNcaProvider); + Assertions.assertNotNull(system2ExFatNcaProvider); + + System.out.println("FAT " + system2FatNcaProvider.getFile().getName() + "\t" + exportFat); + System.out.println("ExFAT " + system2ExFatNcaProvider.getFile().getName() + "\t" + exportExFat); + + testExportedFiles(system2FatNcaProvider, exportFat); + testExportedFiles(system2ExFatNcaProvider, exportExFat); + } + catch (Exception e){ + e.printStackTrace(); + } + } + void makeKeys() throws Exception{ + String keyValue = new String(Files.readAllBytes(Paths.get(XCI_HEADER_KEYS_FILE_LOCATION))).trim(); + Assertions.assertNotEquals(0, keyValue.length()); + keyChainHolder = new KeyChainHolder(KEYS_FILE_LOCATION, keyValue); + } + String[] collectNcaFileNames(String pathToFirmware){ + File firmware = new File(pathToFirmware); + Assertions.assertTrue(firmware.exists()); + String[] ncaFileNames = firmware.list((File directory, String file) -> ( ! file.endsWith(".cnmt.nca") && file.endsWith(".nca"))); + Assertions.assertNotNull(ncaFileNames); + return ncaFileNames; + } + List makeNcaProviders(String[] ncaFileNames, String pathToFirmware) throws Exception{ + List ncaProviders = new ArrayList<>(); + for (String ncaFileName : ncaFileNames){ + File nca = new File(pathToFirmware+File.separator+ncaFileName); + NCAProvider provider = new NCAProvider(nca, keyChainHolder.getRawKeySet()); + ncaProviders.add(provider); + } + + Assertions.assertNotEquals(0, ncaProviders.size()); + + return ncaProviders; + } + + void testExportedFiles(NCAProvider system2NcaProvider, String exportIntoFolder) throws Exception{ + RomFsProvider romFsProvider = system2NcaProvider.getNCAContentProvider(0).getRomfs(); + + FileSystemEntry package2FileSystemEntry = romFsProvider.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); + + InFileStreamProducer producer = romFsProvider.getStreamProducer(package2FileSystemEntry); + System2Provider providerStream = new System2Provider(producer, keyChainHolder); + for (KIP1Provider kip1Provider : providerStream.getIni1Provider().getKip1List()){ + String kip1Name = kip1Provider.getHeader().getName(); + if (kip1Name.equals("FS")) { + System.out.println("Exported: "+kip1Provider.exportAsDecompressed(exportIntoFolder)); + break; + } + } + } +}