diff options
| author | Sylvie <35663410+Rangi42@users.noreply.github.com> | 2024-09-26 00:45:11 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-09-26 00:45:11 -0400 |
| commit | 9a2f1b2170dab77a88d0230fe479e31e8356ce04 (patch) | |
| tree | 30ecbbf470fa13d339cbb9c579861dfa75e7a004 /tools/pkmncompress.c | |
| parent | This byte is written to, but not used (#471) (diff) | |
| download | pokeyellow-9a2f1b2170dab77a88d0230fe479e31e8356ce04.tar.gz pokeyellow-9a2f1b2170dab77a88d0230fe479e31e8356ce04.tar.xz pokeyellow-9a2f1b2170dab77a88d0230fe479e31e8356ce04.zip | |
Implement .pic to .2bpp decompression (#470)
Diffstat (limited to 'tools/pkmncompress.c')
| -rw-r--r-- | tools/pkmncompress.c | 310 |
1 files changed, 219 insertions, 91 deletions
diff --git a/tools/pkmncompress.c b/tools/pkmncompress.c index 0e20df4f..a890fc53 100644 --- a/tools/pkmncompress.c +++ b/tools/pkmncompress.c @@ -1,25 +1,29 @@ -/* - * Copyright © 2013 stag019 <stag019@gmail.com> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - #define PROGRAM_NAME "pkmncompress" -#define USAGE_OPTS "infile.2bpp outfile.pic" +#define USAGE_OPTS "[-h|--help] [-u|--uncompress] infile.2bpp outfile.pic" #include "common.h" -uint8_t compressed[15 * 15 * 0x10]; +void parse_args(int argc, char *argv[], bool *uncomp) { + struct option long_options[] = { + {"uncompress", no_argument, 0, 'u'}, + {"help", no_argument, 0, 'h'}, + {0} + }; + for (int opt; (opt = getopt_long(argc, argv, "uh", long_options)) != -1;) { + switch (opt) { + case 'u': + *uncomp = true; + break; + case 'h': + usage_exit(0); + break; + default: + usage_exit(1); + } + } +} + +uint8_t output[15 * 15 * 0x10]; int cur_bit; int cur_byte; @@ -28,7 +32,27 @@ void write_bit(int bit) { cur_byte++; cur_bit = 0; } - compressed[cur_byte] |= bit << (7 - cur_bit); + output[cur_byte] |= bit << (7 - cur_bit); +} + +int read_bit(uint8_t *data) { + if (cur_bit == -1) { + cur_byte++; + cur_bit = 7; + } + return (data[cur_byte] >> cur_bit--) & 1; +} + +void transpose_tiles(uint8_t *data, int width) { + int size = width * width * 0x10; + uint8_t *transposed = xmalloc(size); + for (int i = 0; i < size; i++) { + int j = (i / 0x10) * width * 0x10; + j = (j % size) + 0x10 * (j / size) + (i % 0x10); + transposed[j] = data[i]; + } + memcpy(data, transposed, size); + free(transposed); } void compress_plane(uint8_t *plane, int width) { @@ -83,32 +107,26 @@ void write_data_packet(uint8_t *bit_groups, int n) { } } -int interpret_compress(uint8_t *plane1, uint8_t *plane2, int mode, int order, int width) { +int interpret_compress(uint8_t *planes[2], int mode, int order, int width) { int ram_size = width * width * 8; - uint8_t *_plane1 = xmalloc(ram_size); - uint8_t *_plane2 = xmalloc(ram_size); - if (order) { - memcpy(_plane1, plane2, ram_size); - memcpy(_plane2, plane1, ram_size); - } else { - memcpy(_plane1, plane1, ram_size); - memcpy(_plane2, plane2, ram_size); - } - if (mode != 1) { + uint8_t *rams[2] = {xmalloc(ram_size), xmalloc(ram_size)}; + memcpy(rams[0], planes[order], ram_size); + memcpy(rams[1], planes[order ^ 1], ram_size); + if (mode != 0) { for (int i = 0; i < ram_size; i++) { - _plane2[i] ^= _plane1[i]; + rams[1][i] ^= rams[0][i]; } } - compress_plane(_plane1, width); - if (mode != 2) { - compress_plane(_plane2, width); + compress_plane(rams[0], width); + if (mode != 1) { + compress_plane(rams[1], width); } cur_bit = 7; cur_byte = 0; - memset(compressed, 0, COUNTOF(compressed)); - compressed[0] = (width << 4) | width; + memset(output, 0, COUNTOF(output)); + output[0] = (width << 4) | width; write_bit(order); - uint8_t bit_groups[0x1000] = {0}; + uint8_t bit_groups[15 * 4 * 15 * 8] = {0}; int index = 0; for (int plane = 0; plane < 2; plane++) { int type = 0; @@ -117,9 +135,18 @@ int interpret_compress(uint8_t *plane1, uint8_t *plane2, int mode, int order, in for (int x = 0; x < width; x++) { for (int bit = 0; bit < 8; bit += 2) { for (int y = 0, byte = x * width * 8; y < width * 8; y++, byte++) { - int bit_group = ((plane ? _plane2 : _plane1)[byte] >> (6 - bit)) & 3; - if (!bit_group) { - if (!type) { + int bit_group = (rams[plane][byte] >> (6 - bit)) & 3; + if (bit_group) { + if (type == 0) { + write_bit(1); + } else if (type == 1) { + rle_encode_number(nums); + } + type = 2; + bit_groups[index++] = bit_group; + nums = 0; + } else { + if (type == 0) { write_bit(0); } else if (type == 1) { nums++; @@ -131,15 +158,6 @@ int interpret_compress(uint8_t *plane1, uint8_t *plane2, int mode, int order, in type = 1; memset(bit_groups, 0, COUNTOF(bit_groups)); index = 0; - } else { - if (!type) { - write_bit(1); - } else if (type == 1) { - rle_encode_number(nums); - } - type = 2; - bit_groups[index++] = bit_group; - nums = 0; } } } @@ -150,83 +168,193 @@ int interpret_compress(uint8_t *plane1, uint8_t *plane2, int mode, int order, in write_data_packet(bit_groups, index); } if (!plane) { - if (mode < 2) { + if (mode == 0) { write_bit(0); } else { write_bit(1); - write_bit(mode - 2); + write_bit(mode - 1); } } } - free(_plane1); - free(_plane2); + free(rams[0]); + free(rams[1]); return (cur_byte + 1) * 8 + cur_bit; } -int compress(uint8_t *data, int width) { +int get_width(long filesize) { + int width = 0; + for (int w = 1; w < 16; w++) { + if (filesize == w * w * 0x10) { + width = w; + break; + } + } + if (!width) { + error_exit("Image is not a square, or is larger than 15x15 tiles"); + } + return width; +} + +int compress(uint8_t *data, long filesize) { + int width = get_width(filesize); int ram_size = width * width * 8; - uint8_t *plane1 = xmalloc(ram_size); - uint8_t *plane2 = xmalloc(ram_size); + uint8_t *planes[2] = {xmalloc(ram_size), xmalloc(ram_size)}; + transpose_tiles(data, width); for (int i = 0; i < ram_size; i++) { - plane1[i] = data[i * 2]; - plane2[i] = data[i * 2 + 1]; + planes[0][i] = data[i * 2]; + planes[1][i] = data[i * 2 + 1]; } - uint8_t current[COUNTOF(compressed)] = {0}; + uint8_t current[COUNTOF(output)] = {0}; int compressed_size = -1; - for (int mode = 1; mode < 4; mode++) { + for (int mode = 0; mode < 3; mode++) { for (int order = 0; order < 2; order++) { - if (mode == 1 && order == 0) { + if (mode == 0 && order == 0) { continue; } - int new_size = interpret_compress(plane1, plane2, mode, order, width); + int new_size = interpret_compress(planes, mode, order, width); if (compressed_size == -1 || new_size < compressed_size) { compressed_size = new_size; memset(current, 0, COUNTOF(current)); - memcpy(current, compressed, compressed_size / 8); + memcpy(current, output, compressed_size / 8); } } } - memset(compressed, 0, COUNTOF(compressed)); - memcpy(compressed, current, compressed_size / 8); - free(plane1); - free(plane2); + memset(output, 0, COUNTOF(output)); + memcpy(output, current, compressed_size / 8); + free(planes[0]); + free(planes[1]); return compressed_size / 8; } -uint8_t *transpose_tiles(uint8_t *data, int width) { - int size = width * width * 0x10; - uint8_t *transposed = xmalloc(size); - for (int i = 0; i < size; i++) { - int j = (i / 0x10) * width * 0x10; - j = (j % size) + 0x10 * (j / size) + (i % 0x10); - transposed[j] = data[i]; +int read_int(uint8_t *data, int count) { + int n = 0; + while (count--) { + n = (n << 1) | read_bit(data); } - free(data); - return transposed; + return n; } -int main(int argc, char *argv[]) { - if (argc != 3) { - usage_exit(1); +uint8_t *fill_plane(uint8_t *data, int width) { + static int table[0x10] = { + 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, + 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, + }; + int mode = read_bit(data); + int size = width * width * 0x20; + uint8_t *plane = xmalloc(size); + int len = 0; + while (len < size) { + if (mode) { + while (len < size) { + int bit_group = read_int(data, 2); + if (!bit_group) { + break; + } + plane[len++] = bit_group; + } + } else { + size_t w = 0; + while (read_bit(data)) { + w++; + } + if (w >= COUNTOF(table)) { + error_exit("Invalid compressed data"); + } + int n = table[w] + read_int(data, w + 1); + while (len < size && n--) { + plane[len++] = 0; + } + } + mode ^= 1; + } + if (len > size) { + error_exit("Invalid compressed data"); } + uint8_t *ram = xmalloc(size); + len = 0; + for (int y = 0; y < width; y++) { + for (int x = 0; x < width * 8; x++) { + for (int i = 0; i < 4; i++) { + ram[len++] = plane[(y * 4 + i) * width * 8 + x]; + } + } + } + for (int i = 0; i < size - 3; i += 4) { + ram[i / 4] = (ram[i] << 6) | (ram[i + 1] << 4) | (ram[i + 2] << 2) | ram[i + 3]; + } + free(plane); + return ram; +} - long filesize; - uint8_t *data = read_u8(argv[1], &filesize); +void uncompress_plane(uint8_t *plane, int width) { + static int codes[2][0x10] = { + {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}, + }; + for (int x = 0; x < width * 8; x++) { + int bit = 0; + for (int y = 0; y < width; y++) { + int i = y * width * 8 + x; + int nybble_hi = (plane[i] >> 4) & 0xF; + int code_hi = codes[bit][nybble_hi]; + bit = code_hi & 1; + int nybble_lo = plane[i] & 0xF; + int code_lo = codes[bit][nybble_lo]; + bit = code_lo & 1; + plane[i] = (code_hi << 4) | code_lo; + } + } +} - int width = 0; - for (int w = 1; w < 16; w++) { - if (filesize == w * w * 0x10) { - width = w; - break; +int uncompress(uint8_t *data) { + cur_bit = 7; + int width = read_int(data, 4); + if (read_int(data, 4) != width) { + error_exit("Image is not a square"); + } + int size = width * width * 8; + uint8_t *rams[2]; + int order = read_bit(data); + rams[order] = fill_plane(data, width); + int mode = read_bit(data); + if (mode) { + mode += read_bit(data); + } + rams[order ^ 1] = fill_plane(data, width); + uncompress_plane(rams[order], width); + if (mode != 1) { + uncompress_plane(rams[order ^ 1], width); + } + if (mode != 0) { + for (int i = 0; i < size; i++) { + rams[order ^ 1][i] ^= rams[order][i]; } } - if (!width) { - error_exit("Image is not a square, or is larger than 15x15 tiles"); + for (int i = 0; i < size; i++) { + output[i * 2] = rams[0][i]; + output[i * 2 + 1] = rams[1][i]; + } + transpose_tiles(output, width); + free(rams[0]); + free(rams[1]); + return size * 2; +} + +int main(int argc, char *argv[]) { + bool uncomp = false; + parse_args(argc, argv, &uncomp); + + argc -= optind; + argv += optind; + if (argc < 1) { + usage_exit(1); } - data = transpose_tiles(data, width); - int compressed_size = compress(data, width); - write_u8(argv[2], compressed, compressed_size); + long filesize; + uint8_t *data = read_u8(argv[0], &filesize); + + int output_size = uncomp ? uncompress(data) : compress(data, filesize); + write_u8(argv[1], output, output_size); free(data); return 0; |
