libKonogonka/src/main/java/libKonogonka/Tools/NPDM/NPDMProvider.java

168 lines
7.6 KiB
Java

/*
Copyright 2019-2020 Dmitry Isaenko
This file is part of Konogonka.
Konogonka 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.
Konogonka 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 Konogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package libKonogonka.Tools.NPDM;
import libKonogonka.Tools.ASuperInFileProvider;
import libKonogonka.Tools.NPDM.ACI0.ACI0Provider;
import libKonogonka.Tools.NPDM.ACID.ACIDProvider;
import java.io.File;
import java.io.PipedInputStream;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import static libKonogonka.LoperConverter.*;
public class NPDMProvider extends ASuperInFileProvider {
private String magicNum;
private byte[] reserved1;
private byte MMUFlags;
private byte reserved2;
private byte mainThreadPrio;
private byte mainThreadCoreNum;
private byte[] reserved3;
private int personalMmHeapSize; // safe-to-store
private int version; // safe?
private long mainThreadStackSize; // TODO: check if safe
private String titleName;
private byte[] productCode;
private byte[] reserved4;
private int aci0offset; // originally 4-bytes (u-int)
private int aci0size; // originally 4-bytes (u-int)
private int acidOffset; // originally 4-bytes (u-int)
private int acidSize; // originally 4-bytes (u-int)
private ACI0Provider aci0;
private ACIDProvider acid;
public NPDMProvider(PipedInputStream pis) throws Exception{
byte[] mainBuf = new byte[0x80];
if(pis.read(mainBuf) != 0x80)
throw new Exception("NPDMProvider: Failed to read 'META'");
aci0offset = getLEint(mainBuf, 0x70);
aci0size = getLEint(mainBuf, 0x74);
acidOffset = getLEint(mainBuf, 0x78);
acidSize = getLEint(mainBuf, 0x7C);
byte[] aci0Buf;
byte[] acidBuf;
if (aci0offset < acidOffset){
if (pis.skip(aci0offset - 0x80) != (aci0offset - 0x80))
throw new Exception("NPDMProvider: Failed to skip bytes till 'ACI0'");
if ((aci0Buf = readFromStream(pis, aci0size)) == null)
throw new Exception("NPDMProvider: Failed to read 'ACI0'");
if (pis.skip(acidOffset - aci0offset - aci0size) != (acidOffset - aci0offset - aci0size))
throw new Exception("NPDMProvider: Failed to skip bytes till 'ACID'");
if ((acidBuf = readFromStream(pis, acidSize)) == null)
throw new Exception("NPDMProvider: Failed to read 'ACID'");
}
else {
if (pis.skip(acidOffset - 0x80) != (acidOffset - 0x80))
throw new Exception("NPDMProvider: Failed to skip bytes till 'ACID'");
if ((acidBuf = readFromStream(pis, acidSize)) == null)
throw new Exception("NPDMProvider: Failed to read 'ACID'");
if (pis.skip(aci0offset - acidOffset - acidSize) != (aci0offset - acidOffset - acidSize))
throw new Exception("NPDMProvider: Failed to skip bytes till 'ACI0'");
if ((aci0Buf = readFromStream(pis, aci0size)) == null)
throw new Exception("NPDMProvider: Failed to read 'ACI0'");
}
magicNum = new String(mainBuf, 0, 4, StandardCharsets.UTF_8);
reserved1 = Arrays.copyOfRange(mainBuf, 0x4, 0xC);
MMUFlags = mainBuf[0xC];
reserved2 = mainBuf[0xD];
mainThreadPrio = mainBuf[0xE];
mainThreadCoreNum = mainBuf[0xF];
reserved3 = Arrays.copyOfRange(mainBuf, 0x10, 0x14);
personalMmHeapSize = getLEint(mainBuf, 0x14);
version = getLEint(mainBuf, 0x18);
mainThreadStackSize = getLElongOfInt(mainBuf, 0x1C);
titleName = new String(mainBuf, 0x20, 0x10, StandardCharsets.UTF_8);
productCode = Arrays.copyOfRange(mainBuf, 0x30, 0x40);
reserved4 = Arrays.copyOfRange(mainBuf, 0x40, 0x70);
aci0 = new ACI0Provider(aci0Buf);
acid = new ACIDProvider(acidBuf);
}
public NPDMProvider(File file) throws Exception { this(file, 0); }
public NPDMProvider(File file, long offset) throws Exception {
if (file.length() - offset < 0x80) // Header's size
throw new Exception("NPDMProvider: File is too small.");
RandomAccessFile raf = new RandomAccessFile(file, "r");
raf.seek(offset);
// Get META
byte[] metaBuf = new byte[0x80];
if (raf.read(metaBuf) != 0x80)
throw new Exception("NPDMProvider: Failed to read 'META'");
magicNum = new String(metaBuf, 0, 4, StandardCharsets.UTF_8);
reserved1 = Arrays.copyOfRange(metaBuf, 0x4, 0xC);
MMUFlags = metaBuf[0xC];
reserved2 = metaBuf[0xD];
mainThreadPrio = metaBuf[0xE];
mainThreadCoreNum = metaBuf[0xF];
reserved3 = Arrays.copyOfRange(metaBuf, 0x10, 0x14);
personalMmHeapSize = getLEint(metaBuf, 0x14);
version = getLEint(metaBuf, 0x18);
mainThreadStackSize = getLElongOfInt(metaBuf, 0x1C);
titleName = new String(metaBuf, 0x20, 0x10, StandardCharsets.UTF_8);
productCode = Arrays.copyOfRange(metaBuf, 0x30, 0x40);
reserved4 = Arrays.copyOfRange(metaBuf, 0x40, 0x70);
aci0offset = getLEint(metaBuf, 0x70);
aci0size = getLEint(metaBuf, 0x74);
acidOffset = getLEint(metaBuf, 0x78);
acidSize = getLEint(metaBuf, 0x7C);
// Get ACI0
raf.seek(aci0offset);
metaBuf = new byte[aci0size]; // TODO: NOTE: we read all size but it's memory consuming
if (raf.read(metaBuf) != aci0size)
throw new Exception("NPDMProvider: Failed to read 'ACI0'");
aci0 = new ACI0Provider(metaBuf);
// Get ACID
raf.seek(acidOffset);
metaBuf = new byte[acidSize]; // TODO: NOTE: we read all size but it's memory consuming
if (raf.read(metaBuf) != acidSize)
throw new Exception("NPDMProvider: Failed to read 'ACID'");
acid = new ACIDProvider(metaBuf);
raf.close();
}
public String getMagicNum() { return magicNum; }
public byte[] getReserved1() { return reserved1; }
public byte getMMUFlags() { return MMUFlags; }
public byte getReserved2() { return reserved2; }
public byte getMainThreadPrio() { return mainThreadPrio; }
public byte getMainThreadCoreNum() { return mainThreadCoreNum; }
public byte[] getReserved3() { return reserved3; }
public int getPersonalMmHeapSize() { return personalMmHeapSize; }
public int getVersion() { return version; }
public long getMainThreadStackSize() { return mainThreadStackSize; }
public String getTitleName() { return titleName; }
public byte[] getProductCode() { return productCode; }
public byte[] getReserved4() { return reserved4; }
public int getAci0offset() { return aci0offset; }
public int getAci0size() { return aci0size; }
public int getAcidOffset() { return acidOffset; }
public int getAcidSize() { return acidSize; }
public ACI0Provider getAci0() { return aci0; }
public ACIDProvider getAcid() { return acid; }
}