Alex Darby Profile picture
Aug 25, 2019 54 tweets 22 min read Read on X
Lately I've been messing with reverse engineering #panzerdragoonsaga file formats to get at the textures & models for a challenge. Texture (CGB) format wasn't too hard, after reading up some tech docs on saturn development it was fairly basic, except they had no meta data at all!
So while most image file formats contain data on width/height/bpp these were just pure texture data, not even a number of textures in the files nor any barriers. Just 4 bit per pixel indexed colour textures usually with 16 bit per colour (5bit per channel) palettes at the end.
CGB texture files for models usually are accompanied by an MCB file which I assumed was the mesh data... but I couldn't figure that out yet, no easy way of telling what relates to the textures in it without first knowing some textures to find. Catch 22!
So I started hacking together a tool for manually declaring the number of palettes, texturees and the texture sizes and have it automatically redistribute the pixel data as you define textures. Works rather well, especially when I figured out the byte alignment rules Image
So having figured out the texture format and a tool made to investigate them, next would be the mesh file format which is a bit more tricky. Until this point I had not managed to find any texture sizes in the MCB files nor where any MCB files were loaded in Yabause's memory.
I had used Yabause's debug tools extensively to poke around the texture data both to help figure things our and to confirm my thoughts, particularly about palette locations in the files. No MCB files in memory must mean the files are loaded & processed then deleted from memory.
Makes it difficult to see what they're used for, especially when your skills at following decompiled ASM are shockingly bad. However, following Yabause's source debugging a draw command lead me to this little gem: Width is stored as number of 8-pixel sequences! Image
No wonder I couldn't find the texture dimensions, I was looking for the wrong values! With this I could find a food hold. I found dimensions in the MCB, roughly the number I expect for the amount of quads in the two models in the file, because the Saturn used quads not triangles.
Eyeballing the data each dimension seems to have either 68 or 112 bytes of data accompanying it. The 44 bytes has 2 for the width/height, 16 sets of 4 bytes fixed point vertex coordinates and 6 other bytes, unsure about the 70 byte blocks, if they even are 70 byte blocks. Image
There's still a lot to figure out. As of yet I've not tested my guess about the vertex points or managed to find where the texture data position/offset is defined, what palette they use, not even the amount of quads or if there's animation data in there. Hope I figure it out!
In case anyone's wondering, I'm doing this purely for the challenge and out of love for the game and console. The Saturn is a mysterious machine and #panzerdragoon saga is a legendary game. It's fun to peek behind the curtains on both. Maybe I should write a blog about it all...
The #PanzerDragoon texture viewer has been progressing nicely. I have a large chunk of the MCB format figured out now, still lots to do but I've got the core sprite data figured out so I can now use that data to properly load up the matching CGB files. Not 100% solid but it works
I have the vertex positions loading along with the objects and sprites but I haven't figured out or found the hierachy and transform data yet so all the objects load at the origin so lots of work to do but coming along nicely.
Here's a higher quality version of that video in case anyone is interested:
Meshes from #PanzerDragoon Saga are now loading in Unity, complete with textures, some extra MCB bits figured out for flipping sprites on x/y axis. Still no hierarchy yet so here's a screenshot of the Basic Wing haphazardly pieced together by hand. Image
A big issue is texture distortion due to the quads now being made of triangles rather than actual quads. The original quad rendering with one vert moved closer to the next would have the texture squash equally all the way down, but triangles would have only one triangle distorted
The obvious solution would be to subdivide the quads, not sure if there's a better (possibly shader based) solution for it.
And here is the shockingly expensive solution to simulating how a 20+ yr old console renders quads on modern hardware: reverse bilinear interpolation as explained here reedbeta.com/blog/quadrilat… theres som artifacts on exteme deformations but overall it works well. #gamedev #unity3d
Turns out there was a slight deformation when the quad was aligned at certain angles due to the transform from 3D to 2D space skewing it. Added a second rotation to make the bottom edge always be horizontal and it's much better #panzerdragoon #screenshotsaturday L:before R:after Image
Having said that, I now say ARRRRGGGHHHH!! It seems certain angles between edges cause the UVs to offset. No idea why, which is wondeful. But in good news what I thought was the hierarchy data really is the hierarchy data so I now have that loading, still need transform data. Image
So it's a bit working now. Hierarchy figured out (ObjectOffset, FirstChildOffset, NextSiblingOffset), the transform block is what I thought, the position just needed multiplaying by 16, go figure. Still need to figure out the rotation. #PanzerDragoonSaga #segasaturn #unity3d ImageImage
fun fact: the world map with the small dragon in #PanzerDragoon Saga is scaled to the proper dragon model, it's not small: Image
fun fact #2: Atolm has a high res version of it's front with no rear half for the close ups of Azel riding it and a low res version for the full views. Azel on the low res version is a separate mesh but on the high res she is part of the mesh. #PanzerDragoon #PanzerDragoonSaga ImageImage
Not a fun fact, but fun none the less: All (possibly?) the characters from the on-foot sections. Duplicates are, I assume, from different scenes, probably with different animations. #PanzerDragoon #PanzerDragoonSaga #retrogaming #segasaturn Image
Fun fact #4: the mounted rider models are obscenely #lowpoly. The arms are just quads and the legs are an odd M shaped mesh rather than individual legs. Such a great job by the artist it's impossible to tell in game #PanzerDragoon #segasaturn #retrogaming Image
It seems I broke the thread here, it carries on here:
Taking a break from shiny pics for a moment, I've basically got the Hierarchy, Mesh and Transform blocks of the MCB format figured out. Rotations are still kicking my arse, I still have a load of sprite flags to figure out and some unknown sprite data possibly for render settings
Particularly there's a block of data after a sprite definition, the length of which seems dependant on a flag. Another couple of flags seem to relate to whether a texture & palette are used. I need to dig into the VDP1 docs again to see if I can match up render modes etc.
I still have the animation block to figure out, which is obviously quite large even for a single animation of a small mesh. Then there's about 3 other block types which I haven't even looked at yet, no idea what they might relate to.
This is all not even mentioning the "level" MCB files which don't have transform blocks, pretty much just tons of meshes. They are more than likely positioned by the PRG code which is going to be a total pain in the arse and need decompiling.
Probably going to look into figuring out the VDP2 background textures next, I've got a hunch they're either the PNB or SCB files, could be both. SCB for scrolling, PNB for positioned maybe? Just guessing at the moment.
A little update on the sprite flags, out of the 6 flag bytes, they actually seem to be 3 int16 with the 2nd being a CMDCTRL command the and the 3rd being a CMDPMOD command. Texture flipping, mesh flag, colour mode, draw type all match. Unsure of the first int16 still though.
In general the "sprite definitions" as I called them seem to be a rough command table, only referencing addresses relative to the files rather than the memory and no CMDXA-CMDYD, also doesn't seem to be a CMDLINK. Given me a good jump on figuring things out.
Tangent: I've used MENUEN.PRG to match MCB's to enemies as their names don't match, but I just noticed the attack names have a matching pair of int32's after the text, bits 0 to 3 the 2nd are the attack directions, which is neat. Image
0x01 = forward
0x02 = backward
0x04 = left
0x08 = right
0x16 = variable
Turns out the first int32 is the memory offset of the attack name string. PRG's are always loaded into exactly the same location so they can reference exactly memory locations.
An issue with figuring out how enviroments are constructed in their PRG's is the MCB's aren't kept in memory, they seem to be processed on load and then unloaded, so I've yet to be able to find where the command tables are then stored and how the PRG references them.
Aha, MCB's are kept in memory, the data in them is just manipulated to make sense in memory (e.g. file offsets transformed into memory addresses) which is why it's been difficult to find them. FLD_A3.MCB lives at 0x00029c6a0. I'm getting somewhere!
I had been wondering about both the extra sprite data and the fact some objects have different colours in-game than their textures. Turns our that's what the extra data is for! Cue using yabause's memory editor to make Hopper's textures white and fiddling with the data! ImageImage
I haven't figured out the data exactly, there seems to be more going on than just vertex colours. Those screens show the colour changing based on the angle/distance which is also controlled by the data (12 bytes per vertex). Is this using Saturn's Gouraud shading system?
So it turns out the the dragons are split between Field (Dragon#.mcb), Camp (DragonC#.mcb) and Morph (DragonM#.mcb). The field versions have animation sets; the camp versions don't and only account for forms 0 through 4; the morph has the stat variants of 1 through 5 and 7 as so: Image
I might be a bit insane here. Got fed up with poking values into Yabause's memory through HXD to experiment with what the vertex colour data actually does, so I have experimented with a writing a memory hook in Unity so I can have some realtime feedback in Yabause.
So morphing seems to be working okay. Some issues with the texture mapping need to be sorted (see the tail on the Defence form) but it's mostly a fine first pass at it. #PanzerDragoon
And here's a little fly by of all 6 morphable dragons performing the same morphs at the same time. #PanzerDragoon
Meshes are now being bound to skeletons using their original objects, so I added support to blend skeleton transforms and bindposes so the mesh won't screw up when animating. I've also improved the texture blending and blendshape weighting.
Animations are working nicely now. Next to animated while morphing. Dunno whether to generate multiple animationclips for each morph or do something like later blending for the different bindposes. #panzerdragoon #gamedev #segasaturn
Taking a small break from the meshes/animations/etc. to figure out the SCB files. 8x8 tiles, arranged into 1x1/2x2 patterns, which are put into 32x32/64x64 pages then 1x1/2x1/2x2 Planes 2x2/4x4 Maps. This palette is 256 colours and baked into the TITLEE.PRG code, fun times! Image
And here's the semi-constructed #PanzerDragoon Saga title screen. I think I've figured out what the PNB files are for, they seem to dfine which Character Patterns from the SCB file are used to construct the Planes, so I need to figure out how to use them. #SegaSaturn #Gamedev Image
So scene PRG files are at 0x06054000 in saturn RAM. If you find SCB and PNB filenames in them, offset the address by the PRG RAM location, search for that value in the PRG you should find next to it the VDP2 RAM location of the SCB/PNB data near by, along with the palette address
That's the theory, anyway. PNB files seem to have values (multiplied by 0x80?) offsetting from the start of VDP2 RAM to the start of the referenced character pattern. Obviously hard to get the right data out of the SCB unless you know it's RAM location. #SegaSaturn is fun! 🙃
Also VDP2 RAM starts at 0x05E00000 so obviously the PRG files store the pointers to them as 0x25E00000 because why not? I assume the 2 is some meta data of some kind.
finally got around to playing with this again. Scroll textures are now loading in Unity, hooray! At least these five are. Every one requires finding the filename in the related PRG and using that location to find related data for palette, etc. Not a quick and easy process! Image
I need to create descriptors for each SCB/PNB combo which tells my scripts the necessart data. There are 174 SCB files! Tons of work to get them all loading correctly.
Experimenting with subdividing the mesh instead of using my janky bilinear UV interpolation shader. I think the results are quite good, though still not entirely accurate to the Saturn. Also 160k triangles for a Saturn mesh at 2 triangles per texel might be a bit overkill haha
Of course, the major issue with naively subdividing like this is it brings back the whole concave quad issue which is less simple than just spinning the internal edge as I previously did for the bilinear UV interpolation. Not sure how exactly best to approach this edgecase!

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with Alex Darby

Alex Darby Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us!

:(