mkds.nkm module¶
- class mkds.nkm.Section(data, size)[source]¶
Bases:
objectBase class for sections in an NKM file.
Overview¶
- Most NKM sections (all except STAG) begin with an 8-byte section header:
0x00 (4 bytes): Section magic (ASCII, e.g., “OBJI”).
0x04 (4 bytes): Number of entries in the section (UInt32).
Immediately after the header the section contains entry_count entries, each with a fixed stride (size) which is specified by the concrete Section subclass.
- This base class encapsulates:
raw data for the whole section (header + entries)
stride (size in bytes of a single entry)
entry_count parsed from the header
an iterator over each fixed-size entry slice
Notes / Caveats¶
This class assumes the data passed includes the section header.
If a section contains variable-length entries (rare in NKM), a custom parser should be used instead of relying on a fixed stride.
Many sections have fields where 0xFFFF or 0xFF indicates “unused” or “none” — callers should treat those sentinel values accordingly.
- class mkds.nkm.OBJI(data)[source]¶
Bases:
SectionOBJI — Object Instances section.
Stride: 0x3C bytes per entry.
Purpose¶
Describes every object placed in the track: visual decorations, interactive objects, obstacle instances, item boxes, etc. These objects are instantiated by the game engine and can be linked to PATH routes.
OBJI Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32pos3D position vector (FX32).
0x0CVecFx32rot3D rotation vector.
0x18VecFx32scale3D scale vector.
0x24u16object_idObject ID (model/behavior).
0x26u16route_idRoute ID (0xFFFF = none).
0x28u32setting0Object-specific setting 0.
0x2Cu32setting1Object-specific setting 1.
0x30u32setting2Object-specific setting 2.
0x34u32setting3Object-specific setting 3.
0x38u32show_ttVisible in Time Trials (1=yes,0=no).
Gameplay Context¶
The object ID decides both model and in some cases runtime logic (collision, activatable behaviors).
The route_id links the object to a PATH. If present, the object will be moved/animated by the PATH’s POIT points at runtime (e.g., moving platforms, cameras).
The object settings are used by many object types to control behavior: rotation speed, spawn flags, timers, random seeds, or other per-object parameters. Different object IDs require different decoding logic.
Reverse-Engineering Notes / Tips¶
The four 32-bit settings differ radically between object types — community wikis often contain per-object decode rules (use object ID to branch).
The presence of a non-0 or non-0xFFFFFFFF route_id often means the object will be animated along that route; route indices are bytes in PATH entries but stored here as a 16-bit value (keep an eye on sign/width).
Show-in-time-trials: historically some editors used 0/1 inverse; verify against known tracks when in doubt.
Parsing Caveat¶
We read object_id as read_u16 and route_id as read_u16. Consumers of this class should treat 0xFFFF in route_id as “no route”.
- class mkds.nkm.PATH(data)[source]¶
Bases:
SectionPATH — Path metadata.
Stride: 0x04 bytes per entry.
Purpose¶
Describes metadata for routes used by objects and cameras. Each PATH entry points to a sequence of POIT entries (control points) that define the route.
PATH Entry Structure¶
Offset
Type
Name
Description
0x00u8route_idRoute identifier.
0x01u8loop1 if route loops, else 0.
0x02u16point_ctNumber of POIT points.
Gameplay Context¶
Objects or cameras that reference a route will move along the POIT points in order; if the loop flag is set, the route repeats.
Route ID is commonly a small integer; the PATH list enumerates all routes in use on the track.
Reverse-Engineering Notes / Caveats¶
The canonical spec states: 0x01 == 1 if the route loops, 0 otherwise. In the code you originally used read_u8(d, 0x01) != 1 (which inverts the meaning). I have NOT altered your logic — but be aware of the discrepancy: callers should expect True when the route loops. Consider changing to read_u8(… ) == 1 for clarity.
point_count enumerates POIT entries but the mapping from global POIT index to route is (index offset + length) — consumers should reconstruct the actual POIT index ranges using CPAT/EPAT/IPAT/MEPA grouping sections when applicable (these groupings partition points).
- class mkds.nkm.POIT(data)[source]¶
Bases:
SectionPOIT — Path points (control points).
Stride: 0x14 bytes per entry.
Purpose¶
Stores the actual 3D points used by PATH routes. Points are grouped by route using the counts stored in PATH plus the various *PAT grouping sections.
POIT Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32pos3D position vector.
0x0Cu8indexPoint index in route.
0x0Du8unknown1Unknown/padding.
0x0Es16durationPoint duration (optional).
0x10u32unknown2Reserved/unknown.
Gameplay Context¶
Moving objects and cameras read these points sequentially to interpolate positions. The “point_index” normally indicates position in the route’s ordering (0,1,2,…).
Some cameras or scripted objects use point_duration to wait between points, enabling non-linear motion.
POIT order in the file is important: group membership is often determined by successive ranges; use PAT sections to map ranges to specific routes.
Reverse-Engineering Notes¶
The unknown fields often show consistent patterns per track editor — check community resources to decode them for special behaviors.
Some older track versions or beta files encode rotation differently; when in doubt, cross-check with KTPJ notes for version-dependent behavior.
- class mkds.nkm.STAG(data)[source]¶
Bases:
objectSTAG — Stage (track) information.
Fixed-size: The STAG section is unique in NKM: it does NOT have a section header and is a single 0x2C-byte structure placed directly in the file (after POIT in the canonical header ordering).
Purpose¶
Contains global track settings: track ID, default lap count, fog settings, colors used by KCL (collision visual palettes), and other miscellaneous bytes.
STAG Entry Structure¶
Offset
Type
Name
Description
0x00char[4]magicAlways “STAG”.
0x04u16track_idTrack ID.
0x06u16lapsLap count.
0x08u8unknown1Unknown small integer.
0x09u8fog_enabled1 = fog on, 0 = off.
0x0Au8fog_modeFog generation mode.
0x0Bu8fog_slopeFog slope.
0x0Cu8u0Unknown.
0x0Du8u1Unknown.
0x0Eu8u2Unknown.
0x0Fu8u3Unknown.
0x10u8u4Unknown.
0x11u8u5Unknown.
0x12u8u6Unknown.
0x13u8u7Unknown.
0x14Fx32fog_distFog distance.
0x18GXRgbfog_colorFog color.
0x1Au16fog_alphaFog alpha (0–15).
0x1CGXRgbkcl_color1Default KCL color 1.
0x1EGXRgbkcl_color2Default KCL color 2.
0x20GXRgbkcl_color3Default KCL color 3.
0x22GXRgbkcl_color4Default KCL color 4.
0x24u8v0Unknown.
0x25u8v1Unknown.
0x26u8v2Unknown.
0x27u8v3Unknown.
0x28u8v4Unknown.
0x29u8v5Unknown.
0x2Au8v6Unknown.
0x2Bu8v7Unknown.
Gameplay Context¶
Amount of laps controls how many laps the race uses by default for the stage; some tracks use lap_count = 0 for special cases (verify per track).
Fog parameters influence rendering: enabling fog can hide distant objects and alter perceived depth; fog color & distance control atmosphere.
KCL colors are the default palette for collision visualization (useful for editors and collision debugging).
Implementation Notes¶
This class sets placeholders for color fields (GXRgb) — you can implement GXRgb decoding (usually 2 bytes per color or platform-specific) and populate these fields for richer output.
The unknown arrays are repeated in code (unknown2 and unknown3) — that mirrors the spec layout but may be redundant; keep one copy or rename for clarity if desired.
- class mkds.nkm.KTPS(data)[source]¶
Bases:
SectionKTPS — Kart/Start Positions (Start points for racers).
Stride: 0x1C bytes per entry.
Purpose¶
Defines start positions (spawn/starting grid) for players/racers. Typically used for the main race starts; can also be used in battle or mission modes.
KTPS Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32pos3D position vector (spawn).
0x0CVecFx32rot3D rotation vector.
0x18u16paddingPadding (0xFFFF).
0x1Au16indexStart index (battle/mission).
Gameplay Context¶
On race start, the game picks starting positions from this section.
start_position_index is relevant for battle stages or mission mode where start ordering differs from main racing.
The rotation vector might be stored differently in beta versions — the canonical community notes indicate the Y-rotation sometimes needs to be computed via Atan2 on rotation vector components for older versions.
Notes / Community Tips¶
Typically the number of KTPS entries equals the number of players or more (some tracks list more possible starts than vehicles).
If you want to reposition start locations, modify positions and write the file back in FX32 format.
- class mkds.nkm.KTPJ(data)[source]¶
Bases:
SectionKTPJ — Respawn positions (kart respawn).
Stride: 0x20 bytes per entry.
Purpose¶
Positions to which a kart (player) can respawn after falling off or during certain scripted events. Contains references to enemy/item points (EPOI/IPOI) to determine nearby behavior or AI context.
KTPJ Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32pos3D position vector.
0x0CVecFx32rot3D rotation vector.
0x18u16enemy_idEnemy position ID (EPOI).
0x1Au16item_idItem position ID (IPOI).
0x1Cu32respawn_idRespawn identifier.
Gameplay Context¶
When a kart falls off the track or hits a severe collision, the engine picks a KTPJ respawn that matches the current lap and nearby conditions.
The enemy/item IDs let respawn logic pick nearby AI/item spawn points for smoother reintroduction.
Version Notes¶
For version 0x1E (older beta), the final Respawn ID (0x1C) may not exist.
The rotation encoding changed for early beta versions; if reading older tracks, compute Y-rotation via atan2(Rx, Rz) to convert to degrees.
Remarks for Parser¶
We read respawn_id as a 32-bit value; if parsing older tracks that omit it, ensure you guard read beyond section length.
- class mkds.nkm.KTP2(data)[source]¶
Bases:
SectionKTP2 — Lap checkpoints (points to pass to count lap progress).
Stride: 0x1C bytes per entry.
Purpose¶
Defines the “lap gate” points that the engine checks to determine whether a player completed a lap. Usually combined with timing/ordering checks.
KTP2 Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32pos3D position vector.
0x0CVecFx32rot3D rotation vector.
0x18u16paddingPadding (0xFFFF).
0x1Au16indexIndex (0xFFFF typical).
Gameplay Context¶
The race logic queries these points as canonical lap markers.
Often unused fields are set to 0xFFFF; do not treat them as valid indices.
Notes
The “Index” is usually unused (set to 0xFFFF); if present, it may participate in specialized lap logic or developer tools.
- class mkds.nkm.KTPC(data)[source]¶
Bases:
SectionKTPC — Cannon / Pipe destination points.
Stride: 0x1C bytes per entry.
Purpose¶
Describes destinations for cannons/pipes used in certain stages (mostly in battle stages). Cannon indices are used by specialized collision types.
KTPC Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32pos3D position (destination).
0x0CVecFx32rot3D rotation vector.
0x18u16unknownUnknown.
0x1Au16cannon_idxCannon index (links activator/destination).
Gameplay Context¶
Cannon/pipe logic teleports an object/player to the KTPC destination.
The cannon_index can be used as a link between activator and destination.
Notes
In many tracks this section is small or absent; treat missing sections gracefully when writing tools that modify KTPC entries.
- class mkds.nkm.KTPM(data)[source]¶
Bases:
SectionKTPM — Mission points.
Stride: 0x1C bytes per entry.
Purpose¶
Points used by mission objectives (mission mode). They often resemble KTPS/KTP2 entries but have mission-specific indexing.
KTPM Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32pos3D position vector.
0x0CVecFx32rot3D rotation vector.
0x18u16paddingPadding (0xFFFF).
0x1Au16indexMission index.
Gameplay Context¶
Missions may use these points to define target positions or spawns.
index can be used in mission scripts to select a specific point out of the KTPM array.
Notes
If the track is not a mission type, these often default to 0xFFFF.
- class mkds.nkm.CPOI(data)[source]¶
Bases:
SectionCPOI — Checkpoints (2D oriented).
Stride: 0x24 bytes per entry.
Purpose¶
Defines checkpoint segments used for lap counting, key handling, and respawn logic. CPOI includes two 2D positions and precomputed trig/distance values used by the engine to determine crossing and checkpoint ordering.
CPOI Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32 (2D)pos12D position 1.
0x08VecFx32 (2D)pos22D position 2 (other edge).
0x10Fx32sinPrecomputed sine.
0x14Fx32cosPrecomputed cosine.
0x18Fx32distanceDistance between pos1/pos2.
0x1Cs16section1Section data 1 (unknown).
0x1Es16section2Section data 2 (unknown).
0x20u16key_id0x0000=lap, 0xFFFF=none.
0x22u8respawn_idRespawn ID.
0x23u8unknownUnknown/padding.
Gameplay Context¶
CPOIs are the canonical gate the engine checks for lap progress.
The Key ID allows some checkpoints to behave as keys (for gates, or race logic). If Key ID == 0x0000 the point counts as the lap marker.
The precomputed sinus/cosinus/distance allow the engine to quickly test whether a player crossed the checkpoint along the correct orientation.
Reverse-Engineering Notes¶
Community docs note the section_data fields are partially decoded for special track logic. If you need precise behavior, compare original tracks and observe in-engine behavior.
- class mkds.nkm.CPAT(data)[source]¶
Bases:
SectionCPAT — CPOI grouping (checkpoint groups).
Stride: 0x0C bytes per entry.
Purpose¶
Groups CPOI entries into logical sequences (routes/sections). Each CPAT entry points to a contiguous block of CPOI points and describes adjacency (previous/next groups) and section order.
CPAT Entry Structure¶
Offset
Type
Name
Description
0x00u16point_startStart index into CPOI array.
0x02u16point_lenNumber of points.
0x04u8next0Next group index 0.
0x05u8next1Next group index 1.
0x06u8next2Next group index 2.
0x07u8prev0Previous group index 0.
0x08u8prev1Previous group index 1.
0x09u8prev2Previous group index 2.
0x0As16section_orderSection ordering index.
Gameplay Context¶
CPAT allows complex checkpoint graphs (non-linear courses) by connecting multiple CPOI groups.
Useful when a single lap uses multiple discontiguous checkpoint regions.
Implementation Notes¶
The next_group and prev_group arrays use 0xFF as “unused”; treat sentinel values accordingly.
- class mkds.nkm.IPOI(data)[source]¶
Bases:
SectionIPOI — Item spawn points.
Stride: 0x14 bytes per entry.
Purpose¶
Describes where items (like red shells, bananas) may spawn or where items follow a path along a route. IPOI entries are often referenced by KTPJ (respawn) or object logic.
IPOI Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32pos3D position vector.
0x0CFx32scalePoint scale (size/weight).
0x10u32unknownReserved field.
Gameplay Context¶
Items may spawn at IPOI locations or be used for scripted item routes.
point_scale may modify spawn probability or area radius.
Notes
The unknown 32-bit value is often zero; it may contain bitflags in certain custom tracks.
- class mkds.nkm.IPAT(data)[source]¶
Bases:
SectionIPAT — IPOI grouping.
Stride: 0x0C bytes per entry.
Purpose¶
Group IPOI entries into contiguous point ranges and define adjacency, much like CPAT but for item points.
IPAT Entry Structure¶
Offset
Type
Name
Description
0x00u16point_startStart index into IPOI array.
0x02u16point_lenNumber of points.
0x04u8next0Next group index 0.
0x05u8next1Next group index 1.
0x06u8next2Next group index 2.
0x07u8prev0Previous group index 0.
0x08u8prev1Previous group index 1.
0x09u8prev2Previous group index 2.
0x0As16section_orderSection ordering index.
Gameplay Context¶
Used by item routing and by respawn selection to find nearby item points.
- class mkds.nkm.EPOI(data)[source]¶
Bases:
SectionEPOI — Enemy/CPU path points.
Stride: 0x18 bytes per entry.
Purpose¶
Defines points that the CPU opponents use for their routing (AI paths). These influence how CPUs drive the course (lines, drifting behavior, etc).
EPOI Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32pos3D position vector.
0x0CFx32scalePoint scale (radius/weight).
0x10s16driftDrifting parameter.
0x12u16unknown1Unknown flag.
0x14u32unknown2Engine metadata.
Gameplay Context¶
CPU behavior heavily depends on EPOI positions and the drifting parameter — altering these can change how tight/loose CPUs corner.
EPOI groups are linked via EPAT entries (below).
Notes
The meaning of the 0x14 32-bit word is partially unknown in community docs; experiments suggest it can contain flags for AI behavior.
- class mkds.nkm.EPAT(data)[source]¶
Bases:
SectionEPAT — EPOI grouping.
Stride: 0x0C bytes per entry.
Purpose¶
Groups EPOI points into contiguous blocks and defines adjacency (next/prev groups) and section ordering. Used by CPU pathing code to navigate routes.
EPAT Entry Structure¶
Offset
Type
Name
Description
0x00u16point_startStart index into EPOI array.
0x02u16point_lenNumber of points.
0x04u8next0Next group index 0.
0x05u8next1Next group index 1.
0x06u8next2Next group index 2.
0x07u8prev0Previous group index 0.
0x08u8prev1Previous group index 1.
0x09u8prev2Previous group index 2.
0x0As16section_orderSection ordering index.
Gameplay Context¶
EPAT partitions EPOI arrays into logical AI routes; enabling multiple CPU strategies per segment.
- class mkds.nkm.MEPO(data)[source]¶
Bases:
SectionMEPO — Mini-game enemy points.
Stride: 0x18 bytes per entry.
Purpose¶
Similar to EPOI but used by specific mini-games; describes where minigame entities spawn/move.
MEPO Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32pos3D position vector.
0x0CFx32scalePoint scale.
0x10s32driftingDrifting parameter (32-bit).
0x14u32unknownUnknown/reserved.
Gameplay Context¶
MEPO entries are used only in special mini-game contexts and may allow more variety (hence Int32 for drifting).
- class mkds.nkm.MEPA(data)[source]¶
Bases:
SectionMEPA — MEPO grouping (for mini-games).
Stride: 0x14 bytes per entry.
Purpose¶
Groups MEPO points into sequences. MEPA entries support up to 8 next and 8 previous groups (Byte[8]) because mini-games may have richer topology.
Gameplay Context¶
Use MEPA to create complex mini-game movement graphs (multiple branching).
- class mkds.nkm.AREA(data)[source]¶
Bases:
SectionAREA — Camera/zone areas.
Stride: 0x48 bytes per entry.
Purpose¶
Defines 3D regions used by the engine for camera selection and environmental triggers (sounds like waterfalls). Each area contains a center position, axes/length vectors (defining an oriented bounding box), and metadata such as area type and camera ID.
AREA Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32posCenter position vector.
0x0CVecFx32length_vecLength vector.
0x18VecFx32x_vecX-axis vector.
0x24VecFx32y_vecY-axis vector.
0x30VecFx32z_vecZ-axis vector.
0x3Cs16u0Unknown.
0x3Es16u1Unknown.
0x40s16u2Unknown.
0x42u8u3Unknown.
0x43u8camera_idCamera index (CAME ref).
0x44u8area_typeArea type (0x01=camera, etc.).
0x45s16u4Unknown.
0x47u8u5Unknown.
Gameplay Context¶
AREA sections usually determine which camera the engine should switch to when the player is inside the region.
Camera ID links to CAME entries; Area type allows special behavior (e.g., waterfall sound triggers).
Good for editors to preview camera transitions.
Notes
Several fields are still undocumented; their meaning varies by track.
- class mkds.nkm.CAME(data)[source]¶
Bases:
SectionCAME — Camera definitions.
Stride: 0x4C bytes per entry.
Purpose¶
Defines camera motions and static camera positions used in cutscenes, intros, and dynamic in-game camera triggers. Camera entries are often linked by AREA zones or object routes.
CAME Entry Structure¶
Offset
Type
Name
Description
0x00VecFx32pos1Primary position vector.
0x0CVecFx32rotRotation vector.
0x18VecFx32pos2Secondary position vector.
0x24VecFx32pos3Tertiary position vector.
0x30s16fov_beginField-of-view start.
0x32Fx16fov_beg_sinPrecomputed sine for FOV start.
0x34Fx16fov_beg_cosPrecomputed cosine for FOV start.
0x36s16fov_endField-of-view end.
0x38Fx16fov_end_sinPrecomputed sine for FOV end.
0x3AFx16fov_end_cosPrecomputed cosine for FOV end.
0x3Cu16zoomCamera zoom factor.
0x3Eu16typeCamera type.
0x40u16linked_routeLinked PATH route index (0xFFFF = none).
0x42u16route_speedSpeed along linked route.
0x44u16point_speedSpeed between points.
0x46u16durationDuration (1/60s units).
0x48u16next_camNext camera index (0xFFFF = none).
0x4Au8intro_panIntro pan indicator (0=none,1=top,2=bot).
0x4Bu8unknownUnknown (1 if type==5).
Camera Type (common)¶
Bit Offset
Description
0x00After race camera
0x01Unknown (with route)
0x02Unknown
0x03Intro camera (top screen)
0x04Intro camera (bottom screen)
0x05Unknown
0x06Unknown
0x07Battle mode camera
0x08Mission finish camera
Gameplay Context¶
CAME entries drive cinematic camera movement during intros, cutscenes, and special camera modes.
Linked route + point speed define how the camera follows a PATH.
Camera duration uses 1/60th-second units — helpful for exact timing.
Notes & Community Tips¶
Many of these fields are precomputed (sine/cosine) for faster in-engine interpolation. Editors can either preserve or recompute them.
The “next camera” field allows building camera chains for sequences.
When building camera editors, expose both positions and FOV values to enable previewing transitions correctly.
- class mkds.nkm.NKM(data)[source]¶
Bases:
objectNKM — Mario Kart DS Course Map parser.
Purpose¶
Top-level container that parses the NKM file header and initializes objects representing each section (OBJI, PATH, POIT, STAG, KTPS, … , CAME).
Usage Example¶
>>> nkm = NKM.from_file("my_course.nkm") >>> print(len(nkm._OBJI)) # number of object instances >>> print(nkm._STAG.amt_of_laps) # global lap count for the stage
Implementation Details¶
The typical header length is 0x4C and contains offsets (UInt32) to 17 canonical sections in standard order. Offsets are relative to the end of the header (H), so they are added to _header_offset to compute absolute positions inside the file.
This parser assumes the “typical” header layout. If an NKM contains additional special sections (like NKMI) or a different header length, additional handling will be required.
Parsing is defensive but assumes the file is well-formed; for robust tools consider adding bounds checks when slicing self._data[…].
Known Limitations¶
Some fields (GXRgb, camera_type, some unknown bytes) are left as None or unknown placeholders. Implementing GXRgb and Fx16/Fx32 conversions will enable richer output.
The code currently uses PATH.has_loop = read_u8(…) != 1 which inverts the canonical meaning (spec: 1 means loop). Consider normalizing this if you rely on literal interpretation elsewhere.
See also
The,and