package de.mas.wiiu.jnus.fuse_wiiu; import de.mas.wiiu.jnus.fuse_wiiu.implementation.GroupFuseContainer; import de.mas.wiiu.jnus.fuse_wiiu.implementation.GroupFuseContainerDefault; import de.mas.wiiu.jnus.fuse_wiiu.interfaces.FuseContainer; import de.mas.wiiu.jnus.fuse_wiiu.utils.FuseContainerWrapper; import de.mas.wiiu.jnus.utils.HashUtil; import de.mas.wiiu.jnus.utils.Utils; import org.apache.commons.cli.*; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.util.Arrays; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; public class Main { private static final String DEV_COMMON_KEY = "devcommon.key"; private static final String COMMON_KEY = "common.key"; private final static String OPTION_HELP = "help"; private final static String OPTION_MOUNTPATH = "mountpath"; private final static String OPTION_INPUT = "in"; private final static String OPTION_DISCKEYS = "disckeypath"; private final static String OPTION_TITLEKEYS = "titlekeypath"; private static final String OPTION_COMMON_KEY = "commonkey"; private static final String OPTION_DEV_COMMON_KEY = "devcommonkey"; private static final String HOMEPATH = System.getProperty("user.home") + File.separator + ".wiiu"; private static final String DISC_KEY_PATH = "discKeys"; private static final String TITLE_KEY_PATH = "titleKeys"; private static Optional readKey(File file) { if (file.isFile()) { byte[] key; try { key = Files.readAllBytes(file.toPath()); if (key != null && key.length == 16) { return Optional.of(key); } } catch (IOException e) { } } return Optional.empty(); } private static void checkKeysForFolder(File folder) { if (folder.exists()) { File commonkeyFile = new File(folder.getAbsolutePath() + File.separator + COMMON_KEY); File commonkeyDevFile = new File(folder.getAbsolutePath() + File.separator + DEV_COMMON_KEY); if (commonkeyFile.exists()) { readKey(commonkeyFile).ifPresent(key -> Settings.retailCommonKey = key); } if (commonkeyDevFile.exists()) { readKey(commonkeyDevFile).ifPresent(key -> Settings.devCommonKey = key); } } } public static void main(String[] args) throws Exception { if (!Charset.defaultCharset().toString().equals("UTF-8")) { System.err.println("This application needs to be started with the \"UTF-8\" charset."); System.out.println("Use the jvm argument \"-Dfile.encoding=UTF-8\"."); System.exit(-1); } File homewiiufolder = new File(HOMEPATH); checkKeysForFolder(homewiiufolder); checkKeysForFolder(new File(".")); Options options = getOptions(); if (args.length == 0) { showHelp(options); return; } Settings.disckeyPath = new File(HOMEPATH + File.separator + DISC_KEY_PATH); Settings.titlekeyPath = new File(HOMEPATH + File.separator + TITLE_KEY_PATH); CommandLineParser parser = new DefaultParser(); CommandLine cmd = null; String mountPath = ""; cmd = parser.parse(options, args); String inputPath = ""; if (cmd.hasOption(OPTION_MOUNTPATH)) { mountPath = cmd.getOptionValue(OPTION_MOUNTPATH); } if (cmd.hasOption(OPTION_INPUT)) { inputPath = cmd.getOptionValue(OPTION_INPUT); } if (cmd.hasOption(OPTION_COMMON_KEY)) { String commonKey = cmd.getOptionValue(OPTION_COMMON_KEY); byte[] key = Utils.StringToByteArray(commonKey); if (key != null && key.length == 0x10) { Settings.retailCommonKey = key; System.out.println("Common key was set from command line."); } } if (cmd.hasOption(OPTION_DEV_COMMON_KEY)) { String devCommonKey = cmd.getOptionValue(OPTION_DEV_COMMON_KEY); byte[] key = Utils.StringToByteArray(devCommonKey); if (key != null && key.length == 0x10) { Settings.devCommonKey = key; System.out.println("Dev common key was set from command line."); } } if (cmd.hasOption(OPTION_DISCKEYS)) { Settings.disckeyPath = new File(cmd.getOptionValue(OPTION_DISCKEYS)); } if (cmd.hasOption(OPTION_TITLEKEYS)) { Settings.titlekeyPath = new File(cmd.getOptionValue(OPTION_TITLEKEYS)); } File mount = new File(mountPath); File mountparent = mount.getParentFile(); if (mountparent != null && !mountparent.exists()) { System.err.println("Mounting to " + mount + " is not possible." + mountparent + " does not exist"); return; } else if (mount.exists() && System.getProperty("os.name").contains("Windows")) { System.err.println("Mounting to " + mount + " is not possible. It's already mounted or in use"); return; } if (!Arrays.equals(HashUtil.hashSHA1(Settings.retailCommonKey), Settings.retailCommonKeyHash)) { System.err.println("WARNING: Retail common key is not as expected"); } else { System.out.println("retail common key is okay"); } if (!Arrays.equals(HashUtil.hashSHA1(Settings.devCommonKey), Settings.devCommonKeyHash)) { System.err.println("WARNING: Dev common key is not as expected"); } else { System.out.println("dev common key is okay"); } System.out.println("disc key path is: " + Settings.disckeyPath.getAbsolutePath()); System.out.println("title key path is: " + Settings.titlekeyPath.getAbsolutePath()); GroupFuseContainer root = new GroupFuseContainerDefault(Optional.empty()); File input = new File(inputPath); Map containers = FuseContainerWrapper.createFuseContainer(Optional.of(root), input); for (Entry c : containers.entrySet()) { String name = c.getKey(); if (name.isEmpty()) { name = input.getAbsolutePath().replaceAll("[\\\\/:*?\"<>|]", ""); } root.addFuseContainer(name, c.getValue()); } RootFuseFS stub = new RootFuseFS(root); try { System.out.println("Mounting " + new File(inputPath).getAbsolutePath() + " to " + mount.getAbsolutePath()); stub.mount(mount.toPath(), true, false); } finally { stub.umount(); } } private static Options getOptions() { Options options = new Options(); options.addOption(Option.builder(OPTION_MOUNTPATH).required().hasArg() .desc("The target mount path.").build()); options.addOption(Option.builder(OPTION_INPUT).required().hasArg().desc("input path").build()); options.addOption(Option.builder(OPTION_DISCKEYS).optionalArg(true).hasArg() .desc("Path of .key files used to decrypt WUD/WUX. If not set \"" + HOMEPATH + File.separator + DISC_KEY_PATH + "\" will be used.").build()); options.addOption(Option.builder(OPTION_TITLEKEYS).optionalArg(true).hasArg() .desc("Path of [TITLTEID].key files used to decrypt encrypted titles (.app,.tmd etc.). If not set \"" + HOMEPATH + File.separator + TITLE_KEY_PATH + "\" will be used.") .build()); options.addOption(Option.builder(OPTION_COMMON_KEY).optionalArg(true).hasArg() .desc("Wii U retail common key as binary string. Will be used even if a key is specified in \"" + HOMEPATH + File.separator + COMMON_KEY + "\" or \"" + HOMEPATH + File.separator + COMMON_KEY + "\"") .build()); options.addOption(Option.builder(OPTION_DEV_COMMON_KEY).optionalArg(true).hasArg() .desc("Wii U dev common key as binary string. Will be used even if a key is specified in \"" + HOMEPATH + File.separator + DEV_COMMON_KEY + "\" or \"" + HOMEPATH + File.separator + DEV_COMMON_KEY + "\"") .build()); options.addOption(OPTION_HELP, false, "shows this text"); return options; } private static void showHelp(Options options) { HelpFormatter formatter = new HelpFormatter(); formatter.setWidth(100); formatter.printHelp(" ", options); } }