package de.mas.jnustool; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import de.mas.jnustool.util.Util; public class FST { private TitleMetaData tmd; long totalContentSize = 0L; long totalContentSizeInNUS = 0L; List fileEntries = new ArrayList<>(); int totalContentCount = 0; int totalEntries = 0; int dirEntries = 0; private Directory FSTDirectory = new Directory("root"); private Directory contentDirectory = new Directory("root"); public FST(byte[] decrypteddata, TitleMetaData tmd) throws IOException { parse(decrypteddata,tmd); setTmd(tmd); buildDirectories(); } private void buildDirectories() { String contentfolder = ""; Directory curContent = contentDirectory; for(FEntry f : getFileEntries()){ if(!f.isDir() && f.isInNUSTitle()){ contentfolder = String.format("%08X",tmd.contents[f.getContentID()].ID); if(!contentDirectory.containsFolder(contentfolder)){ Directory newDir = new Directory(contentfolder); contentDirectory.addFolder(newDir); } curContent = contentDirectory.getFolder(contentfolder); Directory current = FSTDirectory; int i = 0; for(String s :f.getPathList()){ i++; //Content if(curContent.containsFolder(s)){ curContent = curContent.get(s); }else{ Directory newDir = new Directory(s); curContent.addFolder(newDir); curContent = newDir; } if(i==f.getPathList().size()){ curContent.addFile(f); } //FST if(current.containsFolder(s)){ current = current.get(s); }else{ Directory newDir = new Directory(s); current.addFolder(newDir); current = newDir; } if(i==f.getPathList().size()){ current.addFile(f); } } } } } private void parse(byte[] decrypteddata, TitleMetaData tmd) throws IOException { if(!Arrays.equals(Arrays.copyOfRange(decrypteddata, 0, 3), new byte[]{0x46,0x53,0x54})){ System.err.println("Not a FST. Maybe a wrong key?"); throw new IllegalArgumentException("File not a FST"); } this.totalContentCount = Util.getIntFromBytes(decrypteddata, 8); int base_offset = 0x20+totalContentCount*0x20; this.totalEntries = Util.getIntFromBytes(decrypteddata, base_offset+8); int nameOff = base_offset + totalEntries * 0x10; int level=0; int[] LEntry = new int[16]; int[] Entry = new int[16]; for(int i = 0;i 0) { while( LEntry[level-1] == i ) { level--; } } int offset = base_offset + i*0x10; //getting the type type = (int) decrypteddata[offset]+128; if((type & FEntry.DIR_FLAG) == 1) dir = true; if((type & FEntry.NOT_IN_NUSTITLE_FLAG) == 0 ) in_nus_title = false; //getting Name decrypteddata[offset] = 0; int nameoff_entry_offset = Util.getIntFromBytes(decrypteddata, offset); int j = 0; int nameoff_entry = nameOff + nameoff_entry_offset; while(decrypteddata[nameoff_entry + j] != 0){j++;} filename = new String(Arrays.copyOfRange(decrypteddata,nameoff_entry, nameoff_entry + j)); //getting offsets. save in two ways offset+=4; fileOffset = (long) Util.getIntFromBytes(decrypteddata, offset); offset+=4; fileLength = Util.getIntAsLongFromBytes(decrypteddata, offset); @SuppressWarnings("unused") int parentOffset = (int) fileOffset; int nextOffset = (int) fileLength; //grabbing flags offset+=4; int flags = Util.getShortFromBytes(decrypteddata, offset); if((flags & FEntry.EXTRACT_WITH_HASH_FLAG) > 0) extract_withHash = true; if((flags & FEntry.CHANGE_OFFSET_FLAG) == 0) fileOffset <<=5; //grabbing contentid offset+=2; contentID = Util.getShortFromBytes(decrypteddata, offset) ; //remember total size this.totalContentSize += fileLength; if(in_nus_title)this.totalContentSizeInNUS += fileLength; List pathList = new ArrayList<>(); //getting the full path of entry if(dir) { dirEntries++; Entry[level] = i; LEntry[level++] = nextOffset ; if( level > 15 ) // something is wrong! { break; } }else{ StringBuilder sb = new StringBuilder(); int k = 0; int nameoffoff,nameoff_entrypath; for( j=0; j getFileEntries() { return fileEntries; } public void setFileEntries(List fileEntries) { this.fileEntries = fileEntries; } public int getTotalContentCount() { return totalContentCount; } public void setTotalContentCount(int totalContentCount) { this.totalContentCount = totalContentCount; } public int getTotalEntries() { return totalEntries; } public void setTotalEntries(int totalEntries) { this.totalEntries = totalEntries; } public int getDirEntries() { return dirEntries; } public void setDirEntries(int dirEntries) { this.dirEntries = dirEntries; } @Override public String toString(){ return "entryCount: " + totalContentCount+ " entries: " + totalEntries; } public int getFileCount() { int i = 0; for(FEntry f: getFileEntries()){ if(!f.isDir()) i++; } return i; } public int getFileCountInNUS() { int i = 0; for(FEntry f: getFileEntries()){ if(!f.isDir() && f.isInNUSTitle()) i++; } return i; } public Directory getFSTDirectory() { return FSTDirectory; } public Directory getContentDirectory() { return contentDirectory; } public TitleMetaData getTmd() { return tmd; } public void setTmd(TitleMetaData tmd) { this.tmd = tmd; } }