summaryrefslogtreecommitdiff
path: root/src/compressors
diff options
context:
space:
mode:
authorDabomstew <dbs.stew@gmail.com>2016-02-21 13:12:16 +1300
committerDabomstew <dbs.stew@gmail.com>2016-02-21 13:12:16 +1300
commit40af2283b038f3b33b35f208b91a070a2744ddbd (patch)
tree15420d60e55351c445f5053fdef50761a137065a /src/compressors
parent76f2c51c92de206adf8220132ff81766fc06dee9 (diff)
Working on Gen 1 mascot image, ugh that decompression.
Diffstat (limited to 'src/compressors')
-rw-r--r--src/compressors/DSDecmp.java195
-rw-r--r--src/compressors/Gen1Decmp.java348
2 files changed, 543 insertions, 0 deletions
diff --git a/src/compressors/DSDecmp.java b/src/compressors/DSDecmp.java
new file mode 100644
index 0000000..8f6442a
--- /dev/null
+++ b/src/compressors/DSDecmp.java
@@ -0,0 +1,195 @@
+package compressors;
+
+import com.dabomstew.pkrandom.FileFunctions;
+
+//MODIFIED DSDECMP-JAVA SOURCE FOR RANDOMIZER'S NEEDS
+//License is below
+
+//Copyright (c) 2010 Nick Kraayenbrink
+//
+//Permission is hereby granted, free of charge, to any person obtaining a copy
+//of this software and associated documentation files (the "Software"), to deal
+//in the Software without restriction, including without limitation the rights
+//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//copies of the Software, and to permit persons to whom the Software is
+//furnished to do so, subject to the following conditions:
+//
+//The above copyright notice and this permission notice shall be included in
+//all copies or substantial portions of the Software.
+//
+//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//THE SOFTWARE.
+
+public class DSDecmp {
+
+ public static byte[] Decompress(byte[] data) {
+ return Decompress(data, 0);
+ }
+
+ public static byte[] Decompress(byte[] data, int offset) {
+ switch (data[offset] & 0xFF) {
+ case 0x10:
+ return decompress10LZ(data, offset);
+ case 0x11:
+ return decompress11LZ(data, offset);
+ default:
+ return null;
+ }
+ }
+
+ private static byte[] decompress10LZ(byte[] data, int offset) {
+ offset++;
+ int length = (data[offset] & 0xFF) | ((data[offset + 1] & 0xFF) << 8)
+ | ((data[offset + 2] & 0xFF) << 16);
+ offset += 3;
+ if (length == 0) {
+ length = FileFunctions.readFullInt(data, offset);
+ offset += 4;
+ }
+
+ byte[] outData = new byte[length];
+ int curr_size = 0;
+ int flags;
+ boolean flag;
+ int disp, n, b, cdest;
+ while (curr_size < outData.length) {
+ flags = data[offset++] & 0xFF;
+ for (int i = 0; i < 8; i++) {
+ flag = (flags & (0x80 >> i)) > 0;
+ if (flag) {
+ disp = 0;
+ b = data[offset++] & 0xFF;
+ n = b >> 4;
+ disp = (b & 0x0F) << 8;
+ disp |= data[offset++] & 0xFF;
+ n += 3;
+ cdest = curr_size;
+ if (disp > curr_size)
+ throw new ArrayIndexOutOfBoundsException(
+ "Cannot go back more than already written");
+ for (int j = 0; j < n; j++)
+ outData[curr_size++] = outData[cdest - disp - 1 + j];
+
+ if (curr_size > outData.length)
+ break;
+ } else {
+ b = data[offset++] & 0xFF;
+ try {
+ outData[curr_size++] = (byte) b;
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ if (b == 0)
+ break;
+ }
+
+ if (curr_size > outData.length)
+ break;
+ }
+ }
+ }
+ return outData;
+ }
+
+ private static byte[] decompress11LZ(byte[] data, int offset) {
+ offset++;
+ int length = (data[offset] & 0xFF) | ((data[offset + 1] & 0xFF) << 8)
+ | ((data[offset + 2] & 0xFF) << 16);
+ offset += 3;
+ if (length == 0) {
+ length = FileFunctions.readFullInt(data, offset);
+ offset += 4;
+ }
+
+ byte[] outData = new byte[length];
+
+ int curr_size = 0;
+ int flags;
+ boolean flag;
+ int b1, bt, b2, b3, len, disp, cdest;
+
+ while (curr_size < outData.length) {
+ flags = data[offset++] & 0xFF;
+
+ for (int i = 0; i < 8 && curr_size < outData.length; i++) {
+ flag = (flags & (0x80 >> i)) > 0;
+ if (flag) {
+ b1 = data[offset++] & 0xFF;
+
+ switch (b1 >> 4) {
+ case 0:
+ // ab cd ef
+ // =>
+ // len = abc + 0x11 = bc + 0x11
+ // disp = def
+
+ len = b1 << 4;
+ bt = data[offset++] & 0xFF;
+ len |= bt >> 4;
+ len += 0x11;
+
+ disp = (bt & 0x0F) << 8;
+ b2 = data[offset++] & 0xFF;
+ disp |= b2;
+ break;
+
+ case 1:
+ // ab cd ef gh
+ // =>
+ // len = bcde + 0x111
+ // disp = fgh
+ // 10 04 92 3F => disp = 0x23F, len = 0x149 + 0x11 =
+ // 0x15A
+ bt = data[offset++] & 0xFF;
+ b2 = data[offset++] & 0xFF;
+ b3 = data[offset++] & 0xFF;
+
+ len = (b1 & 0xF) << 12; // len = b000
+ len |= bt << 4; // len = bcd0
+ len |= (b2 >> 4); // len = bcde
+ len += 0x111; // len = bcde + 0x111
+ disp = (b2 & 0x0F) << 8; // disp = f
+ disp |= b3; // disp = fgh
+ break;
+
+ default:
+ // ab cd
+ // =>
+ // len = a + threshold = a + 1
+ // disp = bcd
+
+ len = (b1 >> 4) + 1;
+
+ disp = (b1 & 0x0F) << 8;
+ b2 = data[offset++] & 0xFF;
+ disp |= b2;
+ break;
+ }
+
+ if (disp > curr_size)
+ throw new ArrayIndexOutOfBoundsException(
+ "Cannot go back more than already written");
+
+ cdest = curr_size;
+
+ for (int j = 0; j < len && curr_size < outData.length; j++)
+ outData[curr_size++] = outData[cdest - disp - 1 + j];
+
+ if (curr_size > outData.length)
+ break;
+ } else {
+ outData[curr_size++] = data[offset++];
+
+ if (curr_size > outData.length)
+ break;
+ }
+ }
+
+ }
+ return outData;
+ }
+
+}
diff --git a/src/compressors/Gen1Decmp.java b/src/compressors/Gen1Decmp.java
new file mode 100644
index 0000000..3f07d2a
--- /dev/null
+++ b/src/compressors/Gen1Decmp.java
@@ -0,0 +1,348 @@
+package compressors;
+
+/**
+ * Pokemon Gen 1 sprite decompressor
+ * Source: https://github.com/pret/pokemon-reverse-engineering-tools/blob/master/pokemontools/pic.py
+ * (and gfx.py for flatten())
+ * Ported to Java by Dabomstew
+ *
+ */
+public class Gen1Decmp {
+
+ public static int bitflip(int x, int n) {
+ int r = 0;
+ while (n > 0) {
+ r = (r << 1) | (x & 1);
+ x >>= 1;
+ n -= 1;
+ }
+ return r;
+ }
+
+ static int[] table1, table3;
+ static int[][] table2 = {
+ { 0x0, 0x1, 0x3, 0x2, 0x7, 0x6, 0x4, 0x5, 0xf, 0xe, 0xc, 0xd, 0x8,
+ 0x9, 0xb, 0xa },
+ { 0xf, 0xe, 0xc, 0xd, 0x8, 0x9, 0xb, 0xa, 0x0, 0x1, 0x3, 0x2, 0x7,
+ 0x6, 0x4, 0x5 },
+ { 0x0, 0x8, 0xc, 0x4, 0xe, 0x6, 0x2, 0xa, 0xf, 0x7, 0x3, 0xb, 0x1,
+ 0x9, 0xd, 0x5 },
+ { 0xf, 0x7, 0x3, 0xb, 0x1, 0x9, 0xd, 0x5, 0x0, 0x8, 0xc, 0x4, 0xe,
+ 0x6, 0x2, 0xa }, };
+
+ static {
+ table1 = new int[16];
+ table3 = new int[16];
+
+ for (int i = 0; i < 16; i++) {
+ table1[i] = (2 << i) - 1;
+ table3[i] = bitflip(i, 4);
+ }
+ }
+
+ static int tilesize = 8;
+
+ private BitStream bs;
+ private boolean mirror, planar;
+ private byte[] data;
+ private int sizex, sizey, size;
+ private int ramorder;
+
+ public Gen1Decmp(byte[] input, int baseOffset) {
+ this(input, baseOffset, false, true);
+ }
+
+ public Gen1Decmp(byte[] input, int baseOffset, boolean mirror,
+ boolean planar) {
+ this.bs = new BitStream(input, baseOffset);
+ this.mirror = mirror;
+ this.planar = planar;
+ this.data = null;
+ }
+
+ public void decompress() {
+ byte[][] rams = new byte[2][];
+
+ this.sizex = this.readint(4) * tilesize;
+ this.sizey = this.readint(4);
+
+ this.size = this.sizex * this.sizey;
+
+ this.ramorder = this.readbit();
+
+ int r1 = this.ramorder;
+ int r2 = this.ramorder ^ 1;
+
+ fillram(rams, r1);
+ int mode = this.readbit();
+ if (mode == 1) {
+ mode += this.readbit();
+ }
+ fillram(rams, r2);
+
+ rams[0] = this.bitgroups_to_bytes(rams[0]);
+ rams[1] = this.bitgroups_to_bytes(rams[1]);
+
+ if (mode == 0) {
+ this.decode(rams[0]);
+ this.decode(rams[1]);
+ } else if (mode == 1) {
+ this.decode(rams[r1]);
+ this.xor(rams[r1], rams[r2]);
+ } else if (mode == 2) {
+ this.decode(rams[r2], false);
+ this.decode(rams[r1]);
+ this.xor(rams[r1], rams[r2]);
+ }
+
+ if (this.planar) {
+ data = new byte[this.size * 2];
+ for (int i = 0; i < rams[0].length; i++) {
+ data[i * 2] = rams[0][i];
+ data[i * 2 + 1] = rams[1][i];
+ }
+ } else {
+ byte[] tmpdata = new byte[this.size * 8];
+ BitStream r0S = new BitStream(rams[0]);
+ BitStream r1S = new BitStream(rams[1]);
+ for (int i = 0; i < tmpdata.length; i++) {
+ tmpdata[i] = (byte) (r0S.next() | (r1S.next() << 1));
+ }
+ data = this.bitgroups_to_bytes(tmpdata);
+ }
+
+ }
+
+ public void transpose() {
+ if (data == null) {
+ return;
+ }
+ int tiles = data.length / 16;
+ int width = this.sizex / tilesize;
+ int height = this.sizey;
+
+ byte[] newData = new byte[data.length];
+ for (int tile = 0; tile < tiles; tile++) {
+ int oldTileX = tile % width;
+ int oldTileY = tile / width;
+ int newTileNum = oldTileX * height + oldTileY;
+ System.out.printf("tile %d => %d\n", tile, newTileNum);
+ System.arraycopy(data, tile * 16, newData, newTileNum * 16, 16);
+ }
+ data = newData;
+
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public byte[] getFlattenedData() {
+ return flatten(data);
+ }
+
+ public int getWidth() {
+ return this.sizex;
+ }
+
+ public int getHeight() {
+ return this.sizey * tilesize;
+ }
+
+ private void fillram(byte[][] rams, int rOffset) {
+ int size = this.size * 4;
+ rams[rOffset] = new byte[size];
+ boolean rleMode = this.readbit() == 0;
+ int written = 0;
+ while (written < size) {
+ if (rleMode) {
+ written += this.read_rle_chunk(rams[rOffset], written);
+ } else {
+ written += this.read_data_chunk(rams[rOffset], written, size);
+ }
+ rleMode = !rleMode;
+ }
+ rams[rOffset] = deinterlace_bitgroups(rams[rOffset]);
+ }
+
+ private byte[] deinterlace_bitgroups(byte[] bits) {
+ byte[] l = new byte[bits.length];
+ int offs = 0;
+ for (int y = 0; y < this.sizey; y++) {
+ for (int x = 0; x < this.sizex; x++) {
+ int i = 4 * y * this.sizex + x;
+ for (int j = 0; j < 4; j++) {
+ l[offs++] = bits[i];
+ i += this.sizex;
+ }
+ }
+ }
+ return l;
+ }
+
+ private int read_rle_chunk(byte[] ram, int baseOffset) {
+ int i = 0;
+ while (this.readbit() == 1) {
+ i++;
+ }
+
+ int n = table1[i];
+ int a = this.readint(i + 1);
+ n += a;
+
+ for (i = 0; i < n; i++) {
+ ram[baseOffset + i] = 0;
+ }
+ return n;
+ }
+
+ private int read_data_chunk(byte[] ram, int baseOffset, int size) {
+ int written = 0;
+ while (true) {
+ int bitgroup = this.readint(2);
+ if (bitgroup == 0) {
+ break;
+ }
+ ram[baseOffset + written] = (byte) bitgroup;
+ written++;
+
+ if (baseOffset + written >= size) {
+ break;
+ }
+ }
+ return written;
+ }
+
+ private int readbit() {
+ return bs.next();
+ }
+
+ private int readint(int count) {
+ return readint(bs, count);
+ }
+
+ private int readint(BitStream strm, int count) {
+ int n = 0;
+ while (count > 0) {
+ n <<= 1;
+ n |= strm.next();
+ count -= 1;
+ }
+ return n;
+ }
+
+ private byte[] bitgroups_to_bytes(byte[] bits) {
+ int limiter = bits.length - 3;
+ byte[] ret = new byte[bits.length / 4];
+ for (int i = 0; i < limiter; i += 4) {
+ int n = ((bits[i + 0] << 6) | (bits[i + 1] << 4)
+ | (bits[i + 2] << 2) | (bits[i + 3] << 0));
+ ret[i / 4] = (byte) n;
+ }
+ return ret;
+ }
+
+ private void decode(byte[] ram) {
+ decode(ram, this.mirror);
+ }
+
+ private void decode(byte[] ram, boolean mirror) {
+ for (int x = 0; x < this.sizex; x++) {
+ int bit = 0;
+ for (int y = 0; y < this.sizey; y++) {
+ int i = y * this.sizex + x;
+ int a = ((ram[i] & 0xFF) >> 4) & 0x0F;
+ int b = ram[i] & 0x0F;
+
+ a = table2[bit][a];
+ bit = a & 1;
+ if (mirror) {
+ a = table3[a];
+ }
+
+ b = table2[bit][b];
+ bit = b & 1;
+ if (mirror) {
+ b = table3[b];
+ }
+
+ ram[i] = (byte) ((a << 4) | b);
+ }
+ }
+ }
+
+ private void xor(byte[] ram1, byte[] ram2) {
+ xor(ram1, ram2, this.mirror);
+ }
+
+ private void xor(byte[] ram1, byte[] ram2, boolean mirror) {
+ for (int i = 0; i < ram2.length; i++) {
+ if (mirror) {
+ int a = ((ram2[i] & 0xFF) >> 4) & 0x0F;
+ int b = ram2[i] & 0x0F;
+ a = table3[a];
+ b = table3[b];
+ ram2[i] = (byte) ((a << 4) | b);
+ }
+
+ ram2[i] = (byte) ((ram2[i] & 0xFF) ^ (ram1[i] & 0xFF));
+ }
+ }
+
+ private static class BitStream {
+ private byte[] data;
+ private int offset;
+ private int bitsLeft;
+ private int bufVal;
+
+ private static final int bmask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f,
+ 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff,
+ 0x3fff, 0x7fff, 0xffff, 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff,
+ 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff, 0x1ffffff, 0x3ffffff,
+ 0x7ffffff, 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
+ 0xffffffff };
+
+ public BitStream(byte[] data) {
+ this(data, 0);
+ }
+
+ public BitStream(byte[] data, int baseOffset) {
+ this.data = data;
+ this.offset = baseOffset - 1;
+ this.bitsLeft = 0;
+ this.bufVal = -1;
+ }
+
+ public int next() {
+ if (bitsLeft == 0) {
+ offset++;
+ bufVal = data[offset] & 0xFF;
+ if (offset >= data.length) {
+ return -1;
+ }
+ bitsLeft = 8;
+ }
+
+ int retval = bufVal >> (bitsLeft - 1);
+ bufVal &= bmask[bitsLeft - 1];
+ bitsLeft--;
+
+ return retval;
+ }
+ }
+
+ private static byte[] flatten(byte[] planar) {
+ byte[] strips = new byte[planar.length * 4];
+ for (int j = 0; j < planar.length / 2; j++) {
+ int bottom = planar[j * 2] & 0xFF;
+ int top = planar[j * 2 + 1] & 0xFF;
+ byte[] strip = new byte[8];
+ for (int i = 7; i >= 0; i--) {
+ strip[7 - i] = (byte) (((bottom >>> i) & 1) + ((top * 2 >>> i) & 2));
+ }
+ System.arraycopy(strip, 0, strips, j * 8, 8);
+ }
+ return strips;
+ }
+
+}