Along the way, I realized several of my cases, single tile, tile bank, BG mapping, are all variations on take a BLOB of CHR tiles and arrange them in a grid, sometimes the grid is 1x1, sometimes its 256x256 for a bank, sometimes its some other set of configurable criteria.
As such, I put together "chrtopng" the source of which is here: https://gitlab.com/segaloco/misc/-/blob ... chrtopng.c
Included is a manpage but some of the applications may not be readily apparent just from the documentation. Essentially there are three configurable values as well as the input of the tile data itself. By default, the utility will process up to 256 tiles into a PNG image 16-tiles wide, resulting in a 16x16 tile (128x128 pixel) PNG on the standard output. The PNG uses a palette and bit depth of 2bpp for simplicity. The palette is by default assembled from an included set of PPU color IDs, although the "-p" option can be used to provide a four-byte file containing the palette IDs to use instead. This points to one limitation, a single pass only applies a single palette, this tool can't currently assemble an image with variations in palette IDs for different tiles. I don't think I'll add that to this tool, instead if that is desired, one would produce all the PNGs of the desired tiles at the desired colors and then some separate utility could sample those into an image from a mapping.
Anywho, the per-row and maximum tile limits are also options, "-r" and "-m" respectively, which accept an integer describing these dimensions. While a file can be passed as an argument, this utility will read the standard input if no such file is supplied. This allows for using this utility as a filter in pipelines. Here are a few application examples, note I've suffixed each pipeline with:
Code: Select all
... | convert png:- -scale 200% png:- | png
So first is the most basic case, reading a single tile supplied on stdin:
Code: Select all
chrtopng <data/chr.smb/chr_bg_0C.bin
This demonstrates an important point. Despite the default row maximum of 16 tiles, if less than a full row of tiles is provided, the width is adjusted accordingly. This is to avoid a bunch of dead space when looking at just a handful of tiles.
However, providing a BLOB with enough tiles to meet the 256 tile default maximum:
Code: Select all
chrtopng smchar.fc.bin
Two new facts are observed here. First, the input file can be passed as an undecorated argument, internally it is simply freopen(3)'d as stdin. Second, the dimension limitations are now in effect. After reaching the 16th tile, the image generation wrapped around. In addition, despite this being the full CHR image, only the OBJ page has printed, demonstrating the 256 tile default maximum in action. While the maximum does not force a smaller number of tiles to this max, it cannot be exceeded. Internally, this value is used to size the buffer used for the operation and as a result, the maximum number of tiles the tool will read. The sizing of the image is then based on how much was *read*, not how much was requested. Really this is just to keep things pipe-friendly. The buffer size can't be as easily determined automatically by a pipe since you can't, for instance, seek to the end of a pipe reliably to tell its length. Additionally, as you can see here, the default built-in palette is shamelessly Mario's colors. This is the bank I worked it up with so that's how it fell, my last iteration of tools was the Dragon Quest player character palette for similar reasons.
Anywho, these just demonstrate the basic lower and upper bounds of operation provided by default, things get more interesting by manipulating these limits.
A quite useful feature of reading stdin is piping in from other data filtering tools like dd(1):
Code: Select all
dd if=smchar.fc.bin bs=16 skip=256 count=10 | chrtopng
So basically I was able to use dd(1) to manipulate the BLOB in units of tiles and snip out a subset of tiles to send down the pipe to my tool. As a result, I can snip out the digit tiles from the CHR BLOB simply by knowing they begin the BG segment. Operate in 16 byte (CHR) blocks, discard the first 256 (OBJ), and then take the next 10 (digits). This can prove quite useful in needle-in-haystack situations and could easily be made into a script of its own wrapping this for simple CHR lookups in a bank. What is tile 32 of the BG bank? Skip the first 256 to get to the bank, another 32 to get to the tile in question, and count 1 to pull just that tile back. Pipe it to something like the png CLI and you've got a "show me these tiles" tool with no fuss.
Anywho, the default palette is just so there is something to refer to, but likely one would want to see graphics in context:
Code: Select all
printf "\x0F\x30\x0F\x0F" >pal.bin
dd if=smchar.fc.bin bs=16 skip=256 count=10 | chrtopng -p pal.bin
While the 16 wide and 256 total limits come from one of the most common cases, viewing an entire bank, both limitations are adjustable.
The row width can either be raised:
Code: Select all
chrtopng -r 24 smchar.fc.bin
Code: Select all
dd if=smchar.fc.bin bs=16 skip=256 count=10 | chrtopng -p pal.bin -r 2
Code: Select all
chrtopng -m 512 smchar.fc.bin
This hopefully demonstrates some of the capabilities. One follow on tool I intend to write is a utility that will take a complete (or partial, but contiguous) nametable map and sample from a CHR collection into a BLOB arranged appropriately such that it can be passed through this tool to get an image of that screen. In reality it's just creating a bank that is arranged such that the desired tiles appear in the output in the right places. With the row and maximum settings, this could be further enhanced to display individually mapped things like sprites and metatiles by ordering them in memory properly and then applying the correct dimensions.
Anywho, licensing is included in the file, typical BSD stuff, do what you want with it, just don't come crying to me if it eats your morning breakfast. I'm lazy so didn't provide a Makefile, this isn't going to replace your favorite graphic editor of the day, it's just a tool primarily targeting bozos like me that type words at black and white screens most of the time and are scared of mice. My compile line is:
Code: Select all
cc -O2 -I/usr/include/libpng16 -o chrtopng chrtopng.c -lpng
Finally there is some error checking but naturally don't trust this any more than any other random code you get off the net. Pipes are your friend, then my grubby code doesn't touch your pristine files at all. Anywho as I build up new parts of this stuff I'll share it here.