Added multi selection and meta downloader

-added a progress function
-added a meta download button
-added mutli selection of titles
-fixed byte[] to String function
This commit is contained in:
Maschell 2016-02-06 18:25:08 +01:00
parent 8a597aa147
commit 626104209d
16 changed files with 315 additions and 43 deletions

3
.gitignore vendored
View File

@ -50,4 +50,5 @@ bin
/config
/tmp*
UpdateGrabber.java
updatetitles.csv
updatetitles.csv
src/de/mas/jnustool/StarterPRIATE.java

Binary file not shown.

View File

@ -155,13 +155,16 @@ public class FEntry {
return folder;
}
public void downloadAndDecrypt() {
public void downloadAndDecrypt(Progress progress) {
createFolder();
long titleID = getTitleID();
File f = new File(String.format("%016X", titleID) +"/" +getFullPath().substring(1, getFullPath().length()));
if(f.exists()){
if(f.length() == getFileLength()){
Logger.log("Skipping: " + String.format("%8.2f MB ",getFileLength()/1024.0/1024.0) + getFullPath());
if(progress != null){
progress.finish();
}
return;
}
}
@ -172,6 +175,7 @@ public class FEntry {
if(f.length() == fst.getTmd().contents[this.getContentID()].size){
Logger.log("Decrypting: " + String.format("%8.2f MB ", getFileLength()/1024.0/1024.0) + getFullPath());
Decryption decrypt = new Decryption(fst.getTmd().getNUSTitle().getTicket());
decrypt.setProgressListener(progress);
decrypt.decrypt(this,getDownloadPath());
return;
}else{
@ -203,7 +207,7 @@ public class FEntry {
}
}
Logger.log("Downloading: " + String.format("%8.2f MB ", getFileLength()/1024.0/1024.0) + getFullPath());
Downloader.getInstance().downloadAndDecrypt(this);
Downloader.getInstance().downloadAndDecrypt(this,progress);
} catch (IOException e) {

View File

@ -5,17 +5,26 @@ import java.util.concurrent.Callable;
public class FEntryDownloader implements Callable<Integer>
{
FEntry f;
Progress progress = null;
public void setTitle(FEntry f){
this.f = f;
}
public FEntryDownloader(FEntry f){
public FEntryDownloader(FEntry f,Progress fatherProgress){
setTitle(f);
createProgressListener(fatherProgress);
}
private void createProgressListener(Progress fatherProgress) {
if(fatherProgress != null){
progress = new Progress();
progress.setTotal(f.getFileLength());
fatherProgress.add(progress);
}
}
@Override
public Integer call() throws Exception {
f.downloadAndDecrypt();
f.downloadAndDecrypt(progress);
return null;
}

View File

@ -19,6 +19,7 @@ public class FST {
int totalEntries = 0;
int dirEntries = 0;
public FEntry metaFENtry;
public List<FEntry> metaFolder = new ArrayList<>();
private Directory FSTDirectory = new Directory("root");
private Directory contentDirectory = new Directory("root");
@ -165,6 +166,8 @@ public class FST {
this.totalContentSize += fileLength;
if(in_nus_title)this.totalContentSizeInNUS += fileLength;
boolean metafolder = false;
List<String> pathList = new ArrayList<>();
//getting the full path of entry
if(dir)
@ -180,7 +183,7 @@ public class FST {
StringBuilder sb = new StringBuilder();
int k = 0;
int nameoffoff,nameoff_entrypath;
for( j=0; j<level; ++j )
{
nameoffoff = Util.getIntFromBytes(decrypteddata,base_offset+Entry[j]*0x10);
@ -188,9 +191,11 @@ public class FST {
nameoff_entrypath = nameOff + nameoffoff;
while(decrypteddata[nameoff_entrypath + k] != 0){k++;}
String tmpname = new String(Arrays.copyOfRange(decrypteddata,nameoff_entrypath, nameoff_entrypath + k));
if(j==1 && tmpname.equals("meta")){
metafolder = true;
}
if(!tmpname.equals("")){
pathList.add(tmpname);
pathList.add(tmpname);
}
sb.append(tmpname);
@ -205,6 +210,9 @@ public class FST {
if(filename.equals("meta.xml")){
metaFENtry = tmp;
}
if(metafolder){
metaFolder.add(tmp);
}
//Logger.log(fileEntries.get(i));
}
@ -231,6 +239,10 @@ public class FST {
this.totalContentSizeInNUS = totalContentSizeInNUS;
}
public List<FEntry> getMetaFolder() {
return metaFolder;
}
public List<FEntry> getFileEntries() {
return fileEntries;

View File

@ -1,5 +1,8 @@
package de.mas.jnustool;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import de.mas.jnustool.gui.NUSGUI;
public class Logger {
@ -7,7 +10,12 @@ public class Logger {
public static void log(String string) {
NUSGUI.output.append(string + "\n");
NUSGUI.output.setCaretPosition(NUSGUI.output.getDocument().getLength());
//System.out.println(string);
}
public static void messageBox(String string) {
JOptionPane.showMessageDialog(new JFrame(), string);
}
}

View File

@ -214,15 +214,14 @@ public class NUSTitle {
this.titleID = titleId;
}
public void decryptFEntries(List<FEntry> list) {
public void decryptFEntries(List<FEntry> list,Progress progress) {
ForkJoinPool pool = ForkJoinPool.commonPool();
List<FEntryDownloader> dlList = new ArrayList<>();
for(FEntry f : list){
if(!f.isDir() && f.isInNUSTitle()){
dlList.add(new FEntryDownloader(f));
dlList.add(new FEntryDownloader(f,progress));
}
}
pool.invokeAll(dlList);
Logger.log("Done!");
}

View File

@ -0,0 +1,92 @@
package de.mas.jnustool;
import java.util.ArrayList;
import java.util.List;
public class Progress {
private long total;
private long current;
private ProgressUpdateListener progressUpdateListener = null;
List<Progress> children = new ArrayList<>();
Progress father = null;
public long getTotalOfSingle() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public long getCurrentOfSingle() {
return current;
}
public void setCurrent(long current) {
this.current = current;
update();
}
public void addCurrent(int i) {
if(this.current + i > getTotalOfSingle()){
setCurrent(getTotalOfSingle());
}else{
setCurrent(getCurrent() + i);
}
}
private void update() {
if(father != null) father.update();
if(progressUpdateListener != null){
progressUpdateListener.updatePerformed(this);
}
}
public void add(Progress progress) {
progress.setFather(this);
children.add(progress);
}
private void setFather(Progress progressListener) {
this.father = progressListener;
}
public long getCurrent() {
long tmp = getCurrentOfSingle();
for(Progress p : children){
tmp +=p.getCurrent();
}
return tmp;
}
public long getTotal() {
long tmp = getTotalOfSingle();
for(Progress p : children){
tmp +=p.getTotal();
}
return tmp;
}
public void setProgressUpdateListener(ProgressUpdateListener progressUpdateListener) {
this.progressUpdateListener = progressUpdateListener;
}
public void clear() {
setCurrent(0);
setTotal(0);
children = new ArrayList<>();
}
public int statusInPercent() {
return (int) ((getCurrent()*1.0)/(getTotal()*1.0)*100);
}
public void finish() {
setCurrent(getTotalOfSingle());
}
}

View File

@ -0,0 +1,9 @@
package de.mas.jnustool;
public interface ProgressUpdateListener {
/**
* Invoked when an action occurs.
*/
public void updatePerformed(Progress p);
}

View File

@ -6,6 +6,10 @@ import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import de.mas.jnustool.gui.NUSGUI;
import de.mas.jnustool.gui.UpdateChooser;
@ -37,23 +41,37 @@ public class Starter {
if( args.length > 1 && args[1].length() == 32){
key = args[1].substring(0, 32);
}
if(titleID != 0){
NUSGUI m = new NUSGUI(new NUSTitle(titleID, key));
m.setVisible(true);
}
}else{
titleID = getTitleID().getTitleID();
}
if(titleID != 0){
NUSGUI m = new NUSGUI(new NUSTitle(titleID, key), null);
m.setVisible(true);
for(NUSTitleInformation nus : getTitleID()){
final long tID = nus.getTitleID();
new Thread(new Runnable() {
@Override
public void run() {
NUSGUI m = new NUSGUI(new NUSTitle(tID, null));
m.setVisible(true);
}
}).start();;
}
}
}
private static NUSTitleInformation getTitleID() {
private static List<NUSTitleInformation> getTitleID() {
List<NUSTitleInformation> updatelist = readUpdateCSV();
NUSTitleInformation result = null;
List<NUSTitleInformation> result = null;
if(updatelist != null){
result = new NUSTitleInformation();
result = new ArrayList<>();
UpdateChooser.createAndShowGUI(updatelist,result);
synchronized (result) {
try {
@ -63,6 +81,9 @@ public class Starter {
e.printStackTrace();
}
}
}else{
Logger.messageBox("Updatefile is missing or not in config?");
System.exit(2);
}
return result;
}
@ -80,6 +101,7 @@ public class Starter {
while((line = in.readLine()) != null){
String[] infos = line.split(";");
if(infos.length != 7) {
Logger.messageBox("Updatelist is broken!");
System.out.println("Updatelist is broken!");
return null;
}
@ -96,9 +118,10 @@ public class Starter {
in.close();
} catch (IOException | NumberFormatException e) {
try {
in.close();
if(in != null)in.close();
} catch (IOException e1) {
}
Logger.messageBox("Updatelist is broken or missing");
System.out.println("Updatelist is broken!");
return null;
}
@ -112,6 +135,7 @@ public class Starter {
Downloader.URL_BASE = in.readLine();
String commonkey = in.readLine();
if(commonkey.length() != 32){
Logger.messageBox("CommonKey length is wrong");
System.out.println("Commonkey length is wrong");
System.exit(1);
}
@ -121,4 +145,35 @@ public class Starter {
}
public static void downloadMeta(List<NUSTitleInformation> output_, Progress totalProgress) {
ForkJoinPool pool = ForkJoinPool.commonPool();
List<ForkJoinTask<Boolean>> list = new ArrayList<>();
for(NUSTitleInformation nus : output_){
final long tID = nus.getTitleID();
list.add(pool.submit(new Callable<Boolean>(){
@Override
public Boolean call() throws Exception {
NUSTitle nus = new NUSTitle(tID, null);
Progress childProgress = new Progress();
totalProgress.add(childProgress);
nus.decryptFEntries(nus.getFst().getMetaFolder(),childProgress);
return true;
}
}));
}
for(ForkJoinTask<Boolean> task : list){
try {
task.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

View File

@ -18,13 +18,12 @@ import javax.swing.tree.TreePath;
import de.mas.jnustool.FEntry;
import de.mas.jnustool.NUSTitle;
import de.mas.jnustool.util.Settings;
public class NUSGUI extends JFrame {
private static final long serialVersionUID = 4648172894076113183L;
public static JTextArea output = new JTextArea(1,10);
public NUSGUI(NUSTitle nus,Settings mode) {
public NUSGUI(NUSTitle nus) {
super();
this.setResizable(false);
setSize(600, 768);
@ -64,7 +63,7 @@ public class NUSGUI extends JFrame {
}
}
nus.decryptFEntries(list);
nus.decryptFEntries(list, null);
}}).start();
}

View File

@ -15,7 +15,9 @@ import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
@ -25,6 +27,9 @@ import javax.swing.ScrollPaneConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import de.mas.jnustool.Progress;
import de.mas.jnustool.ProgressUpdateListener;
import de.mas.jnustool.Starter;
import de.mas.jnustool.util.NUSTitleInformation;
public class UpdateChooser extends JPanel {
@ -42,7 +47,8 @@ public class UpdateChooser extends JPanel {
setSize(800, 600);
Collections.sort(list_);
output_.init(list_.get(0));
output_.add(list_.get(0));
String[] columnNames = { "TitleID", "Region", "Name" };
String[][] tableData = new String[list_.size()][];
int i = 0;
@ -95,7 +101,7 @@ public class UpdateChooser extends JPanel {
listSelectionModel.setSelectionMode(
ListSelectionModel.SINGLE_SELECTION);
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
//Build output area.
output = new JTextArea(1, 10);
@ -123,38 +129,75 @@ public class UpdateChooser extends JPanel {
splitPane.add(topHalf);
JPanel listContainer = new JPanel(new GridLayout(1,1));
add(listContainer, BorderLayout.NORTH);
JButton btnNewButton = new JButton("Okay");
JPanel panel = new JPanel();
add(panel, BorderLayout.SOUTH);
JButton btnNewButton = new JButton("Open FST");
panel.add(btnNewButton);
JProgressBar progressBar;
progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
progressBar.setStringPainted(true);
JButton btnDownloadMeta = new JButton("Download META");
JProgressBar progressBar_1 = new JProgressBar();
panel.add(progressBar_1);
progressBar_1.setValue(0);
Progress progress = new Progress();
progress.setProgressUpdateListener(new ProgressUpdateListener() {
@Override
public void updatePerformed(Progress p) {
progressBar_1.setValue((int)p.statusInPercent());
}
});
btnDownloadMeta.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(progressBar_1.getValue() == 0 || progressBar_1.getValue() == 100){
progressBar_1.setValue(1);
progress.clear();
new Thread(new Runnable(){
@Override
public void run() {
Starter.downloadMeta(output_,progress);
JOptionPane.showMessageDialog(window, "Finished");
}
}).start();
}else{
JOptionPane.showMessageDialog(window, "Operation still in progress, please wait");
}
}
});
panel.add(btnDownloadMeta);
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("lol");
public void actionPerformed(ActionEvent e) {
synchronized (output_) {
window.setVisible(false);
output_.notifyAll();
}
}
});
add(btnNewButton, BorderLayout.SOUTH);
JPanel bottomHalf = new JPanel(new BorderLayout());
bottomHalf.add(controlPane, BorderLayout.PAGE_START);
bottomHalf.add(outputPane, BorderLayout.CENTER);
//XXX: next line needed if bottomHalf is a scroll pane:
//bottomHalf.setMinimumSize(new Dimension(400, 50));
}
private static NUSTitleInformation output_;
private static List<NUSTitleInformation> output_;
static List<NUSTitleInformation> list_;
public static void createAndShowGUI(List<NUSTitleInformation> list,NUSTitleInformation output) {
public static void createAndShowGUI(List<NUSTitleInformation> list,List<NUSTitleInformation> result) {
//Create and set up the window.
JFrame frame = new JFrame("Select the title");
//Create and set up the content pane.
list_ = list;
output_ =output;
output_ =result;
UpdateChooser demo = new UpdateChooser(frame);
demo.setOpaque(true);
frame.setContentPane(demo);
@ -178,9 +221,12 @@ public class UpdateChooser extends JPanel {
// Find out which indexes are selected.
int minIndex = lsm.getMinSelectionIndex();
int maxIndex = lsm.getMaxSelectionIndex();
output_.clear();
for (int i = minIndex; i <= maxIndex; i++) {
if (lsm.isSelectedIndex(i)) {
output_.init(list_.get(i));
if(!output_.contains(list_.get(i))){
output_.add(list_.get(i));
}
}
}
}

View File

@ -20,6 +20,7 @@ import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import de.mas.jnustool.FEntry;
import de.mas.jnustool.Progress;
import de.mas.jnustool.TIK;
public class Decryption {
@ -171,6 +172,10 @@ public class Decryption {
boolean first = true;
ByteArrayBuffer overflow = new ByteArrayBuffer(BLOCKSIZE);
if(progressListener != null){
progressListener.setTotal(toDownload.getFileLength());
progressListener.setCurrent(0);
}
do{
inBlockBuffer = getChunkFromStream(inputStream,blockBuffer,overflow,BLOCKSIZE);
if(first){
@ -184,7 +189,9 @@ public class Decryption {
if((wrote + inBlockBuffer) > toDownload.getFileLength()){
inBlockBuffer = (int) (toDownload.getFileLength()- wrote);
}
if(progressListener != null){
progressListener.addCurrent(inBlockBuffer);
}
wrote += inBlockBuffer;
outputStream.write(output, 0, inBlockBuffer);
}while(inBlockBuffer == BLOCKSIZE);
@ -209,9 +216,16 @@ public class Decryption {
long wrote = 0;
int inBlockBuffer;
if(progressListener != null){
progressListener.setTotal(toDownload.getFileLength()/HASHBLOCKSIZE*BLOCKSIZE);
progressListener.setCurrent(0);
}
do{
inBlockBuffer = getChunkFromStream(inputStream,encryptedBlockBuffer,overflow,BLOCKSIZE);
if(progressListener != null){
progressListener.addCurrent(inBlockBuffer);
}
if( writeSize > size )
writeSize = size;
@ -295,5 +309,12 @@ public class Decryption {
return inBlockBuffer;
}
private Progress progressListener = null;
public void setProgressListener(Progress progressOfFile) {
this.progressListener = progressOfFile;
}
}

View File

@ -7,6 +7,7 @@ import java.net.HttpURLConnection;
import java.net.URL;
import de.mas.jnustool.FEntry;
import de.mas.jnustool.Progress;
public class Downloader {
private static Downloader instance;
@ -22,7 +23,7 @@ public class Downloader {
}
public void downloadAndDecrypt(FEntry toDownload) throws IOException{
public void downloadAndDecrypt(FEntry toDownload, Progress progressOfFile) 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("/");
@ -45,7 +46,7 @@ public class Downloader {
connection.connect();
Decryption decryption = new Decryption(toDownload.getTicket());
decryption.setProgressListener(progressOfFile);
InputStream input = connection.getInputStream();
FileOutputStream outputStream = new FileOutputStream(String.format("%016X", toDownload.getTitleID()) +"/" + toDownload.getFullPath().substring(1, toDownload.getFullPath().length()));
if(!decryptWithHash){

View File

@ -12,6 +12,7 @@ public class NUSTitleInformation implements Comparable<NUSTitleInformation>, Ser
private String content_platform;
private String company_code;
private int region;
private byte[] key;
public enum Region{
@ -123,6 +124,21 @@ public class NUSTitleInformation implements Comparable<NUSTitleInformation>, Ser
setID6(n.ID6);
setLongnameEN(n.longnameEN);
setProduct_code(n.product_code);
setKey(n.key);
}
public byte[] getKey() {
return key;
}
public void setKey(byte[] key) {
this.key = key;
}
@Override
public boolean equals(Object o){
return titleID == ((NUSTitleInformation)o).titleID;
}
}

View File

@ -23,7 +23,7 @@ public class Util {
{
StringBuilder hex = new StringBuilder(ba.length * 2);
for(byte b : ba){
hex.append(String.format("%X", b));
hex.append(String.format("%02X", b));
}
return hex.toString();
}