Original text by mkdasher
NKM Data Structure
• Header: Based on checksums. It shows the number of parts and the structure of the .nkm file.
• Object Positions (OBJI): Contains the Object Position Data and Object settings (if that object can have some variations). The Object Positions Data starts with "OBJI". They are linked with Routes if those objects has routes to follow.
• Routes (PATH/POIT): Routes can be used for more than one thing, mainly Object Routes and Camera Routes. Routes are divided in Sections, "PATH" will determine those Sections and "POIT" will determine the Routes Positions.
• "STAG": I don't really know too much about it. I think it has to do with start gate info. I can just say that it determines the number of laps, and I think the alignment of the racers can be changed here but I don't know how.
• Start Position (KTPS): The position where you start the race.
• Respawn Positions (KTPJ): Determines the respawn positions in the track after falling down.
• "KTP2": The position you have to pass to finish a lap.
• Cannon Position (KTPC): Determines the Cannon exit (when the cannon stops being effective). Only used in Waluigi Pinball, Airship Fortress and Pipe Plaza battle arena. The beginning of the cannon can't be modified here since its data is contained in the .kcl file.
• "KTPM": Unused, it never contains any information.
• Checkpoints (CPOI/CPAT): line segments bounded by two end points. Lap count will happen if the kart has passed between those end points (I believe you can just skip one in a row). "CPOI" will determine those end points and they are linked with the Respawn Positions. "CPAT" will determine the Checkpoint Sections.
• Item Routes (IPOI/IPAT): Determines the item routes. These routes are made with points. "IPOI" determines those points and "IPAT" determines the Item Routes Sections.
• Enemy Routes (EPOI/EPAT): Determines the routes that the CPU karts will follow. These routes are made with points. "EPOI" determines those points and "EPAT" determines the Enemy Routes Sections.
• AREA: I can't completely explain how AREA works, but in most of the cases it's related to cameras. It is also made with points, and when a kart gets near an AREA point in replay mode, it calls upon the camera that is assigned to that AREA point., but this just happens with some of the AREA points, there are some other points that have different purposes I don't know.
• CAME: Camera position. There are three kind of cameras, the ones in the track intro, the ones in the replay and the one after you finish the 3 laps that looks at your character. They are linked with the Routes. Routes would be the camera itself and "CAME" would be where the camera is looking.
(Note: Final tracks has this structure inside the .nkm file. However, Beta tracks doesn't use all of them).
File format / Endianness
It's important to understand how the data is stored in the .nkm file.
Endianness is the ordering of individually addressable sub-units (words, bytes, or even bits) within a longer data word stored in external memory. The .nkm file uses the little endian, which means that The least significant byte is at the lowest address and the other bytes follow in increasing order of significance, in other words, you need to read each 4 bytes from right to left.
How to transform byte data into values
This transformation will be used mainly to know the coordinates of one point, angles or some extra information that can be understood in the Decimal system:
To explain the transformation, I'll use the Sky Garden .nkm file as an example (old_sky_agb).
First of all, each data is stored in 4 bytes, so when we're applying the transformation we will need to take 4 bytes. I'll use two examples:
(We'll transform 00 A0 41 00 into a Decimal value)
1.First of all, we need to analyse the 4-byte, and we can find 2 possibilities:
The last byte is "00" or "01". This means that the value will be positive.
The last byte is "FF" or "FE". This means that the value will be negative.
In this case we have a positive value, so we don't need to change anything.
2.As I said before, the .nkm file has a little endian format, so we will need to read the 4-byte from right to left.
(We should read it like this: (00 41 A0 00)).
3.After that, convert it from hexadecimal to decimal:
(00 41 A0 00) 4300800
4.Then, divide the value (4300800) by 4096
4300800 / 4096 = 1050
The real value for 00 A0 41 00 is 1050.
Now we'll see an example if the value is negative. I'll use this 4-byte as an example:
(We'll transform 00 B0 96 FF into a Decimal value)
1.Check the last byte and we notice it's "FF", that means it'll be negative.
2.Read the 4-byte from right to left (FF 96 B0 00).
3.This is a negative value. Every time there is a negative value we may do this:
100000000 - value = result (Hexadecimal subtraction) In this case we will do:
(100000000 - FF96B000 = 695000)
4.Convert the result into Decimal: 695000 6901760
5.Divide by 4096, then add a minus sign since the value needs to be negative.
6901760 / 4096 = 1685 -1685
The real value for 00 B0 96 FF is -1685.
One of the most important things to understand about the .nkm file is the Header. The header is based on checksums, so it must be written correctly to make the file work. This is the Header Structure (again I'll use Sky garden as an example):
The file always starts with "NKMD" (4E 4B 4D 44).
Then, from 0x04 to 0x07 we will always find 25 00 xx 00, where xx is where the Object Position Data starts. (In other words, where the word "OBJI" is situated. We can see that here it's situated in 0x4c and that's why it's 0x06 is "4C".
Actually, every final track will be 25 00 4C 00 since they all have the same header, but it may change in some beta files.
After that, each 4-byte data will indicate where each Section beggining (OBJI, PATH, POIT, etc):
How to calculate the beginning of a section Data:
For this, I'll use the "PATH beginning" (C4 07 00 00) as an example.
Read the 4-bytes from right to left (like this: 000007c4).
Now you need to add to this value the Header length (in this case, 4c)
07c4 + 4c = 0810
In this case, PATH will start in 0x810:
When a .nkm file is modified, it's always necessary to change this values in the Header to make the file work. "OBJI beginning" will always be 00 00 00 00 since it's always directly after the Header.
Coordinates and common settings
• Block size / amount: It always appear at the beginning of a block (OBJI, PATH, POIT, etc.). Its size is 4 byte, 1st byte is always the Amount of objects / points the block has (written in hexadecimal). Then it is usually followed by "000000".
Example: If a file has 14 objects, it will be (0E000000).
• Coordinates: since Mario Kart DS is a 3d game, it uses 3 coordinates. I will follow the .nkm file order so first we have the X coordinate, then Y coordinate (which will be the heigh of the course), and then the Z coordinate. The way to calculate its value is the one mentioned previously.
• Coordinate angle: it determines the angle of the coordinate. The most useful angle is the Y angle, since it's the one that will be able to make you turn around the horizontal plane. By increasing the angle value, the kart will turn counter-clockwise. The way to calculate its value is the one mentioned previously.
• Coordinate scale: it can make an object bigger or smaller depending on its value. The way to calculate its value is the one mentioned previously. If its value is 1, it will use the standard size, if its value of 2 (in x angle, y angle and z angle), then the size will be duplicated.
Object Positions (OBJI)
Object Position has the following structure:
• Object ID: Determines the object. Check the Object ID List.
• Object Route ID: Determines the object route.
None: FFFF 1st route: 0100 2nd route: 0200 3rd route: 0300 …
• Settings: Additional information to the objects, not really known at the moment. Setting 5 is 01000000 if the object appears in Time Trial Mode and 00000000 is the object doesn't (for example, itemboxes).
This would be GBA Sky Garden's OBJI Block:
Respawn Positions (KTPJ)
Respawn Position has the following structure:
- Those settings are unknown at the moment. The respawn ID is just the number of the respawn.
The Respawn ID is missing in the beta courses that freeze, so adding these will make them stop freezing. After that you will have to change the Y angle value (unless you're fixing it in the demo version, where they use X and Z values) since they are all 00000000 in the beta courses. And as mentioned before, always remember to edit the header.
Sky Garden "KTPJ" section:
Each object in the OBJI section has an ID and a Route ID. If the Route ID is FFFF then there is no route assigned to the object, but if it's something else then there is a route assigned to the object. The route data is found in this section.
Routes are found in the PATH and POIT sections. The actual points of the route are found in the POIT section, while the route IDs and number of points in the routes are found in the PATH section. POIT has this structure:
- First 4 bytes: POIT
- Second 4 bytes: Number of routes.
- Route 1 first point: X, Y (height), Z, 00000000, 00000000.
- Route 1 second point: X, Y, Z, 01000000, 00000000.
- Route 1 third point...
- Route 2 points...
Routes do not have angle values or scale values, since the data is just points which are the places where a route changes direction. The 0000000 and 01000000 variable is the position ID in the route, so 00 is the first position in the route, 01 is the second position, 02 is the third etc. After the route position ID it always has 00000000. To know the number of points that make up a route, check PATH. This section has this structure:
- First 4 bytes: PATH
- Second 4 bytes: Number of route IDs.
- Route ID 1: xxyyzzzz
- Route ID 2: xxyyzzzz
- Route ID 3...
The xx is the ID of the route. So if it's the first route then xx will be 00, if it's the second route xx will be 01.
The yy can be 00 or 01:
- 01: Only used for objects,the object Route is a loop (e.g. Balloon, car, moving chomp, etc).
- 00: The Route is used for a camera or for another kind of object. (e.g: goomba, crab, fireball, etc.). If the route has 2 points or less it will always be 00 even they loop.
The zzzz is the number of points in the route. Since it's little endian, if the route has 2 points in it, zzzz will be 0200, if it has 5 points in it, zzzz will be 0500, if it has 300 points in it, zzzz will be 2C01.
- The black highlighted areas is the first 4 bytes of PATH, the second 4 bytes of PATH and the data of the first 2 route IDs of beach_course.
- The red highlighted areas are the first 4 bytes of POIT, the second 4 bytes of POIT and the data of the first route of beach_course.
- The green highlighted areas are the data of the second route of beach_course.
This is part of the OBJI section of beach_course. As you can see, the blue highlighted areas are the object ID of a crab, 01AC (AC01 because of little endian), with routes 7, 8, 9, and 10 assigned to them. Even thought is says 0600, 0700, 0800 and 0900, the route 1 is 0000, so that's why the numbers are one below what you would expect. So basically the routes which are assigned in the OBJI section are controlled in the PATH and POIT sections.
If you modify routes, always edit the header. Always edit the header anyway. And if you add a route, remember to edit the second 4 bytes to the number of routes there now are in hex.