diff options
Diffstat (limited to 'src/com/pkrandom/newnds')
-rwxr-xr-x | src/com/pkrandom/newnds/CRC16.java | 52 | ||||
-rw-r--r-- | src/com/pkrandom/newnds/NARCArchive.java | 221 | ||||
-rwxr-xr-x | src/com/pkrandom/newnds/NDSFile.java | 118 | ||||
-rwxr-xr-x | src/com/pkrandom/newnds/NDSRom.java | 712 | ||||
-rwxr-xr-x | src/com/pkrandom/newnds/NDSY9Entry.java | 139 |
5 files changed, 1242 insertions, 0 deletions
diff --git a/src/com/pkrandom/newnds/CRC16.java b/src/com/pkrandom/newnds/CRC16.java new file mode 100755 index 0000000..8ae8ad2 --- /dev/null +++ b/src/com/pkrandom/newnds/CRC16.java @@ -0,0 +1,52 @@ +package com.pkrandom.newnds; + +/*----------------------------------------------------------------------------*/ +/*-- CRC16.java - crc16 calculator for NDS checksums --*/ +/*-- Code derived from "Nintendo DS rom tool", copyright (C) DevkitPro --*/ +/*-- Original Code by Rafael Vuijk, Dave Murphy, Alexei Karpenko --*/ +/*-- --*/ +/*-- --*/ +/*-- 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/>. --*/ +/*----------------------------------------------------------------------------*/ + +public class CRC16 { + private static final int[] table = { 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, + 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, + 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, + 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, + 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, + 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, + 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, + 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, + 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, + 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, + 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, + 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, + 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, + 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, + 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, + 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, + 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, + 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 }; + + public static short calculate(byte[] data, int offset, int length) { + int crc = 0xFFFF; + for (int i = 0; i < length; i++) { + crc = ((crc >>> 8) ^ table[(crc ^ data[i + offset]) & 0xff]); + } + return (short) crc; + } +}
\ No newline at end of file diff --git a/src/com/pkrandom/newnds/NARCArchive.java b/src/com/pkrandom/newnds/NARCArchive.java new file mode 100644 index 0000000..dd6c239 --- /dev/null +++ b/src/com/pkrandom/newnds/NARCArchive.java @@ -0,0 +1,221 @@ +package com.pkrandom.newnds; + +/*----------------------------------------------------------------------------*/ +/*-- NARCArchive.java - class for packing/unpacking GARC archives --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer ZX" by the UPR-ZX team --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2020. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- 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/>. --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class NARCArchive { + + private List<String> filenames = new ArrayList<>(); + public List<byte[]> files = new ArrayList<>(); + + private boolean hasFilenames = false; + + public NARCArchive() { + // creates a new empty NARC with no filenames by default + } + + public NARCArchive(byte[] data) throws IOException { + Map<String, byte[]> frames = readNitroFrames(data); + if (!frames.containsKey("FATB") || !frames.containsKey("FNTB") || !frames.containsKey("FIMG")) { + throw new IOException("Not a valid narc file"); + } + + // File contents + byte[] fatbframe = frames.get("FATB"); + byte[] fimgframe = frames.get("FIMG"); + int fileCount = readLong(fatbframe, 0); + for (int i = 0; i < fileCount; i++) { + int startOffset = readLong(fatbframe, 4 + i * 8); + int endOffset = readLong(fatbframe, 8 + i * 8); + int length = (endOffset - startOffset); + byte[] thisFile = new byte[length]; + System.arraycopy(fimgframe, startOffset, thisFile, 0, length); + files.add(thisFile); + } + + // Filenames? + byte[] fntbframe = frames.get("FNTB"); + int unk1 = readLong(fntbframe, 0); + if (unk1 == 8) { + // Filenames exist + hasFilenames = true; + int offset = 8; + for (int i = 0; i < fileCount; i++) { + int fnLength = (fntbframe[offset] & 0xFF); + offset++; + byte[] filenameBA = new byte[fnLength]; + System.arraycopy(fntbframe, offset, filenameBA, 0, fnLength); + String filename = new String(filenameBA, "US-ASCII"); + filenames.add(filename); + } + } else { + hasFilenames = false; + for (int i = 0; i < fileCount; i++) { + filenames.add(null); + } + } + } + + public byte[] getBytes() throws IOException { + // Get bytes required for FIMG frame + int bytesRequired = 0; + for (byte[] file : files) { + bytesRequired += Math.ceil(file.length / 4.0) * 4; + } + // FIMG frame & FATB frame build + + // 4 for numentries, 8*size for entries, 8 for nitro header + byte[] fatbFrame = new byte[4 + files.size() * 8 + 8]; + // bytesRequired + 8 for nitro header + byte[] fimgFrame = new byte[bytesRequired + 8]; + + // Nitro headers + fatbFrame[0] = 'B'; + fatbFrame[1] = 'T'; + fatbFrame[2] = 'A'; + fatbFrame[3] = 'F'; + writeLong(fatbFrame, 4, fatbFrame.length); + + fimgFrame[0] = 'G'; + fimgFrame[1] = 'M'; + fimgFrame[2] = 'I'; + fimgFrame[3] = 'F'; + writeLong(fimgFrame, 4, fimgFrame.length); + int offset = 0; + + writeLong(fatbFrame, 8, files.size()); + for (int i = 0; i < files.size(); i++) { + byte[] file = files.get(i); + int bytesRequiredForFile = (int) (Math.ceil(file.length / 4.0) * 4); + System.arraycopy(file, 0, fimgFrame, offset + 8, file.length); + for (int filler = file.length; filler < bytesRequiredForFile; filler++) { + fimgFrame[offset + 8 + filler] = (byte) 0xFF; + } + writeLong(fatbFrame, 12 + i * 8, offset); + writeLong(fatbFrame, 16 + i * 8, offset + file.length); + offset += bytesRequiredForFile; + } + + // FNTB Frame + int bytesForFNTBFrame = 16; + if (hasFilenames) { + for (String filename : filenames) { + bytesForFNTBFrame += filename.getBytes("US-ASCII").length + 1; + } + } + byte[] fntbFrame = new byte[bytesForFNTBFrame]; + + fntbFrame[0] = 'B'; + fntbFrame[1] = 'T'; + fntbFrame[2] = 'N'; + fntbFrame[3] = 'F'; + writeLong(fntbFrame, 4, fntbFrame.length); + + if (hasFilenames) { + writeLong(fntbFrame, 8, 8); + writeLong(fntbFrame, 12, 0x10000); + int fntbOffset = 16; + for (String filename : filenames) { + byte[] fntbfilename = filename.getBytes("US-ASCII"); + fntbFrame[fntbOffset] = (byte) fntbfilename.length; + System.arraycopy(fntbfilename, 0, fntbFrame, fntbOffset + 1, fntbfilename.length); + fntbOffset += 1 + fntbfilename.length; + } + } else { + writeLong(fntbFrame, 8, 4); + writeLong(fntbFrame, 12, 0x10000); + } + + // Now for the actual Nitro file + int nitrolength = 16 + fatbFrame.length + fntbFrame.length + fimgFrame.length; + byte[] nitroFile = new byte[nitrolength]; + nitroFile[0] = 'N'; + nitroFile[1] = 'A'; + nitroFile[2] = 'R'; + nitroFile[3] = 'C'; + writeWord(nitroFile, 4, 0xFFFE); + writeWord(nitroFile, 6, 0x0100); + writeLong(nitroFile, 8, nitrolength); + writeWord(nitroFile, 12, 0x10); + writeWord(nitroFile, 14, 3); + System.arraycopy(fatbFrame, 0, nitroFile, 16, fatbFrame.length); + System.arraycopy(fntbFrame, 0, nitroFile, 16 + fatbFrame.length, fntbFrame.length); + System.arraycopy(fimgFrame, 0, nitroFile, 16 + fatbFrame.length + fntbFrame.length, fimgFrame.length); + + return nitroFile; + } + + private Map<String, byte[]> readNitroFrames(byte[] data) throws IOException { + + // Read the number of frames + int frameCount = readWord(data, 0x0E); + + // each frame + int offset = 0x10; + Map<String, byte[]> frames = new TreeMap<>(); + for (int i = 0; i < frameCount; i++) { + byte[] magic = new byte[] { data[offset + 3], data[offset + 2], data[offset + 1], data[offset] }; + String magicS = new String(magic, "US-ASCII"); + + int frame_size = readLong(data, offset + 4); + // Patch for BB/VW and other DS hacks which don't update + // the size of their expanded NARCs correctly + if (i == frameCount - 1 && offset + frame_size < data.length) { + frame_size = data.length - offset; + } + byte[] frame = new byte[frame_size - 8]; + System.arraycopy(data, offset + 8, frame, 0, frame_size - 8); + frames.put(magicS, frame); + offset += frame_size; + } + return frames; + } + + private int readWord(byte[] data, int offset) { + return (data[offset] & 0xFF) | ((data[offset + 1] & 0xFF) << 8); + } + + private int readLong(byte[] data, int offset) { + return (data[offset] & 0xFF) | ((data[offset + 1] & 0xFF) << 8) | ((data[offset + 2] & 0xFF) << 16) + | ((data[offset + 3] & 0xFF) << 24); + } + + private void writeWord(byte[] data, int offset, int value) { + data[offset] = (byte) (value & 0xFF); + data[offset + 1] = (byte) ((value >> 8) & 0xFF); + } + + private void writeLong(byte[] data, int offset, int value) { + data[offset] = (byte) (value & 0xFF); + data[offset + 1] = (byte) ((value >> 8) & 0xFF); + data[offset + 2] = (byte) ((value >> 16) & 0xFF); + data[offset + 3] = (byte) ((value >> 24) & 0xFF); + } + +} diff --git a/src/com/pkrandom/newnds/NDSFile.java b/src/com/pkrandom/newnds/NDSFile.java new file mode 100755 index 0000000..c3564f2 --- /dev/null +++ b/src/com/pkrandom/newnds/NDSFile.java @@ -0,0 +1,118 @@ +package com.pkrandom.newnds; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; + +import com.pkrandom.FileFunctions; + +/*----------------------------------------------------------------------------*/ +/*-- NDSFile.java - an entry in the FAT/FNT filesystem --*/ +/*-- Code based on "Nintendo DS rom tool", copyright (C) DevkitPro --*/ +/*-- Original Code by Rafael Vuijk, Dave Murphy, Alexei Karpenko --*/ +/*-- --*/ +/*-- --*/ +/*-- 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/>. --*/ +/*----------------------------------------------------------------------------*/ + +public class NDSFile { + + private NDSRom parent; + public int offset, size; + public int fileID; + public String fullPath; + private Extracted status = Extracted.NOT; + private String extFilename; + public byte[] data; + public long originalCRC; + + public NDSFile(NDSRom parent) { + this.parent = parent; + } + + public byte[] getContents() throws IOException { + if (this.status == Extracted.NOT) { + // extract file + parent.reopenROM(); + RandomAccessFile rom = parent.getBaseRom(); + byte[] buf = new byte[this.size]; + rom.seek(this.offset); + rom.readFully(buf); + originalCRC = FileFunctions.getCRC32(buf); + if (parent.isWritingEnabled()) { + // make a file + String tmpDir = parent.getTmpFolder(); + this.extFilename = fullPath.replaceAll("[^A-Za-z0-9_]+", ""); + File tmpFile = new File(tmpDir + extFilename); + FileOutputStream fos = new FileOutputStream(tmpFile); + fos.write(buf); + fos.close(); + tmpFile.deleteOnExit(); + this.status = Extracted.TO_FILE; + this.data = null; + return buf; + } else { + this.status = Extracted.TO_RAM; + this.data = buf; + byte[] newcopy = new byte[buf.length]; + System.arraycopy(buf, 0, newcopy, 0, buf.length); + return newcopy; + } + } else if (this.status == Extracted.TO_RAM) { + byte[] newcopy = new byte[this.data.length]; + System.arraycopy(this.data, 0, newcopy, 0, this.data.length); + return newcopy; + } else { + String tmpDir = parent.getTmpFolder(); + return FileFunctions.readFileFullyIntoBuffer(tmpDir + this.extFilename); + } + } + + public void writeOverride(byte[] data) throws IOException { + if (status == Extracted.NOT) { + // temp extract + getContents(); + } + if (status == Extracted.TO_FILE) { + String tmpDir = parent.getTmpFolder(); + FileOutputStream fos = new FileOutputStream(new File(tmpDir + this.extFilename)); + fos.write(data); + fos.close(); + } else { + if (this.data.length == data.length) { + // copy new in + System.arraycopy(data, 0, this.data, 0, data.length); + } else { + // make new array + this.data = null; + this.data = new byte[data.length]; + System.arraycopy(data, 0, this.data, 0, data.length); + } + } + } + + // returns null if no override + public byte[] getOverrideContents() throws IOException { + if (status == Extracted.NOT) { + return null; + } + return getContents(); + } + + private enum Extracted { + NOT, TO_FILE, TO_RAM + } + +} diff --git a/src/com/pkrandom/newnds/NDSRom.java b/src/com/pkrandom/newnds/NDSRom.java new file mode 100755 index 0000000..fee90a1 --- /dev/null +++ b/src/com/pkrandom/newnds/NDSRom.java @@ -0,0 +1,712 @@ +package com.pkrandom.newnds; + +import java.io.*; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +import com.pkrandom.SysConstants; +import com.pkrandom.FileFunctions; +import com.pkrandom.RomFunctions; + +import com.pkrandom.exceptions.CannotWriteToLocationException; +import com.pkrandom.exceptions.RandomizerIOException; +import cuecompressors.BLZCoder; + +/*----------------------------------------------------------------------------*/ +/*-- NDSRom.java - base class for opening/saving ROMs --*/ +/*-- Code based on "Nintendo DS rom tool", copyright (C) DevkitPro --*/ +/*-- Original Code by Rafael Vuijk, Dave Murphy, Alexei Karpenko --*/ +/*-- --*/ +/*-- --*/ +/*-- 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/>. --*/ +/*----------------------------------------------------------------------------*/ + +public class NDSRom { + + private String romCode; + private byte version; + private String romFilename; + private RandomAccessFile baseRom; + private boolean romOpen; + private Map<String, NDSFile> files; + private Map<Integer, NDSFile> filesByID; + private Map<Integer, NDSY9Entry> arm9overlaysByFileID; + private NDSY9Entry[] arm9overlays; + private byte[] fat; + private String tmpFolder; + private boolean writingEnabled; + private boolean arm9_open, arm9_changed, arm9_has_footer; + private boolean arm9_compressed; + private int arm9_ramoffset; + private int arm9_szoffset; + private byte[] arm9_footer; + private byte[] arm9_ramstored; + private long originalArm9CRC; + + private static final int arm9_align = 0x1FF, arm7_align = 0x1FF; + private static final int fnt_align = 0x1FF, fat_align = 0x1FF; + private static final int banner_align = 0x1FF, file_align = 0x1FF; + + public NDSRom(String filename) throws IOException { + this.romFilename = filename; + this.baseRom = new RandomAccessFile(filename, "r"); + this.romOpen = true; + // TMP folder? + String rawFilename = new File(filename).getName(); + String dataFolder = "tmp_" + rawFilename.substring(0, rawFilename.lastIndexOf('.')); + // remove nonsensical chars + dataFolder = dataFolder.replaceAll("[^A-Za-z0-9_]+", ""); + File tmpFolder = new File(SysConstants.ROOT_PATH + dataFolder); + tmpFolder.mkdir(); + if (tmpFolder.canWrite()) { + writingEnabled = true; + this.tmpFolder = SysConstants.ROOT_PATH + dataFolder + File.separator; + tmpFolder.deleteOnExit(); + } else { + writingEnabled = false; + } + readFileSystem(); + arm9_open = false; + arm9_changed = false; + arm9_ramstored = null; + } + + public void reopenROM() throws IOException { + if (!this.romOpen) { + this.baseRom = new RandomAccessFile(this.romFilename, "r"); + this.romOpen = true; + } + } + + public void closeROM() throws IOException { + if (this.romOpen && this.baseRom != null) { + this.baseRom.close(); + this.baseRom = null; + this.romOpen = false; + } + } + + private void readFileSystem() throws IOException { + // read rom code + baseRom.seek(0x0C); + + byte[] sig = new byte[4]; + baseRom.readFully(sig); + this.romCode = new String(sig, "US-ASCII"); + + baseRom.seek(0x1E); + this.version = baseRom.readByte(); + + baseRom.seek(0x28); + this.arm9_ramoffset = readFromFile(baseRom, 4); + + baseRom.seek(0x40); + int fntOffset = readFromFile(baseRom, 4); + readFromFile(baseRom, 4); // fntSize not needed + int fatOffset = readFromFile(baseRom, 4); + int fatSize = readFromFile(baseRom, 4); + + // Read full FAT table + baseRom.seek(fatOffset); + fat = new byte[fatSize]; + baseRom.readFully(fat); + + Map<Integer, String> directoryPaths = new HashMap<>(); + directoryPaths.put(0xF000, ""); + int dircount = readFromFile(baseRom, fntOffset + 0x6, 2); + files = new HashMap<>(); + filesByID = new HashMap<>(); + + // read fnt table + baseRom.seek(fntOffset); + int[] subTableOffsets = new int[dircount]; + int[] firstFileIDs = new int[dircount]; + int[] parentDirIDs = new int[dircount]; + for (int i = 0; i < dircount && i < 0x1000; i++) { + subTableOffsets[i] = readFromFile(baseRom, 4) + fntOffset; + firstFileIDs[i] = readFromFile(baseRom, 2); + parentDirIDs[i] = readFromFile(baseRom, 2); + } + + // get dirnames + String[] directoryNames = new String[dircount]; + Map<Integer, String> filenames = new TreeMap<>(); + Map<Integer, Integer> fileDirectories = new HashMap<>(); + for (int i = 0; i < dircount && i < 0x1000; i++) { + firstPassDirectory(i, subTableOffsets[i], firstFileIDs[i], directoryNames, filenames, fileDirectories); + } + + // get full dirnames + for (int i = 1; i < dircount && i < 0x1000; i++) { + String dirname = directoryNames[i]; + if (dirname != null) { + StringBuilder fullDirName = new StringBuilder(); + int curDir = i; + while (dirname != null && !dirname.isEmpty()) { + if (fullDirName.length() > 0) { + fullDirName.insert(0, "/"); + } + fullDirName.insert(0, dirname); + int parentDir = parentDirIDs[curDir]; + if (parentDir >= 0xF001 && parentDir <= 0xFFFF) { + curDir = parentDir - 0xF000; + dirname = directoryNames[curDir]; + } else { + break; + } + } + directoryPaths.put(i + 0xF000, fullDirName.toString()); + } else { + directoryPaths.put(i + 0xF000, ""); + } + } + + // parse files + for (int fileID : filenames.keySet()) { + String filename = filenames.get(fileID); + int directory = fileDirectories.get(fileID); + String dirPath = directoryPaths.get(directory + 0xF000); + String fullFilename = filename; + if (!dirPath.isEmpty()) { + fullFilename = dirPath + "/" + filename; + } + NDSFile nf = new NDSFile(this); + int start = readFromByteArr(fat, fileID * 8, 4); + int end = readFromByteArr(fat, fileID * 8 + 4, 4); + nf.offset = start; + nf.size = end - start; + nf.fullPath = fullFilename; + nf.fileID = fileID; + files.put(fullFilename, nf); + filesByID.put(fileID, nf); + } + + // arm9 overlays + int arm9_ovl_table_offset = readFromFile(baseRom, 0x50, 4); + int arm9_ovl_table_size = readFromFile(baseRom, 0x54, 4); + int arm9_ovl_count = arm9_ovl_table_size / 32; + byte[] y9table = new byte[arm9_ovl_table_size]; + arm9overlays = new NDSY9Entry[arm9_ovl_count]; + arm9overlaysByFileID = new HashMap<>(); + baseRom.seek(arm9_ovl_table_offset); + baseRom.readFully(y9table); + + // parse overlays + for (int i = 0; i < arm9_ovl_count; i++) { + NDSY9Entry overlay = new NDSY9Entry(this); + int fileID = readFromByteArr(y9table, i * 32 + 24, 4); + int start = readFromByteArr(fat, fileID * 8, 4); + int end = readFromByteArr(fat, fileID * 8 + 4, 4); + overlay.offset = start; + overlay.size = end - start; + overlay.original_size = end - start; + overlay.fileID = fileID; + overlay.overlay_id = i; + overlay.ram_address = readFromByteArr(y9table, i * 32 + 4, 4); + overlay.ram_size = readFromByteArr(y9table, i * 32 + 8, 4); + overlay.bss_size = readFromByteArr(y9table, i * 32 + 12, 4); + overlay.static_start = readFromByteArr(y9table, i * 32 + 16, 4); + overlay.static_end = readFromByteArr(y9table, i * 32 + 20, 4); + overlay.compressed_size = readFromByteArr(y9table, i * 32 + 28, 3); + overlay.compress_flag = y9table[i * 32 + 31] & 0xFF; + arm9overlays[i] = overlay; + arm9overlaysByFileID.put(fileID, overlay); + } + } + + public void saveTo(String filename) throws IOException { + this.reopenROM(); + + // Initialize new ROM + RandomAccessFile fNew = new RandomAccessFile(filename, "rw"); + + int headersize = readFromFile(this.baseRom, 0x84, 4); + this.baseRom.seek(0); + copy(this.baseRom, fNew, headersize); + + // arm9 + int arm9_offset = ((int) (fNew.getFilePointer() + arm9_align)) & (~arm9_align); + int old_arm9_offset = readFromFile(this.baseRom, 0x20, 4); + int arm9_size = readFromFile(this.baseRom, 0x2C, 4); + if (arm9_open && arm9_changed) { + // custom arm9 + byte[] newARM9 = getARM9(); + if (arm9_compressed) { + newARM9 = new BLZCoder(null).BLZ_EncodePub(newARM9, true, false, "arm9.bin"); + if (arm9_szoffset > 0) { + int newValue = newARM9.length + arm9_ramoffset; + writeToByteArr(newARM9, arm9_szoffset, 4, newValue); + } + } + arm9_size = newARM9.length; + // copy new arm9 + fNew.seek(arm9_offset); + fNew.write(newARM9); + // footer? + if (arm9_has_footer) { + fNew.write(arm9_footer); + } + + } else { + // copy arm9+footer + this.baseRom.seek(old_arm9_offset); + fNew.seek(arm9_offset); + copy(this.baseRom, fNew, arm9_size + 12); + } + + // arm9 ovl + int arm9_ovl_offset = (int) fNew.getFilePointer(); + int arm9_ovl_size = arm9overlays.length * 32; + + // don't actually write arm9 ovl yet + + // arm7 + int arm7_offset = arm9_ovl_offset + arm9_ovl_size + arm7_align & (~arm7_align); + int old_arm7_offset = readFromFile(this.baseRom, 0x30, 4); + int arm7_size = readFromFile(this.baseRom, 0x3C, 4); + // copy arm7 + this.baseRom.seek(old_arm7_offset); + fNew.seek(arm7_offset); + copy(this.baseRom, fNew, arm7_size); + + // arm7 ovl + int arm7_ovl_offset = (int) fNew.getFilePointer(); + int old_arm7_ovl_offset = readFromFile(this.baseRom, 0x58, 4); + int arm7_ovl_size = readFromFile(this.baseRom, 0x5C, 4); + + // copy arm7 ovl + this.baseRom.seek(old_arm7_ovl_offset); + fNew.seek(arm7_ovl_offset); + copy(this.baseRom, fNew, arm7_ovl_size); + + // banner + int banner_offset = ((int) (fNew.getFilePointer() + banner_align)) & (~banner_align); + int old_banner_offset = readFromFile(this.baseRom, 0x68, 4); + int banner_size = 0x840; + // copy banner + this.baseRom.seek(old_banner_offset); + fNew.seek(banner_offset); + copy(this.baseRom, fNew, banner_size); + + // filename table (doesn't change) + int fnt_offset = ((int) (fNew.getFilePointer() + fnt_align)) & (~fnt_align); + int old_fnt_offset = readFromFile(this.baseRom, 0x40, 4); + int fnt_size = readFromFile(this.baseRom, 0x44, 4); + // copy fnt + this.baseRom.seek(old_fnt_offset); + fNew.seek(fnt_offset); + copy(this.baseRom, fNew, fnt_size); + + // make space for the FAT table + int fat_offset = ((int) (fNew.getFilePointer() + fat_align)) & (~fat_align); + int fat_size = fat.length; + + // Now for actual files + // Make a new FAT as needed + // also make a new y9 table + byte[] newfat = new byte[fat.length]; + byte[] y9table = new byte[arm9overlays.length * 32]; + int base_offset = fat_offset + fat_size; + int filecount = fat.length / 8; + for (int fid = 0; fid < filecount; fid++) { + int offset_of_file = (base_offset + file_align) & (~file_align); + int file_len = 0; + boolean copiedCustom = false; + if (filesByID.containsKey(fid)) { + byte[] customContents = filesByID.get(fid).getOverrideContents(); + if (customContents != null) { + // copy custom + fNew.seek(offset_of_file); + fNew.write(customContents); + copiedCustom = true; + file_len = customContents.length; + } + } + if (arm9overlaysByFileID.containsKey(fid)) { + NDSY9Entry entry = arm9overlaysByFileID.get(fid); + int overlay_id = entry.overlay_id; + byte[] customContents = entry.getOverrideContents(); + if (customContents != null) { + // copy custom + fNew.seek(offset_of_file); + fNew.write(customContents); + copiedCustom = true; + file_len = customContents.length; + } + // regardless, fill in y9 table + writeToByteArr(y9table, overlay_id * 32, 4, overlay_id); + writeToByteArr(y9table, overlay_id * 32 + 4, 4, entry.ram_address); + writeToByteArr(y9table, overlay_id * 32 + 8, 4, entry.ram_size); + writeToByteArr(y9table, overlay_id * 32 + 12, 4, entry.bss_size); + writeToByteArr(y9table, overlay_id * 32 + 16, 4, entry.static_start); + writeToByteArr(y9table, overlay_id * 32 + 20, 4, entry.static_end); + writeToByteArr(y9table, overlay_id * 32 + 24, 4, fid); + writeToByteArr(y9table, overlay_id * 32 + 28, 3, entry.compressed_size); + writeToByteArr(y9table, overlay_id * 32 + 31, 1, entry.compress_flag); + } + if (!copiedCustom) { + // copy from original ROM + int file_starts = readFromByteArr(fat, fid * 8, 4); + int file_ends = readFromByteArr(fat, fid * 8 + 4, 4); + file_len = file_ends - file_starts; + this.baseRom.seek(file_starts); + fNew.seek(offset_of_file); + copy(this.baseRom, fNew, file_len); + } + // write to new FAT + writeToByteArr(newfat, fid * 8, 4, offset_of_file); + writeToByteArr(newfat, fid * 8 + 4, 4, offset_of_file + file_len); + // update base_offset + base_offset = offset_of_file + file_len; + } + + // write new FAT table + fNew.seek(fat_offset); + fNew.write(newfat); + + // write y9 table + fNew.seek(arm9_ovl_offset); + fNew.write(y9table); + + // tidy up ending + // base_offset is the end of the last file + int newfilesize = base_offset; + newfilesize = (newfilesize + 3) & ~3; + int application_end_offset = newfilesize; + if (newfilesize != base_offset) { + fNew.seek(newfilesize - 1); + fNew.write(0); + } + + // calculate device capacity; + newfilesize |= newfilesize >> 16; + newfilesize |= newfilesize >> 8; + newfilesize |= newfilesize >> 4; + newfilesize |= newfilesize >> 2; + newfilesize |= newfilesize >> 1; + newfilesize++; + if (newfilesize <= 128 * 1024) { + newfilesize = 128 * 1024; + } + int devcap = -18; + int x = newfilesize; + while (x != 0) { + x >>= 1; + devcap++; + } + int devicecap = ((devcap < 0) ? 0 : devcap); + + // Update offsets in ROM header + writeToFile(fNew, 0x20, 4, arm9_offset); + writeToFile(fNew, 0x2C, 4, arm9_size); + writeToFile(fNew, 0x30, 4, arm7_offset); + writeToFile(fNew, 0x3C, 4, arm7_size); + writeToFile(fNew, 0x40, 4, fnt_offset); + writeToFile(fNew, 0x48, 4, fat_offset); + writeToFile(fNew, 0x50, 4, arm9_ovl_offset); + writeToFile(fNew, 0x58, 4, arm7_ovl_offset); + writeToFile(fNew, 0x68, 4, banner_offset); + writeToFile(fNew, 0x80, 4, application_end_offset); + writeToFile(fNew, 0x14, 1, devicecap); + + // Update header CRC + fNew.seek(0); + byte[] headerForCRC = new byte[0x15E]; + fNew.readFully(headerForCRC); + short crc = CRC16.calculate(headerForCRC, 0, 0x15E); + writeToFile(fNew, 0x15E, 2, (crc & 0xFFFF)); + + // done + fNew.close(); + closeROM(); + } + + private void copy(RandomAccessFile from, RandomAccessFile to, int bytes) throws IOException { + int sizeof_copybuf = Math.min(256 * 1024, bytes); + byte[] copybuf = new byte[sizeof_copybuf]; + while (bytes > 0) { + int size2 = (bytes >= sizeof_copybuf) ? sizeof_copybuf : bytes; + int read = from.read(copybuf, 0, size2); + to.write(copybuf, 0, read); + bytes -= read; + } + } + + // get rom code for opened rom + public String getCode() { + return this.romCode; + } + + public byte getVersion() { + return this.version; + } + + // returns null if file doesn't exist + public byte[] getFile(String filename) throws IOException { + if (files.containsKey(filename)) { + return files.get(filename).getContents(); + } else { + return null; + } + } + + public byte[] getOverlay(int number) throws IOException { + if (number >= 0 && number < arm9overlays.length) { + return arm9overlays[number].getContents(); + } else { + return null; + } + } + + public int getOverlayAddress(int number) { + if (number >= 0 && number < arm9overlays.length) { + return arm9overlays[number].ram_address; + } else { + return -1; + } + } + + public byte[] getARM9() throws IOException { + if (!arm9_open) { + arm9_open = true; + this.reopenROM(); + int arm9_offset = readFromFile(this.baseRom, 0x20, 4); + int arm9_size = readFromFile(this.baseRom, 0x2C, 4); + byte[] arm9 = new byte[arm9_size]; + this.baseRom.seek(arm9_offset); + this.baseRom.readFully(arm9); + originalArm9CRC = FileFunctions.getCRC32(arm9); + // footer check + int nitrocode = readFromFile(this.baseRom, 4); + if (nitrocode == 0xDEC00621) { + // found a footer + arm9_footer = new byte[12]; + writeToByteArr(arm9_footer, 0, 4, 0xDEC00621); + this.baseRom.readFully(arm9_footer, 4, 8); + arm9_has_footer = true; + } else { + arm9_has_footer = false; + } + // Any extras? + while ((readFromByteArr(arm9, arm9.length - 12, 4) == 0xDEC00621) + || ((readFromByteArr(arm9, arm9.length - 12, 4) == 0 + && readFromByteArr(arm9, arm9.length - 8, 4) == 0 && readFromByteArr(arm9, arm9.length - 4, + 4) == 0))) { + if (!arm9_has_footer) { + arm9_has_footer = true; + arm9_footer = new byte[0]; + } + byte[] newfooter = new byte[arm9_footer.length + 12]; + System.arraycopy(arm9, arm9.length - 12, newfooter, 0, 12); + System.arraycopy(arm9_footer, 0, newfooter, 12, arm9_footer.length); + arm9_footer = newfooter; + byte[] newarm9 = new byte[arm9.length - 12]; + System.arraycopy(arm9, 0, newarm9, 0, arm9.length - 12); + arm9 = newarm9; + } + // Compression? + arm9_compressed = false; + arm9_szoffset = 0; + if (((int) arm9[arm9.length - 5]) >= 0x08 && ((int) arm9[arm9.length - 5]) <= 0x0B) { + int compSize = readFromByteArr(arm9, arm9.length - 8, 3); + if (compSize > (arm9.length * 9 / 10) && compSize < (arm9.length * 11 / 10)) { + arm9_compressed = true; + byte[] compLength = new byte[4]; + writeToByteArr(compLength, 0, 4, arm9.length + arm9_ramoffset); + List<Integer> foundOffsets = RomFunctions.search(arm9, compLength); + if (foundOffsets.size() == 1) { + arm9_szoffset = foundOffsets.get(0); + } else { + throw new RandomizerIOException("Could not read ARM9 size offset. May be a bad ROM."); + } + } + } + + if (arm9_compressed) { + arm9 = new BLZCoder(null).BLZ_DecodePub(arm9, "arm9.bin"); + } + + // Now actually make the copy or w/e + if (writingEnabled) { + File arm9file = new File(tmpFolder + "arm9.bin"); + FileOutputStream fos = new FileOutputStream(arm9file); + fos.write(arm9); + fos.close(); + arm9file.deleteOnExit(); + this.arm9_ramstored = null; + return arm9; + } else { + this.arm9_ramstored = arm9; + byte[] newcopy = new byte[arm9.length]; + System.arraycopy(arm9, 0, newcopy, 0, arm9.length); + return newcopy; + } + } else { + if (writingEnabled) { + return FileFunctions.readFileFullyIntoBuffer(tmpFolder + "arm9.bin"); + } else { + byte[] newcopy = new byte[this.arm9_ramstored.length]; + System.arraycopy(this.arm9_ramstored, 0, newcopy, 0, this.arm9_ramstored.length); + return newcopy; + } + } + } + + // returns null if file doesn't exist + public void writeFile(String filename, byte[] data) throws IOException { + if (files.containsKey(filename)) { + files.get(filename).writeOverride(data); + } + } + + public void writeOverlay(int number, byte[] data) throws IOException { + if (number >= 0 && number <= arm9overlays.length) { + arm9overlays[number].writeOverride(data); + } + } + + public void writeARM9(byte[] arm9) throws IOException { + if (!arm9_open) { + getARM9(); + } + arm9_changed = true; + if (writingEnabled) { + FileOutputStream fos = new FileOutputStream(new File(tmpFolder + "arm9.bin")); + fos.write(arm9); + fos.close(); + } else { + if (this.arm9_ramstored.length == arm9.length) { + // copy new in + System.arraycopy(arm9, 0, this.arm9_ramstored, 0, arm9.length); + } else { + // make new array + this.arm9_ramstored = null; + this.arm9_ramstored = new byte[arm9.length]; + System.arraycopy(arm9, 0, this.arm9_ramstored, 0, arm9.length); + } + } + } + + private void firstPassDirectory(int dir, int subTableOffset, int firstFileID, String[] directoryNames, + Map<Integer, String> filenames, Map<Integer, Integer> fileDirectories) throws IOException { + // read subtable + baseRom.seek(subTableOffset); + while (true) { + int control = baseRom.read(); + if (control == 0x00) { + // done + break; + } + int namelen = control & 0x7F; + byte[] rawname = new byte[namelen]; + baseRom.readFully(rawname); + String name = new String(rawname, "US-ASCII"); + if ((control & 0x80) > 0x00) { + // sub-directory + int subDirectoryID = readFromFile(baseRom, 2); + directoryNames[subDirectoryID - 0xF000] = name; + } else { + int fileID = firstFileID++; + filenames.put(fileID, name); + fileDirectories.put(fileID, dir); + } + } + } + + public void printRomDiagnostics(PrintStream logStream) { + List<String> overlayList = new ArrayList<>(); + List<String> fileList = new ArrayList<>(); + for (Map.Entry<Integer, NDSY9Entry> entry : arm9overlaysByFileID.entrySet()) { + if (entry.getValue().originalCRC != 0) { + overlayList.add("overlay9_" + entry.getKey() + ": " + String.format("%08X", entry.getValue().originalCRC)); + } + } + for (Map.Entry<String, NDSFile> entry : files.entrySet()) { + if (entry.getValue().originalCRC != 0) { + fileList.add(entry.getKey() + ": " + String.format("%08X", entry.getValue().originalCRC)); + } + } + Collections.sort(overlayList); + Collections.sort(fileList); + Path p = Paths.get(this.romFilename); + logStream.println("File name: " + p.getFileName().toString()); + logStream.println("arm9: " + String.format("%08X", originalArm9CRC)); + for (String overlayLog : overlayList) { + logStream.println(overlayLog); + } + for (String fileLog : fileList) { + logStream.println(fileLog); + } + } + + public String getTmpFolder() { + return tmpFolder; + } + + public RandomAccessFile getBaseRom() { + return baseRom; + } + + public boolean isWritingEnabled() { + return writingEnabled; + } + + private int readFromByteArr(byte[] data, int offset, int size) { + int result = 0; + for (int i = 0; i < size; i++) { + result |= (data[i + offset] & 0xFF) << (i * 8); + } + return result; + } + + private void writeToByteArr(byte[] data, int offset, int size, int value) { + for (int i = 0; i < size; i++) { + data[offset + i] = (byte) ((value >> (i * 8)) & 0xFF); + } + } + + private int readFromFile(RandomAccessFile file, int size) throws IOException { + return readFromFile(file, -1, size); + } + + // use -1 offset to read from current position + // useful if you want to read blocks + private int readFromFile(RandomAccessFile file, int offset, int size) throws IOException { + byte[] buf = new byte[size]; + if (offset >= 0) + file.seek(offset); + file.readFully(buf); + int result = 0; + for (int i = 0; i < size; i++) { + result |= (buf[i] & 0xFF) << (i * 8); + } + return result; + } + + public void writeToFile(RandomAccessFile file, int size, int value) throws IOException { + writeToFile(file, -1, size, value); + } + + private void writeToFile(RandomAccessFile file, int offset, int size, int value) throws IOException { + byte[] buf = new byte[size]; + for (int i = 0; i < size; i++) { + buf[i] = (byte) ((value >> (i * 8)) & 0xFF); + } + if (offset >= 0) + file.seek(offset); + file.write(buf); + } + +} diff --git a/src/com/pkrandom/newnds/NDSY9Entry.java b/src/com/pkrandom/newnds/NDSY9Entry.java new file mode 100755 index 0000000..20c2836 --- /dev/null +++ b/src/com/pkrandom/newnds/NDSY9Entry.java @@ -0,0 +1,139 @@ +package com.pkrandom.newnds; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; + +import com.pkrandom.FileFunctions; + +import cuecompressors.BLZCoder; + +/*----------------------------------------------------------------------------*/ +/*-- NDSY9Entry.java - an entry in the arm9 overlay system --*/ +/*-- Code based on "Nintendo DS rom tool", copyright (C) DevkitPro --*/ +/*-- Original Code by Rafael Vuijk, Dave Murphy, Alexei Karpenko --*/ +/*-- --*/ +/*-- --*/ +/*-- 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/>. --*/ +/*----------------------------------------------------------------------------*/ + +public class NDSY9Entry { + + private NDSRom parent; + public int offset, size, original_size; + public int fileID; + public int overlay_id; + public int ram_address, ram_size; + public int bss_size; + public int static_start, static_end; + public int compressed_size; + public int compress_flag; + private Extracted status = Extracted.NOT; + private String extFilename; + public byte[] data; + public long originalCRC; + private boolean decompressed_data = false; + + public NDSY9Entry(NDSRom parent) { + this.parent = parent; + } + + public byte[] getContents() throws IOException { + if (this.status == Extracted.NOT) { + // extract file + parent.reopenROM(); + RandomAccessFile rom = parent.getBaseRom(); + byte[] buf = new byte[this.original_size]; + rom.seek(this.offset); + rom.readFully(buf); + originalCRC = FileFunctions.getCRC32(buf); + // Compression? + if (compress_flag != 0 && this.original_size == this.compressed_size && this.compressed_size != 0) { + buf = new BLZCoder(null).BLZ_DecodePub(buf, "overlay " + overlay_id); + decompressed_data = true; + } + if (parent.isWritingEnabled()) { + // make a file + String tmpDir = parent.getTmpFolder(); + String fullPath = String.format("overlay_%04d", overlay_id); + this.extFilename = fullPath.replaceAll("[^A-Za-z0-9_]+", ""); + File tmpFile = new File(tmpDir + extFilename); + FileOutputStream fos = new FileOutputStream(tmpFile); + fos.write(buf); + fos.close(); + tmpFile.deleteOnExit(); + this.status = Extracted.TO_FILE; + this.data = null; + return buf; + } else { + this.status = Extracted.TO_RAM; + this.data = buf; + byte[] newcopy = new byte[buf.length]; + System.arraycopy(buf, 0, newcopy, 0, buf.length); + return newcopy; + } + } else if (this.status == Extracted.TO_RAM) { + byte[] newcopy = new byte[this.data.length]; + System.arraycopy(this.data, 0, newcopy, 0, this.data.length); + return newcopy; + } else { + String tmpDir = parent.getTmpFolder(); + return FileFunctions.readFileFullyIntoBuffer(tmpDir + this.extFilename); + } + } + + public void writeOverride(byte[] data) throws IOException { + if (status == Extracted.NOT) { + // temp extract + getContents(); + } + size = data.length; + if (status == Extracted.TO_FILE) { + String tmpDir = parent.getTmpFolder(); + FileOutputStream fos = new FileOutputStream(new File(tmpDir + this.extFilename)); + fos.write(data); + fos.close(); + } else { + if (this.data.length == data.length) { + // copy new in + System.arraycopy(data, 0, this.data, 0, data.length); + } else { + // make new array + this.data = null; + this.data = new byte[data.length]; + System.arraycopy(data, 0, this.data, 0, data.length); + } + } + } + + // returns null if no override + public byte[] getOverrideContents() throws IOException { + if (status == Extracted.NOT) { + return null; + } + byte[] buf = getContents(); + if (this.decompressed_data) { + buf = new BLZCoder(null).BLZ_EncodePub(buf, false, false, "overlay " + overlay_id); + // update our compressed size + this.compressed_size = buf.length; + } + return buf; + } + + private enum Extracted { + NOT, TO_FILE, TO_RAM + } + +} |