/** * Creates a FST by the given raw byte data * * @param fstData * raw decrypted FST data * @param contentsMappedByIndex * map of index/content * @return */ public static FST parseFST(byte[] fstData, Dictionary <int, Content> contentsMappedByIndex) { if (!Arrays.Equals(Arrays.copyOfRange(fstData, 0, 3), new byte[] { 0x46, 0x53, 0x54 })) { //throw new NullPointerException(); return(null); // throw new IllegalArgumentException("Not a FST. Maybe a wrong key?"); } int unknownValue = Utils.SwapEndianness(BitConverter.ToInt32(fstData, 0x04)); int contentCount = Utils.SwapEndianness(BitConverter.ToInt32(fstData, 0x08)); FST result = new FST(unknownValue, contentCount); int contentfst_offset = 0x20; int contentfst_size = 0x20 * contentCount; int fst_offset = contentfst_offset + contentfst_size; int fileCount = Utils.SwapEndianness(BitConverter.ToInt32(fstData, fst_offset + 0x08)); int fst_size = fileCount * 0x10; int nameOff = fst_offset + fst_size; int nameSize = nameOff + 1; // Get list with null-terminated Strings. Ends with \0\0. for (int i = nameOff; i < fstData.Length - 1; i++) { if (fstData[i] == 0 && fstData[i + 1] == 0) { nameSize = i - nameOff; } } Dictionary <int, ContentFSTInfo> contentFSTInfos = result.contentFSTInfos; for (int i = 0; i < contentCount; i++) { byte[] contentFST = Arrays.copyOfRange(fstData, contentfst_offset + (i * 0x20), contentfst_offset + ((i + 1) * 0x20)); contentFSTInfos.Add(i, ContentFSTInfo.parseContentFST(contentFST)); } byte[] fstSection = Arrays.copyOfRange(fstData, fst_offset, fst_offset + fst_size); byte[] nameSection = Arrays.copyOfRange(fstData, nameOff, nameOff + nameSize); FSTEntry root = result.root; FSTService.parseFST(root, fstSection, nameSection, contentsMappedByIndex, contentFSTInfos); return(result); }
private static byte[] getFSTEntryAsByte(String filename, WUDPartition partition, FST fst, WUDDiscReader discReader, byte[] key) { FSTEntry entry = getEntryByName(fst.root, filename); ContentFSTInfo info = fst.contentFSTInfos[((int)entry.contentFSTID)]; // Calculating the IV ByteBuffer byteBuffer = ByteBuffer.allocate(0x10); byteBuffer.position(0x08); long l = entry.fileOffset >> 16; byte[] ar = BitConverter.GetBytes(l); byte[] IV = new byte[0x10];//= copybyteBuffer.putLong(entry.fileOffset >> 16).ToArray(); Array.ConstrainedCopy(ar, 0, IV, 0x08, 0x08); return(discReader.readDecryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET + (long)partition.partitionOffset + (long)info.getOffset(), entry.fileOffset, (int)entry.fileSize, key, IV)); }
public static void parseFST(FSTEntry rootEntry, byte[] fstSection, byte[] namesSection, Dictionary <int, Content> contentsByIndex, Dictionary <int, ContentFSTInfo> contentsFSTByIndex) { int totalEntries = Utils.SwapEndianness(BitConverter.ToInt32(fstSection, 0x08)); int level = 0; int[] LEntry = new int[16]; int[] Entry = new int[16]; String[] pathStrings = new String[16]; for (int i = 0; i < 16; i++) { pathStrings[i] = ""; } Dictionary <int, FSTEntry> fstEntryToOffsetMap = new Dictionary <int, FSTEntry>(); Entry[level] = 0; LEntry[level++] = 0; fstEntryToOffsetMap.Add(0, rootEntry); int lastlevel = level; String path = "\\"; FSTEntry last = null; for (int i = 1; i < totalEntries; i++) { int entryOffset = i; if (level > 0) { while (LEntry[level - 1] == i) { level--; } } byte[] curEntry = Arrays.copyOfRange(fstSection, i * 0x10, (i + 1) * 0x10); FSTEntryParam entryParam = new FSTEntryParam(); if (lastlevel != level) { path = pathStrings[level] + getFullPath(level - 1, level, fstSection, namesSection, Entry); lastlevel = level; } String filename = getName(curEntry, namesSection); long fileOffset = Utils.SwapEndianness(BitConverter.ToInt32(curEntry, 0x04)); long fileSize = (uint)Utils.SwapEndianness(BitConverter.ToInt32(curEntry, 0x08)); short flags = Utils.SwapEndianness(BitConverter.ToInt16(curEntry, 0x0C)); short contentIndex = Utils.SwapEndianness(BitConverter.ToInt16(curEntry, 0x0E)); if ((curEntry[0] & FSTEntry.FSTEntry_notInNUS) == FSTEntry.FSTEntry_notInNUS) { entryParam.notInPackage = (true); } FSTEntry parent = null; if ((curEntry[0] & FSTEntry.FSTEntry_DIR) == FSTEntry.FSTEntry_DIR) { entryParam.isDir = (true); int parentOffset = (int)fileOffset; int nextOffset = (int)fileSize; parent = fstEntryToOffsetMap[parentOffset]; Entry[level] = i; LEntry[level++] = nextOffset; pathStrings[level] = path; if (level > 15) { //MessageBox.Show("level > 15"); break; } } else { entryParam.FileOffset = (fileOffset << 5); entryParam.FileSize = (fileSize); parent = fstEntryToOffsetMap[(Entry[level - 1])]; } entryParam.Flags = (flags); entryParam.Filename = (filename); entryParam.Path = (path); if (contentsByIndex != null) { Content content = contentsByIndex[contentIndex]; if (content == null) { //MessageBox.Show("Content for FST Entry not found"); } else { if (content.isHashed() && (content.getDecryptedFileSize() < (fileOffset << 5))) { // TODO: Figure out how this works... entryParam.FileOffset = (fileOffset); } entryParam.Content = (content); ContentFSTInfo contentFSTInfo = contentsFSTByIndex[(int)contentIndex]; if (contentFSTInfo == null) { //MessageBox.Show("ContentFSTInfo for FST Entry not found"); } else { content.contentFSTInfo = (contentFSTInfo); } } } entryParam.ContentFSTID = (contentIndex); entryParam.Parent = (parent); FSTEntry entry = new FSTEntry(entryParam); last = entry; fstEntryToOffsetMap.Add(entryOffset, entry); } }