The Odd One Out: Sonic Chronicles (2/3)

(This is part 2 of 3 of my report about the progress on Sonic Chronicles. Part 1 can be found here. Part 3 can be found here.)

After having implemented readers for the common BioWare formats, I turned to the graphics formats. They’re, for the most part, stock Nintendo DS formats, which means I could build upon detective work from the Nintendo modding scene. I have to thank three people in particular: Martin Korth, of NO$GBA fame, whose GBATEK documentation is invaluable, lowlines who figured out much of the gory details of Nintendo’s formats and pleoNeX, whose GPLv3-licensed tool Tinke provided the base on which I implemented my code.

SMALL

When I looked over the files inside the Sonic Chronicles archives, I noticed a peculiar thing. There’s a lot of files with names ending in “.small”. I suspected a compression scheme, especially after examining the files in a hexeditor. And sure enough, there are several compression algorithms provided by the Nintendo DS firmware. The one used by Sonic Chronicles is an LZSS variant, which can be decompressed using barubary’s MIT-licensed dsdecmp tool (GitHub mirror). I pulled the decompressor into xoreos.

NSBTX

The first graphics format in Sonic Chronicles I inspected was NSBTX. NSBTX files are texture; or rather: archives of several textures used by a single model each. Implementing the reading of these was simple enough, and I added a small program to our tools collection that can “extract” them into TGAs.

HillHill BoarBoar TailsTails

NFTR

Next up, I wanted to see the fonts, NFTR, used in the game. They’re bitmap fonts, with each glyph an image. The image can be 1-bit black and white, or it can be greyscale for anti-aliasing, shadowing or outlining purposes. Additionally, there’s mapping tables to translate a code point in a certain encoding (UTF-16, UTF-32, CP1252 or Shift-JIS) into the index of the glyph it represents.

There was a bit of trial and error involved, as the documentation and existing FLOSS projects to display the fonts weren’t quite correct in certain details (which might not even be their fault; Nintendo likes to subtly change formats between firmware versions). But, before long, I could print arbitrary strings in these fonts in xoreos.

Font drawing testFont drawing test

NBFS/NBFP

Sonic Chronicles comes with a few NBFS files, which is a dead-simple raw format: 8-bit paletted image data, with the palette (in 16-bit RGB555 values) in an NBFP file of the same name. They’re mostly used for images spanning a whole Nintendo DS screen.

Mini mapMini map Conversation cardConversation card

NCGR/NCLR

The main image format used in Sonic Chronicles, however, was still missing: NCGR. As I went along implementing a reader, I learned these ugly facts:

  • The palettes are in separate NCLR files that are shared among NCGR
  • Most of the images are made up of several NCGR files, arranged on a grid
  • The NCGR image data itself is made up of 8x8 pixel tiles

Essentially, this image of Sonic is divided into these parts:

SonicSonic NCGR cellsNCGR cells NCGR tilesNCGR tiles

This all makes assembling the final image a bit…ugly. But hey, I made it work in the end.

…except for one thing: a few of the NCGR files don’t specify their width and height. By fiddling with the values a bit, I managed to find these values manually, but the resulting image looks off: it’s as if the image is supposed to be rearranged afterwards, different pieces drawn at different places. Each of those file also has an NCER file with the same name. I assume that means information on how to draw these NCGR are within the NCER. A thing for the TODO pile.

NSBMD

I then went for the big one: the 3D model format NSBMD. This involved a lot of fiddling, guessing and trial-and-error, as the documentation of these formats is sparse, and oftentimes wrong.

Conceptually, an NSBMD file consists of these parts:

  • Bones
  • Bone commands
  • Materials
  • Polygons
  • Polygon commands

A bone consists of a name and a transformation that displaces it from its (at that point unknown) parent bone. They are stored as a flat list. The bone commands then specify how the bones connect together. And they give each bone a location in the Nintendo DS’s matrix stack (a list of transformation matrices containing the absolute transformation of each bone at the time of rendering).

Broken boar skeletonBroken boar skeleton Getting there...Getting there… Correct boar skeletonCorrect boar skeleton

The material contains the texture name (which reference textures inside the NSBTX with the same name as the NSBMD), and a few properties.

Each polygon can use a single material, and contains a list of polygon commands. These polygon commands produce the actual geometry. They set color, normal and texture coordinates, and generate vertices. They also manipulate the matrix stack, specifically replacing the working matrix with the matrix from the stack position of certain bones. In essence, this bases the vertices on the position of the bone.

No.No. Boar with holesBoar with holes Correct boarCorrect boar

While the Nintendo DS interprets the polygon commands on-the-fly while rendering, and while they can be nearly directly converted to OpenGL-1.2-era glBegin()/glEnd() blocks, this is not really want we want to do. So instead, we, while loading, interpret the polygon commands into an intermediate structure.

SonicSonic AmyAmy TailsTails

The result is a relatively massive loader for these files, and that doesn’t yet include support for animations.

One interesting anecdote: the Nintendo DS doesn’t use floating-point numbers to represent real numbers, but various formats of fixed-point numbers. Those are found extensively in the NSBMD files. But when I implemented the GFF4 format earlier (see part 1 of my Sonic Chronicles progress report), I found, in the GFF4 files used by Sonic Chronicles, a field type not described in the Dragon Age toolset wiki. Turns out, those are Nintendo DS fixed-point numbers!

CBGT/PAL and CDPTH

With those pesky models out of the way, I was ready to show the areas, right? Wrong. There’s yet another graphics format in Sonic Chronicles: CBGT, used for the area background images.

However, CBGT isn’t a Nintendo format. No, it’s one of BioWare’s creation. It does, though, take inspiration from the Nintendo DS formats. It consists of blocks of 64x64 pixels, each compressed using the LZSS algorithm found in SMALL files, and each block divided into 8x8 pixel tiles. PAL files of the same name carry palettes, with each CBGT able to use a different palette within the PAL.

Since I already knew how to puzzle together those cells and tiles from the NCGR format, getting the image itself was not a problem. But I was at a loss where to get the dimensions of the image from, and how to distribute the palettes onto the cells. I figured out an algorithm for the latter, that worked for nearly all images, but the outliers still annoyed me. Then it hit me: for each CBGT/PAL pair, there’s a third file: a 2DA. And that one contains the information which cell uses which palette, neatly organized in a 2D table exactly how the cells are arranged in the final image. This, of course, is enough to calculate the final image dimensions as well.

Wrong palette distributionWrong palette distribution Correct palette distributionCorrect palette distribution

I also found a fourth file for nearly each CBGT/PAL/2DA tuple: a CDPTH. Arranged in a similar fashion to the CBGT, it contains 16-bit depth information for each area background. This is used to let certain background pieces draw over the 3D models in the game, when they should appear behind something.

Depth valuesDepth values

Now I was ready to implement actual Sonic Chronicles stuff. I’ll describe that in part 3.