Added a lot of things!

Added a settings class! (no runtime configuration atm)
added decryption from files
added download to files
This commit is contained in:
Maschell 2016-02-02 22:55:33 +01:00
parent eb5657ebf6
commit 0e03adf10f
11 changed files with 394 additions and 77 deletions

View File

@ -4,7 +4,10 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import de.mas.jnustool.util.Decryption;
import de.mas.jnustool.util.Downloader; 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; private FST fst;
@ -124,7 +127,7 @@ public class FEntry {
} }
private void createFolder() { private void createFolder() {
long titleID = fst.getTmd().titleID; long titleID = getTitleID();
String [] path = getFullPath().split("/"); String [] path = getFullPath().split("/");
File f = new File (String.format("%016X", titleID)); File f = new File (String.format("%016X", titleID));
if(!f.exists())f.mkdir(); if(!f.exists())f.mkdir();
@ -140,20 +143,71 @@ public class FEntry {
} }
} }
} }
f = new File(String.format("%016X", titleID) +"/" +getFullPath().substring(1, getFullPath().length()));
}
public String getDownloadPath(){
String [] path = getFullPath().split("/");
String folder = String.format("%016X", getTitleID()) +"/";
for(int i = 0;i<path.length-1;i++){
if(!path[i].equals("")){
folder += path[i] + "/";
}
}
return folder;
}
public void downloadAndDecrypt() throws ExitException {
createFolder();
long titleID = getTitleID();
File f = new File(String.format("%016X", titleID) +"/" +getFullPath().substring(1, getFullPath().length()));
if(f.exists()){ if(f.exists()){
if(f.length() == getFileLength()){ if(f.length() == getFileLength()){
System.out.println("Skipping: " + String.format("%8.2f MB ",getFileLength()/1024.0/1024.0) + getFullPath()); System.out.println("Skipping: " + String.format("%8.2f MB ",getFileLength()/1024.0/1024.0) + getFullPath());
return; return;
} }
} }
try {
if(Settings.useCachedFiles){
f = new File(getContentPath());
if(f.exists()){
if(f.length() == fst.getTmd().contents[this.getContentID()].size){
System.out.println("Decrypting: " + String.format("%8.2f MB ", getFileLength()/1024.0/1024.0) + getFullPath());
Decryption decrypt = new Decryption(fst.getTmd().getNUSTitle().getTicket());
decrypt.decrypt(this,getDownloadPath());
return;
}else{
if(!Settings.downloadWhenCachedFilesMissingOrBroken){
System.out.println("Cached content has the wrong size! Please check your: "+ getContentPath() + " Downloading not allowed");
if(!Settings.skipBrokenFiles){
throw new ExitException("");
}else{
System.out.println("Ignoring the missing file: " + this.getFileName());
}
}else{
System.out.println("Content missing. Downloading the file from the server: " + this.getFileName());
} }
public void downloadAndDecrypt() { }
}else{
if(!Settings.downloadWhenCachedFilesMissingOrBroken){
System.out.println("Content missing. Downloading not allowed");
if(!Settings.skipBrokenFiles){
throw new ExitException("");
}else{
System.out.println("Ignoring the missing file: " + this.getFileName());
}
}else{
System.out.println("Content missing. Downloading the file from the server: " + this.getFileName());
}
}
}
System.out.println("Downloading: " + String.format("%8.2f MB ", getFileLength()/1024.0/1024.0) + getFullPath()); System.out.println("Downloading: " + String.format("%8.2f MB ", getFileLength()/1024.0/1024.0) + getFullPath());
try {
Downloader.getInstance().downloadAndDecrypt(this); Downloader.getInstance().downloadAndDecrypt(this);
} catch (IOException e) { } catch (IOException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
@ -168,6 +222,18 @@ public class FEntry {
this.pathList = pathList; this.pathList = pathList;
} }
public String getContentPath() {
return fst.getTmd().getContentPath() + "/" + String.format("%08X", getNUScontentID()) + ".app";
}
public long getTitleID() {
return fst.getTmd().titleID;
}
public TIK getTicket() {
return fst.getTmd().getNUSTitle().getTicket();
}

View File

@ -2,14 +2,13 @@ package de.mas.jnustool;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
public class TitleDownloader implements Callable<Integer> public class FEntryDownloader implements Callable<Integer>
{ {
FEntry f; FEntry f;
public void setTitle(FEntry f){ public void setTitle(FEntry f){
this.f = f; this.f = f;
} }
public TitleDownloader(FEntry f){ public FEntryDownloader(FEntry f){
setTitle(f); setTitle(f);
} }

View File

@ -1,40 +1,144 @@
package de.mas.jnustool; package de.mas.jnustool;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import de.mas.jnustool.util.Decryption; import de.mas.jnustool.util.Decryption;
import de.mas.jnustool.util.Downloader; import de.mas.jnustool.util.Downloader;
import de.mas.jnustool.util.Util; import de.mas.jnustool.util.ExitException;
import de.mas.jnustool.util.Settings;
public class NUSTitle { public class NUSTitle {
private TitleMetaData tmd; private TitleMetaData tmd;
private long titleID;
private TIK ticket; private TIK ticket;
private FST fst; private FST fst;
public NUSTitle(long titleId,String key){ private long titleID;
public NUSTitle(long titleId,String key) throws ExitException{
setTitleID(titleId);
try { try {
titleID = titleId; if(Settings.downloadContent){
Downloader.getInstance().titleID = titleId; File f = new File(getContentPath());
Decryption decryption = new Decryption(Util.commonKey,titleId); if(!f.exists())f.mkdir();
}
if(Settings.downloadContent){
tmd = new TitleMetaData(Downloader.getInstance().downloadTMDToByteArray()); File f = new File(getContentPath() + "/" + "tmd");
if(!(f.exists() && Settings.skipExistingTMDTICKET)){
if(key == null){ System.out.println("Downloading TMD");
ticket = new TIK(Downloader.getInstance().downloadTicketToByteArray(),tmd.titleID); Downloader.getInstance().downloadTMD(titleId,getContentPath());
}else{ }else{
ticket = new TIK(key,titleId); System.out.println("Skipped download of TMD. Already existing");
}
f = new File(getContentPath() + "/" + "cetk");
if(!(f.exists() && Settings.skipExistingTMDTICKET)){
if(key == null){
System.out.print("Downloading Ticket");
Downloader.getInstance().downloadTicket(titleId,getContentPath());
}
}else{
System.out.println("Skipped download of ticket. Already existing");
}
} }
if(Settings.useCachedFiles){
File f = new File(getContentPath() + "/" + "tmd");
if(f.exists()){
System.out.println("Using cached TMD.");
tmd = new TitleMetaData(f);
}else{
System.out.println("No cached TMD found.");
}
}
Downloader.getInstance().ticket = ticket; if(tmd == null){
decryption.init(ticket.getDecryptedKey(),0); if(Settings.downloadWhenCachedFilesMissingOrBroken){
if(Settings.useCachedFiles) System.out.println("Getting missing tmd from Server!");
tmd = new TitleMetaData(Downloader.getInstance().downloadTMDToByteArray(titleId));
}else{
System.out.println("Downloading of missing files is not enabled. Exiting");
throw new ExitException("TMD missing.");
}
}
byte[] encryptedFST = Downloader.getInstance().downloadContentToByteArray(tmd.contents[0].ID); if(key != null){
System.out.println("Using ticket from parameter.");
ticket = new TIK(key,titleId);
}else{
if(Settings.useCachedFiles){
File f = new File(getContentPath() + "/" + "cetk");
if(f.exists()){
System.out.println("Using cached cetk.");
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{
System.out.println("Downloading of missing files is not enabled. Exiting");
throw new ExitException("Ticket missing.");
}
}
}
if(Settings.downloadContent){
File f = new File(getContentPath() + "/" + String.format("%08x", tmd.contents[0].ID) + ".app");
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){
System.out.println("FST already existing, but broken. Downloading it again.");
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{
System.out.println("Skipped download of FST. Already existing");
}
}
}
Decryption decryption = new Decryption(ticket.getDecryptedKey(),0);
byte[] encryptedFST = null;
if(Settings.useCachedFiles){
String path = getContentPath() + "/" + String.format("%08x", tmd.contents[0].ID) + ".app";
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.");
}
}
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); byte[] decryptedFST = decryption.decrypt(encryptedFST);
fst = new FST(decryptedFST,tmd); fst = new FST(decryptedFST,tmd);
tmd.setFst(fst); tmd.setNUSTitle(this);
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 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 Decrypted Files: " + ((int)((fst.getTotalContentSizeInNUS()/1024.0/1024.0)*100))/100.0 +" MB");
@ -49,26 +153,61 @@ public class NUSTitle {
} }
public FST getFst() { public FST getFst() {
return fst; return fst;
} }
public void setFst(FST fst) { public void setFst(FST fst) {
this.fst = fst; this.fst = fst;
} }
public long getTitleID() { public TitleMetaData getTmd() {
return titleID; return tmd;
} }
public void setTitleID(long titleID) { public void setTmd(TitleMetaData tmd) {
this.titleID = titleID; this.tmd = tmd;
}
public TIK getTicket() {
return ticket;
}
public void setTicket(TIK ticket) {
this.ticket = ticket;
} }
public long getTotalContentSize() { public long getTotalContentSize() {
return tmd.getTotalContentSize(); return tmd.getTotalContentSize();
} }
public String getContentPath() {
return getContentPathPrefix() + String.format("%016X", getTitleID());
}
public String getContentPathPrefix() {
return "tmp_";
}
private long getTitleID() {
return titleID;
}
private void setTitleID(long titleId) {
this.titleID = titleId;
}
} }

View File

@ -1,6 +0,0 @@
package de.mas.jnustool;
public class Settings {
}

View File

@ -7,6 +7,7 @@ import java.io.IOException;
import de.mas.jnustool.gui.NUSGUI; import de.mas.jnustool.gui.NUSGUI;
import de.mas.jnustool.util.Downloader; import de.mas.jnustool.util.Downloader;
import de.mas.jnustool.util.ExitException;
import de.mas.jnustool.util.Util; import de.mas.jnustool.util.Util;
public class Starter { public class Starter {
@ -28,7 +29,13 @@ public class Starter {
if( args.length > 1 && args[1].length() == 32){ if( args.length > 1 && args[1].length() == 32){
key = args[1].substring(0, 32); key = args[1].substring(0, 32);
} }
NUSGUI m = new NUSGUI(new NUSTitle(titleID, key), null); NUSGUI m;
try {
m = new NUSGUI(new NUSTitle(titleID, key), null);
} catch (ExitException e) {
System.out.println("Error: " + e.getMessage());
return;
}
m.setVisible(true); m.setVisible(true);
}else{ }else{
System.out.println("Need parameters: TITLEID [KEY]"); System.out.println("Need parameters: TITLEID [KEY]");

View File

@ -3,8 +3,18 @@ package de.mas.jnustool;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile; 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.Util;
public class TitleMetaData { public class TitleMetaData {
@ -27,7 +37,8 @@ public class TitleMetaData {
ContentInfo[] contentInfos = new ContentInfo[64]; // 0x1E4 ContentInfo[] contentInfos = new ContentInfo[64]; // 0x1E4
Content[] contents; // 0x1E4 Content[] contents; // 0x1E4
private FST fst;
private NUSTitle nus;
private long totalContentSize; private long totalContentSize;
@ -107,7 +118,6 @@ public class TitleMetaData {
byte[] buffer = new byte[0x20]; // 16 0xB14 byte[] buffer = new byte[0x20]; // 16 0xB14
f.read(buffer,0, 0x20); f.read(buffer,0, 0x20);
this.contents[i] = new Content(ID,index,type,size,buffer); this.contents[i] = new Content(ID,index,type,size,buffer);
} }
f.close(); f.close();
@ -153,12 +163,49 @@ public class TitleMetaData {
return totalContentSize; return totalContentSize;
} }
public FST getFst() { public void downloadContents() throws IOException, ExitException{
return fst; String tmpPath = getContentPath();
File f = new File(tmpPath);
if(!f.exists())f.mkdir();
for(Content c : contents){
if(c != contents[0]){
f = new File(tmpPath + "/" + String.format("%08X", c.ID ) + ".app");
if(f.exists()){
if(f.length() == c.size){
System.out.println("Skipping Content: " + String.format("%08X", c.ID));
}else{
if(Settings.downloadWhenCachedFilesMissingOrBroken){
System.out.println("Content " +String.format("%08X", c.ID) + " is broken. Downloading it again.");
Downloader.getInstance().downloadContent(titleID,c.ID,tmpPath);
}else{
if(Settings.skipBrokenFiles){
System.out.println("Content " +String.format("%08X", c.ID) + " is broken. Ignoring it.");
}else{
System.out.println("Content " +String.format("%08X", c.ID) + " is broken. Downloading not allowed.");
throw new ExitException("Content missing.");
}
}
}
}else{
System.out.println("Download Content: " + String.format("%08X", c.ID));
Downloader.getInstance().downloadContent(titleID,c.ID,tmpPath);
}
}
} }
public void setFst(FST fst) { }
this.fst = fst;
public String getContentPath() {
return nus.getContentPath();
}
public NUSTitle getNUSTitle() {
return nus;
}
public void setNUSTitle(NUSTitle nus) {
this.nus = nus;
} }
} }

View File

@ -13,10 +13,10 @@ import javax.swing.JScrollPane;
import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;
import de.mas.jnustool.Settings;
import de.mas.jnustool.FEntry; import de.mas.jnustool.FEntry;
import de.mas.jnustool.NUSTitle; import de.mas.jnustool.NUSTitle;
import de.mas.jnustool.TitleDownloader; import de.mas.jnustool.util.Settings;
import de.mas.jnustool.FEntryDownloader;
public class NUSGUI extends JFrame { public class NUSGUI extends JFrame {
@ -39,16 +39,19 @@ public class NUSGUI extends JFrame {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
new Thread(new Runnable() { public void run() { new Thread(new Runnable() { public void run() {
ForkJoinPool pool = ForkJoinPool.commonPool(); ForkJoinPool pool = ForkJoinPool.commonPool();
List<TitleDownloader> list = new ArrayList<>(); List<FEntryDownloader> list = new ArrayList<>();
TreePath[] paths = cbt.getCheckedPaths(); TreePath[] paths = cbt.getCheckedPaths();
for (TreePath tp : paths) { for (TreePath tp : paths) {
Object obj = tp.getPath()[tp.getPath().length-1]; Object obj = tp.getPath()[tp.getPath().length-1];
if(((DefaultMutableTreeNode)obj).getUserObject() instanceof FEntry){ if(((DefaultMutableTreeNode)obj).getUserObject() instanceof FEntry){
FEntry f = (FEntry) ((DefaultMutableTreeNode)obj).getUserObject(); FEntry f = (FEntry) ((DefaultMutableTreeNode)obj).getUserObject();
if(!f.isDir() && f.isInNUSTitle()) if(!f.isDir() && f.isInNUSTitle()){
list.add(new TitleDownloader(f)); list.add(new FEntryDownloader(f));
}
} }
} }
pool.invokeAll(list); pool.invokeAll(list);

View File

@ -2,6 +2,7 @@ package de.mas.jnustool.util;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -334,6 +335,34 @@ public class Decryption {
} }
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);
}
}

View File

@ -1,6 +1,5 @@
package de.mas.jnustool.util; package de.mas.jnustool.util;
import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -8,7 +7,6 @@ import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import de.mas.jnustool.FEntry; import de.mas.jnustool.FEntry;
import de.mas.jnustool.TIK;
public class Downloader { public class Downloader {
private static Downloader instance; private static Downloader instance;
@ -23,11 +21,9 @@ public class Downloader {
} }
public long titleID =0;
public TIK ticket = null;
public void downloadAndDecrypt(FEntry toDownload) throws IOException{ public void downloadAndDecrypt(FEntry toDownload) throws IOException{
String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/" + String.format("%08X", toDownload.getNUScontentID()); String URL = URL_BASE + "/" + String.format("%016X", toDownload.getTitleID()) + "/" + String.format("%08X", toDownload.getNUScontentID());
URL url = new URL(URL); URL url = new URL(URL);
String [] path = toDownload.getFullPath().split("/"); String [] path = toDownload.getFullPath().split("/");
boolean decryptWithHash = false; boolean decryptWithHash = false;
@ -48,10 +44,10 @@ public class Downloader {
connection.connect(); connection.connect();
Decryption decryption = new Decryption(ticket); Decryption decryption = new Decryption(toDownload.getTicket());
InputStream input = connection.getInputStream(); InputStream input = connection.getInputStream();
FileOutputStream outputStream = new FileOutputStream(String.format("%016X", titleID) +"/" + toDownload.getFullPath().substring(1, toDownload.getFullPath().length())); FileOutputStream outputStream = new FileOutputStream(String.format("%016X", toDownload.getTitleID()) +"/" + toDownload.getFullPath().substring(1, toDownload.getFullPath().length()));
if(!decryptWithHash){ if(!decryptWithHash){
decryption.decryptFile(input, outputStream, toDownload); decryption.decryptFile(input, outputStream, toDownload);
}else{ }else{
@ -63,19 +59,22 @@ public class Downloader {
public static String URL_BASE = ""; public static String URL_BASE = "";
public void downloadTMD(int version) throws IOException { public void downloadTMD(long titleID,int version,String path) throws IOException {
downloadTMD(); downloadTMD(titleID,path);
} }
public void downloadTMD() throws IOException { public void downloadTMD(long titleID,String path) throws IOException {
String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/tmd"; String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/tmd";
downloadFile(URL, "tmd"); downloadFile(URL, "tmd",path);
} }
public void downloadFile(String fileURL,String filename) throws IOException{ public void downloadFile(String fileURL,String filename,String tmpPath) throws IOException{
int BUFFER_SIZE = 0x800; int BUFFER_SIZE = 0x800;
URL url = new URL(fileURL); URL url = new URL(fileURL);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
InputStream inputStream = httpConn.getInputStream(); InputStream inputStream = httpConn.getInputStream();
if(tmpPath != null){
filename = tmpPath + "/" + filename;
}
FileOutputStream outputStream = new FileOutputStream(filename); FileOutputStream outputStream = new FileOutputStream(filename);
@ -90,20 +89,22 @@ public class Downloader {
httpConn.disconnect(); httpConn.disconnect();
} }
public void downloadTicket() throws IOException {
String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/cetk";
downloadFile(URL, "cetk");
}
public void downloadContent(int contentID) throws IOException {
String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/" + String.format("%08X", contentID);
downloadFile(URL, String.format("%08X", contentID));
public void downloadFile(String fileURL,String filename) throws IOException{
downloadFile(fileURL, filename,null);
} }
public byte[] downloadContentToByteArray(int contentID) throws IOException { 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); String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/" + String.format("%08X", contentID);
return downloadFileToByteArray(URL); return downloadFileToByteArray(URL);
} }
public byte[] downloadTMDToByteArray() throws IOException { public byte[] downloadTMDToByteArray(long titleID) throws IOException {
String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/tmd"; String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/tmd";
return downloadFileToByteArray(URL); return downloadFileToByteArray(URL);
} }
@ -141,9 +142,15 @@ public class Downloader {
return file; return file;
} }
public byte[] downloadTicketToByteArray() throws IOException { public byte[] downloadTicketToByteArray(long titleID) throws IOException {
String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/cetk"; String URL = URL_BASE + "/" + String.format("%016X", titleID) + "/cetk";
return downloadFileToByteArray(URL); 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);
}
} }

View File

@ -0,0 +1,14 @@
package de.mas.jnustool.util;
public class ExitException extends Exception {
public ExitException(String string) {
super(string);
}
/**
*
*/
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,12 @@
package de.mas.jnustool.util;
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;
}