mirror of
https://github.com/Maschell/JNUSLib.git
synced 2024-11-22 07:59:19 +01:00
Add initial support for .wumad files.
Doesn't support all .wumads yet. Multiple paritions are not supported. SI partition is required
This commit is contained in:
parent
23f7066f0a
commit
cb0874deda
107
src/de/mas/wiiu/jnus/NUSDataProviderWumad.java
Normal file
107
src/de/mas/wiiu/jnus/NUSDataProviderWumad.java
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
****************************************************************************/
|
||||||
|
package de.mas.wiiu.jnus;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
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.interfaces.NUSDataProvider;
|
||||||
|
import de.mas.wiiu.jnus.utils.StreamUtils;
|
||||||
|
|
||||||
|
public class NUSDataProviderWumad implements NUSDataProvider {
|
||||||
|
|
||||||
|
private final WumadInfo info;
|
||||||
|
|
||||||
|
private final Map<String, ZipEntry> files = new HashMap<>();
|
||||||
|
|
||||||
|
public NUSDataProviderWumad(WumadInfo info) {
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, ZipEntry> loadFileList(ZipFile zipFile) {
|
||||||
|
Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
|
||||||
|
Map<String, ZipEntry> result = new HashMap<>();
|
||||||
|
while (zipEntries.hasMoreElements()) {
|
||||||
|
ZipEntry entry = (ZipEntry) zipEntries.nextElement();
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
String entryName = entry.getName();
|
||||||
|
result.put(entryName.toLowerCase(Locale.ENGLISH), entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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() + "."))
|
||||||
|
.filter(e -> e.getName().endsWith(content.getFilename().toLowerCase())).findFirst().orElseThrow(() -> new FileNotFoundException());
|
||||||
|
InputStream in = info.getZipFile().getInputStream(entry);
|
||||||
|
StreamUtils.skipExactly(in, offset);
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<byte[]> getContentH3Hash(Content content) throws IOException {
|
||||||
|
WUDPartitionHeader partitionHeader = info.getPartitionHeader();
|
||||||
|
if (!partitionHeader.isCalculatedHashes()) {
|
||||||
|
try {
|
||||||
|
partitionHeader.calculateHashes(TMD.parseTMD(getRawTMD().get()).getAllContents());
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return partitionHeader.getH3Hash(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<byte[]> getRawTMD() throws IOException {
|
||||||
|
return info.getTmdData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<byte[]> getRawTicket() throws IOException {
|
||||||
|
return info.getTicketData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<byte[]> getRawCert() throws IOException {
|
||||||
|
return info.getCertData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanup() throws IOException {
|
||||||
|
info.getZipFile().close();
|
||||||
|
}
|
||||||
|
}
|
46
src/de/mas/wiiu/jnus/NUSTitleLoaderWumad.java
Normal file
46
src/de/mas/wiiu/jnus/NUSTitleLoaderWumad.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
****************************************************************************/
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
40
src/de/mas/wiiu/jnus/implementations/wumad/WumadInfo.java
Normal file
40
src/de/mas/wiiu/jnus/implementations/wumad/WumadInfo.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
****************************************************************************/
|
||||||
|
package de.mas.wiiu.jnus.implementations.wumad;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
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<byte[]> tmdData = Optional.empty();
|
||||||
|
@Setter private Optional<byte[]> ticketData = Optional.empty();
|
||||||
|
@Setter private Optional<byte[]> certData = Optional.empty();
|
||||||
|
|
||||||
|
@Setter private ZipFile zipFile;
|
||||||
|
@Setter private WUDPartitionHeader partitionHeader;
|
||||||
|
|
||||||
|
@Setter private String partition;
|
||||||
|
|
||||||
|
WumadInfo() {
|
||||||
|
}
|
||||||
|
}
|
97
src/de/mas/wiiu/jnus/implementations/wumad/WumadParser.java
Normal file
97
src/de/mas/wiiu/jnus/implementations/wumad/WumadParser.java
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
****************************************************************************/
|
||||||
|
package de.mas.wiiu.jnus.implementations.wumad;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipException;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
|
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.utils.FSTUtils;
|
||||||
|
import de.mas.wiiu.jnus.utils.StreamUtils;
|
||||||
|
|
||||||
|
public class WumadParser {
|
||||||
|
|
||||||
|
public static final String WUD_TMD_FILENAME = "title.tmd";
|
||||||
|
public static final String WUD_TICKET_FILENAME = "title.tik";
|
||||||
|
public static final String WUD_CERT_FILENAME = "title.cert";
|
||||||
|
|
||||||
|
public static final String SI_FST_FILENAME = "sip.fst.00000000.app";
|
||||||
|
public static final String P01_HEADER = "";
|
||||||
|
|
||||||
|
public static WumadInfo createWumadInfo(File wumadFile) throws IOException, ParserConfigurationException, SAXException, ParseException {
|
||||||
|
WumadInfo result = new WumadInfo();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ZipFile zipFile = new ZipFile(wumadFile);
|
||||||
|
result.setZipFile(zipFile);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
ZipEntry data = zipFile.getEntry(String.format("sip.s00%s.00000000.app", dirRoot.getFilename()));
|
||||||
|
|
||||||
|
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()));
|
||||||
|
|
||||||
|
byte[] header = StreamUtils.getBytesFromStream(zipFile.getInputStream(headerEntry), (int) headerEntry.getSize());
|
||||||
|
|
||||||
|
result.setPartitionHeader(WUDPartitionHeader.parseHeader(header));
|
||||||
|
result.setPartition(dirRoot.getFilename());
|
||||||
|
|
||||||
|
} catch (ZipException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new ParseException("Failed to parse wumad:" + e.getMessage(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<byte[]> getFSTEntryAsByte(String filePath, FSTEntry dirRoot, ZipFile zipFile, ZipEntry data) throws IOException {
|
||||||
|
Optional<FSTEntry> entryOpt = FSTUtils.getEntryByFullPath(dirRoot, filePath);
|
||||||
|
if (!entryOpt.isPresent()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
FSTEntry entry = entryOpt.get();
|
||||||
|
|
||||||
|
InputStream in = zipFile.getInputStream(data);
|
||||||
|
|
||||||
|
StreamUtils.skipExactly(in, entry.getFileOffset());
|
||||||
|
return Optional.of(StreamUtils.getBytesFromStream(in, (int) entry.getFileSize()));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user