diff --git a/src/de/mas/wiiu/jnus/NUSDataProviderWumad.java b/src/de/mas/wiiu/jnus/NUSDataProviderWumad.java index d22cd9f..9c83df3 100644 --- a/src/de/mas/wiiu/jnus/NUSDataProviderWumad.java +++ b/src/de/mas/wiiu/jnus/NUSDataProviderWumad.java @@ -30,19 +30,21 @@ import java.util.zip.ZipFile; import de.mas.wiiu.jnus.entities.TMD; import de.mas.wiiu.jnus.entities.content.Content; -import de.mas.wiiu.jnus.implementations.wud.parser.WUDPartitionHeader; -import de.mas.wiiu.jnus.implementations.wumad.WumadInfo; +import de.mas.wiiu.jnus.implementations.wud.GamePartitionHeader; +import de.mas.wiiu.jnus.implementations.wud.wumad.WumadGamePartition; import de.mas.wiiu.jnus.interfaces.NUSDataProvider; import de.mas.wiiu.jnus.utils.StreamUtils; public class NUSDataProviderWumad implements NUSDataProvider { - - private final WumadInfo info; + private final ZipFile wumad; + private final WumadGamePartition partition; private final Map files = new HashMap<>(); - public NUSDataProviderWumad(WumadInfo info) { - this.info = info; + public NUSDataProviderWumad(WumadGamePartition gamePartition, ZipFile wudmadFile) { + this.wumad = wudmadFile; + this.partition = gamePartition; + files.putAll(loadFileList(wudmadFile)); } private Map loadFileList(ZipFile zipFile) { @@ -60,13 +62,9 @@ public class NUSDataProviderWumad implements NUSDataProvider { @Override public InputStream readContentAsStream(Content content, long offset, long size) throws IOException { - if (files.isEmpty()) { - files.putAll(loadFileList(info.getZipFile())); - } - - ZipEntry entry = files.values().stream().filter(e -> e.getName().startsWith("p" + info.getPartition() + ".")) + ZipEntry entry = files.values().stream().filter(e -> e.getName().startsWith("p" + partition.getPartitionName() + ".")) .filter(e -> e.getName().endsWith(content.getFilename().toLowerCase())).findFirst().orElseThrow(() -> new FileNotFoundException()); - InputStream in = info.getZipFile().getInputStream(entry); + InputStream in = wumad.getInputStream(entry); StreamUtils.skipExactly(in, offset); return in; @@ -74,7 +72,7 @@ public class NUSDataProviderWumad implements NUSDataProvider { @Override public Optional getContentH3Hash(Content content) throws IOException { - WUDPartitionHeader partitionHeader = info.getPartitionHeader(); + GamePartitionHeader partitionHeader = partition.getPartitionHeader(); if (!partitionHeader.isCalculatedHashes()) { try { partitionHeader.calculateHashes(TMD.parseTMD(getRawTMD().get()).getAllContents()); @@ -87,21 +85,21 @@ public class NUSDataProviderWumad implements NUSDataProvider { @Override public Optional getRawTMD() throws IOException { - return info.getTmdData(); + return Optional.of(partition.getRawTMD()); } @Override public Optional getRawTicket() throws IOException { - return info.getTicketData(); + return Optional.of(partition.getRawTicket()); } @Override public Optional getRawCert() throws IOException { - return info.getCertData(); + return Optional.of(partition.getRawCert()); } @Override public void cleanup() throws IOException { - info.getZipFile().close(); + wumad.close(); } } diff --git a/src/de/mas/wiiu/jnus/NUSTitleLoaderWumad.java b/src/de/mas/wiiu/jnus/NUSTitleLoaderWumad.java deleted file mode 100644 index 6da8619..0000000 --- a/src/de/mas/wiiu/jnus/NUSTitleLoaderWumad.java +++ /dev/null @@ -1,46 +0,0 @@ -/**************************************************************************** - * Copyright (C) 2016-2019 Maschell - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - ****************************************************************************/ -package de.mas.wiiu.jnus; - -import java.io.File; -import java.io.IOException; -import java.text.ParseException; - -import javax.xml.parsers.ParserConfigurationException; - -import org.xml.sax.SAXException; - -import de.mas.wiiu.jnus.implementations.wumad.WumadInfo; -import de.mas.wiiu.jnus.implementations.wumad.WumadParser; - -public final class NUSTitleLoaderWumad { - - private NUSTitleLoaderWumad() { - - } - - public static NUSTitle loadNUSTitle(File inputFile, byte[] commonKey) throws IOException, ParserConfigurationException, SAXException, ParseException { - NUSTitleConfig config = new NUSTitleConfig(); - - config.setCommonKey(commonKey); - - WumadInfo wumadInfo = WumadParser.createWumadInfo(inputFile); - - return NUSTitleLoader.loadNusTitle(config, () -> new NUSDataProviderWumad(wumadInfo)); - } - -} diff --git a/src/de/mas/wiiu/jnus/WumadLoader.java b/src/de/mas/wiiu/jnus/WumadLoader.java new file mode 100644 index 0000000..e087ec4 --- /dev/null +++ b/src/de/mas/wiiu/jnus/WumadLoader.java @@ -0,0 +1,57 @@ +package de.mas.wiiu.jnus; + +import java.io.File; +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipFile; + +import javax.xml.parsers.ParserConfigurationException; + +import org.xml.sax.SAXException; + +import de.mas.wiiu.jnus.implementations.FSTDataProviderNUSTitle; +import de.mas.wiiu.jnus.implementations.wud.wumad.WumadGamePartition; +import de.mas.wiiu.jnus.implementations.wud.wumad.WumadInfo; +import de.mas.wiiu.jnus.implementations.wud.wumad.WumadParser; +import de.mas.wiiu.jnus.interfaces.FSTDataProvider; +import lombok.NonNull; +import lombok.val; + +public class WumadLoader { + + public static WumadInfo load(File wumadFile, byte[] commonKey) throws IOException, ParserConfigurationException, SAXException, ParseException { + return WumadParser.createWumadInfo(wumadFile); + } + + public static List getGamePartionsAsNUSTitles(@NonNull WumadInfo wumadInfo, byte[] commonKey) throws IOException, ParseException { + List result = new ArrayList<>(); + for (val gamePartition : wumadInfo.getGamePartitions()) { + result.add(convertGamePartitionToNUSTitle(gamePartition, wumadInfo.getZipFile(), commonKey)); + } + return result; + } + + private static NUSTitle convertGamePartitionToNUSTitle(WumadGamePartition gamePartition, ZipFile wudmadFile, byte[] commonKey) + throws IOException, ParseException { + final NUSTitleConfig config = new NUSTitleConfig(); + config.setCommonKey(commonKey); + gamePartition.getTmd(); + return NUSTitleLoader.loadNusTitle(config, () -> new NUSDataProviderWumad(gamePartition, wudmadFile)); + } + + public static List getPartitonsAsFSTDataProvider(@NonNull WumadInfo wumadInfo, byte[] commonKey) throws IOException, ParseException { + List result = new ArrayList<>(); + for (val gamePartition : wumadInfo.getGamePartitions()) { + NUSTitle t = convertGamePartitionToNUSTitle(gamePartition, wumadInfo.getZipFile(), commonKey); + FSTDataProviderNUSTitle res = new FSTDataProviderNUSTitle(t); + res.setName(gamePartition.getPartitionName()); + result.add(res); + } + + return result; + + } + +} diff --git a/src/de/mas/wiiu/jnus/implementations/NUSDataProviderWUD.java b/src/de/mas/wiiu/jnus/implementations/NUSDataProviderWUD.java index a73be6c..445994f 100644 --- a/src/de/mas/wiiu/jnus/implementations/NUSDataProviderWUD.java +++ b/src/de/mas/wiiu/jnus/implementations/NUSDataProviderWUD.java @@ -23,8 +23,8 @@ import java.util.Optional; import de.mas.wiiu.jnus.entities.content.Content; import de.mas.wiiu.jnus.entities.content.ContentFSTInfo; import de.mas.wiiu.jnus.entities.fst.FST; +import de.mas.wiiu.jnus.implementations.wud.GamePartitionHeader; import de.mas.wiiu.jnus.implementations.wud.parser.WUDGamePartition; -import de.mas.wiiu.jnus.implementations.wud.parser.WUDPartitionHeader; import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReader; import de.mas.wiiu.jnus.interfaces.NUSDataProvider; import de.mas.wiiu.jnus.utils.FSTUtils; @@ -87,7 +87,7 @@ public class NUSDataProviderWUD implements NUSDataProvider { return Optional.of(getGamePartition().getRawCert()); } - public WUDPartitionHeader getGamePartitionHeader() { + public GamePartitionHeader getGamePartitionHeader() { return getGamePartition().getPartitionHeader(); } diff --git a/src/de/mas/wiiu/jnus/implementations/wud/parser/WUDPartitionHeader.java b/src/de/mas/wiiu/jnus/implementations/wud/GamePartitionHeader.java similarity index 93% rename from src/de/mas/wiiu/jnus/implementations/wud/parser/WUDPartitionHeader.java rename to src/de/mas/wiiu/jnus/implementations/wud/GamePartitionHeader.java index 578e9f8..ee886d5 100644 --- a/src/de/mas/wiiu/jnus/implementations/wud/parser/WUDPartitionHeader.java +++ b/src/de/mas/wiiu/jnus/implementations/wud/GamePartitionHeader.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . ****************************************************************************/ -package de.mas.wiiu.jnus.implementations.wud.parser; +package de.mas.wiiu.jnus.implementations.wud; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -34,17 +34,17 @@ import lombok.Setter; import lombok.extern.java.Log; @Log -public final class WUDPartitionHeader { +public final class GamePartitionHeader { @Getter @Setter private boolean calculatedHashes = false; @Getter private final HashMap h3Hashes = new HashMap<>(); @Getter(AccessLevel.PRIVATE) @Setter(AccessLevel.PRIVATE) private byte[] rawData; - private WUDPartitionHeader() { + private GamePartitionHeader() { } // TODO: real processing. Currently we are ignoring everything except the hashes - public static WUDPartitionHeader parseHeader(byte[] header) { - WUDPartitionHeader result = new WUDPartitionHeader(); + public static GamePartitionHeader parseHeader(byte[] header) { + GamePartitionHeader result = new GamePartitionHeader(); result.setRawData(header); return result; } diff --git a/src/de/mas/wiiu/jnus/implementations/wud/parser/WUDGamePartition.java b/src/de/mas/wiiu/jnus/implementations/wud/parser/WUDGamePartition.java index 6456ad6..3b41589 100644 --- a/src/de/mas/wiiu/jnus/implementations/wud/parser/WUDGamePartition.java +++ b/src/de/mas/wiiu/jnus/implementations/wud/parser/WUDGamePartition.java @@ -19,6 +19,7 @@ package de.mas.wiiu.jnus.implementations.wud.parser; import java.text.ParseException; import de.mas.wiiu.jnus.entities.TMD; +import de.mas.wiiu.jnus.implementations.wud.GamePartitionHeader; import lombok.Data; import lombok.EqualsAndHashCode; @@ -26,14 +27,14 @@ import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) public class WUDGamePartition extends WUDPartition { - private final WUDPartitionHeader partitionHeader; + private final GamePartitionHeader partitionHeader; private final TMD tmd; private final byte[] rawTMD; private final byte[] rawCert; private final byte[] rawTicket; - public WUDGamePartition(String partitionName, long partitionOffset, WUDPartitionHeader partitionHeader, byte[] rawTMD, byte[] rawCert, byte[] rawTicket) + public WUDGamePartition(String partitionName, long partitionOffset, GamePartitionHeader partitionHeader, byte[] rawTMD, byte[] rawCert, byte[] rawTicket) throws ParseException { super(partitionName, partitionOffset); this.partitionHeader = partitionHeader; diff --git a/src/de/mas/wiiu/jnus/implementations/wud/parser/WUDInfoParser.java b/src/de/mas/wiiu/jnus/implementations/wud/parser/WUDInfoParser.java index f8da951..c35c120 100644 --- a/src/de/mas/wiiu/jnus/implementations/wud/parser/WUDInfoParser.java +++ b/src/de/mas/wiiu/jnus/implementations/wud/parser/WUDInfoParser.java @@ -31,6 +31,7 @@ import de.mas.wiiu.jnus.Settings; import de.mas.wiiu.jnus.entities.content.ContentFSTInfo; import de.mas.wiiu.jnus.entities.fst.FST; import de.mas.wiiu.jnus.entities.fst.FSTEntry; +import de.mas.wiiu.jnus.implementations.wud.GamePartitionHeader; import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReader; import de.mas.wiiu.jnus.utils.ByteUtils; import de.mas.wiiu.jnus.utils.FSTUtils; @@ -156,7 +157,7 @@ public final class WUDInfoParser { byte[] header = wudInfo.getWUDDiscReader().readEncryptedToByteArray(curPartitionOffset, 0, curHeaderSize); - WUDPartitionHeader partitionHeader = WUDPartitionHeader.parseHeader(header); + GamePartitionHeader partitionHeader = GamePartitionHeader.parseHeader(header); WUDGamePartition curPartition = new WUDGamePartition(curPartitionPair.getKey(), curPartitionOffset + curHeaderSize, partitionHeader, rawTMD, rawCert, rawTIK); diff --git a/src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadGamePartition.java b/src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadGamePartition.java new file mode 100644 index 0000000..947e2a0 --- /dev/null +++ b/src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadGamePartition.java @@ -0,0 +1,29 @@ +package de.mas.wiiu.jnus.implementations.wud.wumad; + +import java.text.ParseException; + +import de.mas.wiiu.jnus.entities.TMD; +import de.mas.wiiu.jnus.implementations.wud.GamePartitionHeader; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class WumadGamePartition extends WumadPartition { + private final GamePartitionHeader partitionHeader; + + private final TMD tmd; + private final byte[] rawTMD; + private final byte[] rawCert; + private final byte[] rawTicket; + + public WumadGamePartition(String partitionName, GamePartitionHeader partitionHeader, byte[] rawTMD, byte[] rawCert, byte[] rawTicket) + throws ParseException { + super(partitionName); + this.partitionHeader = partitionHeader; + this.rawTMD = rawTMD; + this.tmd = TMD.parseTMD(rawTMD); + this.rawCert = rawCert; + this.rawTicket = rawTicket; + } +} diff --git a/src/de/mas/wiiu/jnus/implementations/wumad/WumadInfo.java b/src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadInfo.java similarity index 69% rename from src/de/mas/wiiu/jnus/implementations/wumad/WumadInfo.java rename to src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadInfo.java index 17b66dd..b4c8d0f 100644 --- a/src/de/mas/wiiu/jnus/implementations/wumad/WumadInfo.java +++ b/src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadInfo.java @@ -14,26 +14,26 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . ****************************************************************************/ -package de.mas.wiiu.jnus.implementations.wumad; +package de.mas.wiiu.jnus.implementations.wud.wumad; -import java.util.Optional; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; import java.util.zip.ZipFile; -import de.mas.wiiu.jnus.implementations.wud.parser.WUDPartitionHeader; import lombok.Data; import lombok.Setter; @Data public class WumadInfo { - @Setter private Optional tmdData = Optional.empty(); - @Setter private Optional ticketData = Optional.empty(); - @Setter private Optional certData = Optional.empty(); + private final List partitions = new ArrayList<>(); + + public List getGamePartitions() { + return partitions.stream().filter(p -> p instanceof WumadGamePartition).map(p -> (WumadGamePartition) p).collect(Collectors.toList()); + } @Setter private ZipFile zipFile; - @Setter private WUDPartitionHeader partitionHeader; - - @Setter private String partition; WumadInfo() { } diff --git a/src/de/mas/wiiu/jnus/implementations/wumad/WumadParser.java b/src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadParser.java similarity index 66% rename from src/de/mas/wiiu/jnus/implementations/wumad/WumadParser.java rename to src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadParser.java index 69fdd9d..42dd277 100644 --- a/src/de/mas/wiiu/jnus/implementations/wumad/WumadParser.java +++ b/src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadParser.java @@ -14,9 +14,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . ****************************************************************************/ -package de.mas.wiiu.jnus.implementations.wumad; +package de.mas.wiiu.jnus.implementations.wud.wumad; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.text.ParseException; @@ -31,7 +32,8 @@ import org.xml.sax.SAXException; import de.mas.wiiu.jnus.entities.fst.FST; import de.mas.wiiu.jnus.entities.fst.FSTEntry; -import de.mas.wiiu.jnus.implementations.wud.parser.WUDPartitionHeader; +import de.mas.wiiu.jnus.implementations.wud.GamePartitionHeader; +import de.mas.wiiu.jnus.implementations.wud.parser.WUDGamePartition; import de.mas.wiiu.jnus.utils.FSTUtils; import de.mas.wiiu.jnus.utils.StreamUtils; @@ -53,26 +55,30 @@ public class WumadParser { // TODO: some .wumad doesn't have SI files. ZipEntry fst = zipFile.getEntry(SI_FST_FILENAME); - + byte[] fstBytes = StreamUtils.getBytesFromStream(zipFile.getInputStream(fst), (int) fst.getSize()); FST fstdd = FST.parseFST(fstBytes); - // TODO: add support for multiple partition inside a wumad - FSTEntry dirRoot = fstdd.getRoot().getDirChildren().get(0); + for (FSTEntry dirRoot : fstdd.getRoot().getDirChildren()) { + ZipEntry data = zipFile.getEntry(String.format("sip.s00%s.00000000.app", dirRoot.getFilename())); - ZipEntry data = zipFile.getEntry(String.format("sip.s00%s.00000000.app", dirRoot.getFilename())); + byte[] rawTMD = getFSTEntryAsByte(dirRoot.getFullPath() + "/" + WUD_TMD_FILENAME, dirRoot, zipFile, data) + .orElseThrow(() -> new FileNotFoundException()); + byte[] rawCert = getFSTEntryAsByte(dirRoot.getFullPath() + "/" + WUD_CERT_FILENAME, dirRoot, zipFile, data) + .orElseThrow(() -> new FileNotFoundException()); + byte[] rawTIK = getFSTEntryAsByte(dirRoot.getFullPath() + "/" + WUD_TICKET_FILENAME, dirRoot, zipFile, data) + .orElseThrow(() -> new FileNotFoundException()); - result.setTmdData(getFSTEntryAsByte(dirRoot.getFullPath() + "/" + WUD_TMD_FILENAME, dirRoot, zipFile, data)); - result.setCertData(getFSTEntryAsByte(dirRoot.getFullPath() + "/" + WUD_CERT_FILENAME, dirRoot, zipFile, data)); - result.setTicketData(getFSTEntryAsByte(dirRoot.getFullPath() + "/" + WUD_TICKET_FILENAME, dirRoot, zipFile, data)); + ZipEntry headerEntry = zipFile.getEntry(String.format("p%s.header.bin", dirRoot.getFilename())); - ZipEntry headerEntry = zipFile.getEntry(String.format("p%s.header.bin", dirRoot.getFilename())); + byte[] header = StreamUtils.getBytesFromStream(zipFile.getInputStream(headerEntry), (int) headerEntry.getSize()); - byte[] header = StreamUtils.getBytesFromStream(zipFile.getInputStream(headerEntry), (int) headerEntry.getSize()); + WumadGamePartition curPartition = new WumadGamePartition(dirRoot.getFilename(), GamePartitionHeader.parseHeader(header), rawTMD, rawCert, + rawTIK); - result.setPartitionHeader(WUDPartitionHeader.parseHeader(header)); - result.setPartition(dirRoot.getFilename()); + result.getPartitions().add(curPartition); + } } catch (ZipException e) { e.printStackTrace(); diff --git a/src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadPartition.java b/src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadPartition.java new file mode 100644 index 0000000..4f2522d --- /dev/null +++ b/src/de/mas/wiiu/jnus/implementations/wud/wumad/WumadPartition.java @@ -0,0 +1,8 @@ +package de.mas.wiiu.jnus.implementations.wud.wumad; + +import lombok.Data; + +@Data +public class WumadPartition { + private final String partitionName; +}