diff --git a/.classpath b/.classpath
deleted file mode 100644
index fceb480..0000000
--- a/.classpath
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index bdb0cab..0000000
--- a/.gitattributes
+++ /dev/null
@@ -1,17 +0,0 @@
-# Auto detect text files and perform LF normalization
-* text=auto
-
-# Custom for Visual Studio
-*.cs diff=csharp
-
-# Standard to msysgit
-*.doc diff=astextplain
-*.DOC diff=astextplain
-*.docx diff=astextplain
-*.DOCX diff=astextplain
-*.dot diff=astextplain
-*.DOT diff=astextplain
-*.pdf diff=astextplain
-*.PDF diff=astextplain
-*.rtf diff=astextplain
-*.RTF diff=astextplain
diff --git a/.project b/.project
deleted file mode 100644
index 51d1814..0000000
--- a/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- JNusTool
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
-
- org.eclipse.jdt.core.javanature
-
-
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
deleted file mode 100644
index 82bae0f..0000000
--- a/.settings/org.eclipse.core.resources.prefs
+++ /dev/null
@@ -1,2 +0,0 @@
-eclipse.preferences.version=1
-encoding//src/Util.java=UTF-8
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index 3a21537..0000000
--- a/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,11 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.8
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.8
diff --git a/jar/JNUSTool.jar b/jar/JNUSTool.jar
deleted file mode 100644
index 2aeef33..0000000
Binary files a/jar/JNUSTool.jar and /dev/null differ
diff --git a/jar/JNUSTool_Java7.jar b/jar/JNUSTool_Java7.jar
deleted file mode 100644
index 4b7917a..0000000
Binary files a/jar/JNUSTool_Java7.jar and /dev/null differ
diff --git a/jar/config b/jar/config
deleted file mode 100644
index 3814033..0000000
--- a/jar/config
+++ /dev/null
@@ -1,2 +0,0 @@
-http://ccs.cdn.wup.shop.nintendo.net/ccs/download
-[COMMONKEY]
\ No newline at end of file
diff --git a/src/de/mas/jnustool/Content.java b/src/de/mas/jnustool/Content.java
index 8fd28ca..741130a 100644
--- a/src/de/mas/jnustool/Content.java
+++ b/src/de/mas/jnustool/Content.java
@@ -1,25 +1,27 @@
package de.mas.jnustool;
-import de.mas.jnustool.util.Util;
+import de.mas.jnustool.util.ConversionUtils;
-public class Content {
-
- int ID; // 0 0xB04
- short index; // 4 0xB08
- short type; // 6 0xB0A
- long size; // 8 0xB0C
- byte[] SHA2 = new byte[32]; // 16 0xB14
-
-
- public Content(int ID, short index, short type, long size, byte[] SHA2) {
+public class Content
+{
+ int ID; // 0 0xB04
+ short index; // 4 0xB08
+ short type; // 6 0xB0A
+ long size; // 8 0xB0C
+ byte[] SHA2 = new byte[32]; // 16 0xB14
+
+ public Content(int ID, short index, short type, long size, byte[] SHA2)
+ {
this.ID = ID;
this.index = index;
this.type = type;
this.size = size;
this.SHA2 = SHA2;
}
+
@Override
- public String toString(){
- return "ID: " + ID +" index: " + index + " type: " + type + " size: " + size + " SHA2: " + Util.ByteArrayToString(SHA2);
+ public String toString()
+ {
+ return "ID: " + ID + " index: " + index + " type: " + type + " size: " + size + " SHA2: " + ConversionUtils.ByteArrayToString(SHA2);
}
}
diff --git a/src/de/mas/jnustool/ContentInfo.java b/src/de/mas/jnustool/ContentInfo.java
index add9032..4b6c19c 100644
--- a/src/de/mas/jnustool/ContentInfo.java
+++ b/src/de/mas/jnustool/ContentInfo.java
@@ -1,32 +1,34 @@
package de.mas.jnustool;
-import de.mas.jnustool.util.Util;
+import de.mas.jnustool.util.ConversionUtils;
-public class ContentInfo {
- public short indexOffset; // 0 0x204
- public short commandCount; // 2 0x206
- public byte[] SHA2 = new byte[32]; // 12 0x208
-
- //TODO: Test, size checking
- /*
- * untested
- */
-
- public ContentInfo(byte[] info){
- this.indexOffset=(short)( ((info[0]&0xFF)<<8) | (info[1]&0xFF) );
- this.commandCount=(short)( ((info[2]&0xFF)<<8) | (info[3]&0xFF) );
- for(int i = 0;i<32;i++){
- this.SHA2[i] = info[4+i];
+public class ContentInfo
+{
+ public short indexOffset; // 0 0x204
+ public short commandCount; // 2 0x206
+ public byte[] SHA2 = new byte[32]; // 12 0x208
+
+ // TODO: Test, size checking
+ public ContentInfo(byte[] info)
+ {
+ this.indexOffset = (short) (((info[0] & 0xFF) << 8) | (info[1] & 0xFF));
+ this.commandCount = (short) (((info[2] & 0xFF) << 8) | (info[3] & 0xFF));
+ for (int i = 0; i < 32; i++)
+ {
+ this.SHA2[i] = info[4 + i];
}
}
- public ContentInfo(short indexOffset, short commandCount, byte[] SHA2) {
+ public ContentInfo(short indexOffset, short commandCount, byte[] SHA2)
+ {
this.indexOffset = indexOffset;
this.commandCount = commandCount;
this.SHA2 = SHA2;
}
+
@Override
- public String toString(){
- return "indexOffset: " + indexOffset +" commandCount: " + commandCount + " SHA2: " + Util.ByteArrayToString(SHA2);
+ public String toString()
+ {
+ return "indexOffset: " + indexOffset + " commandCount: " + commandCount + " SHA2: " + ConversionUtils.ByteArrayToString(SHA2);
}
-}
+}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/Directory.java b/src/de/mas/jnustool/Directory.java
index 75bc4e1..cc8f4d5 100644
--- a/src/de/mas/jnustool/Directory.java
+++ b/src/de/mas/jnustool/Directory.java
@@ -5,89 +5,85 @@ import java.util.TreeMap;
import javax.swing.tree.DefaultMutableTreeNode;
-public class Directory {
+public class Directory
+{
String name = "";
- TreeMap folder = new TreeMap<>();
- TreeMap files = new TreeMap<>();
-
- public Directory get(String s){
+ TreeMap folder = new TreeMap<>();
+ TreeMap files = new TreeMap<>();
+
+ public Directory get(String s)
+ {
return folder.get(s);
}
-
- public Directory(String name){
+
+ public Directory(String name)
+ {
setName(name);
}
- public boolean containsFolder(String s){
+ public boolean containsFolder(String s)
+ {
return folder.containsKey(s);
}
-
- public Directory getFolder(String s){
+
+ public Directory getFolder(String s)
+ {
return folder.get(s);
}
-
- public Directory addFolder(Directory s){
- return folder.put(s.getName(),s);
+
+ public Directory addFolder(Directory s)
+ {
+ return folder.put(s.getName(), s);
}
- public boolean containsFile(String s){
- return files.containsKey(s);
+
+ public FEntry addFile(FEntry s)
+ {
+ return files.put(s.getFileName(), s);
}
-
- public FEntry getFile(String s){
- return files.get(s);
- }
-
- public FEntry addFile(FEntry s){
- return files.put(s.getFileName(),s);
- }
-
- public String getName() {
+
+ public String getName()
+ {
return name;
}
- public void setName(String name) {
+ public void setName(String name)
+ {
this.name = name;
}
-
-
- public Collection getFolder() {
- return folder.values();
+
+ public Collection getFolder()
+ {
+ return folder.values();
}
-
-
- public Collection getFiles() {
- return files.values();
- }
-
- public void setFiles(TreeMap files) {
- this.files = files;
+ public Collection getFiles()
+ {
+ return files.values();
}
@Override
- public String toString(){
+ public String toString()
+ {
System.out.println(name + ":");
- for(Directory d : folder.values()){
- System.out.println(d);
- }
- for(String s : files.keySet()){
- System.out.println(s);
- }
+ folder.values().forEach(System.out::println);
+ files.keySet().forEach(System.out::println);
+
return "";
}
-
- public DefaultMutableTreeNode getNodes(){
+
+ public DefaultMutableTreeNode getNodes()
+ {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(getName());
-
- for(Directory f: getFolder()){
- node.add(f.getNodes());
- }
-
- for(FEntry f: getFiles()){
+
+ for (Directory directory : getFolder())
+ {
+ node.add(directory.getNodes());
+ }
+
+ for (FEntry f : getFiles())
+ {
node.add(new DefaultMutableTreeNode(f));
}
return node;
}
-
-
-}
+}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/FEntry.java b/src/de/mas/jnustool/FEntry.java
index b19b9f5..57b8815 100644
--- a/src/de/mas/jnustool/FEntry.java
+++ b/src/de/mas/jnustool/FEntry.java
@@ -2,6 +2,7 @@ package de.mas.jnustool;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
import java.util.List;
import de.mas.jnustool.util.Decryption;
@@ -9,29 +10,30 @@ import de.mas.jnustool.util.Downloader;
import de.mas.jnustool.util.ExitException;
import de.mas.jnustool.util.Settings;
-public class FEntry {
+public class FEntry
+{
private FST fst;
-
+
public static int DIR_FLAG = 1;
- public static int NOT_IN_NUSTITLE_FLAG = 0x80;
- public static int EXTRACT_WITH_HASH_FLAG = 0x440;
+ public static int NOT_IN_NUS_TITLE_FLAG = 0x80;
+ public static int EXTRACT_WITH_HASH_FLAG = 0x440;
public static int CHANGE_OFFSET_FLAG = 0x04;
-
+
private boolean dir = false;
private boolean in_nus_title = false;
private boolean extract_withHash = false;
-
+
private String fileName = "";
private String path = "";
- private long fileOffset = 0L;
+ private long fileOffset = 0L;
private long fileLength = 0;
private int contentID = 0;
- private int NUScontentID = 0;
+ private int NUSContentID = 0;
private List pathList;
-
- public FEntry(String path, String filename, int contentID,int NUScontentID, long fileOffset, long fileLength, boolean dir,
- boolean in_nus_title, boolean extract_withHash, List pathList,FST fst) {
+ public FEntry(String path, String filename, int contentID, int NUSContentID, long fileOffset, long fileLength, boolean dir,
+ boolean in_nus_title, boolean extract_withHash, List pathList, FST fst)
+ {
setPath(path);
setFileName(filename);
setContentID(contentID);
@@ -40,202 +42,246 @@ public class FEntry {
setDir(dir);
setInNusTitle(in_nus_title);
setExtractWithHash(extract_withHash);
- setNUScontentID(NUScontentID);
+ setNUSContentID(NUSContentID);
setPathList(pathList);
this.fst = fst;
}
- public boolean isDir() {
+ public boolean isDir()
+ {
return dir;
}
- private void setDir(boolean dir) {
+ private void setDir(boolean dir)
+ {
this.dir = dir;
}
- public boolean isInNUSTitle() {
+ public boolean isInNUSTitle()
+ {
return in_nus_title;
}
- private void setInNusTitle(boolean in_nus_title) {
+ private void setInNusTitle(boolean in_nus_title)
+ {
this.in_nus_title = in_nus_title;
}
- public boolean isExtractWithHash() {
+ public boolean isExtractWithHash()
+ {
return extract_withHash;
}
- private void setExtractWithHash(boolean extract_withHash) {
+ private void setExtractWithHash(boolean extract_withHash)
+ {
this.extract_withHash = extract_withHash;
}
- public String getFileName() {
+ public String getFileName()
+ {
return fileName;
}
- private void setFileName(String filename) {
+ private void setFileName(String filename)
+ {
this.fileName = filename;
}
- public String getPath() {
- return path;
- }
-
- public String getFullPath() {
+ public String getFullPath()
+ {
return path + fileName;
}
- private void setPath(String path) {
+ private void setPath(String path)
+ {
this.path = path;
}
- public long getFileOffset() {
+ public long getFileOffset()
+ {
return fileOffset;
}
- private void setFileOffset(long fileOffset) {
+ private void setFileOffset(long fileOffset)
+ {
this.fileOffset = fileOffset;
}
- public int getContentID() {
+ public int getContentID()
+ {
return contentID;
}
- private void setContentID(int contentID) {
+ private void setContentID(int contentID)
+ {
this.contentID = contentID;
}
- public long getFileLength() {
+ public long getFileLength()
+ {
return fileLength;
}
- private void setFileLength(long fileLength) {
+ private void setFileLength(long fileLength)
+ {
this.fileLength = fileLength;
}
-
+
@Override
- public String toString(){
- return getFullPath() + " Content ID:" + contentID + " Size: " + fileLength +"MB Offset: " + fileOffset;
+ public String toString()
+ {
+ return getFullPath() + " Content ID:" + contentID + " Size: " + fileLength + "MB Offset: " + fileOffset;
}
- public int getNUScontentID() {
- return NUScontentID;
+ public int getNUSContentID()
+ {
+ return NUSContentID;
}
- private void setNUScontentID(int nUScontentID) {
- NUScontentID = nUScontentID;
+ private void setNUSContentID(int nusContentID)
+ {
+ NUSContentID = nusContentID;
}
-
- private void createFolder() {
+
+ private void createFolder()
+ {
long titleID = getTitleID();
- String [] path = getFullPath().split("/");
- File f = new File (String.format("%016X", titleID));
- if(!f.exists())f.mkdir();
-
- String folder = String.format("%016X", titleID) +"/";
- File folder_ = null;
- for(int i = 0;i getPathList() {
+ public List getPathList()
+ {
return pathList;
}
- public void setPathList(List pathList) {
+ public void setPathList(List pathList)
+ {
this.pathList = pathList;
}
- public String getContentPath() {
- return fst.getTmd().getContentPath() + "/" + String.format("%08X", getNUScontentID()) + ".app";
+ public String getContentPath()
+ {
+ return fst.getTmd().getContentPath() + "/" + String.format("%08X", getNUSContentID()) + ".app";
}
- public long getTitleID() {
+ public long getTitleID()
+ {
return fst.getTmd().titleID;
}
- public TIK getTicket() {
+ public TIK getTicket()
+ {
return fst.getTmd().getNUSTitle().getTicket();
}
-
-
-
-
-
-}
+}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/FEntryDownloader.java b/src/de/mas/jnustool/FEntryDownloader.java
index 755927b..886f8c9 100644
--- a/src/de/mas/jnustool/FEntryDownloader.java
+++ b/src/de/mas/jnustool/FEntryDownloader.java
@@ -4,19 +4,22 @@ import java.util.concurrent.Callable;
public class FEntryDownloader implements Callable
{
- FEntry f;
- public void setTitle(FEntry f){
- this.f = f;
- }
- public FEntryDownloader(FEntry f){
- setTitle(f);
- }
-
-
- @Override
- public Integer call() throws Exception {
- f.downloadAndDecrypt();
- return null;
+ private FEntry fEntry;
+
+ public void setTitle(FEntry fEntry)
+ {
+ this.fEntry = fEntry;
}
-}
+ public FEntryDownloader(FEntry fEntry)
+ {
+ setTitle(fEntry);
+ }
+
+ @Override
+ public Integer call() throws Exception
+ {
+ fEntry.downloadAndDecrypt();
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/FST.java b/src/de/mas/jnustool/FST.java
index b4887cf..a555ff9 100644
--- a/src/de/mas/jnustool/FST.java
+++ b/src/de/mas/jnustool/FST.java
@@ -5,310 +5,288 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import de.mas.jnustool.util.Util;
+import de.mas.jnustool.util.ConversionUtils;
-public class FST {
+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);
+
+ 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);
+ private void buildDirectories()
+ {
+ String contentFolder;
+ Directory curContent;
+ 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++;
-
+ curContent = contentDirectory.getFolder(contentFolder);
+
+ Directory current = FSTDirectory;
+ int i = 0;
+
+ for (String s : f.getPathList())
+ {
+ i++;
+
//Content
- if(curContent.containsFolder(s)){
+ if (curContent.containsFolder(s))
+ {
curContent = curContent.get(s);
- }else{
+ } else
+ {
Directory newDir = new Directory(s);
curContent.addFolder(newDir);
curContent = newDir;
- }
- if(i==f.getPathList().size()){
+ }
+ if (i == f.getPathList().size())
+ {
curContent.addFile(f);
}
-
-
+
//FST
- if(current.containsFolder(s)){
+ if (current.containsFolder(s))
+ {
current = current.get(s);
- }else{
+ } else
+ {
Directory newDir = new Directory(s);
current.addFolder(newDir);
current = newDir;
}
- if(i==f.getPathList().size()){
+ 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})){
+ 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);
+
+ this.totalContentCount = ConversionUtils.getIntFromBytes(decryptedData, 8);
+ int base_offset = 0x20 + totalContentCount * 0x20;
+
+ this.totalEntries = ConversionUtils.getIntFromBytes(decryptedData, base_offset + 8);
int nameOff = base_offset + totalEntries * 0x10;
-
- int level=0;
+
+ int level = 0;
int[] LEntry = new int[16];
int[] Entry = new int[16];
-
- for(int i = 0;i 0)
+
+ if (level > 0)
{
- while( LEntry[level-1] == i )
- {
+ while (LEntry[level - 1] == i)
+ {
level--;
}
}
-
-
- int offset = base_offset + i*0x10;
-
+
+ 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;
-
-
+ type = (int) decryptedData[offset] + 128;
+ if ((type & FEntry.DIR_FLAG) == 1)
+ {
+ dir = true;
+ }
+ if ((type & FEntry.NOT_IN_NUS_TITLE_FLAG) == 0)
+ {
+ in_nus_title = false;
+ }
+
//getting Name
- decrypteddata[offset] = 0;
- int nameoff_entry_offset = Util.getIntFromBytes(decrypteddata, offset);
+ decryptedData[offset] = 0;
+ int nameOff_entry_offset = ConversionUtils.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));
-
+ 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;
+ offset += 4;
+ fileOffset = (long) ConversionUtils.getIntFromBytes(decryptedData, offset);
+ offset += 4;
+ fileLength = ConversionUtils.getIntAsLongFromBytes(decryptedData, offset);
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) ;
-
-
+ offset += 4;
+ int flags = ConversionUtils.getShortFromBytes(decryptedData, offset);
+ if ((flags & FEntry.EXTRACT_WITH_HASH_FLAG) > 0)
+ {
+ extract_withHash = true;
+ }
+ if ((flags & FEntry.CHANGE_OFFSET_FLAG) == 0)
+ {
+ fileOffset <<= 5;
+ }
+
+ //grabbing content id
+ offset += 2;
+ contentID = ConversionUtils.getShortFromBytes(decryptedData, offset);
+
//remember total size
this.totalContentSize += fileLength;
- if(in_nus_title)this.totalContentSizeInNUS += fileLength;
-
+ if (in_nus_title)
+ {
+ this.totalContentSizeInNUS += fileLength;
+ }
+
List pathList = new ArrayList<>();
//getting the full path of entry
- if(dir)
+ if (dir)
{
dirEntries++;
Entry[level] = i;
- LEntry[level++] = nextOffset ;
- if( level > 15 ) // something is wrong!
+ LEntry[level++] = nextOffset;
+ if (level > 15) // something is wrong!
{
break;
}
- }else{
- StringBuilder sb = new StringBuilder();
- int k = 0;
- int nameoffoff,nameoff_entrypath;
+ } else
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+ int k;
+ int nameOffOff, nameOff_entryPath;
- for( j=0; j getFileEntries() {
+ public List 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() {
+ 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 String toString()
+ {
+ return "entryCount: " + totalContentCount + " entries: " + totalEntries;
}
-
- public int getFileCount() {
+ public int getFileCount()
+ {
int i = 0;
- for(FEntry f: getFileEntries()){
- if(!f.isDir())
+ 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() {
+ 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() {
+ public TitleMetaData getTmd()
+ {
return tmd;
}
- public void setTmd(TitleMetaData tmd) {
+ public void setTmd(TitleMetaData tmd)
+ {
this.tmd = tmd;
}
-
-
-
-}
+}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/NUSTitle.java b/src/de/mas/jnustool/NUSTitle.java
index a4b97e0..1c443af 100644
--- a/src/de/mas/jnustool/NUSTitle.java
+++ b/src/de/mas/jnustool/NUSTitle.java
@@ -11,203 +11,230 @@ import de.mas.jnustool.util.Downloader;
import de.mas.jnustool.util.ExitException;
import de.mas.jnustool.util.Settings;
-public class NUSTitle {
+public class NUSTitle
+{
private TitleMetaData tmd;
private TIK ticket;
private FST fst;
private long titleID;
- public NUSTitle(long titleId,String key) throws ExitException{
+
+ public NUSTitle(long titleId, String key) throws ExitException
+ {
setTitleID(titleId);
- try {
- if(Settings.downloadContent){
- File f = new File(getContentPath());
- if(!f.exists())f.mkdir();
+ try
+ {
+ if (Settings.downloadContent)
+ {
+ File f = new File(getContentPath());
+ if (!f.exists())
+ {
+ Files.createDirectory(f.toPath());
+ }
}
- if(Settings.downloadContent){
-
+ if (Settings.downloadContent)
+ {
+
File f = new File(getContentPath() + "/" + "tmd");
- if(!(f.exists() && Settings.skipExistingTMDTICKET)){
+ if (!(f.exists() && Settings.skipExistingTMDTICKET))
+ {
System.out.println("Downloading TMD");
- Downloader.getInstance().downloadTMD(titleId,getContentPath());
- }else{
+ Downloader.getInstance().downloadTMD(titleId, getContentPath());
+ } else
+ {
System.out.println("Skipped download of TMD. Already existing");
}
f = new File(getContentPath() + "/" + "cetk");
- if(!(f.exists() && Settings.skipExistingTMDTICKET)){
- if(key == null){
+ if (!(f.exists() && Settings.skipExistingTMDTICKET))
+ {
+ if (key == null)
+ {
System.out.print("Downloading Ticket");
- Downloader.getInstance().downloadTicket(titleId,getContentPath());
+ Downloader.getInstance().downloadTicket(titleId, getContentPath());
}
- }else{
+ } else
+ {
System.out.println("Skipped download of ticket. Already existing");
}
}
-
- if(Settings.useCachedFiles){
+
+ if (Settings.useCachedFiles)
+ {
File f = new File(getContentPath() + "/" + "tmd");
- if(f.exists()){
+ if (f.exists())
+ {
System.out.println("Using cached TMD.");
tmd = new TitleMetaData(f);
- }else{
+ } else
+ {
System.out.println("No cached TMD found.");
}
}
-
- if(tmd == null){
- if(Settings.downloadWhenCachedFilesMissingOrBroken){
- if(Settings.useCachedFiles) System.out.println("Getting missing tmd from Server!");
+
+ if (tmd == null)
+ {
+ if (Settings.downloadWhenCachedFilesMissingOrBroken)
+ {
+ if (Settings.useCachedFiles)
+ {
+ System.out.println("Getting missing tmd from Server!");
+ }
tmd = new TitleMetaData(Downloader.getInstance().downloadTMDToByteArray(titleId));
- }else{
+ } else
+ {
System.out.println("Downloading of missing files is not enabled. Exiting");
throw new ExitException("TMD missing.");
}
- }
-
- if(key != null){
+ }
+
+ if (key != null)
+ {
System.out.println("Using ticket from parameter.");
- ticket = new TIK(key,titleId);
- }else{
- if(Settings.useCachedFiles){
+ ticket = new TIK(key, titleId);
+ } else
+ {
+ if (Settings.useCachedFiles)
+ {
File f = new File(getContentPath() + "/" + "cetk");
- if(f.exists()){
+ if (f.exists())
+ {
System.out.println("Using cached cetk.");
- ticket = new TIK(f,titleId);
- }else{
+ ticket = new TIK(f, titleId);
+ } else
+ {
System.out.println("No cached ticket found.");
}
}
- if(ticket == null){
- if(Settings.downloadWhenCachedFilesMissingOrBroken){
- if(Settings.useCachedFiles) System.out.println("getting missing ticket");
- ticket = new TIK(Downloader.getInstance().downloadTicketToByteArray(titleId),tmd.titleID);
- }else{
+ if (ticket == null)
+ {
+ if (Settings.downloadWhenCachedFilesMissingOrBroken)
+ {
+ if (Settings.useCachedFiles)
+ {
+ System.out.println("getting missing ticket");
+ }
+ ticket = new TIK(Downloader.getInstance().downloadTicketToByteArray(titleId), tmd.titleID);
+ } else
+ {
System.out.println("Downloading of missing files is not enabled. Exiting");
throw new ExitException("Ticket missing.");
}
}
}
-
- if(Settings.downloadContent){
+
+ if (Settings.downloadContent)
+ {
File f = new File(getContentPath() + "/" + String.format("%08x", tmd.contents[0].ID) + ".app");
- if(!(f.exists() && Settings.skipExistingFiles)){
+ if (!(f.exists() && Settings.skipExistingFiles))
+ {
System.out.println("Downloading FST (" + String.format("%08x", tmd.contents[0].ID) + ")");
- Downloader.getInstance().downloadContent(titleId,tmd.contents[0].ID,getContentPath());
- }else{
- if(f.length() != tmd.contents[0].size){
- if(Settings.downloadWhenCachedFilesMissingOrBroken){
+ Downloader.getInstance().downloadContent(titleId, tmd.contents[0].ID, getContentPath());
+ } else
+ {
+ if (f.length() != tmd.contents[0].size)
+ {
+ if (Settings.downloadWhenCachedFilesMissingOrBroken)
+ {
System.out.println("FST already existing, but broken. Downloading it again.");
- Downloader.getInstance().downloadContent(titleId,tmd.contents[0].ID,getContentPath());
- }else{
+ Downloader.getInstance().downloadContent(titleId, tmd.contents[0].ID, getContentPath());
+ } else
+ {
System.out.println("FST already existing, but broken. No download allowed.");
throw new ExitException("FST missing.");
- }
- }else{
+ }
+ } else
+ {
System.out.println("Skipped download of FST. Already existing");
}
-
+
}
-
+
}
-
- Decryption decryption = new Decryption(ticket.getDecryptedKey(),0);
+
+ Decryption decryption = new Decryption(ticket.getDecryptedKey(), 0);
byte[] encryptedFST = null;
- if(Settings.useCachedFiles){
+ if (Settings.useCachedFiles)
+ {
String path = getContentPath() + "/" + String.format("%08x", tmd.contents[0].ID) + ".app";
- File f = new File(path);
- if(f.exists()){
+ File f = new File(path);
+ if (f.exists())
+ {
System.out.println("Using cached FST");
Path file = Paths.get(path);
encryptedFST = Files.readAllBytes(file);
- }else{
- System.out.println("No cached FST (" + String.format("%08x", tmd.contents[0].ID) + ") found.");
+ } else
+ {
+ System.out.println("No cached FST (" + String.format("%08x", tmd.contents[0].ID) + ") found.");
}
}
- if(encryptedFST == null){
- if(Settings.downloadWhenCachedFilesMissingOrBroken){
- if(Settings.useCachedFiles)System.out.println("Getting FST from server.");
- encryptedFST = Downloader.getInstance().downloadContentToByteArray(titleId,tmd.contents[0].ID);
- }else{
+ if (encryptedFST == null)
+ {
+ if (Settings.downloadWhenCachedFilesMissingOrBroken)
+ {
+ if (Settings.useCachedFiles)
+ {
+ System.out.println("Getting FST from server.");
+ }
+ encryptedFST = Downloader.getInstance().downloadContentToByteArray(titleId, tmd.contents[0].ID);
+ } else
+ {
System.out.println("Downloading of missing files is not enabled. Exiting");
throw new ExitException("");
}
- }
+ }
byte[] decryptedFST = decryption.decrypt(encryptedFST);
-
- fst = new FST(decryptedFST,tmd);
+
+ fst = new FST(decryptedFST, tmd);
tmd.setNUSTitle(this);
-
- if(Settings.downloadContent){
+
+ if (Settings.downloadContent)
+ {
tmd.downloadContents();
}
-
- System.out.println("Total Size of Content Files: " + ((int)((getTotalContentSize()/1024.0/1024.0)*100))/100.0 +" MB");
- System.out.println("Total Size of Decrypted Files: " + ((int)((fst.getTotalContentSizeInNUS()/1024.0/1024.0)*100))/100.0 +" MB");
+
+ System.out.println("Total Size of Content Files: " + ((int) ((getTotalContentSize() / 1024.0 / 1024.0) * 100)) / 100.0 + " MB");
+ System.out.println("Total Size of Decrypted Files: " + ((int) ((fst.getTotalContentSizeInNUS() / 1024.0 / 1024.0) * 100)) / 100.0 + " MB");
System.out.println("Entries: " + fst.getTotalEntries());
System.out.println("Entries: " + fst.getFileCount());
System.out.println("Files in NUSTitle: " + fst.getFileCountInNUS());
-
- } catch (IOException e) {
- // TODO Auto-generated catch block
+
+ } catch (IOException e)
+ {
e.printStackTrace();
}
}
-
-
-
-
-
- public FST getFst() {
+ public FST getFst()
+ {
return fst;
}
-
- public void setFst(FST fst) {
- this.fst = fst;
- }
-
- public TitleMetaData getTmd() {
- return tmd;
- }
-
- public void setTmd(TitleMetaData tmd) {
- this.tmd = tmd;
- }
-
- public TIK getTicket() {
+ public TIK getTicket()
+ {
return ticket;
}
-
-
- public void setTicket(TIK ticket) {
- this.ticket = ticket;
- }
-
- public long getTotalContentSize() {
+ public long getTotalContentSize()
+ {
return tmd.getTotalContentSize();
}
-
-
- public String getContentPath() {
+ public String getContentPath()
+ {
return getContentPathPrefix() + String.format("%016X", getTitleID());
}
-
- public String getContentPathPrefix() {
+
+ public String getContentPathPrefix()
+ {
return "tmp_";
}
-
-
- private long getTitleID() {
+ private long getTitleID()
+ {
return titleID;
}
-
- private void setTitleID(long titleId) {
- this.titleID = titleId;
+
+ private void setTitleID(long titleId)
+ {
+ this.titleID = titleId;
}
-
-
-
-}
+}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/Starter.java b/src/de/mas/jnustool/Starter.java
deleted file mode 100644
index b3f4a54..0000000
--- a/src/de/mas/jnustool/Starter.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package de.mas.jnustool;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-
-import de.mas.jnustool.gui.NUSGUI;
-import de.mas.jnustool.util.Downloader;
-import de.mas.jnustool.util.ExitException;
-import de.mas.jnustool.util.Util;
-
-public class Starter {
-
- public static void main(String[] args) {
- System.out.println("JNUSTool 0.0.2 - pre alpha - by Maschell");
- System.out.println();
- try {
- readConfig();
- } catch (IOException e) {
- System.err.println("Error while reading config! Needs to be:");
- System.err.println("DOWNLOAD URL BASE");
- System.err.println("COMMONKEY");
- return;
- }
- if(args.length != 0){
- long titleID = Util.StringToLong(args[0]);
- String key = null;
- if( args.length > 1 && args[1].length() == 32){
- key = args[1].substring(0, 32);
- }
- NUSGUI m;
- try {
- m = new NUSGUI(new NUSTitle(titleID, key), null);
- } catch (ExitException e) {
- System.out.println("Error: " + e.getMessage());
- return;
- }
- m.setVisible(true);
- }else{
- System.out.println("Need parameters: TITLEID [KEY]");
- }
-
- }
-
- public static void readConfig() throws IOException {
- BufferedReader in = new BufferedReader(new FileReader(new File("config")));
- Downloader.URL_BASE = in.readLine();
- Util.commonKey = Util.hexStringToByteArray(in.readLine());
- in.close();
-
- }
-
-}
diff --git a/src/de/mas/jnustool/TIK.java b/src/de/mas/jnustool/TIK.java
index 76c0aed..c54a18e 100644
--- a/src/de/mas/jnustool/TIK.java
+++ b/src/de/mas/jnustool/TIK.java
@@ -5,67 +5,63 @@ import java.io.IOException;
import java.io.RandomAccessFile;
import de.mas.jnustool.util.Decryption;
-import de.mas.jnustool.util.Util;
+import de.mas.jnustool.util.ConversionUtils;
-public class TIK {
- public static int KEY_LENGTH = 16;
- private byte[] encryptedKey = new byte[16];
- private byte[] decryptedKey = new byte[16];
-
- public TIK(File cetk,long titleid) throws IOException{
- parse(cetk);
- calculateDecryptedKey(titleid);
- }
+public class TIK
+{
+ private byte[] encryptedKey = new byte[16];
+ private byte[] decryptedKey = new byte[16];
- public TIK(String ticketKey,long titleid) {
- setEncryptedKey(ticketKey);
- calculateDecryptedKey(titleid);
- }
-
+ public TIK(File commonETicket, long titleID) throws IOException
+ {
+ parse(commonETicket);
+ calculateDecryptedKey(titleID);
+ }
- public TIK(byte[] file, long titleID) throws IOException {
- parse(file);
- calculateDecryptedKey(titleID);
- }
+ public TIK(String ticketKey, long titleID)
+ {
+ setEncryptedKey(ticketKey);
+ calculateDecryptedKey(titleID);
+ }
- private void calculateDecryptedKey(long titleid) {
- Decryption decryption = new Decryption(Util.commonKey,titleid);
- decryptedKey = decryption.decrypt(encryptedKey);
- }
+ public TIK(byte[] file, long titleID) throws IOException
+ {
+ parse(file);
+ calculateDecryptedKey(titleID);
+ }
- private void parse(byte[] cetk) throws IOException {
- System.arraycopy(cetk, 0x1bf, this.encryptedKey, 0,16);
- }
-
- private void parse(File cetk) throws IOException {
- RandomAccessFile f = new RandomAccessFile(cetk, "r");
- f.seek(0x1bf);
- f.read(this.encryptedKey, 0, 16);
- f.close();
- }
-
- public void setEncryptedKey(String key) {
- this.encryptedKey = Util.hexStringToByteArray(key);
- }
-
- public byte[] getEncryptedKey() {
- return encryptedKey;
- }
+ private void calculateDecryptedKey(long titleID)
+ {
+ Decryption decryption = new Decryption(ConversionUtils.commonKey, titleID);
+ decryptedKey = decryption.decrypt(encryptedKey);
+ }
- public void setEncryptedKey(byte[] encryptedKey) {
- this.encryptedKey = encryptedKey;
- }
+ private void parse(byte[] commonETicketBytes) throws IOException
+ {
+ System.arraycopy(commonETicketBytes, 0x1bf, this.encryptedKey, 0, 16);
+ }
- public byte[] getDecryptedKey() {
- return decryptedKey;
- }
+ private void parse(File commonETicket) throws IOException
+ {
+ RandomAccessFile f = new RandomAccessFile(commonETicket, "r");
+ f.seek(0x1bf);
+ f.read(this.encryptedKey, 0, 16);
+ f.close();
+ }
- public void setDecryptedKey(byte[] decryptedKey) {
- this.decryptedKey = decryptedKey;
- }
+ public void setEncryptedKey(String key)
+ {
+ this.encryptedKey = ConversionUtils.hexStringToByteArray(key);
+ }
- @Override
- public String toString(){
- return "encrypted key: " + Util.ByteArrayToString(encryptedKey)+ " decrypted key: " + Util.ByteArrayToString(decryptedKey);
- }
-}
+ public byte[] getDecryptedKey()
+ {
+ return decryptedKey;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "encrypted key: " + ConversionUtils.ByteArrayToString(encryptedKey) + " decrypted key: " + ConversionUtils.ByteArrayToString(decryptedKey);
+ }
+}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/TitleMetaData.java b/src/de/mas/jnustool/TitleMetaData.java
index 908348c..90993c8 100644
--- a/src/de/mas/jnustool/TitleMetaData.java
+++ b/src/de/mas/jnustool/TitleMetaData.java
@@ -3,73 +3,73 @@ package de.mas.jnustool;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.security.DigestInputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
import de.mas.jnustool.util.Downloader;
import de.mas.jnustool.util.ExitException;
import de.mas.jnustool.util.Settings;
-import de.mas.jnustool.util.Util;
+import de.mas.jnustool.util.ConversionUtils;
+
+public class TitleMetaData
+{
+ int signatureType; // 0x000
+ byte[] signature = new byte[0x100]; // 0x004
+ byte[] issuer = new byte[0x40]; // 0x140
+ byte version; // 0x180
+ byte CACRLVersion; // 0x181
+ byte signerCRLVersion; // 0x182
+ long systemVersion; // 0x184
+ long titleID; // 0x18C
+ int titleType; // 0x194
+ short groupID; // 0x198
+ byte[] reserved = new byte[62]; // 0x19A
+ int accessRights; // 0x1D8
+ short titleVersion; // 0x1DC
+ short contentCount; // 0x1DE
+ short bootIndex; // 0x1E0
+ byte[] SHA2 = new byte[32]; // 0x1E4
+ ContentInfo[] contentInfoArray = new ContentInfo[64]; // 0x1E4
+ Content[] contents; // 0x1E4
+
-public class TitleMetaData {
- int signatureType; // 0x000
- byte[] signature = new byte[0x100]; // 0x004
- byte[] issuer = new byte[0x40]; // 0x140
- byte version; // 0x180
- byte CACRLVersion; // 0x181
- byte signerCRLVersion; // 0x182
- long systemVersion; // 0x184
- long titleID; // 0x18C
- int titleType; // 0x194
- short groupID; // 0x198
- byte[] reserved = new byte[62]; // 0x19A
- int accessRights; // 0x1D8
- short titleVersion; // 0x1DC
- short contentCount; // 0x1DE
- short bootIndex; // 0x1E0
- byte[] SHA2 = new byte[32]; // 0x1E4
- ContentInfo[] contentInfos = new ContentInfo[64]; // 0x1E4
- Content[] contents; // 0x1E4
-
-
private NUSTitle nus;
-
+
private long totalContentSize;
-
- public TitleMetaData(File tmd) throws IOException {
+
+ public TitleMetaData(File tmd) throws IOException
+ {
parse(tmd);
setTotalContentSize();
}
- public TitleMetaData(byte[] downloadTMDToByteArray) throws IOException {
- if(downloadTMDToByteArray != null){
- File tempFile = File.createTempFile("bla","blubb");
+ public TitleMetaData(byte[] downloadTMDToByteArray) throws IOException
+ {
+ if (downloadTMDToByteArray != null)
+ {
+ File tempFile = File.createTempFile("bla", "blubb");
FileOutputStream fos = new FileOutputStream(tempFile);
fos.write(downloadTMDToByteArray);
fos.close();
parse(tempFile);
setTotalContentSize();
- }else{
+ } else
+ {
System.err.println("Invalid TMD");
throw new IllegalArgumentException("Invalid TMD");
}
}
- private void parse(File tmd) throws IOException {
- RandomAccessFile f = new RandomAccessFile(tmd, "r");
+ private void parse(File tmd) throws IOException
+ {
+ RandomAccessFile f = new RandomAccessFile(tmd, "r");
f.seek(0);
this.signatureType = f.readInt();
-
+
f.read(signature, 0, 0x100);
f.seek(0x140);
f.read(issuer, 0, 0x40);
-
+
f.seek(0x180);
this.version = f.readByte();
this.CACRLVersion = f.readByte();
@@ -89,123 +89,148 @@ public class TitleMetaData {
f.seek(0x1E4);
f.read(SHA2, 0, 32);
f.seek(0x204);
-
+
short indexOffset;
short commandCount;
-
- for(int i =0;i<64;i++){
- f.seek(0x204+(0x24*i));
- indexOffset =f.readShort();
- commandCount =f.readShort();
- byte[] buffer = new byte[0x20]; // 16 0xB14
+
+ for (int i = 0; i < 64; i++)
+ {
+ f.seek(0x204 + (0x24 * i));
+ indexOffset = f.readShort();
+ commandCount = f.readShort();
+ byte[] buffer = new byte[0x20]; // 16 0xB14
f.read(buffer, 0, 0x20);
- this.contentInfos[i] = new ContentInfo(indexOffset,commandCount,buffer);
+ this.contentInfoArray[i] = new ContentInfo(indexOffset, commandCount, buffer);
}
this.contents = new Content[contentCount];
-
- int ID; // 0 0xB04
- short index; // 4 0xB08
- short type; // 6 0xB0A
- long size; // 8 0xB0C
-
-
- for(int i =0;i nodesCheckingState;
+ HashSet checkedPaths = new HashSet();
+
+ // Defining a new event type for the checking mechanism and preparing event-handling mechanism
+ protected EventListenerList listenerList = new EventListenerList();
+
+ public class CheckChangeEvent extends EventObject
+ {
+ private static final long serialVersionUID = -8100230309044193368L;
+
+ public CheckChangeEvent(Object source)
+ {
+ super(source);
+ }
+ }
+
+ public interface CheckChangeEventListener extends EventListener
+ {
+ void checkStateChanged(CheckChangeEvent event);
+ }
+
+ void fireCheckChangeEvent(CheckChangeEvent evt)
+ {
+ Object[] listeners = listenerList.getListenerList();
+ for (int i = 0; i < listeners.length; i++)
+ {
+ if (listeners[i] == CheckChangeEventListener.class)
+ {
+ ((CheckChangeEventListener) listeners[i + 1]).checkStateChanged(evt);
+ }
+ }
+ }
+
+ // Override
+ public void setModel(TreeModel newModel)
+ {
+ super.setModel(newModel);
+ resetCheckingState();
+ }
+
+ // New method that returns only the checked paths (totally ignores original "selection" mechanism)
+ public TreePath[] getCheckedPaths()
+ {
+ return checkedPaths.toArray(new TreePath[checkedPaths.size()]);
+ }
+
+ private void resetCheckingState()
+ {
+ nodesCheckingState = new HashMap<>();
+ checkedPaths = new HashSet<>();
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) getModel().getRoot();
+ if (node == null)
+ {
+ return;
+ }
+ addSubtreeToCheckingStateTracking(node);
+ }
+
+ // Creating data structure of the current model for the checking mechanism
+ private void addSubtreeToCheckingStateTracking(DefaultMutableTreeNode node)
+ {
+ TreeNode[] path = node.getPath();
+ TreePath tp = new TreePath(path);
+ CheckedNode cn = new CheckedNode(false, node.getChildCount() > 0, false);
+ nodesCheckingState.put(tp, cn);
+ for (int i = 0; i < node.getChildCount(); i++)
+ {
+ addSubtreeToCheckingStateTracking((DefaultMutableTreeNode) tp.pathByAddingChild(node.getChildAt(i)).getLastPathComponent());
+ }
+ }
+
+ // Overriding cell renderer by a class that ignores the original "selection" mechanism
+ // It decides how to show the nodes due to the checking-mechanism
+ private class CheckBoxCellRenderer extends JPanel implements TreeCellRenderer
+ {
+ private static final long serialVersionUID = -7341833835878991719L;
+ JCheckBox checkBox;
+
+ public CheckBoxCellRenderer()
+ {
+ super();
+ this.setLayout(new BorderLayout());
+ checkBox = new JCheckBox();
+ add(checkBox, BorderLayout.CENTER);
+ setOpaque(false);
+ }
+
+ @Override
+ public Component getTreeCellRendererComponent(JTree tree, Object value,
+ boolean selected, boolean expanded, boolean leaf, int row,
+ boolean hasFocus)
+ {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
+ Object obj = node.getUserObject();
+ TreePath tp = new TreePath(node.getPath());
+ CheckedNode cn = nodesCheckingState.get(tp);
+ if (cn == null)
+ {
+ return this;
+ }
+ checkBox.setSelected(cn.isSelected);
+ if (obj instanceof FEntry)
+ {
+ FEntry f = (FEntry) obj;
+ checkBox.setText(f.getFileName());
+ } else
+ {
+ checkBox.setText(obj.toString());
+ }
+
+ checkBox.setOpaque(cn.isSelected && cn.hasChildren && !cn.allChildrenSelected);
+ return this;
+ }
+ }
+
+ public JCheckBoxTree(NUSTitle nus)
+ {
+ super();
+ setModel(new DefaultTreeModel(nus.getFst().getFSTDirectory().getNodes()));
- // Defining data structure that will enable to fast check-indicate the state of each node
- // It totally replaces the "selection" mechanism of the JTree
- private class CheckedNode {
- boolean isSelected;
- boolean hasChildren;
- boolean allChildrenSelected;
+ // Disabling toggling by double-click
+ this.setToggleClickCount(0);
+ // Overriding cell renderer by new one defined above
+ CheckBoxCellRenderer cellRenderer = new CheckBoxCellRenderer();
+ this.setCellRenderer(cellRenderer);
- public CheckedNode(boolean isSelected_, boolean hasChildren_, boolean allChildrenSelected_) {
- isSelected = isSelected_;
- hasChildren = hasChildren_;
- allChildrenSelected = allChildrenSelected_;
- }
- }
- HashMap nodesCheckingState;
- HashSet checkedPaths = new HashSet();
+ // Overriding selection model by an empty one
+ DefaultTreeSelectionModel defaultTreeSelectionModel = new DefaultTreeSelectionModel()
+ {
+ private static final long serialVersionUID = -8190634240451667286L;
- // Defining a new event type for the checking mechanism and preparing event-handling mechanism
- protected EventListenerList listenerList = new EventListenerList();
+ // Totally disabling the selection mechanism
+ public void setSelectionPath(TreePath path)
+ {
+ }
- public class CheckChangeEvent extends EventObject {
- private static final long serialVersionUID = -8100230309044193368L;
+ public void addSelectionPath(TreePath path)
+ {
+ }
- public CheckChangeEvent(Object source) {
- super(source);
- }
- }
+ public void removeSelectionPath(TreePath path)
+ {
+ }
- public interface CheckChangeEventListener extends EventListener {
- public void checkStateChanged(CheckChangeEvent event);
- }
+ public void setSelectionPaths(TreePath[] pPaths)
+ {
+ }
+ };
- public void addCheckChangeEventListener(CheckChangeEventListener listener) {
- listenerList.add(CheckChangeEventListener.class, listener);
- }
- public void removeCheckChangeEventListener(CheckChangeEventListener listener) {
- listenerList.remove(CheckChangeEventListener.class, listener);
- }
+ // Calling checking mechanism on mouse click
+ this.addMouseListener(new MouseListener()
+ {
+ public void mouseClicked(MouseEvent arg0)
+ {
+ TreePath tp = selfPointer.getPathForLocation(arg0.getX(), arg0.getY());
+ if (tp == null)
+ {
+ return;
+ }
+ boolean checkMode = !nodesCheckingState.get(tp).isSelected;
+ checkSubTree(tp, checkMode);
+ updatePredecessorsWithCheckMode(tp, checkMode);
+ // Firing the check change event
+ fireCheckChangeEvent(new CheckChangeEvent(new Object()));
+ // Repainting tree after the data structures were updated
+ selfPointer.repaint();
+ }
- void fireCheckChangeEvent(CheckChangeEvent evt) {
- Object[] listeners = listenerList.getListenerList();
- for (int i = 0; i < listeners.length; i++) {
- if (listeners[i] == CheckChangeEventListener.class) {
- ((CheckChangeEventListener) listeners[i + 1]).checkStateChanged(evt);
- }
- }
- }
+ public void mouseEntered(MouseEvent arg0)
+ {
+ }
- // Override
- public void setModel(TreeModel newModel) {
- super.setModel(newModel);
- resetCheckingState();
- }
+ public void mouseExited(MouseEvent arg0)
+ {
+ }
- // New method that returns only the checked paths (totally ignores original "selection" mechanism)
- public TreePath[] getCheckedPaths() {
- return checkedPaths.toArray(new TreePath[checkedPaths.size()]);
- }
+ public void mousePressed(MouseEvent arg0)
+ {
+ }
- // Returns true in case that the node is selected, has children but not all of them are selected
- public boolean isSelectedPartially(TreePath path) {
- CheckedNode cn = nodesCheckingState.get(path);
- return cn.isSelected && cn.hasChildren && !cn.allChildrenSelected;
- }
+ public void mouseReleased(MouseEvent arg0)
+ {
+ }
+ });
- private void resetCheckingState() {
- nodesCheckingState = new HashMap();
- checkedPaths = new HashSet();
- DefaultMutableTreeNode node = (DefaultMutableTreeNode)getModel().getRoot();
- if (node == null) {
- return;
- }
- addSubtreeToCheckingStateTracking(node);
- }
+ this.setSelectionModel(defaultTreeSelectionModel);
+ }
- // Creating data structure of the current model for the checking mechanism
- private void addSubtreeToCheckingStateTracking(DefaultMutableTreeNode node) {
- TreeNode[] path = node.getPath();
- TreePath tp = new TreePath(path);
- CheckedNode cn = new CheckedNode(false, node.getChildCount() > 0, false);
- nodesCheckingState.put(tp, cn);
- for (int i = 0 ; i < node.getChildCount() ; i++) {
- addSubtreeToCheckingStateTracking((DefaultMutableTreeNode) tp.pathByAddingChild(node.getChildAt(i)).getLastPathComponent());
- }
- }
-
- // Overriding cell renderer by a class that ignores the original "selection" mechanism
- // It decides how to show the nodes due to the checking-mechanism
- private class CheckBoxCellRenderer extends JPanel implements TreeCellRenderer {
- private static final long serialVersionUID = -7341833835878991719L;
- JCheckBox checkBox;
- public CheckBoxCellRenderer() {
- super();
- this.setLayout(new BorderLayout());
- checkBox = new JCheckBox();
- add(checkBox, BorderLayout.CENTER);
- setOpaque(false);
- }
-
- @Override
- public Component getTreeCellRendererComponent(JTree tree, Object value,
- boolean selected, boolean expanded, boolean leaf, int row,
- boolean hasFocus) {
- DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
- Object obj = node.getUserObject();
- TreePath tp = new TreePath(node.getPath());
- CheckedNode cn = nodesCheckingState.get(tp);
- if (cn == null) {
- return this;
- }
- checkBox.setSelected(cn.isSelected);
- if(obj instanceof FEntry){
- FEntry f = (FEntry) obj;
- checkBox.setText(f.getFileName());
- }else{
- checkBox.setText(obj.toString());
- }
-
- checkBox.setOpaque(cn.isSelected && cn.hasChildren && ! cn.allChildrenSelected);
- return this;
- }
- }
-
- public JCheckBoxTree(NUSTitle nus) {
- super();
-
-
-
- setModel(new DefaultTreeModel(nus.getFst().getFSTDirectory().getNodes()));
-
- // Disabling toggling by double-click
- this.setToggleClickCount(0);
- // Overriding cell renderer by new one defined above
- CheckBoxCellRenderer cellRenderer = new CheckBoxCellRenderer();
- this.setCellRenderer(cellRenderer);
-
- // Overriding selection model by an empty one
- DefaultTreeSelectionModel dtsm = new DefaultTreeSelectionModel() {
- private static final long serialVersionUID = -8190634240451667286L;
- // Totally disabling the selection mechanism
- public void setSelectionPath(TreePath path) {
- }
- public void addSelectionPath(TreePath path) {
- }
- public void removeSelectionPath(TreePath path) {
- }
- public void setSelectionPaths(TreePath[] pPaths) {
- }
- };
-
-
-
- // Calling checking mechanism on mouse click
- this.addMouseListener(new MouseListener() {
- public void mouseClicked(MouseEvent arg0) {
- TreePath tp = selfPointer.getPathForLocation(arg0.getX(), arg0.getY());
- if (tp == null) {
- return;
- }
- boolean checkMode = ! nodesCheckingState.get(tp).isSelected;
- checkSubTree(tp, checkMode);
- updatePredecessorsWithCheckMode(tp, checkMode);
- // Firing the check change event
- fireCheckChangeEvent(new CheckChangeEvent(new Object()));
- // Repainting tree after the data structures were updated
- selfPointer.repaint();
- }
- public void mouseEntered(MouseEvent arg0) {
- }
- public void mouseExited(MouseEvent arg0) {
- }
- public void mousePressed(MouseEvent arg0) {
- }
- public void mouseReleased(MouseEvent arg0) {
- }
- });
-
- this.setSelectionModel(dtsm);
- }
-
- // When a node is checked/unchecked, updating the states of the predecessors
- protected void updatePredecessorsWithCheckMode(TreePath tp, boolean check) {
- TreePath parentPath = tp.getParentPath();
- // If it is the root, stop the recursive calls and return
- if (parentPath == null) {
- return;
- }
- CheckedNode parentCheckedNode = nodesCheckingState.get(parentPath);
- DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) parentPath.getLastPathComponent();
- parentCheckedNode.allChildrenSelected = true;
- parentCheckedNode.isSelected = false;
- for (int i = 0 ; i < parentNode.getChildCount() ; i++) {
- TreePath childPath = parentPath.pathByAddingChild(parentNode.getChildAt(i));
- CheckedNode childCheckedNode = nodesCheckingState.get(childPath);
- // It is enough that even one subtree is not fully selected
- // to determine that the parent is not fully selected
- if (! childCheckedNode.allChildrenSelected) {
- parentCheckedNode.allChildrenSelected = false;
- }
- // If at least one child is selected, selecting also the parent
- if (childCheckedNode.isSelected) {
- parentCheckedNode.isSelected = true;
- }
- }
- if (parentCheckedNode.isSelected) {
- checkedPaths.add(parentPath);
- } else {
- checkedPaths.remove(parentPath);
- }
- // Go to upper predecessor
- updatePredecessorsWithCheckMode(parentPath, check);
- }
-
- // Recursively checks/unchecks a subtree
- protected void checkSubTree(TreePath tp, boolean check) {
- CheckedNode cn = nodesCheckingState.get(tp);
- cn.isSelected = check;
- DefaultMutableTreeNode node = (DefaultMutableTreeNode) tp.getLastPathComponent();
- for (int i = 0 ; i < node.getChildCount() ; i++) {
- checkSubTree(tp.pathByAddingChild(node.getChildAt(i)), check);
- }
- cn.allChildrenSelected = check;
- if (check) {
- checkedPaths.add(tp);
- } else {
- checkedPaths.remove(tp);
- }
- }
+ // When a node is checked/unchecked, updating the states of the predecessors
+ protected void updatePredecessorsWithCheckMode(TreePath tp, boolean check)
+ {
+ TreePath parentPath = tp.getParentPath();
+ // If it is the root, stop the recursive calls and return
+ if (parentPath == null)
+ {
+ return;
+ }
+ CheckedNode parentCheckedNode = nodesCheckingState.get(parentPath);
+ DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) parentPath.getLastPathComponent();
+ parentCheckedNode.allChildrenSelected = true;
+ parentCheckedNode.isSelected = false;
+ for (int i = 0; i < parentNode.getChildCount(); i++)
+ {
+ TreePath childPath = parentPath.pathByAddingChild(parentNode.getChildAt(i));
+ CheckedNode childCheckedNode = nodesCheckingState.get(childPath);
+ // It is enough that even one subtree is not fully selected
+ // to determine that the parent is not fully selected
+ if (!childCheckedNode.allChildrenSelected)
+ {
+ parentCheckedNode.allChildrenSelected = false;
+ }
+ // If at least one child is selected, selecting also the parent
+ if (childCheckedNode.isSelected)
+ {
+ parentCheckedNode.isSelected = true;
+ }
+ }
+ if (parentCheckedNode.isSelected)
+ {
+ checkedPaths.add(parentPath);
+ } else
+ {
+ checkedPaths.remove(parentPath);
+ }
+ // Go to upper predecessor
+ updatePredecessorsWithCheckMode(parentPath, check);
+ }
+ // Recursively checks/un-checks a subtree
+ protected void checkSubTree(TreePath tp, boolean check)
+ {
+ CheckedNode cn = nodesCheckingState.get(tp);
+ cn.isSelected = check;
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) tp.getLastPathComponent();
+ for (int i = 0; i < node.getChildCount(); i++)
+ {
+ checkSubTree(tp.pathByAddingChild(node.getChildAt(i)), check);
+ }
+ cn.allChildrenSelected = check;
+ if (check)
+ {
+ checkedPaths.add(tp);
+ } else
+ {
+ checkedPaths.remove(tp);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/gui/NUSGUI.java b/src/de/mas/jnustool/gui/NUSGUI.java
deleted file mode 100644
index 5c061f7..0000000
--- a/src/de/mas/jnustool/gui/NUSGUI.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package de.mas.jnustool.gui;
-
-import java.awt.BorderLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ForkJoinPool;
-
-import javax.swing.JButton;
-import javax.swing.JFrame;
-import javax.swing.JScrollPane;
-import javax.swing.tree.DefaultMutableTreeNode;
-import javax.swing.tree.TreePath;
-
-import de.mas.jnustool.FEntry;
-import de.mas.jnustool.NUSTitle;
-import de.mas.jnustool.util.Settings;
-import de.mas.jnustool.FEntryDownloader;
-
-public class NUSGUI extends JFrame {
-
- private static final long serialVersionUID = 4648172894076113183L;
-
- public NUSGUI(NUSTitle nus,Settings mode) {
- super();
- setSize(800, 600);
- getContentPane().setLayout(new BorderLayout(0, 0));
-
- final JCheckBoxTree cbt = new JCheckBoxTree(nus);
- JScrollPane qPane = new JScrollPane(cbt,
- JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
- JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
- this.getContentPane().add(qPane);
-
-
- JButton btnNewButton = new JButton("Download");
- btnNewButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- new Thread(new Runnable() { public void run() {
- ForkJoinPool pool = ForkJoinPool.commonPool();
- List list = new ArrayList<>();
-
-
- TreePath[] paths = cbt.getCheckedPaths();
- for (TreePath tp : paths) {
-
- Object obj = tp.getPath()[tp.getPath().length-1];
- if(((DefaultMutableTreeNode)obj).getUserObject() instanceof FEntry){
- FEntry f = (FEntry) ((DefaultMutableTreeNode)obj).getUserObject();
- if(!f.isDir() && f.isInNUSTitle()){
- list.add(new FEntryDownloader(f));
- }
-
- }
- }
- pool.invokeAll(list);
- System.out.println("Done!");
- }}).start();
-
- }
- });
- getContentPane().add(btnNewButton, BorderLayout.SOUTH);
-
- /*cbt.addCheckChangeEventListener(new JCheckBoxTree.CheckChangeEventListener() {
- public void checkStateChanged(JCheckBoxTree.CheckChangeEvent event) {
- System.out.println("event");
- TreePath[] paths = cbt.getCheckedPaths();
- for (TreePath tp : paths) {
- for (Object pathPart : tp.getPath()) {
- System.out.print(pathPart + ",");
- }
- System.out.println();
- }
- }
- });*/
-
- this.setDefaultCloseOperation(EXIT_ON_CLOSE);
- }
-}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/util/Decryption.java b/src/de/mas/jnustool/util/Decryption.java
index dfd59b4..5e92d0e 100644
--- a/src/de/mas/jnustool/util/Decryption.java
+++ b/src/de/mas/jnustool/util/Decryption.java
@@ -1,15 +1,7 @@
package de.mas.jnustool.util;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
+import de.mas.jnustool.FEntry;
+import de.mas.jnustool.TIK;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@@ -17,354 +9,399 @@ import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
-import de.mas.jnustool.FEntry;
-import de.mas.jnustool.TIK;
+public class Decryption
+{
+ private Cipher cipher2;
+ private byte[] decryptedKey;
-public class Decryption {
- Cipher cipher2;
-
- public Decryption(TIK ticket){
+ public Decryption(TIK ticket)
+ {
this(ticket.getDecryptedKey());
}
-
- public Decryption(byte[] decryptedKey){
- this(decryptedKey,0);
+
+ public Decryption(byte[] decryptedKey)
+ {
+ this(decryptedKey, 0);
}
-
- public Decryption(byte[] decryptedKey, long titleId) {
- try {
+
+ public Decryption(byte[] decryptedKey, long titleId)
+ {
+ try
+ {
cipher2 = Cipher.getInstance("AES/CBC/NoPadding");
this.decryptedKey = decryptedKey;
init(titleId);
-
- } catch (NoSuchAlgorithmException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (NoSuchPaddingException e) {
- // TODO Auto-generated catch block
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e)
+ {
e.printStackTrace();
}
}
+ private void init(byte[] IV)
+ {
+ init(decryptedKey, IV);
+ }
- byte[] decryptedKey;
-
-
- private void init(byte[] IV) {
- init(decryptedKey,IV);
+ private void init(long titleID)
+ {
+ init(ByteBuffer.allocate(16).putLong(titleID).array());
}
-
- private void init(long titleid) {
- init(ByteBuffer.allocate(16).putLong(titleid).array());
- }
-
- public void init(byte[] decryptedKey,long titleid){
- init(decryptedKey,ByteBuffer.allocate(16).putLong(titleid).array());
- }
-
- public void init(byte[] decryptedKey,byte[] iv){
- try {
- this.decryptedKey = decryptedKey;
+
+ public void init(byte[] decryptedKey, byte[] iv)
+ {
+ try
+ {
+ this.decryptedKey = decryptedKey;
SecretKeySpec secretKeySpec = new SecretKeySpec(decryptedKey, "AES");
- cipher2.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
- } catch (Exception e) {
- // TODO Auto-generated catch block
+ cipher2.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
+ } catch (Exception e)
+ {
e.printStackTrace();
}
}
-
- public byte[] decrypt(byte[] input){
- try {
- return cipher2.doFinal(input);
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- // TODO Auto-generated catch block
+
+ public byte[] decrypt(byte[] input)
+ {
+ try
+ {
+ return cipher2.doFinal(input);
+ } catch (IllegalBlockSizeException | BadPaddingException e)
+ {
e.printStackTrace();
}
- return input;
+ return input;
}
-
- public byte[] decrypt(byte[] input,int len){
- return decrypt(input,0,len);
- }
-
- public byte[] decrypt(byte[] input,int offset,int len){
- try {
- return cipher2.doFinal(input, offset, len);
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- // TODO Auto-generated catch block
+
+ public byte[] decrypt(byte[] input, int offset, int len)
+ {
+ try
+ {
+ return cipher2.doFinal(input, offset, len);
+ } catch (IllegalBlockSizeException | BadPaddingException e)
+ {
e.printStackTrace();
}
- return input;
+ return input;
}
-
+
byte[] IV;
- public byte[] decryptFileChunk(byte[] blockBuffer, int BLOCKSIZE, byte[] IV) {
- return decryptFileChunk(blockBuffer,0,BLOCKSIZE, IV);
+
+ public byte[] decryptFileChunk(byte[] blockBuffer, int blockSize, byte[] IV)
+ {
+ return decryptFileChunk(blockBuffer, 0, blockSize, IV);
}
-
- public byte[] decryptFileChunk(byte[] blockBuffer, int offset, int BLOCKSIZE, byte[] IV) {
- if(IV != null) this.IV = IV;
+
+ public byte[] decryptFileChunk(byte[] blockBuffer, int offset, int blockSize, byte[] IV)
+ {
+ if (IV != null)
+ {
+ this.IV = IV;
+ }
init(this.IV);
- byte[] output = decrypt(blockBuffer,offset,BLOCKSIZE);
- this.IV = Arrays.copyOfRange(blockBuffer,BLOCKSIZE-16, BLOCKSIZE);
+ byte[] output = decrypt(blockBuffer, offset, blockSize);
+ this.IV = Arrays.copyOfRange(blockBuffer, blockSize - 16, blockSize);
return output;
}
- byte[] hash = new byte[20];
- byte[] h0 = new byte[20];
-
- public byte[] decryptFileChunkHash(byte[] blockBuffer, int BLOCKSIZE, int block, int contentID){
- if(BLOCKSIZE != 0x10000) throw new IllegalArgumentException("Blocksize not supported");
+ byte[] hash = new byte[20];
+ byte[] h0 = new byte[20];
+
+ public byte[] decryptFileChunkHash(byte[] blockBuffer, int blockSize, int block, int contentID)
+ {
+ if (blockSize != 0x10000)
+ {
+ throw new IllegalArgumentException("Block size not supported");
+ }
IV = new byte[16];
- IV[1] = (byte)contentID;
-
- byte[] hashes = decryptFileChunk(blockBuffer,0x0400,IV);
-
- System.arraycopy(hashes, (int) (0x14*block), IV, 0, 16);
- System.arraycopy(hashes, (int) (0x14*block), h0, 0, 20);
-
- if( block == 0 )
- IV[1] ^= (byte)contentID;
-
- byte[] output = decryptFileChunk(blockBuffer,0x400,0xFC00,IV);
-
+ IV[1] = (byte) contentID;
+
+ byte[] hashes = decryptFileChunk(blockBuffer, 0x0400, IV);
+
+ System.arraycopy(hashes, 0x14 * block, IV, 0, 16);
+ System.arraycopy(hashes, 0x14 * block, h0, 0, 20);
+
+ if (block == 0)
+ {
+ IV[1] ^= (byte) contentID;
+ }
+
+ byte[] output = decryptFileChunk(blockBuffer, 0x400, 0xFC00, IV);
+
hash = hash(output);
- if(block == 0){
-
+ if (block == 0)
+ {
+ assert hash != null;
hash[1] ^= contentID;
-
}
- if(Arrays.equals(hash, h0)){
+ if (Arrays.equals(hash, h0))
+ {
//System.out.println("checksum right");
- }
- else{
+ } else
+ {
System.out.println("checksum failed");
- System.out.println(Util.ByteArrayToString(hash));
- System.out.println(Util.ByteArrayToString(h0));
+ System.out.println(ConversionUtils.ByteArrayToString(hash));
+ System.out.println(ConversionUtils.ByteArrayToString(h0));
throw new IllegalArgumentException("checksumfail");
}
return output;
}
-
- public static byte[] hash(byte[] hashThis) {
- try {
- byte[] hash = new byte[20];
- MessageDigest md = MessageDigest.getInstance("SHA-1");
- hash = md.digest(hashThis);
- return hash;
- } catch (NoSuchAlgorithmException nsae) {
- System.err.println("SHA-1 algorithm is not available...");
- System.exit(2);
- }
- return null;
- }
-
-
- public void decryptFile(InputStream inputSteam, OutputStream outputStream,FEntry toDownload) throws IOException{
- int BLOCKSIZE = 0x8000;
- long dlFileLength = toDownload.getFileLength();
- if(dlFileLength > (dlFileLength/BLOCKSIZE)*BLOCKSIZE){
- dlFileLength = ((dlFileLength/BLOCKSIZE)*BLOCKSIZE) +BLOCKSIZE;
- }
-
- int bytesRead = -1;
-
- byte[] IV = new byte[16];
- IV[1] = (byte)toDownload.getContentID();
-
- byte[] downloadBuffer;
-
- byte[] blockBuffer = new byte[BLOCKSIZE];
- byte[] overflowBuffer = new byte[BLOCKSIZE];
- int overflowsize = 0;
-
- int inBlockBuffer = 0;
- byte[] tmp = new byte[BLOCKSIZE];
- boolean endd = false;
- long downloadTotalsize = 0;
- long wrote = 0;
-
- boolean first = true;
- do{
- downloadBuffer = new byte[BLOCKSIZE-overflowsize];
-
- bytesRead = inputSteam.read(downloadBuffer);
- downloadTotalsize += bytesRead;
- if(bytesRead ==-1){
- endd = true;
- }
-
- if(!endd)System.arraycopy(downloadBuffer, 0, overflowBuffer, overflowsize,bytesRead);
-
- bytesRead += overflowsize;
-
- overflowsize = 0;
- int oldInThisBlock = inBlockBuffer;
-
- if(oldInThisBlock + bytesRead > BLOCKSIZE){
-
- int tooMuch = (oldInThisBlock + bytesRead) - BLOCKSIZE;
- int toRead = BLOCKSIZE - oldInThisBlock;
-
- System.arraycopy(overflowBuffer, 0, blockBuffer, oldInThisBlock, toRead);
- inBlockBuffer += toRead;
-
- overflowsize = tooMuch;
- System.arraycopy(overflowBuffer, toRead, tmp, 0, tooMuch);
-
- System.arraycopy(tmp, 0, overflowBuffer, 0, tooMuch);
-
-
- }else{
- if(!endd)System.arraycopy(overflowBuffer, 0, blockBuffer, inBlockBuffer, bytesRead);
- inBlockBuffer +=bytesRead;
- }
-
- if(inBlockBuffer == BLOCKSIZE || endd){
- if(first){
- first = false;
- }else{
- IV = null;
- }
-
- byte[] output = decryptFileChunk(blockBuffer,BLOCKSIZE,IV);
-
- if((wrote + inBlockBuffer) > toDownload.getFileLength()){
- inBlockBuffer = (int) (toDownload.getFileLength()- wrote);
- }
-
- wrote += inBlockBuffer;
- outputStream.write(output, 0, inBlockBuffer);
-
- inBlockBuffer = 0;
- }
-
- }while(downloadTotalsize < dlFileLength && !endd);
+ public static byte[] hash(byte[] hashThis)
+ {
+ try
+ {
+ byte[] hash;
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
- outputStream.close();
- inputSteam.close();
+ hash = md.digest(hashThis);
+ return hash;
+ } catch (NoSuchAlgorithmException noSuchAlgorithm)
+ {
+ System.err.println("SHA-1 algorithm is not available...");
+ System.exit(2);
+ }
+ return null;
}
-
- public void decryptFileHash(InputStream inputSteam, OutputStream outputStream,FEntry toDownload) throws IOException{
- int BLOCKSIZE = 0x10000;
- int HASHBLOCKSIZE = 0xFC00;
- long writeSize = HASHBLOCKSIZE; // Hash block size
-
- long block = (toDownload.getFileOffset() / HASHBLOCKSIZE) & 0xF;
-
- long soffset = toDownload.getFileOffset() - (toDownload.getFileOffset() / HASHBLOCKSIZE * HASHBLOCKSIZE);
-
- long size = toDownload.getFileLength();
-
- if( soffset+size > writeSize )
- writeSize = writeSize - soffset;
-
- int bytesRead = -1;
- byte[] downloadBuffer;
-
- byte[] encryptedBlockBuffer = new byte[BLOCKSIZE];
- byte[] buffer = new byte[BLOCKSIZE];
-
- int encryptedBytesInBuffer = 0;
- int bufferPostion = 0;
-
-
- byte[] tmp = new byte[BLOCKSIZE];
- boolean lastPart = false;
- long wrote = 0;
-
- do{
- downloadBuffer = new byte[BLOCKSIZE-bufferPostion];
- bytesRead = inputSteam.read(downloadBuffer);
- int bytesInBuffer = bytesRead + bufferPostion;
- if(bytesRead ==-1){
- lastPart = true;
- }else{
- System.arraycopy(downloadBuffer, 0, buffer, bufferPostion,bytesRead); //copy downloaded stuff in buffer
- bufferPostion = 0;
- }
-
- if(encryptedBytesInBuffer + bytesInBuffer > BLOCKSIZE){
- int tooMuch = (encryptedBytesInBuffer + bytesInBuffer) - BLOCKSIZE;
- int toRead = BLOCKSIZE - encryptedBytesInBuffer;
-
- System.arraycopy(buffer, 0, encryptedBlockBuffer, encryptedBytesInBuffer, toRead); // make buffer with encrypteddata full
- encryptedBytesInBuffer += toRead;
-
- bufferPostion = tooMuch; //set buffer position;
- System.arraycopy(buffer, toRead, tmp, 0, tooMuch);
- System.arraycopy(tmp, 0, buffer, 0, tooMuch);
-
- }else{
- if(!lastPart) System.arraycopy(buffer, 0, encryptedBlockBuffer, encryptedBytesInBuffer, bytesInBuffer); //When File if at the end, no more need to copy
- encryptedBytesInBuffer +=bytesInBuffer;
+
+ public void decryptFile(InputStream inputSteam, OutputStream outputStream, FEntry toDownload) throws IOException
+ {
+ int blockSize = 0x8000;
+ long dlFileLength = toDownload.getFileLength();
+ if (dlFileLength > (dlFileLength / blockSize) * blockSize)
+ {
+ dlFileLength = ((dlFileLength / blockSize) * blockSize) + blockSize;
+ }
+
+ int bytesRead;
+
+ byte[] IV = new byte[16];
+ IV[1] = (byte) toDownload.getContentID();
+
+ byte[] downloadBuffer;
+
+ byte[] blockBuffer = new byte[blockSize];
+ byte[] overflowBuffer = new byte[blockSize];
+ int overflowSize = 0;
+
+ int inBlockBuffer = 0;
+ byte[] tmp = new byte[blockSize];
+ boolean end = false;
+ long totalDownloadSize = 0;
+ long wrote = 0;
+
+ boolean first = true;
+ do
+ {
+ downloadBuffer = new byte[blockSize - overflowSize];
+ bytesRead = inputSteam.read(downloadBuffer);
+ totalDownloadSize += bytesRead;
+ if (bytesRead == -1)
+ {
+ end = true;
}
-
- //If downloaded BLOCKSIZE, or file at the end: Decrypt!
- if(encryptedBytesInBuffer == BLOCKSIZE || lastPart){
-
- if( writeSize > size )
- writeSize = size;
-
- byte[] output = decryptFileChunkHash(encryptedBlockBuffer, BLOCKSIZE, (int) block,toDownload.getContentID());
-
- if((wrote + writeSize) > toDownload.getFileLength()){
- writeSize = (int) (toDownload.getFileLength()- wrote);
- }
-
- outputStream.write(output, (int)(0+soffset), (int)writeSize);
- wrote +=writeSize;
- encryptedBytesInBuffer = 0;
-
- block++;
- if( block >= 16 )
- block = 0;
-
- if( soffset > 0)
+
+ if (!end)
+ {
+ System.arraycopy(downloadBuffer, 0, overflowBuffer, overflowSize, bytesRead);
+ }
+
+ bytesRead += overflowSize;
+
+ overflowSize = 0;
+ int oldInThisBlock = inBlockBuffer;
+
+ if (oldInThisBlock + bytesRead > blockSize)
+ {
+ int tooMuch = (oldInThisBlock + bytesRead) - blockSize;
+ int toRead = blockSize - oldInThisBlock;
+
+ System.arraycopy(overflowBuffer, 0, blockBuffer, oldInThisBlock, toRead);
+ inBlockBuffer += toRead;
+
+ overflowSize = tooMuch;
+ System.arraycopy(overflowBuffer, toRead, tmp, 0, tooMuch);
+
+ System.arraycopy(tmp, 0, overflowBuffer, 0, tooMuch);
+ } else
+ {
+ if (!end)
{
- writeSize = HASHBLOCKSIZE;
- soffset = 0;
+ System.arraycopy(overflowBuffer, 0, blockBuffer, inBlockBuffer, bytesRead);
}
- }
- }while(wrote < toDownload.getFileLength() || lastPart);
-
+ inBlockBuffer += bytesRead;
+ }
+
+ if (inBlockBuffer == blockSize || end)
+ {
+ if (first)
+ {
+ first = false;
+ } else
+ {
+ IV = null;
+ }
+
+ byte[] output = decryptFileChunk(blockBuffer, blockSize, IV);
+
+ if ((wrote + inBlockBuffer) > toDownload.getFileLength())
+ {
+ inBlockBuffer = (int) (toDownload.getFileLength() - wrote);
+ }
+
+ wrote += inBlockBuffer;
+ outputStream.write(output, 0, inBlockBuffer);
+
+ inBlockBuffer = 0;
+ }
+
+ } while (totalDownloadSize < dlFileLength && !end);
+
outputStream.close();
inputSteam.close();
-
- }
-
- public void decrypt(FEntry fileEntry,String outputPath) throws IOException {
- String [] path = fileEntry.getFullPath().split("/");
- boolean decryptWithHash = false;
- if(!path[1].equals("code") && fileEntry.isExtractWithHash()){
- decryptWithHash = true;
- }
-
- long fileOffset = fileEntry.getFileOffset();
- if(decryptWithHash){
- int BLOCKSIZE = 0x10000;
- int HASHBLOCKSIZE = 0xFC00;
- fileOffset = ((fileEntry.getFileOffset() / HASHBLOCKSIZE) * BLOCKSIZE);
- }
-
-
- InputStream input = new FileInputStream(fileEntry.getContentPath());
- FileOutputStream outputStream = new FileOutputStream(outputPath + "/" + fileEntry.getFileName());
-
- input.skip(fileOffset);
-
- if(!decryptWithHash){
- decryptFile(input, outputStream, fileEntry);
- }else{
- decryptFileHash(input, outputStream, fileEntry);
- }
-
}
-
-
-
-
-}
+ public void decryptFileHash(InputStream inputSteam, OutputStream outputStream, FEntry toDownload) throws IOException
+ {
+ int blockSize = 0x10000;
+ int hashBlockSize = 0xFC00;
+ long writeSize = hashBlockSize; // Hash block size
+
+ long block = (toDownload.getFileOffset() / hashBlockSize) & 0xF;
+
+ long sOffset = toDownload.getFileOffset() - (toDownload.getFileOffset() / hashBlockSize * hashBlockSize);
+
+ long size = toDownload.getFileLength();
+
+ if (sOffset + size > writeSize)
+ {
+ writeSize = writeSize - sOffset;
+ }
+
+ int bytesRead;
+ byte[] downloadBuffer;
+
+ byte[] encryptedBlockBuffer = new byte[blockSize];
+ byte[] buffer = new byte[blockSize];
+
+ int encryptedBytesInBuffer = 0;
+ int bufferPosition = 0;
+
+ byte[] tmp = new byte[blockSize];
+ boolean lastPart = false;
+ long wrote = 0;
+
+ do
+ {
+ downloadBuffer = new byte[blockSize - bufferPosition];
+ bytesRead = inputSteam.read(downloadBuffer);
+ int bytesInBuffer = bytesRead + bufferPosition;
+ if (bytesRead == -1)
+ {
+ lastPart = true;
+ } else
+ {
+ System.arraycopy(downloadBuffer, 0, buffer, bufferPosition, bytesRead); //copy downloaded stuff in buffer
+ bufferPosition = 0;
+ }
+
+ if (encryptedBytesInBuffer + bytesInBuffer > blockSize)
+ {
+ int tooMuch = (encryptedBytesInBuffer + bytesInBuffer) - blockSize;
+ int toRead = blockSize - encryptedBytesInBuffer;
+
+ System.arraycopy(buffer, 0, encryptedBlockBuffer, encryptedBytesInBuffer, toRead); // make buffer with encrypteddata full
+ encryptedBytesInBuffer += toRead;
+
+ bufferPosition = tooMuch; //set buffer position;
+ System.arraycopy(buffer, toRead, tmp, 0, tooMuch);
+ System.arraycopy(tmp, 0, buffer, 0, tooMuch);
+
+ } else
+ {
+ if (!lastPart)
+ {
+ System.arraycopy(buffer, 0, encryptedBlockBuffer, encryptedBytesInBuffer, bytesInBuffer); //When File if at the end, no more need to copy
+ }
+ encryptedBytesInBuffer += bytesInBuffer;
+ }
+
+ //If downloaded block size, or file at the end: Decrypt!
+ if (encryptedBytesInBuffer == blockSize || lastPart)
+ {
+ if (writeSize > size)
+ {
+ writeSize = size;
+ }
+
+ byte[] output = decryptFileChunkHash(encryptedBlockBuffer, blockSize, (int) block, toDownload.getContentID());
+
+ if ((wrote + writeSize) > toDownload.getFileLength())
+ {
+ writeSize = (int) (toDownload.getFileLength() - wrote);
+ }
+
+ outputStream.write(output, (int) (sOffset), (int) writeSize);
+ wrote += writeSize;
+ encryptedBytesInBuffer = 0;
+
+ block++;
+ if (block >= 16)
+ {
+ block = 0;
+ }
+
+ if (sOffset > 0)
+ {
+ writeSize = hashBlockSize;
+ sOffset = 0;
+ }
+ }
+ } while (wrote < toDownload.getFileLength() || lastPart);
+
+ outputStream.close();
+ inputSteam.close();
+ }
+
+ public void decrypt(FEntry fileEntry, String outputPath) throws IOException
+ {
+ String[] path = fileEntry.getFullPath().split("/");
+ boolean decryptWithHash = false;
+ if (!path[1].equals("code") && fileEntry.isExtractWithHash())
+ {
+ decryptWithHash = true;
+ }
+
+ long fileOffset = fileEntry.getFileOffset();
+ if (decryptWithHash)
+ {
+ int blockSize = 0x10000;
+ int hashBlockSize = 0xFC00;
+ fileOffset = ((fileEntry.getFileOffset() / hashBlockSize) * blockSize);
+ }
+
+ InputStream input = new FileInputStream(fileEntry.getContentPath());
+ FileOutputStream outputStream = new FileOutputStream(outputPath + "/" + fileEntry.getFileName());
+
+ long actualBytesSkipped = 0;
+ long bytesToSkip = fileOffset;
+
+ while (actualBytesSkipped != bytesToSkip)
+ {
+ actualBytesSkipped += input.skip(bytesToSkip - actualBytesSkipped);
+ }
+
+ if (!decryptWithHash)
+ {
+ decryptFile(input, outputStream, fileEntry);
+ } else
+ {
+ decryptFileHash(input, outputStream, fileEntry);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/util/Downloader.java b/src/de/mas/jnustool/util/Downloader.java
index 8c2e503..6ab0594 100644
--- a/src/de/mas/jnustool/util/Downloader.java
+++ b/src/de/mas/jnustool/util/Downloader.java
@@ -1,156 +1,169 @@
package de.mas.jnustool.util;
+import de.mas.jnustool.FEntry;
+
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
-import de.mas.jnustool.FEntry;
-
-public class Downloader {
+public class Downloader
+{
private static Downloader instance;
-
- public static Downloader getInstance(){
- if(instance == null){
+
+ public static Downloader getInstance()
+ {
+ if (instance == null)
+ {
instance = new Downloader();
}
- return instance;
+
+ return instance;
}
- private Downloader(){
-
+
+ private Downloader()
+ {
+
}
-
-
- public void downloadAndDecrypt(FEntry toDownload) throws IOException{
- String URL = URL_BASE + "/" + String.format("%016X", toDownload.getTitleID()) + "/" + String.format("%08X", toDownload.getNUScontentID());
+
+ public void downloadAndDecrypt(FEntry toDownload) throws IOException
+ {
+ String URL = URL_BASE + "/" + String.format("%016X", toDownload.getTitleID()) + "/" + String.format("%08X", toDownload.getNUSContentID());
URL url = new URL(URL);
- String [] path = toDownload.getFullPath().split("/");
+ String[] path = toDownload.getFullPath().split("/");
boolean decryptWithHash = false;
- if(!path[1].equals("code") && toDownload.isExtractWithHash()){
+ if (!path[1].equals("code") && toDownload.isExtractWithHash())
+ {
decryptWithHash = true;
}
- HttpURLConnection connection =(HttpURLConnection) url.openConnection();
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
long fileOffset = toDownload.getFileOffset();
-
- if(decryptWithHash){
- int BLOCKSIZE = 0x10000;
- int HASHBLOCKSIZE = 0xFC00;
- fileOffset = ((toDownload.getFileOffset() / HASHBLOCKSIZE) * BLOCKSIZE);
-
+
+ if (decryptWithHash)
+ {
+ int blockSize = 0x10000;
+ int hashBlockSize = 0xFC00;
+ fileOffset = ((toDownload.getFileOffset() / hashBlockSize) * blockSize);
}
- connection.setRequestProperty("Range", "bytes=" + fileOffset +"-");
-
- connection.connect();
-
- Decryption decryption = new Decryption(toDownload.getTicket());
-
- InputStream input = connection.getInputStream();
- FileOutputStream outputStream = new FileOutputStream(String.format("%016X", toDownload.getTitleID()) +"/" + toDownload.getFullPath().substring(1, toDownload.getFullPath().length()));
- if(!decryptWithHash){
- decryption.decryptFile(input, outputStream, toDownload);
- }else{
- decryption.decryptFileHash(input, outputStream, toDownload);
- }
+ connection.setRequestProperty("Range", "bytes=" + fileOffset + "-");
+
+ connection.connect();
+
+ Decryption decryption = new Decryption(toDownload.getTicket());
+
+ InputStream input = connection.getInputStream();
+ FileOutputStream outputStream = new FileOutputStream(String.format("%016X", toDownload.getTitleID()) + "/" + toDownload.getFullPath().substring(1, toDownload.getFullPath().length()));
+ if (!decryptWithHash)
+ {
+ decryption.decryptFile(input, outputStream, toDownload);
+ } else
+ {
+ decryption.decryptFileHash(input, outputStream, toDownload);
+ }
+
+ connection.disconnect();
+ }
- connection.disconnect();
- }
-
public static String URL_BASE = "";
- public void downloadTMD(long titleID,int version,String path) throws IOException {
- downloadTMD(titleID,path);
+ public void downloadTMD(long titleID, String path) throws IOException
+ {
+ String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/tmd";
+ downloadFile(URL, "tmd", path);
}
- public void downloadTMD(long titleID,String path) throws IOException {
- String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/tmd";
- downloadFile(URL, "tmd",path);
- }
- public void downloadFile(String fileURL,String filename,String tmpPath) throws IOException{
+
+ public void downloadFile(String fileURL, String filename, String tmpPath) throws IOException
+ {
int BUFFER_SIZE = 0x800;
URL url = new URL(fileURL);
- HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
-
- InputStream inputStream = httpConn.getInputStream();
- if(tmpPath != null){
- filename = tmpPath + "/" + filename;
- }
-
- FileOutputStream outputStream = new FileOutputStream(filename);
+ HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
- int bytesRead = -1;
- byte[] buffer = new byte[BUFFER_SIZE];
- while ((bytesRead = inputStream.read(buffer)) != -1) {
- outputStream.write(buffer, 0, bytesRead);
- }
+ InputStream inputStream = httpConn.getInputStream();
+ if (tmpPath != null)
+ {
+ filename = tmpPath + "/" + filename;
+ }
- outputStream.close();
- inputStream.close();
-
- httpConn.disconnect();
+ FileOutputStream outputStream = new FileOutputStream(filename);
+
+ int bytesRead;
+ byte[] buffer = new byte[BUFFER_SIZE];
+ while ((bytesRead = inputStream.read(buffer)) != -1)
+ {
+ outputStream.write(buffer, 0, bytesRead);
+ }
+
+ outputStream.close();
+ inputStream.close();
+
+ httpConn.disconnect();
}
-
- public void downloadFile(String fileURL,String filename) throws IOException{
- downloadFile(fileURL, filename,null);
+
+ public void downloadTicket(long titleID, String path) throws IOException
+ {
+ String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/cetk";
+ downloadFile(URL, "cetk", path);
}
- public void downloadTicket(long titleID,String path) throws IOException {
- String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/cetk";
- downloadFile(URL, "cetk",path);
- }
- public void downloadContent(long titleID,int contentID) throws IOException {
- downloadContent(titleID,contentID, null);
- }
- public byte[] downloadContentToByteArray(long titleID,int contentID) throws IOException {
- String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/" + String.format("%08X", contentID);
- return downloadFileToByteArray(URL);
- }
- public byte[] downloadTMDToByteArray(long titleID) throws IOException {
- String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/tmd";
+
+ public byte[] downloadContentToByteArray(long titleID, int contentID) throws IOException
+ {
+ String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/" + String.format("%08X", contentID);
return downloadFileToByteArray(URL);
}
- private byte[] downloadFileToByteArray(String fileURL) throws IOException {
-
+
+ public byte[] downloadTMDToByteArray(long titleID) throws IOException
+ {
+ String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/tmd";
+ return downloadFileToByteArray(URL);
+ }
+
+ private byte[] downloadFileToByteArray(String fileURL) throws IOException
+ {
int BUFFER_SIZE = 0x800;
URL url = new URL(fileURL);
- HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
- int responseCode = httpConn.getResponseCode();
-
- // always check HTTP response code first
- byte[] file = null;
-
- if (responseCode == HttpURLConnection.HTTP_OK) {
- int contentLength = httpConn.getContentLength();
-
- file = new byte[contentLength];
- // always check HTTP response code first
-
- InputStream inputStream = httpConn.getInputStream();
-
- int bytesRead = -1;
- byte[] buffer = new byte[BUFFER_SIZE];
- int filePostion = 0;
- while ((bytesRead = inputStream.read(buffer)) != -1) {
- System.arraycopy(buffer, 0, file, filePostion,bytesRead);
- filePostion+=bytesRead;
-
- }
- inputStream.close();
- }else{
- System.err.println("File not found: " + fileURL);
- }
- httpConn.disconnect();
- return file;
-
+ HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
+ int responseCode = httpConn.getResponseCode();
+
+ // always check HTTP response code first
+ byte[] file = null;
+
+ if (responseCode == HttpURLConnection.HTTP_OK)
+ {
+ int contentLength = httpConn.getContentLength();
+
+ file = new byte[contentLength];
+ // always check HTTP response code first
+
+ InputStream inputStream = httpConn.getInputStream();
+
+ int bytesRead;
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int filePosition = 0;
+ while ((bytesRead = inputStream.read(buffer)) != -1)
+ {
+ System.arraycopy(buffer, 0, file, filePosition, bytesRead);
+ filePosition += bytesRead;
+ }
+ inputStream.close();
+ } else
+ {
+ System.err.println("File not found: " + fileURL);
+ }
+ httpConn.disconnect();
+ return file;
}
- public byte[] downloadTicketToByteArray(long titleID) throws IOException {
- String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/cetk";
+
+ public byte[] downloadTicketToByteArray(long titleID) throws IOException
+ {
+ String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/cetk";
return downloadFileToByteArray(URL);
}
- public void downloadContent(long titleID,int contentID, String tmpPath) throws IOException {
- String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/" + String.format("%08X", contentID);
- downloadFile(URL, String.format("%08X", contentID) +".app",tmpPath);
-
+
+ public void downloadContent(long titleID, int contentID, String tmpPath) throws IOException
+ {
+ String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/" + String.format("%08X", contentID);
+ downloadFile(URL, String.format("%08X", contentID) + ".app", tmpPath);
}
-
-
-}
+}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/util/ExitException.java b/src/de/mas/jnustool/util/ExitException.java
index 54eae42..e66db24 100644
--- a/src/de/mas/jnustool/util/ExitException.java
+++ b/src/de/mas/jnustool/util/ExitException.java
@@ -1,14 +1,9 @@
package de.mas.jnustool.util;
-public class ExitException extends Exception {
-
- public ExitException(String string) {
+public class ExitException extends Exception
+{
+ public ExitException(String string)
+ {
super(string);
}
-
- /**
- *
- */
- private static final long serialVersionUID = 1L;
-
-}
+}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/util/Settings.java b/src/de/mas/jnustool/util/Settings.java
index c2f1fdf..a2c6e71 100644
--- a/src/de/mas/jnustool/util/Settings.java
+++ b/src/de/mas/jnustool/util/Settings.java
@@ -1,12 +1,11 @@
package de.mas.jnustool.util;
-
-public class Settings {
+public class Settings
+{
public static boolean downloadContent = false;
public static boolean useCachedFiles = false;
public static boolean downloadWhenCachedFilesMissingOrBroken = true;
public static boolean skipBrokenFiles = false;
public static boolean skipExistingFiles = true;
public static boolean skipExistingTMDTICKET = true;
-
-}
+}
\ No newline at end of file
diff --git a/src/de/mas/jnustool/util/Util.java b/src/de/mas/jnustool/util/Util.java
deleted file mode 100644
index f4a366f..0000000
--- a/src/de/mas/jnustool/util/Util.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package de.mas.jnustool.util;
-
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-public class Util {
-
- public static byte[] commonKey;
-
- public static byte[] hexStringToByteArray(String s) {
- int len = s.length();
- byte[] data = new byte[len / 2];
- for (int i = 0; i < len; i += 2) {
- data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
- + Character.digit(s.charAt(i+1), 16));
- }
- return data;
- }
-
-
- public static String ByteArrayToString(byte[] ba)
- {
- StringBuilder hex = new StringBuilder(ba.length * 2);
- for(byte b : ba){
- hex.append(String.format("%X", b));
- }
- return hex.toString();
- }
-
- public static int getIntFromBytes(byte[] input,int offset){
- return ByteBuffer.wrap(Arrays.copyOfRange(input,offset, offset+4)).getInt();
- }
- public static long getIntAsLongFromBytes(byte[] input,int offset){
- long result = 0 ;
- if((int)input[offset]+128 > 0 && (int)input[offset]+128 < 128){
-
- input[offset] += 128;
-
- result = (long)ByteBuffer.wrap(Arrays.copyOfRange(input,offset, offset+4)).getInt();
-
- result += 1024L*1024L*2048L;
- return result;
-
- }
- return (long)ByteBuffer.wrap(Arrays.copyOfRange(input,offset, offset+4)).getInt();
- }
-
- public static short getShortFromBytes(byte[] input, int offset) {
- return ByteBuffer.wrap(Arrays.copyOfRange(input,offset, offset+2)).getShort();
- }
-
- public static long StringToLong(String s) {
- try{
- BigInteger bi = new BigInteger(s, 16);
- return bi.longValue();
- }catch(NumberFormatException e){
- System.err.println("Invalid Title ID");
- return 0L;
- }
- }
-}