summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authordannye <33dannye@gmail.com>2020-11-04 00:06:44 -0600
committerdannye <33dannye@gmail.com>2020-11-04 00:06:44 -0600
commit5647ca687b92954dcf37a6ea6bfbc9a341c32de4 (patch)
treedde1937a1bfdb3a835f4155e1c2eb8f1aaf86f63 /tools
parent53fcd05aa24693093d8af1dc8ec4fedd3957decc (diff)
Sync with pokered
Diffstat (limited to 'tools')
-rw-r--r--tools/gfx.c71
-rw-r--r--tools/pic.py491
-rwxr-xr-xtools/unnamed.py109
3 files changed, 660 insertions, 11 deletions
diff --git a/tools/gfx.c b/tools/gfx.c
index 8c4066ab..7d6dd0ee 100644
--- a/tools/gfx.c
+++ b/tools/gfx.c
@@ -8,7 +8,7 @@
#include "common.h"
static void usage(void) {
- fprintf(stderr, "Usage: gfx [--trim-whitespace] [--remove-whitespace] [--interleave] [--remove-duplicates [--keep-whitespace]] [--remove-xflip] [--remove-yflip] [--png filename] [-d depth] [-h] [-o outfile] infile\n");
+ fprintf(stderr, "Usage: gfx [--trim-whitespace] [--remove-whitespace] [--interleave] [--remove-duplicates [--keep-whitespace]] [--remove-xflip] [--remove-yflip] [--preserve indexes] [--png filename] [-d depth] [-h] [-o outfile] infile\n");
}
static void error(char *message) {
@@ -27,6 +27,8 @@ struct Options {
int keep_whitespace;
int remove_xflip;
int remove_yflip;
+ int *preserved;
+ int num_preserved;
char *png_file;
};
@@ -43,11 +45,13 @@ void get_args(int argc, char *argv[]) {
{"keep-whitespace", no_argument, &Options.keep_whitespace, 1},
{"remove-xflip", no_argument, &Options.remove_xflip, 1},
{"remove-yflip", no_argument, &Options.remove_yflip, 1},
+ {"preserve", required_argument, 0, 'r'},
{"png", required_argument, 0, 'p'},
{"depth", required_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{0}
};
+ char *token;
for (int opt = 0; opt != -1;) {
switch (opt = getopt_long(argc, argv, "ho:d:p:", long_options)) {
case 'h':
@@ -59,6 +63,15 @@ void get_args(int argc, char *argv[]) {
case 'd':
Options.depth = strtoul(optarg, NULL, 0);
break;
+ case 'r':
+ token = strtok(optarg, ",");
+ while (token) {
+ Options.num_preserved++;
+ Options.preserved = realloc(Options.preserved, Options.num_preserved * sizeof(int));
+ Options.preserved[Options.num_preserved-1] = strtoul(token, NULL, 0);
+ token = strtok(NULL, ",");
+ }
+ break;
case 'p':
Options.png_file = optarg;
break;
@@ -73,6 +86,23 @@ void get_args(int argc, char *argv[]) {
}
}
+bool is_preserved(int index) {
+ for (int i = 0; i < Options.num_preserved; i++) {
+ if (Options.preserved[i] == index) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void shift_preserved(int removed_index) {
+ for (int i = 0; i < Options.num_preserved; i++) {
+ if (Options.preserved[i] >= removed_index) {
+ Options.preserved[i]--;
+ }
+ }
+}
+
struct Graphic {
int size;
uint8_t *data;
@@ -91,7 +121,7 @@ bool is_whitespace(uint8_t *tile, int tile_size) {
void trim_whitespace(struct Graphic *graphic) {
int tile_size = Options.depth * 8;
for (int i = graphic->size - tile_size; i > 0; i -= tile_size) {
- if (is_whitespace(&graphic->data[i], tile_size)) {
+ if (is_whitespace(&graphic->data[i], tile_size) && !is_preserved(i / tile_size)) {
graphic->size = i;
} else {
break;
@@ -102,9 +132,15 @@ void trim_whitespace(struct Graphic *graphic) {
void remove_whitespace(struct Graphic *graphic) {
int tile_size = Options.depth * 8;
if (Options.interleave) tile_size *= 2;
+
+ // Make sure we have a whole number of tiles, round down if required
+ graphic->size &= ~(tile_size - 1);
+
int i = 0;
- for (int j = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
- while (is_whitespace(&graphic->data[j], tile_size)) {
+ for (int j = 0, d = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
+ while (j < graphic->size && is_whitespace(&graphic->data[j], tile_size) && !is_preserved(j / tile_size - d)) {
+ shift_preserved(j / tile_size - d);
+ d++;
j += tile_size;
}
if (j >= graphic->size) {
@@ -136,11 +172,17 @@ void remove_duplicates(struct Graphic *graphic) {
int tile_size = Options.depth * 8;
if (Options.interleave) tile_size *= 2;
int num_tiles = 0;
- for (int i = 0, j = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
- while (tile_exists(&graphic->data[j], graphic->data, tile_size, num_tiles)) {
- if (Options.keep_whitespace && is_whitespace(&graphic->data[j], tile_size)) {
+
+ // Make sure we have a whole number of tiles, round down if required
+ graphic->size &= ~(tile_size - 1);
+
+ for (int i = 0, j = 0, d = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
+ while (j < graphic->size && tile_exists(&graphic->data[j], graphic->data, tile_size, num_tiles)) {
+ if ((Options.keep_whitespace && is_whitespace(&graphic->data[j], tile_size)) || is_preserved(j / tile_size - d)) {
break;
}
+ shift_preserved(j / tile_size - d);
+ d++;
j += tile_size;
}
if (j >= graphic->size) {
@@ -155,7 +197,8 @@ void remove_duplicates(struct Graphic *graphic) {
}
bool flip_exists(uint8_t *tile, uint8_t *tiles, int tile_size, int num_tiles, bool xflip, bool yflip) {
- uint8_t *flip = calloc(tile_size, 1);
+ uint8_t flip[tile_size];
+ memset(flip, 0, sizeof(flip));
int half_size = tile_size / 2;
for (int i = 0; i < tile_size; i++) {
int byte = i;
@@ -183,11 +226,17 @@ void remove_flip(struct Graphic *graphic, bool xflip, bool yflip) {
int tile_size = Options.depth * 8;
if (Options.interleave) tile_size *= 2;
int num_tiles = 0;
- for (int i = 0, j = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
- while (flip_exists(&graphic->data[j], graphic->data, tile_size, num_tiles, xflip, yflip)) {
- if (Options.keep_whitespace && is_whitespace(&graphic->data[j], tile_size)) {
+
+ // Make sure we have a whole number of tiles, round down if required
+ graphic->size &= ~(tile_size - 1);
+
+ for (int i = 0, j = 0, d = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
+ while (j < graphic->size && flip_exists(&graphic->data[j], graphic->data, tile_size, num_tiles, xflip, yflip)) {
+ if ((Options.keep_whitespace && is_whitespace(&graphic->data[j], tile_size)) || is_preserved(j / tile_size - d)) {
break;
}
+ shift_preserved(j / tile_size - d);
+ d++;
j += tile_size;
}
if (j >= graphic->size) {
diff --git a/tools/pic.py b/tools/pic.py
new file mode 100644
index 00000000..afc34c90
--- /dev/null
+++ b/tools/pic.py
@@ -0,0 +1,491 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+"""
+A library for use with compressed monster and trainer pics in pokered.
+"""
+from __future__ import absolute_import
+from __future__ import division
+
+import os
+import sys
+import argparse
+from math import sqrt
+
+from pokemontools import gfx
+
+
+def bitflip(x, n):
+ r = 0
+ while n:
+ r = (r << 1) | (x & 1)
+ x >>= 1
+ n -= 1
+ return r
+
+
+class Decompressor:
+ """
+ pokered pic decompression.
+
+ Ported to python 2.7 from the python 3 code at https://github.com/magical/pokemon-sprites-rby.
+ """
+
+ table1 = [(2 << i) - 1 for i in range(16)]
+ 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], # prev ^ 0xf
+ [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], # prev ^ 0xf
+ ]
+ table3 = [bitflip(i, 4) for i in range(16)]
+
+ tilesize = 8
+
+
+ def __init__(self, f, mirror=False, planar=True):
+ self.bs = fbitstream(f)
+ self.mirror = mirror
+ self.planar = planar
+ self.data = None
+
+ def decompress(self):
+ rams = [[], []]
+
+ self.sizex = self._readint(4) * self.tilesize
+ self.sizey = self._readint(4)
+
+ self.size = self.sizex * self.sizey
+
+ self.ramorder = self._readbit()
+
+ r1 = self.ramorder
+ r2 = self.ramorder ^ 1
+
+ self._fillram(rams[r1])
+ mode = self._readbit()
+ if mode:
+ mode += self._readbit()
+ self._fillram(rams[r2])
+
+ rams[0] = bytearray(bitgroups_to_bytes(rams[0]))
+ rams[1] = bytearray(bitgroups_to_bytes(rams[1]))
+
+ if mode == 0:
+ self._decode(rams[0])
+ self._decode(rams[1])
+ elif mode == 1:
+ self._decode(rams[r1])
+ self._xor(rams[r1], rams[r2])
+ elif mode == 2:
+ self._decode(rams[r2], mirror=False)
+ self._decode(rams[r1])
+ self._xor(rams[r1], rams[r2])
+ else:
+ raise Exception("Invalid deinterlace mode!")
+
+ data = []
+ if self.planar:
+ for a, b in zip(rams[0], rams[1]):
+ data += [a, b]
+ self.data = bytearray(data)
+ else:
+ for a, b in zip(bitstream(rams[0]), bitstream(rams[1])):
+ data.append(a | (b << 1))
+ self.data = bitgroups_to_bytes(data)
+
+ def _fillram(self, ram):
+ mode = ['rle', 'data'][self._readbit()]
+ size = self.size * 4
+ while len(ram) < size:
+ if mode == 'rle':
+ self._read_rle_chunk(ram)
+ mode = 'data'
+ elif mode == 'data':
+ self._read_data_chunk(ram, size)
+ mode = 'rle'
+ if len(ram) > size:
+ #ram = ram[:size]
+ raise ValueError(size, len(ram))
+
+ ram[:] = self._deinterlace_bitgroups(ram)
+
+ def _read_rle_chunk(self, ram):
+
+ i = 0
+ while self._readbit():
+ i += 1
+
+ n = self.table1[i]
+ a = self._readint(i + 1)
+ n += a
+
+ for i in range(n):
+ ram.append(0)
+
+ def _read_data_chunk(self, ram, size):
+ while 1:
+ bitgroup = self._readint(2)
+ if bitgroup == 0:
+ break
+ ram.append(bitgroup)
+
+ if size <= len(ram):
+ break
+
+ def _decode(self, ram, mirror=None):
+ if mirror is None:
+ mirror = self.mirror
+
+ for x in range(self.sizex):
+ bit = 0
+ for y in range(self.sizey):
+ i = y * self.sizex + x
+ a = (ram[i] >> 4) & 0xf
+ b = ram[i] & 0xf
+
+ a = self.table2[bit][a]
+ bit = a & 1
+ if mirror:
+ a = self.table3[a]
+
+ b = self.table2[bit][b]
+ bit = b & 1
+ if mirror:
+ b = self.table3[b]
+
+ ram[i] = (a << 4) | b
+
+ def _xor(self, ram1, ram2, mirror=None):
+ if mirror is None:
+ mirror = self.mirror
+
+ for i in range(len(ram2)):
+ if mirror:
+ a = (ram2[i] >> 4) & 0xf
+ b = ram2[i] & 0xf
+ a = self.table3[a]
+ b = self.table3[b]
+ ram2[i] = (a << 4) | b
+
+ ram2[i] ^= ram1[i]
+
+ def _deinterlace_bitgroups(self, bits):
+ l = []
+ for y in range(self.sizey):
+ for x in range(self.sizex):
+ i = 4 * y * self.sizex + x
+ for j in range(4):
+ l.append(bits[i])
+ i += self.sizex
+ return l
+
+
+ def _readbit(self):
+ return next(self.bs)
+
+ def _readint(self, count):
+ return readint(self.bs, count)
+
+
+def fbitstream(f):
+ while 1:
+ char = f.read(1)
+ if not char:
+ break
+ byte = ord(char)
+
+ for i in range(7, -1, -1):
+ yield (byte >> i) & 1
+
+def bitstream(b):
+ for byte in b:
+ for i in range(7, -1, -1):
+ yield (byte >> i) & 1
+
+def readint(bs, count):
+ n = 0
+ while count:
+ n <<= 1
+ n |= next(bs)
+ count -= 1
+ return n
+
+def bitgroups_to_bytes(bits):
+ l = []
+ for i in range(0, len(bits) - 3, 4):
+ n = ((bits[i + 0] << 6)
+ | (bits[i + 1] << 4)
+ | (bits[i + 2] << 2)
+ | (bits[i + 3] << 0))
+ l.append(n)
+ return bytearray(l)
+
+
+def bytes_to_bits(bytelist):
+ return list(bitstream(bytelist))
+
+
+class Compressor:
+ """
+ pokered pic compression.
+
+ Adapted from stag019's C compressor.
+ """
+
+ table1 = [(2 << i) - 1 for i in range(16)]
+ table2 = [
+ [0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4, 0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8],
+ [0x8, 0x9, 0xb, 0xa, 0xe, 0xf, 0xd, 0xc, 0x4, 0x5, 0x7, 0x6, 0x2, 0x3, 0x1, 0x0], # reverse
+ ]
+ table3 = [bitflip(i, 4) for i in range(16)]
+
+ def __init__(self, image, width=None, height=None):
+ self.image = bytearray(image)
+ self.size = len(self.image)
+
+ planar_tile = 8 * 8 // 4
+ tile_size = self.size // planar_tile
+ if height and not width: width = tile_size // height
+ elif width and not height: height = tile_size // width
+ elif not width and not height: width = height = int(sqrt(tile_size))
+ self.width, self.height = width, height
+
+ def compress(self):
+ """
+ Compress the image five times (twice for each mode, except 0)
+ and use the smallest one (in bits).
+ """
+ rams = [[],[]]
+ datas = []
+
+ for mode in range(3):
+
+ # Order is redundant for mode 0.
+
+ # While this seems like an optimization,
+ # it's actually required for 1:1 compression
+ # to the original compressed pics.
+
+ # This appears to be the algorithm
+ # that Game Freak's compressor used.
+
+ # Using order 0 instead of 1 breaks this feature.
+
+ for order in range(2):
+ if mode == 0 and order == 0:
+ continue
+ for i in range(2):
+ rams[i] = self.image[i::2]
+ self._interpret_compress(rams, mode, order)
+ datas += [(self.data[:], int(self.which_bit))]
+
+ # Pick the smallest pic, measured in bits.
+ datas = sorted(datas, key=lambda data_bit: (len(data_bit[0]), -data_bit[1]))
+ self.data, self.which_bit = datas[0]
+
+ def _interpret_compress(self, rams, mode, order):
+ self.data = []
+ self.which_bit = 0
+
+ r1 = order
+ r2 = order ^ 1
+
+ if mode == 0:
+ self._encode(rams[1])
+ self._encode(rams[0])
+ elif mode == 1:
+ self._xor(rams[r1], rams[r2])
+ self._encode(rams[r1])
+ elif mode == 2:
+ self._xor(rams[r1], rams[r2])
+ self._encode(rams[r1])
+ self._encode(rams[r2], mirror=False)
+ else:
+ raise Exception('invalid interlace mode!')
+
+ self._writeint(self.height, 4)
+ self._writeint(self.width, 4)
+
+ self._writebit(order)
+
+ self._fillram(rams[r1])
+ if mode == 0:
+ self._writebit(0)
+ else:
+ self._writebit(1)
+ self._writebit(mode - 1)
+ self._fillram(rams[r2])
+
+ def _fillram(self, ram):
+ rle = 0
+ nums = 0
+ bitgroups = []
+
+ for x in range(self.width):
+ for bit in range(0, 8, 2):
+ byte = x * self.height * 8
+ for y in range(self.height * 8):
+ bitgroup = (ram[byte] >> (6 - bit)) & 3
+ if bitgroup == 0:
+ if rle == 0:
+ self._writebit(0)
+ elif rle == 1:
+ nums += 1
+ else:
+ self._data_packet(bitgroups)
+ self._writebit(0)
+ self._writebit(0)
+ rle = 1
+ bitgroups = []
+ else:
+ if rle == 0:
+ self._writebit(1)
+ elif rle == 1:
+ self._rle(nums)
+ rle = -1
+ bitgroups += [bitgroup]
+ nums = 0
+ byte += 1
+
+ if rle == 1:
+ self._rle(nums)
+ else:
+ self._data_packet(bitgroups)
+
+ def _data_packet(self, bitgroups):
+ for bitgroup in bitgroups:
+ self._writebit((bitgroup >> 1) & 1)
+ self._writebit((bitgroup >> 0) & 1)
+
+ def _rle(self, nums):
+ nums += 1
+
+ # Get the previous power of 2.
+ # Deriving the bitcount from that seems to be
+ # faster on average than using the lookup table.
+ v = nums
+ v += 1
+ v |= v >> 1
+ v |= v >> 2
+ v |= v >> 4
+ v |= v >> 8
+ v |= v >> 16
+ v -= v >> 1
+ v -= 1
+ number = nums - v
+
+ bitcount = -1
+ while v:
+ v >>= 1
+ bitcount += 1
+
+ for j in range(bitcount):
+ self._writebit(1)
+ self._writebit(0)
+ for j in range(bitcount, -1, -1):
+ self._writebit((number >> j) & 1)
+
+ def _encode(self, ram, mirror=None):
+ a = b = 0
+ for i in range(len(ram)):
+ j = i // self.height
+ j += i % self.height * self.width * 8
+ if i % self.height == 0:
+ b = 0
+
+ a = (ram[j] >> 4) & 0xf
+ table = b & 1
+ code_1 = self.table2[table][a]
+
+ b = ram[j] & 0xf
+ table = a & 1
+ code_2 = self.table2[table][b]
+
+ ram[j] = (code_1 << 4) | code_2
+
+ def _xor(self, ram1, ram2):
+ for i in range(len(ram2)):
+ ram2[i] ^= ram1[i]
+
+ def _writebit(self, bit):
+ self.which_bit -= 1
+ if self.which_bit == -1:
+ self.which_bit = 7
+ self.data += [0]
+ if bit: self.data[-1] |= bit << self.which_bit
+
+ def _writeint(self, num, size=None):
+ bits = []
+ if size:
+ for i in range(size):
+ bits += [num & 1]
+ num >>= 1
+ else:
+ while num > 0:
+ bits += [num & 1]
+ num >>= 1
+ for bit in reversed(bits):
+ self._writebit(bit)
+
+
+def decompress(f, offset=None, mirror=False):
+ """
+ Decompress a pic given a file object. Return a planar 2bpp image.
+
+ Optional: offset (for roms).
+ """
+ if offset is not None:
+ f.seek(offset)
+ dcmp = Decompressor(f, mirror=mirror)
+ dcmp.decompress()
+ return dcmp.data
+
+
+def compress(f):
+ """
+ Compress a planar 2bpp into a pic.
+ """
+ comp = Compressor(f)
+ comp.compress()
+ return comp.data
+
+
+def decompress_file(filename):
+ """
+ Decompress a pic given a filename.
+ Export the resulting planar 2bpp image to
+ """
+ pic = open(filename, 'rb')
+ image = decompress(pic)
+ image = gfx.transpose_tiles(image)
+ image = bytearray(image)
+ output_filename = os.path.splitext(filename)[0] + '.2bpp'
+ with open(output_filename, 'wb') as out:
+ out.write(image)
+
+def compress_file(filename):
+ image = open(filename, 'rb').read()
+ image = gfx.transpose_tiles(image)
+ pic = compress(image)
+ pic = bytearray(pic)
+ output_filename = os.path.splitext(filename)[0] + '.pic'
+ with open(output_filename, 'wb') as out:
+ out.write(pic)
+
+
+def main():
+ ap = argparse.ArgumentParser()
+ ap.add_argument('mode')
+ ap.add_argument('filenames', nargs='*')
+ args = ap.parse_args()
+
+ for filename in args.filenames:
+ if args.mode == 'decompress':
+ decompress_file(filename)
+ elif args.mode == 'compress':
+ compress_file(filename)
+
+if __name__ == '__main__':
+ main()
+
diff --git a/tools/unnamed.py b/tools/unnamed.py
new file mode 100755
index 00000000..b2e9aea3
--- /dev/null
+++ b/tools/unnamed.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+
+from sys import stderr, exit
+from subprocess import Popen, PIPE
+from struct import unpack, calcsize
+from enum import Enum
+
+class symtype(Enum):
+ LOCAL = 0
+ IMPORT = 1
+ EXPORT = 2
+
+def unpack_file(fmt, file):
+ size = calcsize(fmt)
+ return unpack(fmt, file.read(size))
+
+def read_string(file):
+ buf = bytearray()
+ while True:
+ b = file.read(1)
+ if b is None or b == b'\0':
+ return buf.decode()
+ else:
+ buf += b
+
+
+# Fix broken pipe when using `head`
+from signal import signal, SIGPIPE, SIG_DFL
+signal(SIGPIPE,SIG_DFL)
+
+import argparse
+parser = argparse.ArgumentParser(description="Parse the symfile to find unnamed symbols")
+parser.add_argument('symfile', type=argparse.FileType('r'), help="the list of symbols")
+parser.add_argument('-r', '--rootdir', type=str, help="scan the output files to obtain a list of files with unnamed symbols (NOTE: will rebuild objects as necessary)")
+parser.add_argument('-l', '--list', type=int, default=0, help="output this many of each file's unnamed symbols (NOTE: requires -r)")
+args = parser.parse_args()
+
+# Get list of object files
+objects = None
+if args.rootdir:
+ for line in Popen(["make", "-C", args.rootdir, "-s", "-p", "DEBUG=1"],
+ stdout=PIPE).stdout.read().decode().split("\n"):
+ if line.startswith("rom_obj := "):
+ objects = line[11:].strip().split()
+ break
+ else:
+ print("Error: Object files not found!", file=stderr)
+ exit(1)
+
+# Scan all unnamed symbols from the symfile
+symbols_total = 0
+symbols = set()
+for line in args.symfile:
+ line = line.split(";")[0].strip()
+ split = line.split(" ")
+ if len(split) < 2:
+ continue
+
+ symbols_total += 1
+
+ symbol = " ".join(split[1:]).strip()
+ if symbol[-3:].lower() == split[0][-3:].lower():
+ symbols.add(symbol)
+
+# If no object files were provided, just print what we know and exit
+print("Unnamed pokeyellow symbols: %d (%.2f%% complete)" % (len(symbols),
+ (symbols_total - len(symbols)) / symbols_total * 100))
+if not objects:
+ for sym in symbols:
+ print(sym)
+ exit()
+
+# Count the amount of symbols in each file
+files = {}
+for objfile in objects:
+ f = open(objfile, "rb")
+ obj_ver = None
+
+ magic = unpack_file("4s", f)[0]
+ if magic == b'RGB6':
+ obj_ver = 6
+ elif magic == b'RGB9':
+ obj_ver = 10 + unpack_file("<I", f)[0]
+
+ if obj_ver not in [6, 10, 11, 12, 13, 15]:
+ print("Error: File '%s' is of an unknown format." % objfile, file=stderr)
+ exit(1)
+
+ num_symbols = unpack_file("<II", f)[0]
+ for x in range(num_symbols):
+ sym_name = read_string(f)
+ sym_type = symtype(unpack_file("<B", f)[0] & 0x7f)
+ if sym_type == symtype.IMPORT:
+ continue
+ sym_filename = read_string(f)
+ unpack_file("<III", f)
+ if sym_name not in symbols:
+ continue
+
+ if sym_filename not in files:
+ files[sym_filename] = []
+ files[sym_filename].append(sym_name)
+
+# Sort the files, the one with the most amount of symbols first
+files = sorted(((f, files[f]) for f in files), key=lambda x: len(x[1]), reverse=True)
+for f in files:
+ filename, unnamed = f
+ sym_list = ', '.join(unnamed[:args.list])
+ print("%s: %d%s" % (filename, len(unnamed), ': ' + sym_list if sym_list else ''))