Ongoing Disassembly Work
-
Oziphantom
- Posts: 1980
- Joined: Tue Feb 07, 2017 2:03 am
Re: Ongoing Disassembly Work
can you not make an include file to pack a bunch of files into a single obj and then link against that single obj file?
-
segaloco
- Posts: 914
- Joined: Fri Aug 25, 2023 11:56 am
Re: Ongoing Disassembly Work
I can I would just rather avoid that overhead if possible. That concern belongs to the linker and would continue to if not for this limit...
-
segaloco
- Posts: 914
- Joined: Fri Aug 25, 2023 11:56 am
Re: Ongoing Disassembly Work
Here's a new little tool I spun up while working on this:
What this does is takes the metatile storage format from SMB3 (and likely others) and creates a list of 32-byte strings representing the necessary sequence of CHR IDs to then render a map of assembled metatiles like the one attached (random palette chosen). This can then essentially be treated as a 32 tile wide "nametable" by emitting the tile data in order. I so happen to have my own nt-to-chr buffer and chr-buffer-to-png utilities, the latter of which accepts arbitrary row lengths and counts, so putting all this together in a pipeline something like:
Results in a nice simple metatile viewer:
This is something I love about the simplicity of UNIX pipelines, each of these pipelines builds on stuff I've made in the past and allows me to do this stuff on pretty bare-bones systems. With most romhacking utilities being graphical Windows programs, it's refreshing not having to worry about being able to run that one particular environment and I can still supplement my work with a rich set of utilities that are easy to modify and understand.
I plan on tinkering on this one a little more, the "final" version will be a different utlity, "mttont", a strange name sure, but the idea is it's a utility that would take some sort of abstract metatile definition and turn it into a nametable of arbitrary width (basically making columns a parameter and rows a function of the size of data supplied). While I could also consider making the metatile size arbitrary, currently the 2x2 is based specifically on the idea of four consecutive, equally sized tables containing in total "TILES" tiles, arranged NW, SW, NE, SE in memory. There's both the question of packed vs sequential tables and the row/column major order. Still, this general idea of emitting a sequential list of CHR IDs assembled together by pivoting a data set into an arbitrarily wide grid space is literally the definition of what a metatile system is doing, so I figure if I can make these sorts of parameters a bit more configurable, I might be able to make a general purpose metatile decoder, general enough for cases like SMB3 at least. This begs the following configuration parameters:
- Metatile width: This is the number of CHR elements that make up one row of a metatile. A nametable width must be a multiple of this width for the resulting nametable to make any sense.
- Metatile height - This is the number of CHR elements that make up one column of a metatile. This many strings must be emitted to the nametable before the next set of tiles is considered.
- Metatile row majority - Boolean value indicating whether metatiles are stored in row or column major order. A major assumption is being made here in that tiles will start with the north-west corner. This may not always be the case, but we'll cross that bridge when we come to it.
- Metatile packing characteristics - For now, I could only imagine supporting packed and sequential tables:
So this would be a switch indicating which of these (or in the future, other) data structures is used.
- Nametable width: The number of distinct CHR elements that will be emitted as a "string" before assuming horizontal retrace. This can be seen as a function of the metatile width multiplied by the number of metatiles to show in a row. Since anything after this in a pipeline would be operating in terms of nametable rows, the concept of "metatiles wide" is discarded.
So then to such a tool, one would pass presumably a data buffer representing the metatile definitions they want to transform as well as the following characteristics: metatile dimensions, metatile count, metatile row majority, metatile packing type, nametable width; and the result would be a multidimensional array of "nametable width" wide CHR strings stored sequentially. At this point this information could be converted to an array of CHR data and then mapped to an image.
When/if I put such a generalized version together I also plan on touching up some other parts of my individual utilities and writing a few packaged scripts that call the lower level stuff with more convenient albeit limited interfaces. I plan on drawing that and some other work I've done together into a more focused 2C02 CLI utilities package for various inspection and graphic transform matters.
What is nice about all of this is since they're all pipelines, you can save off the output of any one step for multiple different versions of subsequent steps, so this makes quite easy for instance generating a nametable once then creating multiple png images for different combinations of tile banks to rapidly overlay banks until one seems to align with a metatile definition. This also allows for on the fly metatile experimentation. For instance, imagine having a variant of the pipeline that generates tiles one at a time and simply displays them, awaits close, then prompts for the next definition. One could have a prepared list of "trial" metatiles or even simply pipe in from an editor converting typed hex characters to their binary values on stdout and experiment with assembling metatiles on the fly.
Also in all of this I'm using png as the output format, but it doesn't need to be, I've written a chrtobmp utility that pretty much does the same thing, a converter could easily be placed in the pipeline, and then similarly other programs besides "display" could be used to display whatever format of image is being generated. Heck, if you've got some application that watches a file and automatically refreshes the display when new data is ready, that is all that is needed for a rudimentary animation viewer. I say all this to highlight the power of UNIX pipelines. One last thing, all of this stuff is great when you get a good pipeline going because then very little to any filesystem interactions are needed beyond reading your data sources. However, sometimes the intermediary steps of pipelines are helpful for debugging. Luckily, tee(1) is able to create a second copy of any data created by a pipeline, so for instance if one was to create such metatile viewer or animation viewer utilities that happily took their input from stdin, any resulting nametables or mappings generated that are so desired could easily be saved off in a file for later use.
Anywho, dunno how directly useful this will be outside of SMB3 right now but the next version of this program hopefully will work on a wide array of titles and can make some of these general purpose utilities more attainable, especially with minimal resources or specific dependencies beyond POSIX utilities and choices of input and output mechanisms.
Code: Select all
/*
* metatile - pivot a metatile table into a linear array of 32-tile strings
*
* Copyright 2025 Matthew Gilmore
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#define QUADRANTS 4
#define TILES 256
#define COLUMNS 32
#define ROWS 32
#define TILE_WIDTH 2
#define TILE_HEIGHT 2
int buffer[QUADRANTS][TILES];
int main(void)
{
int i, j;
int k, l;
for (i = 0; i < (sizeof (buffer) / sizeof (*buffer)); i++) {
for (j = 0; j < (sizeof (*buffer) / sizeof (**buffer)); j++) {
buffer[i][j] = getchar();
}
}
for (i = 0; i < ROWS; i++) {
for (j = 0; j < COLUMNS; j++) {
k = ((j % TILE_WIDTH) * 2)+(i % TILE_HEIGHT);
l = ((i / TILE_HEIGHT)* 16)+((j / TILE_WIDTH));
putchar(buffer[k][l]);
}
}
return 0;
}
Code: Select all
cat metatbl.bin | metatile | nttochr chrbank.bin | chrtopng -r 32 -m 1024 -p chrpal.bin | display -resize 400% -filter point png:-
This is something I love about the simplicity of UNIX pipelines, each of these pipelines builds on stuff I've made in the past and allows me to do this stuff on pretty bare-bones systems. With most romhacking utilities being graphical Windows programs, it's refreshing not having to worry about being able to run that one particular environment and I can still supplement my work with a rich set of utilities that are easy to modify and understand.
I plan on tinkering on this one a little more, the "final" version will be a different utlity, "mttont", a strange name sure, but the idea is it's a utility that would take some sort of abstract metatile definition and turn it into a nametable of arbitrary width (basically making columns a parameter and rows a function of the size of data supplied). While I could also consider making the metatile size arbitrary, currently the 2x2 is based specifically on the idea of four consecutive, equally sized tables containing in total "TILES" tiles, arranged NW, SW, NE, SE in memory. There's both the question of packed vs sequential tables and the row/column major order. Still, this general idea of emitting a sequential list of CHR IDs assembled together by pivoting a data set into an arbitrarily wide grid space is literally the definition of what a metatile system is doing, so I figure if I can make these sorts of parameters a bit more configurable, I might be able to make a general purpose metatile decoder, general enough for cases like SMB3 at least. This begs the following configuration parameters:
- Metatile width: This is the number of CHR elements that make up one row of a metatile. A nametable width must be a multiple of this width for the resulting nametable to make any sense.
- Metatile height - This is the number of CHR elements that make up one column of a metatile. This many strings must be emitted to the nametable before the next set of tiles is considered.
- Metatile row majority - Boolean value indicating whether metatiles are stored in row or column major order. A major assumption is being made here in that tiles will start with the north-west corner. This may not always be the case, but we'll cross that bridge when we come to it.
- Metatile packing characteristics - For now, I could only imagine supporting packed and sequential tables:
Code: Select all
; The difference between:
.byte TILE_NW, TILE_SW, TILE_NE, TILE_SE
.byte TILE_NW_B, TILE_SW_B, TILE_NE_B, TILE_SE_B
; and
tbl_nw:
.byte TILE_NW, TILE_NW_B
tbl_sw:
.byte TILE_SW, TILE_SW_B
tbl_ne:
.byte TILE_NE, TILE_NE_B
tbl_se:
.byte TILE_SE, TILE_SE_B
- Nametable width: The number of distinct CHR elements that will be emitted as a "string" before assuming horizontal retrace. This can be seen as a function of the metatile width multiplied by the number of metatiles to show in a row. Since anything after this in a pipeline would be operating in terms of nametable rows, the concept of "metatiles wide" is discarded.
So then to such a tool, one would pass presumably a data buffer representing the metatile definitions they want to transform as well as the following characteristics: metatile dimensions, metatile count, metatile row majority, metatile packing type, nametable width; and the result would be a multidimensional array of "nametable width" wide CHR strings stored sequentially. At this point this information could be converted to an array of CHR data and then mapped to an image.
When/if I put such a generalized version together I also plan on touching up some other parts of my individual utilities and writing a few packaged scripts that call the lower level stuff with more convenient albeit limited interfaces. I plan on drawing that and some other work I've done together into a more focused 2C02 CLI utilities package for various inspection and graphic transform matters.
What is nice about all of this is since they're all pipelines, you can save off the output of any one step for multiple different versions of subsequent steps, so this makes quite easy for instance generating a nametable once then creating multiple png images for different combinations of tile banks to rapidly overlay banks until one seems to align with a metatile definition. This also allows for on the fly metatile experimentation. For instance, imagine having a variant of the pipeline that generates tiles one at a time and simply displays them, awaits close, then prompts for the next definition. One could have a prepared list of "trial" metatiles or even simply pipe in from an editor converting typed hex characters to their binary values on stdout and experiment with assembling metatiles on the fly.
Also in all of this I'm using png as the output format, but it doesn't need to be, I've written a chrtobmp utility that pretty much does the same thing, a converter could easily be placed in the pipeline, and then similarly other programs besides "display" could be used to display whatever format of image is being generated. Heck, if you've got some application that watches a file and automatically refreshes the display when new data is ready, that is all that is needed for a rudimentary animation viewer. I say all this to highlight the power of UNIX pipelines. One last thing, all of this stuff is great when you get a good pipeline going because then very little to any filesystem interactions are needed beyond reading your data sources. However, sometimes the intermediary steps of pipelines are helpful for debugging. Luckily, tee(1) is able to create a second copy of any data created by a pipeline, so for instance if one was to create such metatile viewer or animation viewer utilities that happily took their input from stdin, any resulting nametables or mappings generated that are so desired could easily be saved off in a file for later use.
Anywho, dunno how directly useful this will be outside of SMB3 right now but the next version of this program hopefully will work on a wide array of titles and can make some of these general purpose utilities more attainable, especially with minimal resources or specific dependencies beyond POSIX utilities and choices of input and output mechanisms.
You do not have the required permissions to view the files attached to this post.
-
segaloco
- Posts: 914
- Joined: Fri Aug 25, 2023 11:56 am
Re: Ongoing Disassembly Work
I've now written a higher level extraction tool based on these lower level components. This can be found with my SMB3 disassembly here: https://gitlab.com/segaloco/smb3/-/blob ... /mtdump.sh
I haven't finished the 2.0 of the metatile extractor with more configurable options but it should be an easy swap out when that is ready. I've now got a tool, closer married to the SMB3 disassembly like the 1.0 metatile tool but this time that takes a tabular format describing metatile maps to extract to pngs. The tabular format looks a little something like (example renderings from this file attached):
Archaic I know but it works and is actually quite nice. The first column is a name assigned to the resulting png file in this case, although the image generator is at the tip, anything that'll assemble a string of CHR tiles with the right line breaks will do the job.
The rest are numbers controlling what files and offsets are home to the various bits that make up a metatile mapping in SMB3. All numbers are given in two-decimal hex, that's really all that matters to me, but if someone wanted to adapt it it all boils down to number manipulation.
Anyway, first is the ID of the PRG bank where the (currently fixed-address/format) metatile bank is located. Second and third are the (MMC3) CHR bank IDs of the low and high 2048-byte CHR chunks loaded in at $0000 and $0800 at runtime. These two bank selections are a function of a few bits in the course header but generally fall along the lines of the different visual styles (hills, underground, clouds, etc.). These two numbers are chosen from a table, one for each, by the same index, so in reality this tool could be even higher level by having the same table internally and just using the index from course headers, but this makes it a bit more general.
The last two numbers are a page and index to the palette from the ROM to use. In this case again this is a higher level tool so a little married to the disassembly here at present. What would have to change is the palette base pointer in PRG bank 0x1B which is hard-coded in the script. This could either change to track the exported symbol in a ld65 map file or otherwise be edited by hand when one knows where the palette table wound up in any given ROM. Similar to the CHR banks, the palette entries have clear identities associated with the different visual styles, but this approach allows arbitrary indexing into the tables, having a header on reference makes it easy enough to know which numbers are which. Pages are assumed to be 12 4-line palette entries. Bounds within a page are not checked.
I have not tested much of anything outside of this working on SMB3 stuff consistently. That means it solves the problem I designed it to solve, but I do hope to generalize the steps I've taken along the way into something that might work well for tuning the low-level tools to support more scenarios. Then this becomes a starting place or even possibly directly applicable to other Nintendo stuff or stuff with compatible metatile formats. Plus if I can make the output scalable, this could generate individual tiles, which then means I could use it to assemble maps and course previews since those would then just be a description of the metatiles to assemble in order.
One last note, the metatile format used by SMB3 as well as many other Nintendo titles utilizes the highest two bits of the metatile number also as the BG attribute for that tile. In other words, the metatile list is split up into 64-tile chunks, each assigned to one of the four palette rows. This utility accounts for this in that it locates the palette and uses the four entries to create each 64-tile chunk and then uses the ImageMagick montage utility to concatenate the images together. To do this without montage would be possible but I would have to majorly change the image generation step since currently it assumes one palette for all emitted tiles. This means for now this pipeline is additionally dependent on ImageMagick. The rest of the tools are split between this disassembly (in the tools directory) and here: https://gitlab.com/segaloco/misc/-/tree ... 2C02_tools
I haven't finished the 2.0 of the metatile extractor with more configurable options but it should be an easy swap out when that is ready. I've now got a tool, closer married to the SMB3 disassembly like the 1.0 metatile tool but this time that takes a tabular format describing metatile maps to extract to pngs. The tabular format looks a little something like (example renderings from this file attached):
Code: Select all
#name:mt_bank_hex:chr_lo_hex:chr_hi_hex:pal_page_hex:pal_id_hex
plains_00:0F:08:60:01:00
fortress_00:15:10:16:02:00
bonus_00:16:24:5E:0F:00
airship_00:17:34:6A:0A:00
giant_00:13:6E:60:0B:00
The rest are numbers controlling what files and offsets are home to the various bits that make up a metatile mapping in SMB3. All numbers are given in two-decimal hex, that's really all that matters to me, but if someone wanted to adapt it it all boils down to number manipulation.
Anyway, first is the ID of the PRG bank where the (currently fixed-address/format) metatile bank is located. Second and third are the (MMC3) CHR bank IDs of the low and high 2048-byte CHR chunks loaded in at $0000 and $0800 at runtime. These two bank selections are a function of a few bits in the course header but generally fall along the lines of the different visual styles (hills, underground, clouds, etc.). These two numbers are chosen from a table, one for each, by the same index, so in reality this tool could be even higher level by having the same table internally and just using the index from course headers, but this makes it a bit more general.
The last two numbers are a page and index to the palette from the ROM to use. In this case again this is a higher level tool so a little married to the disassembly here at present. What would have to change is the palette base pointer in PRG bank 0x1B which is hard-coded in the script. This could either change to track the exported symbol in a ld65 map file or otherwise be edited by hand when one knows where the palette table wound up in any given ROM. Similar to the CHR banks, the palette entries have clear identities associated with the different visual styles, but this approach allows arbitrary indexing into the tables, having a header on reference makes it easy enough to know which numbers are which. Pages are assumed to be 12 4-line palette entries. Bounds within a page are not checked.
I have not tested much of anything outside of this working on SMB3 stuff consistently. That means it solves the problem I designed it to solve, but I do hope to generalize the steps I've taken along the way into something that might work well for tuning the low-level tools to support more scenarios. Then this becomes a starting place or even possibly directly applicable to other Nintendo stuff or stuff with compatible metatile formats. Plus if I can make the output scalable, this could generate individual tiles, which then means I could use it to assemble maps and course previews since those would then just be a description of the metatiles to assemble in order.
One last note, the metatile format used by SMB3 as well as many other Nintendo titles utilizes the highest two bits of the metatile number also as the BG attribute for that tile. In other words, the metatile list is split up into 64-tile chunks, each assigned to one of the four palette rows. This utility accounts for this in that it locates the palette and uses the four entries to create each 64-tile chunk and then uses the ImageMagick montage utility to concatenate the images together. To do this without montage would be possible but I would have to majorly change the image generation step since currently it assumes one palette for all emitted tiles. This means for now this pipeline is additionally dependent on ImageMagick. The rest of the tools are split between this disassembly (in the tools directory) and here: https://gitlab.com/segaloco/misc/-/tree ... 2C02_tools
You do not have the required permissions to view the files attached to this post.
-
segaloco
- Posts: 914
- Joined: Fri Aug 25, 2023 11:56 am
Re: Ongoing Disassembly Work
And a couple more tools, one general, one more specific.
First, mttont is a tool which converts a metatile table into a corresponding nametable. The dimensions of the metatiles and nametable, as well as characteristics such as packing and majority are all configurable. The manual page:
Code can be found here: https://gitlab.com/segaloco/misc/-/blob ... s/mttont.c
This is a generalization of my previous metatile transform tool in that it now allows all these values to be configured. The default case is still reading an unpacked series of 256 metatiles in column-major order which is the default in Super Mario Bros. 3, but the number of CHR wide and high each metatile is can be changed, as can the number of metatiles wide to make the resulting output. This latter value times the CHR width of the tile is then the width of the "nametable" strings emitted, which can then be used with downstream tools to generate an image. More on this with the second tool. The packed nature of the metatile data is configurable, such that if tiles are stored one at a time rather than quadrant at a time, they can still be processed. Similarly the majority can be set, so if tiles are stored, for instance, in nw-ne-sw-se instead of nw-sw-ne-se, that could also be handled. More exotic storage schemes are not currently observed nor supported.
I haven't tested this on other scenarios yet beyond using the feature to limit the total number of tiles. A test I intend to do next is to use this to extract metatiles in other titles like the original Super Mario Bros. and Legend of Zelda since it is no longer specific to the SMB3 storage details.
Anywho, the second tool is SMB3 specific and then feeds into mttont and any tools it then hooks into. This tool, maptomt, converts an SMB3 map screen definition into a metatile table representing the tile assignments necessary to render that screen. In other words, it copies the entries from one metatile map to another, indexed by an array of index values. Since mttont already uses 2x2 metatiles and column major unpacked by default, I simply have maptomt recreate the map in this format. Map definitions in SMB3 are 16 metatiles wide by 9 metatiles tall, so they do not occupy the full 256-tile size of a metatile map. My last tool assumed this 256-tile size so was not suitable to processing the output of such a map decoder. Anywho, the end result from maptomt then going into mttont is a "nametable" that can be rendered through my existing nametable rendering pipeline. The end result is then a simple high-level script I wrote on top of this stuff:
This and the maptomt source live with the SMB3 disassembly for now since they are title-specific.
So in the above script, first I extract the metatile table and CHR bank needed, then the map data that is passed in (on stdin, this script itself is pipeable) is then passed to maptomt, which creates a metatile table from the indicies, that table is then mapped to nametable, SMB3 defaults, containing 144 total metatiles. That nametable is then mapped from the CHR bank into a series of 32-CHR-wide strings, totalling 576 CHR tiles in the resulting png. The result is left on stdout, I can pipe this into a viewer, post-processing, whatever.
This once again highlights the versatility of small tools rather than monolithic applications. I would certainly rewrite these pathways if they were for realtime rendering or production/mission critical applications, but the simplicity and modularity makes for easy modification and understanding problems. Furthermore I've been able to repeatedly lean on older tools I've written, repeatedly benefiting from the time invested in previous projects.
Anywho, attached are the resulting renderings from mapdump on a few of the map pages in SMB3. The palette leaves much to be desired, the weakest point in my rendering pipeline right now is the palette management. I don't select palettes until the chrtopng stage, at which point it's one stream and one palette, no means to say which tiles get what because the chrtopng step isn't aware of attributes or nametable information at all, that's been discarded by that point. The end result is you get precisely one palette and, if not specified on the command line, it's the one baked into chrtopng (which shh, it's Mario's original palette from SMB1).
Anywho, just figured I'd share, mostly because of the versatility of mttont. Even the map renderer is something that theoretically could be more generally focused towards the concern of "array of metatiles", indeed I kept most of the maptomt stuff in loops based on constants that could become variables and be fleshed out in the equations better. I did this on purpose, I want to see if I can revisit this tool for previewing course layouts and other BG matters besides just maps. I already have a general purpose display list viewer. Couple that with this and a course scenery viewer and I have most of the tools I need to do some serious BG editing my my disassembly with rapid feedback viewers, all using little more than a few CLI tools and some bare-bones C utilities. After all this time lamenting not having graphical tools on UNIX-likes for serious rom hacking stuff, I guess I'm fulfilling my own wants one by one.
First, mttont is a tool which converts a metatile table into a corresponding nametable. The dimensions of the metatiles and nametable, as well as characteristics such as packing and majority are all configurable. The manual page:
Code: Select all
MTTONT(1) General Commands Manual MTTONT(1)
NAME
mttont - Metatile to nametable filter
SYNOPSIS
mttont [ -w width ] [ -h height ] [ -t tiles ] [ -c columns ] [ -m ma‐
jority ] [ -pu ] [ metatile_list ]
DESCRIPTION
Mttont filters the provided metatile_list or standard input for a se‐
ries of metatile definitions. The -w and -h options may be used to
give the width and height of metatiles in terms of the underlying
graphic characters. Otherwise, the metatiles are assumed to consist of
a 2x2 square of character tiles.
The -m option allows selecting the array majority of the collection,
either column or row. Pass r to select row-major storage. All other
options select column-major storage. The -p and -u options indicate
whether the metatile definitions are packed (tile-per-table) or un‐
packed (position-per-table). The default is unpacked.
The -t option defines the total number of metatiles in the provided
tile map. The default is 256 metatiles. The -c option may be used to
define the number of columns of metatiles the resulting nametable
should contain. This controls the line-break behavior of string gener‐
ation. The default is 16 columns.
The -m option allows selecting the array majority of the collection,
either column or row. Pass r to select row-major storage. All other
options select column-major storage. The -p and -u options indicate
whether the metatile definitions are packed (tile-per-table) or un‐
packed (position-per-table). The default is unpacked.
SEE ALSO
chrtopng(1), nttochr(1)
BUGS
No bounds checking is performed. Incomplete rows of metatiles are not
currently handled.
MTTONT(1)
This is a generalization of my previous metatile transform tool in that it now allows all these values to be configured. The default case is still reading an unpacked series of 256 metatiles in column-major order which is the default in Super Mario Bros. 3, but the number of CHR wide and high each metatile is can be changed, as can the number of metatiles wide to make the resulting output. This latter value times the CHR width of the tile is then the width of the "nametable" strings emitted, which can then be used with downstream tools to generate an image. More on this with the second tool. The packed nature of the metatile data is configurable, such that if tiles are stored one at a time rather than quadrant at a time, they can still be processed. Similarly the majority can be set, so if tiles are stored, for instance, in nw-ne-sw-se instead of nw-sw-ne-se, that could also be handled. More exotic storage schemes are not currently observed nor supported.
I haven't tested this on other scenarios yet beyond using the feature to limit the total number of tiles. A test I intend to do next is to use this to extract metatiles in other titles like the original Super Mario Bros. and Legend of Zelda since it is no longer specific to the SMB3 storage details.
Anywho, the second tool is SMB3 specific and then feeds into mttont and any tools it then hooks into. This tool, maptomt, converts an SMB3 map screen definition into a metatile table representing the tile assignments necessary to render that screen. In other words, it copies the entries from one metatile map to another, indexed by an array of index values. Since mttont already uses 2x2 metatiles and column major unpacked by default, I simply have maptomt recreate the map in this format. Map definitions in SMB3 are 16 metatiles wide by 9 metatiles tall, so they do not occupy the full 256-tile size of a metatile map. My last tool assumed this 256-tile size so was not suitable to processing the output of such a map decoder. Anywho, the end result from maptomt then going into mttont is a "nametable" that can be rendered through my existing nametable rendering pipeline. The end result is then a simple high-level script I wrote on top of this stuff:
Code: Select all
#!/bin/sh
#
# mapdump - dump SMB3 map page as png image
#
# Copyright 2025 Matthew Gilmore
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if test -z "$TMPDIR"
then
TMPDIR=/tmp
fi
TILE_FILE="bin/prg0C.bin"
CHR_FILE="bin/chr02.bin"
TILE_BANK=`printf "%s/mt_bank.%s.bin" "$TMPDIR" "$$"`
CHR_BANK=`printf "%s/chr_bank.%s.bin" "$TMPDIR" "$$"`
dd if="$TILE_FILE" of="$TILE_BANK" bs=1024 count=1
dd if="$CHR_FILE" of="$CHR_BANK" bs=4096 skip=1
maptomt "$TILE_BANK" | mttont -t 144 | nttochr "$CHR_BANK" | chrtopng -r 32 -m 576
rm "$TILE_BANK" "$CHR_BANK"
So in the above script, first I extract the metatile table and CHR bank needed, then the map data that is passed in (on stdin, this script itself is pipeable) is then passed to maptomt, which creates a metatile table from the indicies, that table is then mapped to nametable, SMB3 defaults, containing 144 total metatiles. That nametable is then mapped from the CHR bank into a series of 32-CHR-wide strings, totalling 576 CHR tiles in the resulting png. The result is left on stdout, I can pipe this into a viewer, post-processing, whatever.
This once again highlights the versatility of small tools rather than monolithic applications. I would certainly rewrite these pathways if they were for realtime rendering or production/mission critical applications, but the simplicity and modularity makes for easy modification and understanding problems. Furthermore I've been able to repeatedly lean on older tools I've written, repeatedly benefiting from the time invested in previous projects.
Anywho, attached are the resulting renderings from mapdump on a few of the map pages in SMB3. The palette leaves much to be desired, the weakest point in my rendering pipeline right now is the palette management. I don't select palettes until the chrtopng stage, at which point it's one stream and one palette, no means to say which tiles get what because the chrtopng step isn't aware of attributes or nametable information at all, that's been discarded by that point. The end result is you get precisely one palette and, if not specified on the command line, it's the one baked into chrtopng (which shh, it's Mario's original palette from SMB1).
Anywho, just figured I'd share, mostly because of the versatility of mttont. Even the map renderer is something that theoretically could be more generally focused towards the concern of "array of metatiles", indeed I kept most of the maptomt stuff in loops based on constants that could become variables and be fleshed out in the equations better. I did this on purpose, I want to see if I can revisit this tool for previewing course layouts and other BG matters besides just maps. I already have a general purpose display list viewer. Couple that with this and a course scenery viewer and I have most of the tools I need to do some serious BG editing my my disassembly with rapid feedback viewers, all using little more than a few CLI tools and some bare-bones C utilities. After all this time lamenting not having graphical tools on UNIX-likes for serious rom hacking stuff, I guess I'm fulfilling my own wants one by one.
You do not have the required permissions to view the files attached to this post.