diff --git a/src/main/java/libKonogonka/TitleKeyChainHolder.java b/src/main/java/libKonogonka/TitleKeyChainHolder.java new file mode 100644 index 0000000..5970f2d --- /dev/null +++ b/src/main/java/libKonogonka/TitleKeyChainHolder.java @@ -0,0 +1,58 @@ +/* + 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 . +*/ +package libKonogonka; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.HashMap; + +public class TitleKeyChainHolder { + private final File keysFile; + private HashMap rawKeySet; + + public TitleKeyChainHolder(String pathToKeysFile) throws Exception{ + this(new File(pathToKeysFile)); + } + + public TitleKeyChainHolder(File keysFile) throws Exception{ + this.keysFile = keysFile; + collectEverything(); + } + + private void collectEverything() throws Exception{ + rawKeySet = new HashMap<>(); + + BufferedReader br = new BufferedReader(new FileReader(keysFile)); + + String fileLine; + String[] keyValue; + + while ((fileLine = br.readLine()) != null){ + keyValue = fileLine.trim().split("\\s*=\\s*", 2); + if (keyValue.length == 2 && keyValue[0].length() > 16 && ! (keyValue[0].length() > 32) && keyValue[1].length() == 32){ + rawKeySet.put(keyValue[0], keyValue[1]); + } + } + } + + public HashMap getKeySet() { + return rawKeySet; + } +} diff --git a/src/main/java/libKonogonka/Tools/ISuperProvider.java b/src/main/java/libKonogonka/Tools/ISuperProvider.java index 55b6678..6aa50d7 100644 --- a/src/main/java/libKonogonka/Tools/ISuperProvider.java +++ b/src/main/java/libKonogonka/Tools/ISuperProvider.java @@ -26,7 +26,8 @@ import java.io.PipedInputStream; public interface ISuperProvider { PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception; PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception; - + boolean exportContent(String saveToLocation, String subFileName) throws Exception; + boolean exportContent(String saveToLocation, int subFileNumber) throws Exception; File getFile(); long getRawFileDataStart(); } diff --git a/src/main/java/libKonogonka/Tools/NCA/NCAContent.java b/src/main/java/libKonogonka/Tools/NCA/NCAContent.java index f962da7..1a34c6f 100644 --- a/src/main/java/libKonogonka/Tools/NCA/NCAContent.java +++ b/src/main/java/libKonogonka/Tools/NCA/NCAContent.java @@ -19,10 +19,8 @@ package libKonogonka.Tools.NCA; import libKonogonka.Converter; -import libKonogonka.RainbowDump; import libKonogonka.Tools.NCA.NCASectionTableBlock.NcaFsHeader; import libKonogonka.Tools.PFS0.IPFS0Provider; -import libKonogonka.Tools.PFS0.PFS0EncryptedProvider; import libKonogonka.Tools.PFS0.PFS0Provider; import libKonogonka.Tools.RomFs.IRomFsProvider; import libKonogonka.Tools.RomFs.RomFsEncryptedProvider; @@ -41,28 +39,28 @@ public class NCAContent { private final static Logger log = LogManager.getLogger(NCAContent.class); private final File file; - private final long offsetPosition; + private final long ncaOffsetPosition; private final NcaFsHeader ncaFsHeader; private final NCAHeaderTableEntry ncaHeaderTableEntry; private final byte[] decryptedKey; - private final LinkedList Pfs0SHA256hashes; + private LinkedList Pfs0SHA256hashes; private IPFS0Provider pfs0; private IRomFsProvider romfs; // TODO: if decryptedKey is empty, throw exception ?? public NCAContent(File file, - long offsetPosition, + long ncaOffsetPosition, NcaFsHeader ncaFsHeader, NCAHeaderTableEntry ncaHeaderTableEntry, byte[] decryptedKey) throws Exception { this.file = file; - this.offsetPosition = offsetPosition; + this.ncaOffsetPosition = ncaOffsetPosition; this.ncaFsHeader = ncaFsHeader; this.ncaHeaderTableEntry = ncaHeaderTableEntry; this.decryptedKey = decryptedKey; - + System.out.println("NCAContent pfs0offsetPosition: "+ncaOffsetPosition); Pfs0SHA256hashes = new LinkedList<>(); // If nothing to do if (ncaHeaderTableEntry.getMediaEndOffset() == 0) @@ -89,60 +87,24 @@ public class NCAContent { } } private void proceedPFS0NotEncrypted() throws Exception{ - RandomAccessFile raf = new RandomAccessFile(file, "r"); - long thisMediaLocation = offsetPosition + (ncaHeaderTableEntry.getMediaStartOffset() * 0x200);// TODO: NOTE already defined inside PFS0 - long hashTableLocation = thisMediaLocation + ncaFsHeader.getSuperBlockPFS0().getHashTableOffset(); - long pfs0Location = thisMediaLocation + ncaFsHeader.getSuperBlockPFS0().getPfs0offset(); - - raf.seek(hashTableLocation); - - byte[] rawData; - long sha256recordsNumber = ncaFsHeader.getSuperBlockPFS0().getHashTableSize() / 0x20; - // Collect hashes - for (int i = 0; i < sha256recordsNumber; i++){ - rawData = new byte[0x20]; // 32 bytes - size of SHA256 hash - if (raf.read(rawData) != -1) - Pfs0SHA256hashes.add(rawData); - else { - raf.close(); - return; // TODO: fix - } - } - raf.close(); - // Get pfs0 - pfs0 = new PFS0Provider(file, pfs0Location); - } - private void proceedPFS0Encrypted() throws Exception{/* - RandomAccessFile raf = new RandomAccessFile(file, "r"); - long thisMediaLocation = offsetPosition + (ncaHeaderTableEntry.getMediaStartOffset() * 0x200); - long hashTableLocation = thisMediaLocation + ncaFsHeader.getSuperBlockPFS0().getHashTableOffset(); - long pfs0Location = thisMediaLocation + ncaFsHeader.getSuperBlockPFS0().getPfs0offset(); - - raf.seek(hashTableLocation); - - byte[] rawData; - long sha256recordsNumber = ncaFsHeader.getSuperBlockPFS0().getHashTableSize() / 0x20; - // Collect hashes - for (int i = 0; i < sha256recordsNumber; i++){ - rawData = new byte[0x20]; // 32 bytes - size of SHA256 hash - if (raf.read(rawData) != -1) - Pfs0SHA256hashes.add(rawData); - else { - raf.close(); - return; // TODO: fix - } - } - raf.close(); - // Get pfs0 - pfs0 = new PFS0Provider(file, pfs0Location); - /*/ - new CryptoSection03Pfs0(file, - offsetPosition, - decryptedKey, - ncaFsHeader, + pfs0 = new PFS0Provider(file, + ncaOffsetPosition, + ncaFsHeader.getSuperBlockPFS0(), ncaHeaderTableEntry.getMediaStartOffset(), ncaHeaderTableEntry.getMediaEndOffset()); - //*/ + Pfs0SHA256hashes = pfs0.getPfs0SHA256hashes(); + } + + private void proceedPFS0Encrypted() throws Exception{ + AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey, ncaFsHeader.getSectionCTR(), + ncaHeaderTableEntry.getMediaStartOffset() * 0x200); + pfs0 = new PFS0Provider(file, + ncaOffsetPosition, + ncaFsHeader.getSuperBlockPFS0(), + decryptor, + ncaHeaderTableEntry.getMediaStartOffset(), + ncaHeaderTableEntry.getMediaEndOffset()); + Pfs0SHA256hashes = pfs0.getPfs0SHA256hashes(); } private void proceedRomFs() throws Exception{ @@ -167,7 +129,7 @@ public class NCAContent { this.romfs = new RomFsEncryptedProvider( ncaFsHeader.getSuperBlockIVFC().getLvl6Offset(), file, - offsetPosition, + ncaOffsetPosition, decryptedKey, ncaFsHeader.getSectionCTR(), ncaHeaderTableEntry.getMediaStartOffset(), @@ -178,174 +140,6 @@ public class NCAContent { public IPFS0Provider getPfs0() { return pfs0; } public IRomFsProvider getRomfs() { return romfs; } - private class CryptoSection03Pfs0 { - CryptoSection03Pfs0(File file, - long offsetPosition, - byte[] decryptedKey, - NcaFsHeader ncaFsHeader, - long mediaStartBlocksOffset, - long mediaEndBlocksOffset) throws Exception - { - log.debug( "-== Crypto Section 03 PFS0 ==-\n" + - "Media start location: " + RainbowDump.formatDecHexString(mediaStartBlocksOffset) + "\n" + - "Media end location: " + RainbowDump.formatDecHexString(mediaEndBlocksOffset) + "\n" + - "Media size: " + RainbowDump.formatDecHexString((mediaEndBlocksOffset-mediaStartBlocksOffset)) + "\n" + - "Media actual location: " + RainbowDump.formatDecHexString((offsetPosition + (mediaStartBlocksOffset * 0x200))) + "\n" + - "SHA256 hash table size: " + RainbowDump.formatDecHexString(ncaFsHeader.getSuperBlockPFS0().getHashTableSize()) + "\n" + - "SHA256 hash table offs: " + RainbowDump.formatDecHexString(ncaFsHeader.getSuperBlockPFS0().getHashTableOffset()) + "\n" + - "PFS0 Offset: " + RainbowDump.formatDecHexString(ncaFsHeader.getSuperBlockPFS0().getPfs0offset()) + "\n" + - "SHA256 records: " + RainbowDump.formatDecHexString(ncaFsHeader.getSuperBlockPFS0().getHashTableSize() / 0x20) + "\n" + - "KEY (decrypted): " + Converter.byteArrToHexString(decryptedKey) + "\n" + - "CTR: " + Converter.byteArrToHexString(ncaFsHeader.getSectionCTR()) + "\n" + - "-----------------------------------------------------------\n"); - if (decryptedKey == null) - throw new Exception("CryptoSection03: unable to proceed. No decrypted key provided."); - - RandomAccessFile raf = new RandomAccessFile(file, "r"); - long absoluteOffsetPosition = offsetPosition + (mediaStartBlocksOffset * 0x200); - raf.seek(absoluteOffsetPosition); - - AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey, ncaFsHeader.getSectionCTR(), - mediaStartBlocksOffset * 0x200); - - byte[] encryptedBlock; - byte[] decryptedBlock; - long mediaBlocksSize = mediaEndBlocksOffset - mediaStartBlocksOffset; - // Prepare thread to parse encrypted data - PipedOutputStream streamOut = new PipedOutputStream(); - PipedInputStream streamInp = new PipedInputStream(streamOut); - - Thread pThread = new Thread(new ParseThread( - streamInp, - ncaFsHeader.getSuperBlockPFS0().getPfs0offset(), - ncaFsHeader.getSuperBlockPFS0().getHashTableOffset(), - ncaFsHeader.getSuperBlockPFS0().getHashTableSize(), - offsetPosition, - file, - decryptedKey, - ncaFsHeader.getSectionCTR(), - mediaStartBlocksOffset, - mediaEndBlocksOffset - )); - pThread.start(); - // Decrypt data - for (int i = 0; i < mediaBlocksSize; i++){ - encryptedBlock = new byte[0x200]; - if (raf.read(encryptedBlock) != -1){ - //decryptedBlock = aesCtr.decrypt(encryptedBlock); - decryptedBlock = decryptor.decryptNext(encryptedBlock); - // Writing decrypted data to pipe - try { - streamOut.write(decryptedBlock); - } - catch (IOException e){ - break; - } - } - } - pThread.join(); - streamOut.close(); - raf.close(); - } - /** - * Since we're representing decrypted data as stream (it's easier to look on it this way), - * this thread will be parsing it. - * */ - private class ParseThread implements Runnable{ - - PipedInputStream pipedInputStream; - - long hashTableOffset; - long hashTableSize; - long hashTableRecordsCount; - long pfs0offset; - - private final long MetaOffsetPositionInFile; - private final File MetaFileWithEncPFS0; - private final byte[] MetaKey; - private final byte[] MetaSectionCTR; - private final long MetaMediaStartOffset; - private final long MetaMediaEndOffset; - - - ParseThread(PipedInputStream pipedInputStream, - long pfs0offset, - long hashTableOffset, - long hashTableSize, - - long MetaOffsetPositionInFile, - File MetaFileWithEncPFS0, - byte[] MetaKey, - byte[] MetaSectionCTR, - long MetaMediaStartOffset, - long MetaMediaEndOffset - ){ - this.pipedInputStream = pipedInputStream; - this.hashTableOffset = hashTableOffset; - this.hashTableSize = hashTableSize; - this.hashTableRecordsCount = hashTableSize / 0x20; - this.pfs0offset = pfs0offset; - - this.MetaOffsetPositionInFile = MetaOffsetPositionInFile; - this.MetaFileWithEncPFS0 = MetaFileWithEncPFS0; - this.MetaKey = MetaKey; - this.MetaSectionCTR = MetaSectionCTR; - this.MetaMediaStartOffset = MetaMediaStartOffset; - this.MetaMediaEndOffset = MetaMediaEndOffset; - - } - - @Override - public void run() { - long counter = 0; // How many bytes already read - - try{ - if (hashTableOffset > 0){ - if (hashTableOffset != pipedInputStream.skip(hashTableOffset)) - return; // TODO: fix? - counter = hashTableOffset; - } - // Loop for collecting all recrods from sha256 hash table - while ((counter - hashTableOffset) < hashTableSize){ - int hashCounter = 0; - byte[] sectionHash = new byte[0x20]; - // Loop for collecting bytes for every SINGLE records, where record size == 0x20 - while (hashCounter < 0x20){ - int currentByte = pipedInputStream.read(); - if (currentByte == -1) - break; - sectionHash[hashCounter] = (byte)currentByte; - hashCounter++; - counter++; - } - // Write after collecting - Pfs0SHA256hashes.add(sectionHash); // From the NCAContentProvider obviously - } - // Skip padding and go to PFS0 location - if (counter < pfs0offset){ - long toSkip = pfs0offset-counter; - if (toSkip != pipedInputStream.skip(toSkip)) - return; // TODO: fix? - counter += toSkip; - } - //--------------------------------------------------------- - pfs0 = new PFS0EncryptedProvider(pipedInputStream, - counter, - MetaOffsetPositionInFile, - MetaFileWithEncPFS0, - MetaKey, - MetaSectionCTR, - MetaMediaStartOffset, - MetaMediaEndOffset); - pipedInputStream.close(); - } - catch (Exception e){ - log.debug("NCA Content parsing thread exception: ", e); - } - //finally { System.out.println("NCA Content thread dies");} - } - } - } /** * Export NCA content AS IS. @@ -362,7 +156,7 @@ public class NCAContent { "Media start location: " + mediaStartBlocksOffset + "\n" + "Media end location: " + mediaEndBlocksOffset + "\n" + "Media size : " + (mediaEndBlocksOffset-mediaStartBlocksOffset) + "\n" + - "Media act. location: " + (offsetPosition + (mediaStartBlocksOffset * 0x200)) + "\n" + + "Media act. location: " + (ncaOffsetPosition + (mediaStartBlocksOffset * 0x200)) + "\n" + "KEY: " + Converter.byteArrToHexString(decryptedKey) + "\n" + "CTR: " + Converter.byteArrToHexString(ncaFsHeader.getSectionCTR()) + "\n"); //---------------------------------------------------------------------------------------------------/ @@ -414,7 +208,7 @@ public class NCAContent { workerThread = new Thread(() -> { try { //RandomAccessFile raf = new RandomAccessFile(file, "r"); - long abosluteOffsetPosition = offsetPosition + (mediaStartBlocksOffset * 0x200); + long abosluteOffsetPosition = ncaOffsetPosition + (mediaStartBlocksOffset * 0x200); raf.seek(abosluteOffsetPosition); AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey, diff --git a/src/main/java/libKonogonka/Tools/NCA/NCAProvider.java b/src/main/java/libKonogonka/Tools/NCA/NCAProvider.java index fb41e99..585a711 100644 --- a/src/main/java/libKonogonka/Tools/NCA/NCAProvider.java +++ b/src/main/java/libKonogonka/Tools/NCA/NCAProvider.java @@ -283,7 +283,8 @@ public class NCAProvider { key = cipher.doFinal(rightsIDkey); } catch (Exception e){ - throw new Exception("No title.keys loaded?", e); + throw new Exception("No title.keys loaded for '"+ + String.format("titlekek_%02x", cryptoTypeReal)+"' or '"+byteArrToHexString(rightsId)+"'? ("+e+")", e); } } getNcaContentByNumber(0, key); diff --git a/src/main/java/libKonogonka/Tools/NCA/NCASectionTableBlock/NcaFsHeader.java b/src/main/java/libKonogonka/Tools/NCA/NCASectionTableBlock/NcaFsHeader.java index 6bedfe0..1fdefe3 100644 --- a/src/main/java/libKonogonka/Tools/NCA/NCASectionTableBlock/NcaFsHeader.java +++ b/src/main/java/libKonogonka/Tools/NCA/NCASectionTableBlock/NcaFsHeader.java @@ -48,6 +48,7 @@ public class NcaFsHeader { private final BucketTreeHeader BktrSection2; private final byte[] generation; + private final byte[] secureValue; private final byte[] sectionCTR; private final SparseInfo sparseInfo; private final CompressionInfo compressionInfo; @@ -79,7 +80,10 @@ public class NcaFsHeader { BktrSection2 = new BucketTreeHeader(Arrays.copyOfRange(tableBlockBytes, 0x130, 0x140)); generation = Arrays.copyOfRange(tableBlockBytes, 0x140, 0x144); - sectionCTR = Arrays.copyOfRange(tableBlockBytes, 0x144, 0x148); + secureValue = Arrays.copyOfRange(tableBlockBytes, 0x144, 0x148); + + sectionCTR = Arrays.copyOfRange(tableBlockBytes, 0x140, 0x148); + sparseInfo = new SparseInfo(Arrays.copyOfRange(tableBlockBytes, 0x148, 0x178)); compressionInfo = new CompressionInfo(Arrays.copyOfRange(tableBlockBytes, 0x178, 0x1a0)); metaDataHashDataInfo = new MetaDataHashDataInfo(Arrays.copyOfRange(tableBlockBytes, 0x1a0, 0x1d0)); @@ -109,6 +113,10 @@ public class NcaFsHeader { public int getEntryCountSection2() { return BktrSection2.getEntryCount(); } public byte[] getPatchInfoUnknownSection2() { return BktrSection2.getUnknown(); } public byte[] getGeneration() {return generation;} + public byte[] getSecureValue() {return secureValue;} + /** + * Used for Aes Ctr decryption in IV context. + * */ public byte[] getSectionCTR() { return sectionCTR; } public SparseInfo getSparseInfo() {return sparseInfo;} public CompressionInfo getCompressionInfo() {return compressionInfo;} diff --git a/src/main/java/libKonogonka/Tools/PFS0/IPFS0Provider.java b/src/main/java/libKonogonka/Tools/PFS0/IPFS0Provider.java index 3482834..0a78532 100644 --- a/src/main/java/libKonogonka/Tools/PFS0/IPFS0Provider.java +++ b/src/main/java/libKonogonka/Tools/PFS0/IPFS0Provider.java @@ -20,6 +20,8 @@ package libKonogonka.Tools.PFS0; import libKonogonka.Tools.ISuperProvider; +import java.util.LinkedList; + public interface IPFS0Provider extends ISuperProvider { boolean isEncrypted(); String getMagic(); @@ -30,4 +32,5 @@ public interface IPFS0Provider extends ISuperProvider { PFS0subFile[] getPfs0subFiles(); void printDebug(); + LinkedList getPfs0SHA256hashes(); } diff --git a/src/main/java/libKonogonka/Tools/PFS0/PFS0EncryptedProvider.java b/src/main/java/libKonogonka/Tools/PFS0/PFS0EncryptedProvider.java deleted file mode 100644 index 6527f9e..0000000 --- a/src/main/java/libKonogonka/Tools/PFS0/PFS0EncryptedProvider.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - 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 . -*/ -package libKonogonka.Tools.PFS0; - -import libKonogonka.Converter; -import libKonogonka.RainbowDump; -import libKonogonka.Tools.RomFs.Level6Header; -import libKonogonka.ctraes.AesCtrDecryptSimple; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.Arrays; - -import static libKonogonka.Converter.*; - -public class PFS0EncryptedProvider implements IPFS0Provider{ - private final static Logger log = LogManager.getLogger(PFS0EncryptedProvider.class); - - //private long rawFileDataStart; // Always -1 @ PFS0EncryptedProvider - - private final String magic; - private final int filesCount; - private final int stringTableSize; - private final byte[] padding; - private final PFS0subFile[] pfs0subFiles; - - //--------------------------------------- - - private long rawBlockDataStart; - - private final long offsetPositionInFile; - private final File file; - private final byte[] key; - private final byte[] sectionCTR; - private final long mediaStartOffset; // In 512-blocks - private final long mediaEndOffset; // In 512-blocks - - public PFS0EncryptedProvider(PipedInputStream pipedInputStream, - long pfs0offsetPosition, - long offsetPositionInFile, - File fileWithEncPFS0, - byte[] key, - byte[] sectionCTR, - long mediaStartOffset, - long mediaEndOffset - ) throws Exception{ - // Populate 'meta' data that is needed for getProviderSubFilePipedInpStream() - this.offsetPositionInFile = offsetPositionInFile + mediaStartOffset*0x200; - this.file = fileWithEncPFS0; - this.key = key; - this.sectionCTR = sectionCTR; - this.mediaStartOffset = mediaStartOffset; - this.mediaEndOffset = mediaEndOffset; - // pfs0offsetPosition is a position relative to Media block. Let's add pfs0 'header's' bytes count and get raw data start position in media block - //rawFileDataStart = -1; // Set -1 for PFS0EncryptedProvider - // Detect raw data start position using next var - rawBlockDataStart = pfs0offsetPosition; - - byte[] fileStartingBytes = new byte[0x10]; - // Read PFS0Provider, files count, header, padding (4 zero bytes) - - for (int i = 0; i < 0x10; i++){ - int currentByte = pipedInputStream.read(); - if (currentByte == -1) { - throw new Exception("PFS0EncryptedProvider: Reading stream suddenly ended while trying to read starting 0x10 bytes"); - } - fileStartingBytes[i] = (byte)currentByte; - } - // Update position - rawBlockDataStart += 0x10; - // Check PFS0Provider - magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII); - if (! magic.equals("PFS0")){ - throw new Exception("PFS0EncryptedProvider: Bad magic"); - } - // Get files count - filesCount = getLEint(fileStartingBytes, 0x4); - if (filesCount <= 0 ) { - throw new Exception("PFS0EncryptedProvider: Files count is too small"); - } - // Get string table - stringTableSize = getLEint(fileStartingBytes, 0x8); - if (stringTableSize <= 0 ){ - throw new Exception("PFS0EncryptedProvider: String table is too small"); - } - padding = Arrays.copyOfRange(fileStartingBytes, 0xc, 0x10); - //--------------------------------------------------------------------------------------------------------- - pfs0subFiles = new PFS0subFile[filesCount]; - - long[] offsetsSubFiles = new long[filesCount]; - long[] sizesSubFiles = new long[filesCount]; - int[] strTableOffsets = new int[filesCount]; - byte[][] zeroBytes = new byte[filesCount][]; - - byte[] fileEntryTable = new byte[0x18]; - for (int i=0; i < filesCount; i++){ - for (int j = 0; j < 0x18; j++){ - int currentByte = pipedInputStream.read(); - if (currentByte == -1) { - throw new Exception("PFS0EncryptedProvider: Reading stream suddenly ended while trying to read File Entry Table #"+i); - } - fileEntryTable[j] = (byte)currentByte; - } - offsetsSubFiles[i] = getLElong(fileEntryTable, 0); - sizesSubFiles[i] = getLElong(fileEntryTable, 0x8); - strTableOffsets[i] = getLEint(fileEntryTable, 0x10); - zeroBytes[i] = Arrays.copyOfRange(fileEntryTable, 0x14, 0x18); - // Update position - rawBlockDataStart += 0x18; - } - //********************************************************************************************************** - // In here pointer in front of String table - String[] subFileNames = new String[filesCount]; - byte[] stringTbl = new byte[stringTableSize]; - - for (int i = 0; i < stringTableSize; i++){ - int currentByte = pipedInputStream.read(); - if (currentByte == -1) { - throw new Exception("PFS0EncryptedProvider: Reading stream suddenly ended while trying to read string table"); - } - stringTbl[i] = (byte)currentByte; - } - // Update position - rawBlockDataStart += stringTableSize; - - for (int i=0; i < filesCount; i++){ - int j = 0; - while (stringTbl[strTableOffsets[i]+j] != (byte)0x00) - j++; - subFileNames[i] = new String(stringTbl, strTableOffsets[i], j, StandardCharsets.UTF_8); - } - for (int i = 0; i < filesCount; i++){ - pfs0subFiles[i] = new PFS0subFile( - subFileNames[i], - offsetsSubFiles[i], - sizesSubFiles[i], - zeroBytes[i] - ); - } - } - - @Override - public boolean isEncrypted() { return true; } - @Override - public String getMagic() { return magic; } - @Override - public int getFilesCount() { return filesCount; } - @Override - public int getStringTableSize() { return stringTableSize; } - @Override - public byte[] getPadding() { return padding; } - @Override - public long getRawFileDataStart() { return rawBlockDataStart; } - @Override - public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; } - @Override - public File getFile(){ return file; } - @Override - public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception { // TODO: rewrite - if (subFileNumber >= pfs0subFiles.length) - throw new Exception("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists"); - - Thread workerThread; - PipedOutputStream streamOut = new PipedOutputStream(); - - PipedInputStream streamIn = new PipedInputStream(streamOut); - workerThread = new Thread(() -> { - log.debug("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Executing thread:\nSub file: " + - pfs0subFiles[subFileNumber].getName() + - "\nFor block # "+((rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) / 0x200) + - "\nAnd initial skipped bytes are: "+offsetPositionInFile + - "\nWhere Raw Block Data Start: "+rawBlockDataStart + - "\nAnd sub file offset: "+pfs0subFiles[subFileNumber].getOffset()+ - "\nSkip bytes "+((rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) - ((rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) / 0x200) * 0x200)+ - "\nKEY "+Converter.byteArrToHexString(key)+ - "\nSection CTR "+Converter.byteArrToHexString(sectionCTR)+ - "\n______________________________________________________________"); - try { - BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath())); - // Check if skip was successful - if (bis.skip(offsetPositionInFile) != offsetPositionInFile) { - System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Failed to skip range "+offsetPositionInFile); - return; - } - - AesCtrDecryptSimple aesCtrDecryptSimple = new AesCtrDecryptSimple(key, sectionCTR, mediaStartOffset * 0x200); - - byte[] encryptedBlock; - byte[] dectyptedBlock; - - //----------------------------- Pre-set: skip non-necessary data -------------------------------- - - long startBlock = (rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) / 0x200; // <- pointing to place where actual data starts - int skipBytes; - - if (startBlock > 0) { - aesCtrDecryptSimple.skipNext(startBlock); - skipBytes = (int)(startBlock * 0x200); - if (bis.skip(skipBytes) != skipBytes) { - System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Failed to skip range "+skipBytes); - return; - } - } - - //----------------------------- Step 1: get starting bytes from the end of the junk block -------------------------------- - - // Since our data could be located in position with some offset from the decrypted block, let's skip bytes left. Considering the case when data is not aligned to block - skipBytes = (int) ((rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) - startBlock * 0x200); // <- How much bytes shall we skip to reach requested data start of sub-file - - if (skipBytes > 0) { - encryptedBlock = new byte[0x200]; - if (bis.read(encryptedBlock) == 0x200) { - dectyptedBlock = aesCtrDecryptSimple.decryptNext(encryptedBlock); - // If we have extra-small file that is less then a block and even more - if ((0x200 - skipBytes) > pfs0subFiles[subFileNumber].getSize()){ - streamOut.write(dectyptedBlock, skipBytes, (int) pfs0subFiles[subFileNumber].getSize()); // safe cast - bis.close(); - streamOut.close(); - return; - } - else - streamOut.write(dectyptedBlock, skipBytes, 0x200 - skipBytes); - } - else { - System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock"); - return; - } - startBlock++; - } - long endBlock = pfs0subFiles[subFileNumber].getSize() / 0x200 + startBlock; // <- pointing to place where any data related to this media-block ends - - //----------------------------- Step 2: Detect if we have junk data on the end of the final block -------------------------------- - int extraData = (int)(rawBlockDataStart+pfs0subFiles[subFileNumber].getOffset()+pfs0subFiles[subFileNumber].getSize() - (endBlock*0x200)); // safe cast - if (extraData < 0){ - endBlock--; - } - //----------------------------- Step 3: Read main part of data -------------------------------- - // Here we're reading main amount of bytes. We can read only less bytes. - while ( startBlock < endBlock) { - encryptedBlock = new byte[0x200]; - if (bis.read(encryptedBlock) == 0x200) { - //dectyptedBlock = aesCtr.decrypt(encryptedBlock); - dectyptedBlock = aesCtrDecryptSimple.decryptNext(encryptedBlock); - // Writing decrypted data to pipe - streamOut.write(dectyptedBlock); - } - else { - System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from bock"); - return; - } - startBlock++; - } - //----------------------------- Step 4: Read what's left -------------------------------- - // Now we have to find out if data overlaps to one more extra block - if (extraData > 0){ // In case we didn't get what we want - encryptedBlock = new byte[0x200]; - if (bis.read(encryptedBlock) == 0x200) { - dectyptedBlock = aesCtrDecryptSimple.decryptNext(encryptedBlock); - streamOut.write(dectyptedBlock, 0, extraData); - } - else { - System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from bock"); - return; - } - } - else if (extraData < 0){ // In case we can get more than we need - encryptedBlock = new byte[0x200]; - if (bis.read(encryptedBlock) == 0x200) { - dectyptedBlock = aesCtrDecryptSimple.decryptNext(encryptedBlock); - streamOut.write(dectyptedBlock, 0, 0x200 + extraData); // WTF ??? THIS LOOKS INCORRECT - } - else { - System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from last bock"); - return; - } - } - bis.close(); - streamOut.close(); - } - catch (Exception e){ - System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): "+e.getMessage()); - e.printStackTrace(); - } - System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Thread died"); - - - }); - workerThread.start(); - return streamIn; - } - - @Override - public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception{ - for (int i = 0; i < pfs0subFiles.length; i++){ - if (pfs0subFiles[i].getName().equals(subFileName)) - return getProviderSubFilePipedInpStream(i); - } - return null; - } - - public void printDebug(){ - log.debug(".:: PFS0EncryptedProvider ::.\n" + - "File name: " + file.getName() + "\n" + - "Raw block data start " + RainbowDump.formatDecHexString(rawBlockDataStart) + "\n" + - "Magic " + magic + "\n" + - "Files count " + RainbowDump.formatDecHexString(filesCount) + "\n" + - "String Table Size " + RainbowDump.formatDecHexString(stringTableSize) + "\n" + - "Padding " + Converter.byteArrToHexString(padding) + "\n\n" + - - "Offset position in file " + RainbowDump.formatDecHexString(offsetPositionInFile) + "\n" + - "Media Start Offset " + RainbowDump.formatDecHexString(mediaStartOffset) + "\n" + - "Media End Offset " + RainbowDump.formatDecHexString(mediaEndOffset) + "\n" - ); - for (PFS0subFile subFile : pfs0subFiles){ - log.debug( - "\nName: " + subFile.getName() + "\n" + - "Offset " + RainbowDump.formatDecHexString(subFile.getOffset()) + "\n" + - "Size " + RainbowDump.formatDecHexString(subFile.getSize()) + "\n" + - "Zeroes " + Converter.byteArrToHexString(subFile.getZeroes()) + "\n" + - "----------------------------------------------------------------" - ); - } - } -} diff --git a/src/main/java/libKonogonka/Tools/PFS0/PFS0Provider.java b/src/main/java/libKonogonka/Tools/PFS0/PFS0Provider.java index 5ff806e..484bef3 100644 --- a/src/main/java/libKonogonka/Tools/PFS0/PFS0Provider.java +++ b/src/main/java/libKonogonka/Tools/PFS0/PFS0Provider.java @@ -20,6 +20,7 @@ package libKonogonka.Tools.PFS0; import libKonogonka.Converter; import libKonogonka.RainbowDump; +import libKonogonka.Tools.NCA.NCASectionTableBlock.SuperBlockPFS0; import libKonogonka.ctraes.AesCtrBufferedInputStream; import libKonogonka.ctraes.AesCtrDecryptSimple; import org.apache.logging.log4j.LogManager; @@ -28,93 +29,124 @@ import org.apache.logging.log4j.Logger; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Arrays; +import java.util.LinkedList; -import static libKonogonka.Converter.*; +import static libKonogonka.Converter.getLEint; +import static libKonogonka.Converter.getLElong; public class PFS0Provider implements IPFS0Provider{ private final static Logger log = LogManager.getLogger(PFS0Provider.class); - private long rawFileDataStartOffset; - private String magic; private int filesCount; private int stringTableSize; private byte[] padding; private PFS0subFile[] pfs0subFiles; + //--------------------------------------- + private long rawBlockDataStart; private final File file; - private final long offsetPosition; // Where data starts, excluding header, string table etc. - private long mediaStartOffset; - private long mediaEndOffset; + private long offsetPositionInFile; + private long mediaStartOffset; // In 512-blocks + private long mediaEndOffset; // In 512-blocks + + private long ncaOffset; + private BufferedInputStream stream; + private SuperBlockPFS0 superBlockPFS0; private AesCtrDecryptSimple decryptor; - private final boolean encrypted; + private LinkedList pfs0SHA256hashes; - public PFS0Provider(File fileWithPfs0, - long offsetPosition, + private boolean encrypted; + + public PFS0Provider(File nspFile) throws Exception{ + this.file = nspFile; + createBufferedInputStream(); + readPfs0Header(); + } + + public PFS0Provider(File file, + long ncaOffset, + SuperBlockPFS0 superBlockPFS0, long mediaStartOffset, - long mediaEndOffset, - AesCtrDecryptSimple decryptor) throws Exception{ - this.file = fileWithPfs0; - this.offsetPosition = offsetPosition + mediaStartOffset*0x200; - this.encrypted = true; - + long mediaEndOffset) throws Exception{ + this.file = file; + this.ncaOffset = ncaOffset; + this.superBlockPFS0 = superBlockPFS0; + this.offsetPositionInFile = ncaOffset + mediaStartOffset * 0x200; this.mediaStartOffset = mediaStartOffset; this.mediaEndOffset = mediaEndOffset; - this.decryptor = decryptor; - proceedPfs0(); - } - - public PFS0Provider(File fileWithPfs0) throws Exception{ this(fileWithPfs0, 0); } - - public PFS0Provider(File fileWithPfs0, long offsetPosition) throws Exception{ - this.file = fileWithPfs0; - this.offsetPosition = offsetPosition; - this.encrypted = false; + this.rawBlockDataStart = superBlockPFS0.getPfs0offset(); //bufferedInputStream = new BufferedInputStream(Files.newInputStream(fileWithPfs0.toPath())); - proceedPfs0(); + createBufferedInputStream(); + long toSkip = offsetPositionInFile + superBlockPFS0.getHashTableOffset(); + if (toSkip != stream.skip(toSkip)) + throw new Exception("Can't skip bytes prior Hash Table offset"); + collectHashes(); + + createBufferedInputStream(); + toSkip = offsetPositionInFile + superBlockPFS0.getPfs0offset(); + if (toSkip != stream.skip(toSkip)) + throw new Exception("Can't skip bytes prior PFS0 offset"); + readPfs0Header(); } - private void proceedPfs0() throws Exception{ - BufferedInputStream bufferedInputStream; - if (encrypted) { - bufferedInputStream = new AesCtrBufferedInputStream(decryptor, - offsetPosition, - mediaStartOffset, - mediaEndOffset, - Files.newInputStream(file.toPath())); - } - else{ - bufferedInputStream = new BufferedInputStream(Files.newInputStream(file.toPath())); - } + public PFS0Provider(File file, + long ncaOffset, + SuperBlockPFS0 superBlockPFS0, + AesCtrDecryptSimple decryptor, + long mediaStartOffset, + long mediaEndOffset + ) throws Exception { + this.file = file; + this.ncaOffset = ncaOffset; + this.superBlockPFS0 = superBlockPFS0; + this.decryptor = decryptor; + this.offsetPositionInFile = ncaOffset + mediaStartOffset * 0x200; + this.mediaStartOffset = mediaStartOffset; + this.mediaEndOffset = mediaEndOffset; + this.rawBlockDataStart = superBlockPFS0.getPfs0offset(); + this.encrypted = true; - if (offsetPosition != bufferedInputStream.skip(offsetPosition)) - throw new Exception("PFS0Provider: Unable to skip initial offset: "+offsetPosition); + createAesCtrEncryptedBufferedInputStream(); + long toSkip = offsetPositionInFile + superBlockPFS0.getHashTableOffset(); + if (toSkip != stream.skip(toSkip)) + throw new Exception("Can't skip bytes prior Hash Table offset"); + collectHashes(); + createAesCtrEncryptedBufferedInputStream(); + toSkip = offsetPositionInFile + superBlockPFS0.getPfs0offset(); + if (toSkip != stream.skip(toSkip)) + throw new Exception("Can't skip bytes prior PFS0 offset"); + readPfs0Header(); + } + + private void readPfs0Header()throws Exception{ byte[] fileStartingBytes = new byte[0x10]; - // Read PFS0Provider, files count, header, padding (4 zero bytes) - if (bufferedInputStream.read(fileStartingBytes) != 0x10){ - throw new Exception("PFS0Provider: Unable to read starting bytes"); - } - rawFileDataStartOffset += 0x10; + if (0x10 != stream.read(fileStartingBytes)) + throw new Exception("Reading stream suddenly ended while trying to read starting 0x10 bytes"); + + // Update position + rawBlockDataStart += 0x10; // Check PFS0Provider magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII); if (! magic.equals("PFS0")){ - throw new Exception("PFS0Provider: Bad magic"); + throw new Exception("Bad magic"); } // Get files count filesCount = getLEint(fileStartingBytes, 0x4); if (filesCount <= 0 ) { - throw new Exception("PFS0Provider: Files count is too small"); + throw new Exception("Files count is too small"); } // Get string table stringTableSize = getLEint(fileStartingBytes, 0x8); if (stringTableSize <= 0 ){ - throw new Exception("PFS0Provider: String table is too small"); + throw new Exception("String table is too small"); } padding = Arrays.copyOfRange(fileStartingBytes, 0xc, 0x10); - //--------------------------------------------------------------------------------------------------------- + //------------------------------------------------------------------- pfs0subFiles = new PFS0subFile[filesCount]; long[] offsetsSubFiles = new long[filesCount]; @@ -123,23 +155,27 @@ public class PFS0Provider implements IPFS0Provider{ byte[][] zeroBytes = new byte[filesCount][]; byte[] fileEntryTable = new byte[0x18]; - for (int i=0; i(); + long hashTableOffset = superBlockPFS0.getHashTableOffset(); + long hashTableSize = superBlockPFS0.getHashTableSize(); + + if (hashTableOffset > 0){ + if (hashTableOffset != stream.skip(hashTableOffset)) + throw new Exception("Unable to skip bytes till Hash Table Offset: "+hashTableOffset); + } + for (int i = 0; i < hashTableSize / 0x20; i++){ + byte[] sectionHash = new byte[0x20]; + if (0x20 != stream.read(sectionHash)) + throw new Exception("Unable to read hash"); + pfs0SHA256hashes.add(sectionHash); } - bufferedInputStream.close(); } @Override - public boolean isEncrypted() { return encrypted; } + public boolean isEncrypted() { return true; } @Override public String getMagic() { return magic; } @Override @@ -168,80 +234,90 @@ public class PFS0Provider implements IPFS0Provider{ @Override public byte[] getPadding() { return padding; } @Override - public long getRawFileDataStart() { return rawFileDataStartOffset; } + public long getRawFileDataStart() { return rawBlockDataStart;} @Override public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; } @Override public File getFile(){ return file; } + @Override - public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception{ // TODO: Throw exceptions? - if (subFileNumber >= pfs0subFiles.length) { - throw new Exception("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists"); - } - PipedOutputStream streamOut = new PipedOutputStream(); - Thread workerThread; - - PipedInputStream streamIn = new PipedInputStream(streamOut); - - workerThread = new Thread(() -> { - System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Executing thread"); - try { - long subFileRealPosition = rawFileDataStartOffset + pfs0subFiles[subFileNumber].getOffset(); - BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); - if (bis.skip(subFileRealPosition) != subFileRealPosition) { - System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to skip requested offset"); - return; - } - - int readPice = 8388608; // 8mb NOTE: consider switching to 1mb 1048576 - - long readFrom = 0; - long realFileSize = pfs0subFiles[subFileNumber].getSize(); - - 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("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to read requested size from file."); - return; - } - streamOut.write(readBuf); - readFrom += readPice; - } - bis.close(); - streamOut.close(); - } catch (IOException ioe) { - System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to provide stream"); - ioe.printStackTrace(); - } - System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Thread died"); - }); - workerThread.start(); - return streamIn; - } - /** - * Some sugar - * */ - @Override - public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception { + public boolean exportContent(String saveToLocation, String subFileName){ for (int i = 0; i < pfs0subFiles.length; i++){ if (pfs0subFiles[i].getName().equals(subFileName)) - return getProviderSubFilePipedInpStream(i); + return exportContent(saveToLocation, i); } - return null; + return false; + } + @Override + public boolean exportContent(String saveToLocation, int subFileNumber){ + PFS0subFile subFile = pfs0subFiles[subFileNumber]; + File location = new File(saveToLocation); + location.mkdirs(); + + try (BufferedOutputStream extractedFileBOS = new BufferedOutputStream( + Files.newOutputStream(Paths.get(saveToLocation+File.separator+subFile.getName())))){ + if (encrypted) + createAesCtrEncryptedBufferedInputStream(); + else + createBufferedInputStream(); + + long subFileSize = subFile.getSize(); + + long toSkip = subFile.getOffset() + mediaStartOffset * 0x200 + rawBlockDataStart; + if (toSkip != stream.skip(toSkip)) + 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){ + log.error("File export failure", e); + return false; + } + return true; + } + + //TODO: REMOVE + @Override + public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception {return null;} + @Override + public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception {return null;} + + + public LinkedList getPfs0SHA256hashes() { + return pfs0SHA256hashes; } public void printDebug(){ log.debug(".:: PFS0Provider ::.\n" + - "File name: " + file.getName() + "\n\n" + - "Raw file data start: " + RainbowDump.formatDecHexString(rawFileDataStartOffset) + "\n" + + "File name: " + file.getName() + "\n" + + "Raw block data start " + RainbowDump.formatDecHexString(rawBlockDataStart) + "\n" + "Magic " + magic + "\n" + "Files count " + RainbowDump.formatDecHexString(filesCount) + "\n" + "String Table Size " + RainbowDump.formatDecHexString(stringTableSize) + "\n" + - "Padding " + Converter.byteArrToHexString(padding) + "\n" + "Padding " + Converter.byteArrToHexString(padding) + "\n\n" + + + "Offset position in file " + RainbowDump.formatDecHexString(offsetPositionInFile) + "\n" + + "Media Start Offset " + RainbowDump.formatDecHexString(mediaStartOffset) + "\n" + + "Media End Offset " + RainbowDump.formatDecHexString(mediaEndOffset) + "\n" ); for (PFS0subFile subFile : pfs0subFiles){ log.debug( @@ -253,4 +329,4 @@ public class PFS0Provider implements IPFS0Provider{ ); } } -} +} \ No newline at end of file diff --git a/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java b/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java index 26e6101..6d5dd4d 100644 --- a/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java +++ b/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java @@ -176,6 +176,17 @@ public class HFS0Provider implements ISuperProvider { workerThread.start(); return streamIn; } + + @Override + public boolean exportContent(String saveToLocation, String subFileName) throws Exception { + return false; + } + + @Override + public boolean exportContent(String saveToLocation, int subFileNumber) throws Exception { + return false; + } + /** * Sugar * */ diff --git a/src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java b/src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java index 426d8ea..b4e8f8f 100644 --- a/src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java +++ b/src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java @@ -32,18 +32,18 @@ public class AesCtrBufferedInputStream extends BufferedInputStream { private final long mediaOffsetPositionEnd; public AesCtrBufferedInputStream(AesCtrDecryptSimple decryptor, - long offsetPosition, + long ncaOffsetPosition, long mediaStartOffset, long mediaEndOffset, InputStream inputStream){ super(inputStream); this.decryptor = decryptor; - this.mediaOffsetPositionStart = offsetPosition + (mediaStartOffset * 0x200); - this.mediaOffsetPositionEnd = offsetPosition + (mediaEndOffset * 0x200); + this.mediaOffsetPositionStart = ncaOffsetPosition + (mediaStartOffset * 0x200); + this.mediaOffsetPositionEnd = ncaOffsetPosition + (mediaEndOffset * 0x200); - log.debug("\nOffset Position "+offsetPosition+ - "\nMediaOffsetPositionStart "+RainbowDump.formatDecHexString(mediaOffsetPositionStart)+ - "\nMediaOffsetPositionEnd "+RainbowDump.formatDecHexString(mediaOffsetPositionEnd)); + log.trace("\n Offset Position "+ncaOffsetPosition+ + "\n MediaOffsetPositionStart "+RainbowDump.formatDecHexString(mediaOffsetPositionStart)+ + "\n MediaOffsetPositionEnd "+RainbowDump.formatDecHexString(mediaOffsetPositionEnd)); } private byte[] decryptedBytes; @@ -214,7 +214,6 @@ public class AesCtrBufferedInputStream extends BufferedInputStream { fillDecryptedCache(); pseudoPos += n; pointerInsideDecryptedSection = (int) leftovers; - log.debug(" "+pseudoPos+" "+pointerInsideDecryptedSection); return n; } log.trace("6. Not encrypted ("+pseudoPos+"-"+(pseudoPos+n)+")"); diff --git a/src/main/java/libKonogonka/ctraes/AesCtrDecryptSimple.java b/src/main/java/libKonogonka/ctraes/AesCtrDecryptSimple.java index a9de59f..dbd7bee 100644 --- a/src/main/java/libKonogonka/ctraes/AesCtrDecryptSimple.java +++ b/src/main/java/libKonogonka/ctraes/AesCtrDecryptSimple.java @@ -70,6 +70,6 @@ public class AesCtrDecryptSimple { // IV for CTR == 16 bytes IVarray = new byte[0x10]; // Populate first 4 bytes taken from Header's section Block CTR (aka SecureValue) - System.arraycopy(Converter.flip(initialSectionCTR), 0x0, IVarray, 0x0, 0x4); + System.arraycopy(Converter.flip(initialSectionCTR), 0x0, IVarray, 0x0, 0x8); } } diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties index b7fcbfc..48ea019 100644 --- a/src/main/resources/log4j2.properties +++ b/src/main/resources/log4j2.properties @@ -15,7 +15,7 @@ status = error name = DebugConfigDevelopmentOnlyVerbose # Configure root logger level -rootLogger.level = TRACE +rootLogger.level = DEBUG # Root logger referring to console appender rootLogger.appenderRef.stdout.ref = consoleLogger