Mario Kart 64 Hacking General Discussion « 1 ... 5 6 7 8 9 ... 17 »
Users browsing this thread: 1 Guest(s)

Holy cow, things are heating up! I guess I'll step up my game a bit too.

I've thrown together the code for rendering F3DEX (the graphics code used in Mario Kart, if I remember right) and a small tool to decode/export the data blocks defined in the Course Data table. Once I figure out how the data is organized and referenced during rendering (pretty much already done by shygoo's level viewer), I should be able to have the levels rendering.

From there, editing will need some kind of export/import 3d file function to allow editing the scenery in an external program. I'll also need to allow moving/rotating/scaling 3d object groups in the OpenGL window to finetune these things.

For creating new levels, I imagine I should be able to append new data to the end of the ROM and then reference that data in the Course Data table. Should be easier than resizing the existing data blocks and then editing the various pointer references throughout the rom that would get offset from that. The tough part will be getting an MIO0 encoder working, but Rena seems to have some assembly code that does just that, so it might not be too bad.

(23-04-2016, 07:54 PM)krom Wrote: Using the help from this forum, I decided to make a Mario Kart 64 hack called 1500cc Edition.

(23-04-2016, 07:54 PM)krom Wrote:
Anyway, I'll try to find out what all these unknown sections inbetween known Kart properties are, and fill in the blanks.
I'll update here with anything new I find out, thanks for all the help to get me started =D

Thanks krom, MK64 1500cc Edition is great! Thanks for publishing all of your notes, patches, and N64 examples.

(26-04-2016, 03:12 PM)mib_f8sm9c Wrote:
I've thrown together the code for rendering F3DEX (the graphics code used in Mario Kart, if I remember right)

(02-02-2015, 08:37 PM)shygoo Wrote:
 "RSP Gfx ucode F3DEX 0.95 Yoshitaka Yasumoto Nintendo."

Seems correct to me Smile

(26-04-2016, 03:12 PM)mib_f8sm9c Wrote:
From there, editing will need some kind of export/import 3d file function to allow editing the scenery in an external program. I'll also need to allow moving/rotating/scaling 3d object groups in the OpenGL window to finetune these things.

For reference SM64 hacking involves two tools, a level importer to import levels and collision data that was modeled in an external program (like Sketchup or Blender) and an editor to add and manipulate objects. This is also the approach I'm taking in the Blast Corps Editor, albeit within one tool. As far as implementation goes (and you may already be going down this path), I recommend just implementing a viewer first, then moving on to modifying the objects' properties through controls, then look at picking and moving within the OpenGL window.

(26-04-2016, 03:12 PM)mib_f8sm9c Wrote:
For creating new levels, I imagine I should be able to append new data to the end of the ROM and then reference that data in the Course Data table. Should be easier than resizing the existing data blocks and then editing the various pointer references throughout the rom that would get offset from that.

I agree with sticking new levels at the end of the ROM and possibly bumping the ROM size up to 16MB. Again, for reference, SM64 Level Importer stores all new levels at the end of the ROM statically allocating about 1.5MB for each level, thus requiring a 64MB ROM. Blast Corps Editor also saves all the new level data at the end of the ROM, but stores the levels back-to-back so it may move all the levels around each save. Pick your poison.

(26-04-2016, 03:12 PM)mib_f8sm9c Wrote:
The tough part will be getting an MIO0 encoder working

This is actually going to be easier than you think. A quick workaround is using fully uncompressed block of data with a fake MIO0 header. The SM64 hacking tools do this. I also have MIO0 decoder and encoder implemented in C (think there may be one or two more that I've come across in the past, but can't locate at the moment).  It either runs as a standalone .exe (which is included with n64split) or as a library. I could also port it to C# if it would help. A third option is to modify the assembly code to expect raw data blocks instead of MIO0 blocks.
(This post was last modified: 23-06-2017, 08:05 PM by queueRAM. Edit Reason: wiki links )

(26-04-2016, 08:53 PM)queueRAM Wrote: I recommend just implementing a viewer first, then moving on to modifying the objects' properties through controls, then look at picking and moving within the OpenGL window.

Yep, that's the plan. I have a viewer already working with F3DZEX, so make it work with F3DEX (and possibly this mysterious compressed display list microcode). Then once that's done, I'll probably make sure objects are getting split up correctly and then work on picking/manipulation of objects/groups, and then export/import.

(26-04-2016, 08:53 PM)queueRAM Wrote:
A quick workaround is using fully uncompressed block of data with a fake MIO0 header.

Whaaaaaaaaat? That's crazy, but looking at the MIO0 info it makes a lot of sense! Looks like I'll be taking the hit to memory size and use fake headers for now to speed up the process, but later on I might fashion a MIO0 encoder myself. It'd be kinda fun to try to optimize one. Thanks for the heads up, you saved me a few hours of work.
(This post was last modified: 23-06-2017, 08:04 PM by queueRAM. Edit Reason: wiki links )

I've been looking into the TKMK00 compressed texture format used to store some of the textures in the title screen and menus. I have written a TKMK00 decoder in C that follows the same procedure used in the MK64 ROM. It's a bit more convoluted than LZ compression like MIO0, and due to pixel averaging, I suspect the data is lossy. I am documenting all this information on the TKMK00 wiki page and in the source code, but also wanted to make a post here in case it helps anyone. I don't yet know enough to make an encoder.

​Win32 build of tkmk00test 0.1: you can use this command line tool to extract the TKMK00 data from a ROM and dump the raw data returned in its A1 and A2 pointers (see decoder parameters below) and the resulting texture as PNG. Only tested in wine on Linux.
​Usage: tkmk00test [-d DIR] [-o OFFSET] [-v] FILE

  Optional arguments:
   -a ALPHA     RGBA color to clear alpha bit [usually 0x0001 or 0x00BE] (default: 0x0001)
   -d DIR       output directory (default: FILE.tkmk00)
   -o OFFSET    offset of TKMK00 data in FILE (default: all)
   -v           verbose progress output
  File arguments:
   FILE        input ROM file


TKMK00 Decoder
As shygoo has identified in his hacking notes, the TKMK00 decoder function is located at 800405D0/0411D0 in the MK64 ROM. It takes 4 parameters:
A0 - pointer to TKMK00 compressed data
A1 - pointer to output buffer (1 byte per pixel)
A2 - pointer to RGBA16 texture output buffer (2 bytes per pixel)
A3 - U16 RGBA mask to clear alpha bit for in output

TKMK00 Header
OffsetLengthDescription
0x000x6File signature: ASCII "TKMK00"
0x060x1Bit mask used for offsets at 0xC
0x070x1Unused? (always 0x0F)
0x080x2Output texture width in pixels
0x0A0x2Output texture height in pixels
0x0C0x20Array of 8 offsets, used with bitmask at 0x06

Example Textures
[Image: 7FA3C0.png]

[Image: 7FC8C0.png]

[Image: 7FBCC0.png]

[Image: 8035C0.png]

[Image: 805FC0.png]

[Image: 8086C0.png]

[Image: 8094C0.png]

[Image: 8162C0.png]
(This post was last modified: 23-06-2017, 08:04 PM by queueRAM. Edit Reason: wiki links )

Nice job on the TKMK00 format! I'm sure once we get more headway on an editor, being able to edit menu/splash screen items will be a priority.

On my end, I tried taking some level data, appending it to the end of the rom and changing the Course Data table to reference that new location. It worked for the items/display list block, but not for the vertex block. Not sure why yet. Other than that, put a fair amount of work into decompressing/rendering the F3DEX data, here's where I'm at right now:

[Image: 2WkMnkU.png]

I've been super busy these weeks, but I might have a good opportunity coming up to make some more progress on this on the weekend.

EDIT:

So after another hour or two of fixing bugs, here's where I'm at:

[Image: wJ5Qdw7.png]

A lot better on the texture quality though still some issues (I NEVER got that road working with Shygoo's viewer either), mostly with those signs & transparencies. My next goals are:

1. Clean up my code (I made a sloppy joe out of my codebase for this)
2. Fix serialization. (aka make it so the data that is extracted from the ROM can be re-inserted into the ROM and have it work correctly)
3. Make a very simple object manipulator (for now, it will move groups of related vertices together to move objects on the level) and test it in an emulator.
4. Push it online to Github so I don't lose my code if this computer dies : P

After all that, I think I'll start learning how to manipulate the course object listing (so course path, item locations, etc.), and then go back to trying to get all the course data blocks to be loaded from anywhere (to allow arbitrary editing of any/new level data).
(This post was last modified: 07-05-2016, 04:40 AM by mib_f8sm9c.)

Lots of exciting things going on in here. For the work on the textures, queueRAM, do you think you're close to being able to replace textures in the ROM? I would love to replace DK with MagiKoopa as was originally intended Smile

I was working on doing this with the high resolution emulator plugin but it treated the images as if there were thousands (due to small changes in lighting and animations that were not actually stored as separate images in the game). I could create appropriate resolution images for the model I created.

https://www.youtube.com/watch?v=8nE_LOM2C0E

(06-05-2016, 05:46 PM)mib_f8sm9c Wrote: On my end, I tried taking some level data, appending it to the end of the rom and changing the Course Data table to reference that new location. It worked for the items/display list block, but not for the vertex block. Not sure why yet. Other than that, put a fair amount of work into decompressing/rendering the F3DEX data, here's where I'm at right now:


That is great progress on the F3DEX decompression and rendering! Glad to hear you are also making headway with manipulating the game data. I'll also play around with the course data on my end and see if I can't help figure out what is expected with the vertex data to allow it to be relocated.

EDIT: I tried out relocating all the level data referenced by the table and did not have any issues. See bass assembly attachment for my tests.  See my example assembly files in this post below.

(06-05-2016, 08:16 PM)Drew Weatherton Wrote:
Lots of exciting things going on in here. For the work on the textures, queueRAM, do you think you're close to being able to replace textures in the ROM? I would love to replace DK with MagiKoopa as was originally intended Smile


I am not close at all to being able to replace these TKMK00 textures in the ROM. I am starting to think that a better approach might be to convert the textures to either raw RGBA16 or compress with MIO0 and modify the code to load them accordingly. I'll experiment with this a little and report back.  That said, I welcome any help figuring out the TKMK00 format Smile
(This post was last modified: 16-05-2016, 03:48 AM by queueRAM. Edit Reason: Add bass patch to relocate table data )

Thanks for checking that out for me. I realize now the issue I was having was because the Vertex and Texture data are stored together, but only the Vertex data is encoded. I was also testing out decoding, then recoding the MIO0 blocks at the same time, so in reality I was throwing away the Texture data when I tried to store the data at the end of the ROM.

Since you helped me with my problem, I'll help you out with yours. I've been looking at the TKMK00 format and trying to implement it myself, it's pretty fun! I found out one good thing: the in_ptr points at the data directly after the TKMK00 header, and there are those 8 other pointers that point to data further along in the file, right? Well, I haven't found out what those other pointers do, but the in_ptr format is a series of variable-size bit commands/variables. They are read using the proc_80040A60 function. The v1 variable stores the size of the command in bits to read out of the in_ptr, and the output is stored in v0. In fact, all proc_80040A60 does is read that value, then shift further down the some_flag variable pulled from in_ptr, and then switch to the next some_flags in in_ptr when it reaches the end of the some_flags.

You can see it being used to determine whether to create a new rgba value or to use an existing one (a yes/no decision, so it only needs one bit) at line 105 in your tkmk00.c file. So I'm guessing that the file consists of a block of commands (starting at 0x2C or 0x30) which tell the decoder how to process the data that's stored in the other pointers. It'll be a lot of work to figure it out, but it's really fun to investigate thankfully. I'll keep looking at it as long as it keeps holding my interest. I'll at least get the C# converted TKMK00 decoder working.

EDIT: Just got the decoder working, I haven't thrown the output data into an image yet but it appears to be matching up to what your program creates. I should be able to step through the process and figure things out a little better now.

EDIT EDIT: I'm getting closer and closer to having it figured out. I believe it is a form of Huffman coding, where the buffer80_u16 and bufferFE_u16 buffers represent the Huffman tree, the proc_80040C54 traverses the tree using a bit to signify left or right that is retrieved through the proc_80040A60. I still need to see how the rgba value is determined in proc_80040C94 and how the Huffman tree is created in proc_80040BC0, but other than that I have a good conception of it.
(This post was last modified: 08-05-2016, 11:30 PM by mib_f8sm9c.)

(07-05-2016, 10:18 PM)mib_f8sm9c Wrote: Thanks for checking that out for me. I realize now the issue I was having was because the Vertex and Texture data are stored together, but only the Vertex data is encoded. I was also testing out decoding, then recoding the MIO0 blocks at the same time, so in reality I was throwing away the Texture data when I tried to store the data at the end of the ROM.


Cool, glad you were able to figure it out. I'm actually going to keep working on this assembly file, configuring it to work with the output of shygoo's mk64extract tool in case we want to use it again in  the future to run some tests. I'll probably toss the M64 files and a texture in there as well.

(07-05-2016, 10:18 PM)mib_f8sm9c Wrote:
Since you helped me with my problem, I'll help you out with yours. I've been looking at the TKMK00 format and trying to implement it myself, it's pretty fun! I found out one good thing: the in_ptr points at the data directly after the TKMK00 header, and there are those 8 other pointers that point to data further along in the file, right? Well, I haven't found out what those other pointers do, but the in_ptr format is a series of variable-size bit commands/variables. They are read using the proc_80040A60 function. The v1 variable stores the size of the command in bits to read out of the in_ptr, and the output is stored in v0. In fact, all proc_80040A60 does is read that value, then shift further down the some_flag variable pulled from in_ptr, and then switch to the next some_flags in in_ptr when it reaches the end of the some_flags.

You can see it being used to determine whether to create a new rgba value or to use an existing one (a yes/no decision, so it only needs one bit) at line 105 in your tkmk00.c file. So I'm guessing that the file consists of a block of commands (starting at 0x2C or 0x30) which tell the decoder how to process the data that's stored in the other pointers. It'll be a lot of work to figure it out, but it's really fun to investigate thankfully. I'll keep looking at it as long as it keeps holding my interest. I'll at least get the C# converted TKMK00 decoder working.

EDIT: Just got the decoder working, I haven't thrown the output data into an image yet but it appears to be matching up to what your program creates. I should be able to step through the process and figure things out a little better now.

EDIT EDIT: I'm getting closer and closer to having it figured out. I believe it is a form of Huffman coding, where the buffer80_u16 and bufferFE_u16 buffers represent the Huffman tree,  the proc_80040C54 traverses the tree using a bit to signify left or right that is retrieved through the proc_80040A60. I still need to see how the rgba value is determined in proc_80040C94 and how the Huffman tree is created in proc_80040BC0, but other than that I have a good conception of it.


Oh, no! I didn't mean to derail your development! Tongue Seems like you're enjoying TKMK00 though and you've gained some good insight into the decoder. I can mostly follow your description of the code. Regarding proc_80040C94, all I've figured out is that it reads one color at a time (in GRB order), storing result in t9. Looking at the bitmasks, I think i have the variables 'red' and 'green' mixed up in the code.

Do you have any idea what data is output into the A1 buffer? It contains 1 byte per pixel with really low values, but sort of take shape of the image. Below is a graphical representation of the A1 from the title screen. I'm not seeing the pointer in A1 used beyond the function call to tkmk00decode/800405D0, so there is a chance that it is just a temporary buffer to use during the decoding process.
[Image: ag36Xd3.png]

I'll put a small summary here but I'll try to keep it short, since it's somewhat complicated and I'm down to one last thing before I have a functional understanding of the whole format. I'll write up a full report later on how it all works together, probably on the wiki. Thankfully, as assembly tends to make things, it's a lot less impossible than it first looks. And no worries about derailing development, I do romhacking because it's fun, and figuring out this compression format has been an absolute blast. It's my form of escapism.

There are three sections to the data. I've labelled them temporarily as Header, Master Commands, and Channel Commands. Header has already been fully explained, and the Master/Channel Commands are similar with a minor difference in functionality. The Master Command section starts directly after the header, at 0x2C. The Channel Commands are the 8 different pointers in the header, that point to different groups of data.

The command sections are all variable-length groups of bits. Often times single bits will be pulled from them, though it can change to a larger number of bits when necessary. The Master Commands handle things like deciding if a previous or already calculated rgba value will be used, and whether to copy the current rgba value down a row, among other tasks. The Channel Command sections are a bit more complicated. The first one holds all the data that is used to created the Huffman tree that holds 5-bit values that represent one color from the RGB spectrum. The rest all hold data that describes what RGB values to grab from the Huffman table. There's also some code for the Channel Commands that can define repeated colors, which is probably where the compression comes in at.

So this brings us to the A1 buffer. For clarity, the A1 buffer is NOT an output, it's a processing aide to determine pixels that are in areas with lots of changing colors. Every time that a pixel's color changes from a previous pixel, in the A1 buffer it adds 1 to the pixel to the right, the pixel that's 2 pixels right, the pixel down, the pixel down & left, the pixel down & right, and the pixel down twice. 6 in total. Effectively this map shows you areas that are heavy in changing colors, and places that are effectively the same color.

The trick to this map is that the values will vary between 0 (no nearby color changes) and 6 (tons of color changes around it). These values then have 1 added to them and are used to determine which channel (1-7) is used when referencing the next color information. This means that each Channel Command grouping is based on how much color changing is going on around the specified pixel.

This has a strong advantage in that the early channels (1, 2, possibly 3) have a HIGH chance of repeating colors (and repeating colors often means that you can save somewhere around 5 bits per pixel). It's a pretty killer idea.

I've replaced 4 out of 5 of the proc_XXXX functions with my own handwritten ones, and they still function, so I'm close to getting the format down. The last thing I have is that when calculating a new pixel color, it grabs a 5-bit value from the Huffman tree (which I understand), but then it takes the average of the pixel up one and the pixel left one, and then does some special comparison between that and the new pixel in proc_80040C94. It seems sometimes it combines the two, and others it just uses the new one. I need to do some use-cases to understand the reasoning behind it, but it's the last big thing.

That being said, making a compressor sounds pretty complex. Deciding how to make the Hoffman tree from the start sounds like an interesting challenge, and it'd probably be hard to make a compressor that'd out-perform the one made for this. I'm having fun though.

EDIT: Okay, I figured out how the new pixel color is calculated from the predicted values & the output from the Channel Commands. I think I have it all figured out. I'll write up my own non-assembly-style decompressor, comment the heck out of it & probably upload it here, and then maybe take a hack at a compressor. But not today. I need a break, I lost my whole weekend to TKMK.

EDIT EDIT: Working C# code for a decoder:
http://pastebin.com/Zyx0xCdz
(This post was last modified: 10-05-2016, 04:19 PM by mib_f8sm9c. Edit Reason: Sharing code )

Mario Kart 64 Hacking General Discussion « 1 ... 5 6 7 8 9 ... 17 »
Users browsing this thread: 1 Guest(s)