diff --git a/README.md b/README.md index e2bda53..c8c1b8e 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,64 @@ A port of smea's client (WUPClient.py) for the wupserver. -Open it with +Open it with ``` -java -jar jwupclient 192.168.x.x +java -jar jwupclient.jar 192.168.x.x ``` -The following commands are supported (in my ulgy written shell). +The following commands are supported (in my ulgy written shell). -cd: -change directory to parent dir: -cd .. -change directory to relative dir: -cd code -change directory to absolute dir: -cd /vol/storage_mlc01 +cd: +change directory to parent dir: +cd .. +change directory to relative dir: +cd code +change directory to absolute dir: +cd /vol/storage_mlc01 -ls: -lists content of the directory +ls: +lists content of the directory -dl: -downloads a file. -dl filename [targetdir] +dl: +downloads a file. +dl filename [targetdir] -dlfp: -same as dl but keeps the absolute path of the wiiu +dlfp: +same as dl but keeps the absolute path of the wiiu -dldir: -downloads a directory +dldir: +downloads a directory -downloading the current dir -dldir -arguments: --src (sets the source folder on the wiiu) --dst (sets the destination folder on your PC) --fullpath (keeps the absolute path of the wiiu) +downloading the current dir +dldir +arguments: +-src (sets the source folder on the wiiu) +-dst (sets the destination folder on your PC) +-fullpath (keeps the absolute path of the wiiu) -Server and smea's client. -https://github.com/smealum/iosuhax/tree/master/wupserver +###dumping a disc### +Command to dump the whole disc (code, content and meta folder) to sd +``` +dumpdisc +``` + +The result will be stored on sd:/dumps/[TITLEID] + +You can set a regular expression all files and dir will be checked to. +*Examples* +To dump only the code folder +``` +dumpdisc -file /code/.* +``` + +To disable the check on dir (and so check the pattern on ALL files of the disc) use the -deepSearch parameter + +Example: to dump all .szs files +``` +dumpdisc -file .*.szs -deepSearch +``` + +Server and smea's client. +https://github.com/smealum/iosuhax/tree/master/wupserver + Everything is from Smea, I'm just porting and extending it! \ No newline at end of file diff --git a/jwupclient.jar b/jwupclient.jar index 3baa4dd..7bee75a 100644 Binary files a/jwupclient.jar and b/jwupclient.jar differ diff --git a/src/de/mas/wupclient/Starter.java b/src/de/mas/wupclient/Starter.java index 861980f..b738c45 100644 --- a/src/de/mas/wupclient/Starter.java +++ b/src/de/mas/wupclient/Starter.java @@ -4,6 +4,8 @@ import java.util.Scanner; import de.mas.wupclient.client.WUPClient; import de.mas.wupclient.client.operations.DownloadUploadOperations; +import de.mas.wupclient.client.operations.DumperOperations; +import de.mas.wupclient.client.operations.FileOperations; import de.mas.wupclient.client.operations.SpecialOperations; import de.mas.wupclient.client.operations.UtilOperations; @@ -16,11 +18,12 @@ public class Starter { WUPClient w = new WUPClient(ip); try { boolean exit = false; - + System.out.println("JWUPClient. Please enter a command. Enter \"exit\" to exit."); System.out.println(); System.out.print(w.getCwd() + " > "); Scanner reader = new Scanner(System.in); // Reading from System.in + while(!exit){ String input = reader.nextLine(); @@ -51,6 +54,8 @@ public class Starter { UtilOperations util = UtilOperations.UtilOperationsFactory(w); SpecialOperations special = SpecialOperations.SpecialOperationsFactory(w); DownloadUploadOperations dlul = DownloadUploadOperations.DownloadUploadOperationsFactory(w); + DumperOperations dump = DumperOperations.DumperOperationsFactory(w); + String[] inputs = input.split(" "); switch(inputs[0]){ case "ls": @@ -117,6 +122,24 @@ public class Starter { case "nandtickets": //download to full path special.parseAndDownloadTickets(); + break; + case "dumpdisc": + String pattern = ".*"; + boolean deepSearch = false; + if(inputs.length > 1){ + for(int i = 1;i < inputs.length;i++){ + if(inputs[i].equals("-file")){ + if(inputs.length >= i+1){ + pattern = inputs[i+1]; + i++; + } + } + if(inputs[i].equals("-deepSearch")){ + deepSearch = true; + } + } + } + dump.dumpDisc(pattern,deepSearch); break; default: System.out.println("Command not found!"); diff --git a/src/de/mas/wupclient/client/operations/DownloadUploadOperations.java b/src/de/mas/wupclient/client/operations/DownloadUploadOperations.java index 862d49f..2352804 100644 --- a/src/de/mas/wupclient/client/operations/DownloadUploadOperations.java +++ b/src/de/mas/wupclient/client/operations/DownloadUploadOperations.java @@ -106,7 +106,7 @@ public class DownloadUploadOperations extends Operations { if(read_result.getResultValue() <= 0) break; if((total_read /1024) % 50 == 0){ - System.out.println(String.format("%.3f", (double)(total_read /1024.0)) + " kb done"); + System.out.print("Downloading file: " + String.format("%.3f", (double)(total_read /1024.0)) + " kb done\r"); } } fsa.FSA_CloseFile(fsa_handle, res.getData()); diff --git a/src/de/mas/wupclient/client/operations/DumperOperations.java b/src/de/mas/wupclient/client/operations/DumperOperations.java new file mode 100644 index 0000000..fa7988a --- /dev/null +++ b/src/de/mas/wupclient/client/operations/DumperOperations.java @@ -0,0 +1,93 @@ +package de.mas.wupclient.client.operations; + +import java.io.ByteArrayInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import de.mas.wupclient.client.WUPClient; +import de.mas.wupclient.client.utils.FEntry; +import de.mas.wupclient.client.utils.Logger; +import de.mas.wupclient.client.utils.MetaInformation; +import de.mas.wupclient.client.utils.Utils; + +public class DumperOperations extends Operations { + private static Map instances = new HashMap<>(); + public static DumperOperations DumperOperationsFactory(WUPClient client){ + if(!instances.containsKey(client)){ + instances.put(client, new DumperOperations(client)); + } + return instances.get(client); + } + UtilOperations util = null; + FileOperations file = null; + DownloadUploadOperations dlul = null; + + private DumperOperations(WUPClient client) { + super(client); + setUtil(UtilOperations.UtilOperationsFactory(client)); + setDLUL(DownloadUploadOperations.DownloadUploadOperationsFactory(client)); + setFile(FileOperations.FileOperationsFactory(client)); + } + + public boolean dumpDisc(String pattern,boolean deepSearch) throws IOException{ + try{ + util.mount_sdcard(); + int res = util.mount("/dev/odd03", "/vol/storage_odd_content_dump", 2); + if(res != 0){ + //throw new MountingFailedException(); + } + System.out.println("Grabbing meta information!"); + byte[] metafile = dlul.downloadFileToByteArray("/vol/storage_odd_content_dump/meta/meta.xml"); + if(metafile == null){ + throw new LoadMetaFailedException(); + } + MetaInformation title = Utils.readMeta(new ByteArrayInputStream(metafile)); + Logger.log("Dumping " + title.getLongnameEN() + "(" + title.getTitleIDAsString() + ")"); + file.mkdir("/vol/storage_sdcard/dumps", 0x600); + file.mkdir("/vol/storage_sdcard/dumps/" + title.getTitleIDAsString(), 0x600); + if(pattern != ".*"){ + System.out.println("Searching matching files. This could take a while"); + } + file.cpdir("/vol/storage_odd_content_dump","/vol/storage_sdcard/dumps/" + title.getTitleIDAsString(),"",pattern,deepSearch); + + //}catch (MountingFailedException e) { + // Logger.logErr("Mounting failed"); + }catch (LoadMetaFailedException e){ + Logger.logErr("Loading meta.xml failed"); + }finally{ + util.unmount("/vol/storage_odd_content_dump", 2); + } + return true; + } + + public UtilOperations getUtil() { + return util; + } + + public void setUtil(UtilOperations util) { + this.util = util; + } + + public DownloadUploadOperations getDLUL() { + return dlul; + } + + public void setDLUL(DownloadUploadOperations dlul) { + this.dlul = dlul; + } + + public FileOperations getFile() { + return file; + } + + public void setFile(FileOperations file) { + this.file = file; + } + +} diff --git a/src/de/mas/wupclient/client/operations/FSAOperations.java b/src/de/mas/wupclient/client/operations/FSAOperations.java index 0b1832f..5072b27 100644 --- a/src/de/mas/wupclient/client/operations/FSAOperations.java +++ b/src/de/mas/wupclient/client/operations/FSAOperations.java @@ -137,12 +137,29 @@ public class FSAOperations extends Operations { if(result.getResultValue() == 0){ stats = new FStats(result.getData()); } - System.out.println(result.getResultValue()); return new Result(result.getResultValue(), stats); } - public Result FSA_ReadFilePtr(int fsa_handle, int src_handle, int i, int block_size, int buffer_ptr) { - // TODO Auto-generated method stub - return null; + + public Result FSA_ReadFilePtr(int handle, int filehandle, int size, int cnt, int ptr) throws IOException { + byte[] inbuffer = new byte[0x520]; + Utils.writeIntToByteArray(inbuffer, size, 0x08); + Utils.writeIntToByteArray(inbuffer, cnt, 0x0C); + Utils.writeIntToByteArray(inbuffer, filehandle, 0x14); + + Result result = system.ioctlv(handle, 0x0F, new byte[][] {inbuffer}, new int[]{0x293}, new int[0][], new int[][]{new int[]{ptr,size*cnt}}); + + return new Result(result.getResultValue(),result.getData()[0]); + } + + public Result FSA_WriteFilePtr(int handle, int file_handle, int size, int cnt, int ptr) throws IOException { + byte[] inbuffer = new byte[0x520]; + Utils.writeIntToByteArray(inbuffer, size, 0x08); + Utils.writeIntToByteArray(inbuffer, cnt, 0x0C); + Utils.writeIntToByteArray(inbuffer, file_handle, 0x14); + + Result result = system.ioctlv(handle, 0x10, new byte[][] {inbuffer}, new int[]{0x293}, new int[][]{new int[]{ptr,size*cnt}},new int[0][]); + + return new Result(result.getResultValue(),result.getData()[0]); } } diff --git a/src/de/mas/wupclient/client/operations/FileOperations.java b/src/de/mas/wupclient/client/operations/FileOperations.java index ce022e5..78c6ff7 100644 --- a/src/de/mas/wupclient/client/operations/FileOperations.java +++ b/src/de/mas/wupclient/client/operations/FileOperations.java @@ -2,9 +2,14 @@ package de.mas.wupclient.client.operations; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import de.mas.wupclient.client.WUPClient; +import de.mas.wupclient.client.utils.FEntry; +import de.mas.wupclient.client.utils.FStats; import de.mas.wupclient.client.utils.Logger; import de.mas.wupclient.client.utils.Result; @@ -34,14 +39,45 @@ public class FileOperations extends Operations { } public int createDir(String path, int flags) throws IOException{ int fsa_handle = getClient().get_fsa_handle(); - return fsa.FSA_MakeDir(fsa_handle, path, 2); + return fsa.FSA_MakeDir(fsa_handle, path, flags); } - /* FSA_ReadFilePtr needs to be implemented + public boolean cp(String source, String target) throws IOException{ return copyFile(source, target); } - + public boolean cpdir(String source, String destination)throws IOException{ + return cpdir(source, destination,"",".*",false); + } + public boolean cpdir(String source, String destination,boolean deepSearch)throws IOException{ + return cpdir(source, destination,"",".*",deepSearch); + } + public boolean cpdir(String source, String destination,String relativePath,String pattern,boolean deepSearch)throws IOException{ + System.out.println("Copying folder " +source + " to " + destination); + List entries = util.ls(source,true); + for(FEntry entry : entries){ + String new_relativePath = relativePath+ "/" + entry.getFilename(); + String src_filename = source + "/" + entry.getFilename(); + String dst_filename = destination + "/" + entry.getFilename(); + Pattern p = Pattern.compile(pattern); + Matcher m = p.matcher(new_relativePath); + Matcher m1 = p.matcher(new_relativePath + "/"); + boolean matches = m.matches() || m1.matches(); + if(matches || deepSearch){ + if(entry.isFile()){ + if(matches){ + boolean result = cp(src_filename,dst_filename); + if(!result) break; + } + }else{ + mkdir(dst_filename, 0x600); + cpdir(src_filename, dst_filename,new_relativePath,pattern,deepSearch); + } + } + } + return false; + } + public boolean copyFile(String source, String destination) throws IOException{ int fsa_handle = getClient().get_fsa_handle(); @@ -50,31 +86,57 @@ public class FileOperations extends Operations { Logger.logErr("copyFile error: couldn't open " + source); return false; } - Result result_dst = fsa.FSA_OpenFile(fsa_handle, destination, "r"); + + Result result_dst = fsa.FSA_OpenFile(fsa_handle, destination, "w"); if(result_dst.getResultValue() != 0){ Logger.logErr("copyFile error: couldn't open " + destination); fsa.FSA_CloseFile(fsa_handle, result_src.getData()); return false; } - int block_size = 0x10000; + int block_size = 0x20000; int buffer_ptr = system.alloc(block_size, 0x40); int i =0; int src_handle = result_src.getData(); int dst_handle = result_dst.getData(); boolean result = true; + + Result result_stat = fsa.FSA_StatFile(fsa_handle, src_handle); + if(result_stat.getResultValue() < 0){ + Logger.log("copyFile error: FSA_StatFile failed."); + return false; + } + long file_size = result_stat.getData().getPhyssize(); + System.out.println("Copying " + source + " to " + destination + " ("+ (file_size /1024.0f) +" kb)"); + long startTime = System.currentTimeMillis(); while(true){ - Result result_read = fsa.FSA_ReadFilePtr(fsa_handle, src_handle, 0x1, block_size, buffer_ptr); - if(result_read.getResultValue() < 0){ + Result result_read = fsa.FSA_ReadFilePtr(fsa_handle, src_handle, 0x1, block_size, buffer_ptr); + int result_val = result_read.getResultValue(); + if(result_val < 0){ Logger.log("copyFile error: reading source file failed."); result = false; break; } + i += result_val; + Result result_write = fsa.FSA_WriteFilePtr(fsa_handle, dst_handle, 0x1, result_val, buffer_ptr); + int result_write_val = result_read.getResultValue(); + if(result_write_val < 0){ + Logger.log("copyFile error: writing destination file failed."); + result = false; + break; + } + long diff_time = System.currentTimeMillis() - startTime; + + System.out.print("" + String.format("Writing File: progress %08X bytes (%02.02f", i,(i*1.0/file_size*1.0)*100) + "%) (" + String.format("%04d", (int)((i*1.0f) /(diff_time*1.0f)))+" kb/s)\r"); + if(result_val < block_size){ + break; + } } + System.out.println("Wrote " + source + " to " + destination + " ("+ (file_size /1024.0f) +" kb)"); system.free(buffer_ptr); fsa.FSA_CloseFile(fsa_handle, src_handle); fsa.FSA_CloseFile(fsa_handle, dst_handle); return result; - }*/ + } public UtilOperations getUtil() { return util; diff --git a/src/de/mas/wupclient/client/operations/UtilOperations.java b/src/de/mas/wupclient/client/operations/UtilOperations.java index c60992f..76a18db 100644 --- a/src/de/mas/wupclient/client/operations/UtilOperations.java +++ b/src/de/mas/wupclient/client/operations/UtilOperations.java @@ -148,7 +148,7 @@ public class UtilOperations extends Operations { fsa.FSA_CloseDir(fsa_handle, result.getData()); final_result = true; }else{ - Logger.logErr("path does not exists"); + Logger.logErr("path does not exists: error " + result.getResultValue()); final_result = false; } getClient().FSA_Close(getClient().get_fsa_handle()); diff --git a/src/de/mas/wupclient/client/utils/FStats.java b/src/de/mas/wupclient/client/utils/FStats.java index 9d7ccdd..1f5cbc9 100644 --- a/src/de/mas/wupclient/client/utils/FStats.java +++ b/src/de/mas/wupclient/client/utils/FStats.java @@ -35,5 +35,19 @@ public class FStats { StringBuilder sb = new StringBuilder(); return sb.append("Size: " + size + " Physicalsize:" + physsize).toString(); } + public int getSize() { + return size; + } + public void setSize(int size) { + this.size = size; + } + public int getPhyssize() { + return physsize; + } + public void setPhyssize(int physsize) { + this.physsize = physsize; + } + + }