168 lines
7.6 KiB
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; }
|
|
}
|