Add support for wumad data partitions

This commit is contained in:
Maschell 2019-06-04 13:16:13 +02:00
parent 6d9cb2b155
commit 3715b757ab
5 changed files with 118 additions and 14 deletions

View File

@ -12,6 +12,8 @@ import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import de.mas.wiiu.jnus.implementations.FSTDataProviderNUSTitle;
import de.mas.wiiu.jnus.implementations.FSTDataProviderWumadDataPartition;
import de.mas.wiiu.jnus.implementations.NUSDataProviderWumad;
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;
@ -50,6 +52,10 @@ public class WumadLoader {
result.add(res);
}
for (val dataPartition : wumadInfo.getDataPartitions()) {
result.add(new FSTDataProviderWumadDataPartition(dataPartition, wumadInfo.getZipFile()));
}
return result;
}

View File

@ -0,0 +1,51 @@
package de.mas.wiiu.jnus.implementations;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import de.mas.wiiu.jnus.entities.fst.FSTEntry;
import de.mas.wiiu.jnus.implementations.wud.wumad.WumadDataPartition;
import de.mas.wiiu.jnus.interfaces.FSTDataProvider;
import de.mas.wiiu.jnus.utils.StreamUtils;
public class FSTDataProviderWumadDataPartition implements FSTDataProvider {
private final WumadDataPartition dataPartition;
private final ZipFile zipFile;
public FSTDataProviderWumadDataPartition(WumadDataPartition dataPartition, ZipFile zipFile) {
this.dataPartition = dataPartition;
this.zipFile = zipFile;
}
@Override
public String getName() {
return dataPartition.getPartitionName();
}
@Override
public FSTEntry getRoot() {
return dataPartition.getFST().getRoot();
}
@Override
public boolean readFileToStream(OutputStream out, FSTEntry entry, long offset, long size) throws IOException {
StreamUtils.saveInputStreamToOutputStream(readFileAsStream(entry, offset, size), out, size);
return true;
}
@Override
public InputStream readFileAsStream(FSTEntry entry, long offset, long size) throws IOException {
ZipEntry zipEntry = zipFile.stream()
.filter(e -> e.getName().equals(String.format("p%s.s%04d.00000000.app", dataPartition.getPartitionName(), entry.getContentIndex()))).findFirst()
.orElseThrow(() -> new FileNotFoundException());
InputStream in = zipFile.getInputStream(zipEntry);
StreamUtils.skipExactly(in, offset + entry.getFileOffset());
return in;
}
}

View File

@ -0,0 +1,15 @@
package de.mas.wiiu.jnus.implementations.wud.wumad;
import de.mas.wiiu.jnus.entities.fst.FST;
import lombok.Getter;
public class WumadDataPartition extends WumadPartition {
@Getter private final FST FST;
public WumadDataPartition(String partitionName, FST fst) {
super(partitionName);
this.FST = fst;
}
}

View File

@ -33,8 +33,13 @@ public class WumadInfo {
return partitions.stream().filter(p -> p instanceof WumadGamePartition).map(p -> (WumadGamePartition) p).collect(Collectors.toList());
}
public List<WumadDataPartition> getDataPartitions() {
return partitions.stream().filter(p -> p instanceof WumadDataPartition).map(p -> ((WumadDataPartition) p)).collect(Collectors.toList());
}
@Setter private ZipFile zipFile;
WumadInfo() {
}
}

View File

@ -21,7 +21,11 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
@ -33,9 +37,9 @@ 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.GamePartitionHeader;
import de.mas.wiiu.jnus.implementations.wud.parser.WUDGamePartition;
import de.mas.wiiu.jnus.utils.FSTUtils;
import de.mas.wiiu.jnus.utils.StreamUtils;
import lombok.val;
public class WumadParser {
@ -53,31 +57,54 @@ public class WumadParser {
ZipFile zipFile = new ZipFile(wumadFile);
result.setZipFile(zipFile);
// TODO: some .wumad doesn't have SI files.
ZipEntry fst = zipFile.getEntry(SI_FST_FILENAME);
// Let's get all possible partitions
Map<String, List<ZipEntry>> allPartitions = zipFile.stream().filter(e -> e.getName().startsWith("p"))
.collect(Collectors.groupingBy(e -> e.getName().substring(1, 3)));
byte[] fstBytes = StreamUtils.getBytesFromStream(zipFile.getInputStream(fst), (int) fst.getSize());
Map<String, FSTEntry> gamepartitions = new HashMap<>();
FST fstdd = FST.parseFST(fstBytes);
// If we have a SI partition, let parse the FST to get all game partitions.
ZipEntry siFST = zipFile.getEntry(SI_FST_FILENAME);
if (siFST != null) {
byte[] fstBytes = StreamUtils.getBytesFromStream(zipFile.getInputStream(siFST), (int) siFST.getSize());
for (FSTEntry dirRoot : fstdd.getRoot().getDirChildren()) {
ZipEntry data = zipFile.getEntry(String.format("sip.s00%s.00000000.app", dirRoot.getFilename()));
FST parsedFST = FST.parseFST(fstBytes);
gamepartitions.putAll(parsedFST.getRoot().getDirChildren().stream().collect(Collectors.toMap(e -> e.getFilename(), e -> e)));
}
byte[] rawTMD = getFSTEntryAsByte(dirRoot.getFullPath() + "/" + WUD_TMD_FILENAME, dirRoot, zipFile, data)
// process all game partitions. Remove the partitions from the "all partitions" list on success.
for (val e : gamepartitions.entrySet()) {
ZipEntry data = zipFile.getEntry(String.format("sip.s00%s.00000000.app", e.getKey()));
byte[] rawTMD = getFSTEntryAsByte("/" + e.getKey() + "/" + WUD_TMD_FILENAME, e.getValue(), zipFile, data)
.orElseThrow(() -> new FileNotFoundException());
byte[] rawCert = getFSTEntryAsByte(dirRoot.getFullPath() + "/" + WUD_CERT_FILENAME, dirRoot, zipFile, data)
byte[] rawCert = getFSTEntryAsByte("/" + e.getKey() + "/" + WUD_CERT_FILENAME, e.getValue(), zipFile, data)
.orElseThrow(() -> new FileNotFoundException());
byte[] rawTIK = getFSTEntryAsByte(dirRoot.getFullPath() + "/" + WUD_TICKET_FILENAME, dirRoot, zipFile, data)
byte[] rawTIK = getFSTEntryAsByte("/" + e.getKey() + "/" + WUD_TICKET_FILENAME, e.getValue(), zipFile, data)
.orElseThrow(() -> new FileNotFoundException());
ZipEntry headerEntry = zipFile.getEntry(String.format("p%s.header.bin", dirRoot.getFilename()));
ZipEntry headerEntry = zipFile.getEntry(String.format("p%s.header.bin", e.getKey()));
byte[] header = StreamUtils.getBytesFromStream(zipFile.getInputStream(headerEntry), (int) headerEntry.getSize());
WumadGamePartition curPartition = new WumadGamePartition(dirRoot.getFilename(), GamePartitionHeader.parseHeader(header), rawTMD, rawCert,
rawTIK);
WumadGamePartition curPartition = new WumadGamePartition(e.getKey(), GamePartitionHeader.parseHeader(header), rawTMD, rawCert, rawTIK);
result.getPartitions().add(curPartition);
allPartitions.remove(e.getKey());
}
// The remaining partitions are data partitions.
for (val e : allPartitions.entrySet()) {
ZipEntry fstEntry = e.getValue().stream().filter(f -> f.getName().contains("fst")).findFirst().orElseThrow(() -> new FileNotFoundException());
byte[] fstBytes = StreamUtils.getBytesFromStream(zipFile.getInputStream(fstEntry), (int) fstEntry.getSize());
FST parsedFST = FST.parseFST(fstBytes);
WumadDataPartition curPartition = new WumadDataPartition(e.getKey(), parsedFST);
result.getPartitions().add(curPartition);
}
} catch (ZipException e) {