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

132 lines
5.6 KiB
Java

/*
Copyright 2019-2022 Dmitry Isaenko
This file is part of libKonogonka.
libKonogonka 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.
libKonogonka 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 libKonogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package libKonogonka.Tools.NPDM;
import libKonogonka.Tools.NPDM.ACI0.ACI0Provider;
import libKonogonka.Tools.NPDM.ACID.ACIDProvider;
import libKonogonka.ctraes.InFileStreamProducer;
import java.io.BufferedInputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import static libKonogonka.Converter.*;
public class NPDMProvider{
private final String magicNum;
private final byte[] reserved1;
private final byte MMUFlags;
private final byte reserved2;
private final byte mainThreadPrio;
private final byte mainThreadCoreNum;
private final byte[] reserved3;
private final int personalMmHeapSize; // safe-to-store
private final int version; // safe?
private final long mainThreadStackSize; // TODO: check if safe
private final String titleName;
private final byte[] productCode;
private final byte[] reserved4;
private final int aci0offset; // originally 4-bytes (u-int)
private final int aci0size; // originally 4-bytes (u-int)
private final int acidOffset; // originally 4-bytes (u-int)
private final int acidSize; // originally 4-bytes (u-int)
private ACI0Provider aci0;
private ACIDProvider acid;
public NPDMProvider(File file) throws Exception {
this(file, 0);
}
public NPDMProvider(File file, long offset) throws Exception {
this(new InFileStreamProducer(file, offset));
}
public NPDMProvider(InFileStreamProducer producer) throws Exception{
try (BufferedInputStream stream = producer.produce()) {
byte[] mainBuf = new byte[0x80];
if (stream.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);
if (aci0offset < acidOffset) {
calculateACI0(stream, aci0offset - 0x80);
calculateACID(stream, acidOffset - aci0offset - aci0size);
} else {
calculateACID(stream, acidOffset - 0x80);
calculateACI0(stream, aci0offset - acidOffset - acidSize);
}
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);
}
}
private void calculateACID(BufferedInputStream stream, int toSkip) throws Exception{
byte[] acidBuf = new byte[acidSize];
if (stream.skip(toSkip) != toSkip)
throw new Exception("NPDMProvider: Failed to skip bytes till 'ACID'");
if (acidSize != stream.read(acidBuf))
throw new Exception("NPDMProvider: Failed to read 'ACID'");
acid = new ACIDProvider(acidBuf);
}
private void calculateACI0(BufferedInputStream stream, int toSkip) throws Exception{
byte[] aci0Buf = new byte[aci0size];
if (stream.skip(toSkip) != toSkip)
throw new Exception("NPDMProvider: Failed to skip bytes till 'ACI0'");
if (aci0size != stream.read(aci0Buf))
throw new Exception("NPDMProvider: Failed to read 'ACI0'");
aci0 = new ACI0Provider(aci0Buf);
}
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; }
}