DKR-R Modding Bible - SDK 2.0
=============================
v1.0.14 release — SDK 2.0 retained

RIGHT - LET'S GET STARTED
-------------------------

SDK 2.0 gives Custom Magic Codes a proper development toolset. We can inspect
racers and objects, change supported values while a game is running, build our
own controller-friendly tools, probe collision, rebuild checkpoints and send
agreed edits through DKR-R Online. The Dev Menu is an ordinary mod using these
public services. There is no secret Dev Menu package ID unlocking extra powers.

We have kept API 1. Existing trophy cups, AI difficulty mods, code packages and
ModernCamera++ do not need to be rewritten just because the authoring kit now
says SDK 2.0. New executable API 2 packages opt into the additional services.
The exact supported modules and limits are part of the reference below.

This book is for v1.0.14 with the retained SDK 2.0/API 1 compatibility. Compilation and package inspection are
not a promise that every possible combination of mods is bug free. Gameplay,
visual, performance, multiplayer and headset testing are carried out by the
players testing the build. Keep backups, work in a development session and
start with one small change at a time.

HOW THIS BOOK IS BUILT
----------------------

The final appendices reproduce the public headers from the same staged SDK
that is packaged with this build. They are not copied from an older release.
The main chapters explain what those interfaces do, when to use them and where
the engine still needs a dedicated adapter. A capability being unavailable is
not permission to call a protected scheduler, renderer or loader by address.

The public headers are the exact names and layouts to compile against. Where
this book says a feature is restricted, that restriction is intentional. We
are giving modders a lot of freedom without handing a mod the audio device,
graphics queue or another player's local camera. That would just make a mess.

WHAT WE ARE BUILDING
--------------------

A Custom Magic Code is a portable executable mod. It is not a GameShark text
code, a host DLL, a texture pack or a patched ROM. We compile C, or optional
freestanding C++, to the same MIPS O32 guest format on every platform. The
result is an .nrm package containing code, symbols, metadata and its declared
resources. The app imports it through MODS / HACKS, just like the other mods.

One package can change race rules, react to native functions, expose settings,
create runtime tools, read logical input and keep private state. Packages can
also export functions and events to declared dependencies. SDK 2 adds a typed
development layer around that existing power. It does not take API 1 away.

There are two good starting points. For a normal gameplay mod, use the small
scaffold and request only the modules you need. For development tools, read
the Dev Menu source alongside this guide. It demonstrates handles, edits,
commands, route inspection, documents and the tools UI in one actual package.
Do not copy its large work profile into a tiny banana-count mod for no reason.

INSTALL THE TOOLS
-----------------

You need Python 3.11 or later, Clang with the MIPS target, LLD and the pinned
RecompModTool from this project's N64Recomp build. Source maps additionally
use llvm-nm and llvm-symbolizer. Use compatible LLVM tools from one toolchain.
The SDK contains the guest headers, runtime helpers, linker script, SDK-only
reference, build script, authoring tools, examples and both revision references.

Windows modders can use WSL. Linux modders can use the same commands directly.
An Android player imports the same .nrm; authors do not build a separate ARM
version of the mod. App binaries are platform-specific, mod guest code is not.
The current app supports the US v1.0 and US v1.1 execution revisions, identified
in code manifests as us.v77 and us.v80. Do not guess offsets from a different ROM.

Keep your project outside the SDK directory, and use a new output directory
for each build. The builder refuses an existing output so a failed compile
does not quietly leave you distributing yesterday's package under today's name.
Neither build.py nor tools.py launches the game or executes your mod.

    python SDK/tools.py new --id my_first_code --name "My First Code" --output my-first-code
    python SDK/build.py my-first-code --output output-001 --mod-tool /path/to/RecompModTool --source-map
    python SDK/tools.py inspect output-001/my_first_code.nrm

Replace SDK and the mod-tool path with your actual locations. On Windows,
invoke these through your chosen LLVM environment or WSL. Paths passed to
WSL must be Linux paths, for example /mnt/c/MyMods rather than C:\MyMods.
Keep the output ELF and source map privately for debugging. Only distribute
the files you intend to share; never put your ROM, save or private log in a mod.

YOUR PROJECT FILES
------------------

mod.toml is the RecompModTool manifest and source list. Keep a stable lowercase
ID using letters, numbers, underscores or hyphens. The ID is the identity of
your mod, not its display name. Changing it creates another installed mod.
Increase the version when publishing an edition. Use an honest description,
author attribution, game_id = "dkr" and the minimum app version you actually need.

dkr.json declares executable API, supported revisions, module versions,
deterministic online policy, systems, dependencies and immutable resources.
The metadata schema is still 1 for both APIs. API 2 is selected with api: 2,
not by changing schema to 2. Tools UI resources have their own schema: 2.

    {
      "schema": 1,
      "api": 2,
      "activation": "restart",
      "online": "deterministic",
      "development": false,
      "profile": "standard",
      "revisions": ["us.v77", "us.v80"],
      "requires": {"core": 1, "game-events": 1, "entities2": 2, "transactions2": 2}
    }

Resources map logical IDs to project-relative files. No absolute paths,
parent-directory escapes or symlinks. Declare every resource you use; a mod
cannot fetch an undeclared file from someone's computer. The example Dev Menu
declares tools-ui -> tools.json. The builder can add a source-map resource.
Do not declare your own source-map resource and also pass --source-map.

The [build] sources list in mod.toml selects .c, .cpp or .cc files. Headers and
.inl files are included by those sources; do not compile an include file as a
second source unit. Keep exported/imported interfaces C-compatible.

IMPORTING AND TRUSTING A MOD
----------------------------

Open MODS / HACKS, choose Import Mods and select the .nrm or .dkrcode file.
Find it under Custom Magic Codes, open Details, read its identity and approve
the code only if you trust it. Enable it and start a new game. Existing running
games use their admitted package set, not whatever is now selected in the UI.

A valid archive is not proof that code is safe or sensible. Advanced game
memory writes and native calls can still break a race. Approval is a trust
decision. Give players source, a useful description and clear requirements.
After importing a changed edition, expect approval and preparation to happen
again. Do not use the same filename to hide a different code payload.

For the Dev Menu, import DKR-R-Dev-Menu.nrm and enable it. Enter a game, then
use Custom Magic Code Tools in the overlay or F2. The shortcut can be changed
inside Tools. Back/Select + Y is an optional controller shortcut. The normal
overlay entry remains available if a controller already uses that combination.

API 1 IS STILL HERE
-------------------

SDK 2.0 is the authoring kit name. Executable API 1 still reports 1 from
dkr_sdk_version. Executable API 2 reports 2. Old import names, struct layouts,
settings, callbacks, dependency ordering and .nrm metadata have not been
renamed. You do not need to convert every existing mod to use the new kit.

The original modules remain version 1: core, memory, events, settings,
resources, random, game, game-hooks, game-events, racers, objects, input,
cameras, collision, hud, audio, racing and trophies. Native-call availability
still depends on the actual dispatcher for the selected revision and backend.
Presentation-domain Camera++ has its separate presentation contract.

Do not change all requires values from 1 to 2. A requirement is a named
capability and version, not the age of the package. A mixed set of API 1 and
API 2 mods is allowed if its dependencies and exclusive systems agree.
Different application engine/protocol versions are still incompatible online.
API compatibility is not a promise that beta 7 can join an older beta lobby.

API 2 MODULE MAP
----------------

reflection2 describes typed fields, their ranges, units, read/write flags and
allowed phase. entities2 enumerates current lifetime-checked entity handles.
transactions2 validates and applies batches of typed edits. inspection2 exposes
reviewed symbols and copied guest-memory inspection. None returns host pointers.

operations2 provides typed native operations: teleport, restricted scenery
clone/delete, checkpoint rebuild, original fog/weather, audio and transitions.
catalogues2 exposes frozen course, cup and character identities, admitted asset
banks and active package/resource/hook/callback/settings/audio/operation records.
collision2 provides bounded
static ray/sphere sweeps plus explicitly selected interaction-sphere queries.
events2 exposes the rewindable lifecycle and racer-change queue.

tools-ui2 renders generic mod-defined pages and copied values. simulation2
controls pause, single update and bounded retail timestep. track-tools2 reads
native checkpoints, routes, scene rules, segment bounds, triangle geometry,
AI graph links, spawn points and triggers. documents2 stages user-approved scoped JSON
imports/exports. presentation2 supplies local debug primitives and an explicit
local camera handoff through the tools UI.

racer-tuning2 scopes weight, handling, acceleration and speed-cap overrides
around native racer updates. overrides2 maintains reviewed property overrides,
including native object shading. live-settings2 provides package-local live
numeric/enum overrides, including explicitly opted-in options in other API 2
packages. reload2 enables the restricted offline reload panel.
All these new module versions are 2. Ask dkr2_capability for the actual version.

Availability is not permission to invoke an operation in every scene. A route
needs loaded checkpoint data, a racer field needs a racer, and weather needs
an initialized weather system. Check the returned status every time.

EXECUTION AND THE THREE DOMAINS
-------------------------------

Gameplay is deterministic simulation state. Mod globals, allocated heap,
service arenas, event queues, tuning, overrides, handles and RNG are included
in checkpoints. A rewind restores them. Do not put shared gameplay decisions
in a native static variable, physical controller poll or wall-clock timer.

Presentation is local. Browsing pages, selecting inspector rows, debug visibility,
camera motion and UI draft values do not advance shared gameplay or shared RNG.
The UI receives copies, not a live RDRAM pointer. It cannot write a racer while
ImGui is drawing a button. An action must travel back to the simulation owner.

Storage is outside callbacks. A player chooses an import/export file in the
host UI. The host reads or writes bounded documents asynchronously and shows
a review before an import is sent to the mod. There is no arbitrary mod file,
socket, process or system-shell API. The Dev Menu console is not a shell.

Read phases can inspect. Tick phases can apply supported mutations. Draw
phases must not mutate gameplay. A named hook called during rendering does
not become a gameplay callback just because the author calls it one. Wrong
phase returns DKR2_PHASE or stops an unsafe expert operation.

CALLBACKS AND EVENT ORDER
-------------------------

Use DKR_CALLBACK(on_boot) for initial private state. on_frame and on_frame_end
surround normal gameplay updates. Scene unload/load, racer spawn, results and
menu callbacks are emitted only where the actual native adapter owns them.
Do not manufacture a second scene load in your callback.

API 2 adds on_tool at a retired development boundary. It also runs while
development gameplay is paused, so a Resume button can actually be consumed.
Put dkr2_action_next here for a tool mod. Ordinary on_frame is not called for
a parked update. Audio, UI, input transport and the native scheduler remain
alive; a tool pause is not a thread suspension or fake graphics completion.

Named callbacks use before_FUNCTION and after_FUNCTION for eligible native
functions. The dispatcher preserves the port's own hooks. It knows the real
selected revision and owned/offline CPU namespace. Do not resolve a native
function through an unrelated process-global table.

    #include "dkr/development.h"
    static Dkr2Session state = {.size = sizeof(Dkr2Session)};
    DKR_CALLBACK(on_frame) void frame(void) {
        state.size = sizeof(state);
        if (dkr2_session(&state) != DKR2_OK) return;
        /* Read current scene, tick and ownership before using saved handles. */
    }

For event tracing, subscribe with dkr2_events(mask), then drain a bounded number
using dkr2_event_next in a tick. Types cover scene, spawn, despawn, lap,
checkpoint, item and result changes. Queue overflow is explicit event 31,
not silent infinite buffering. Collision hits are queries, not an automatic
collision-event subscription. The queue holds 32 records per owner.

HANDLES - DO NOT KEEP DEAD POINTERS
-----------------------------------

A Dkr2Handle is a slot and generation, not an address. Object storage can be
freed and reused for a different object at the same address. Generation checks
are there to prevent your old selection from editing that new object.
Scene changes invalidate previous lifetimes. Reacquire targets after a load.

dkr2_entities enumerates at most 64 results in one request. The first parameter
is a slot cursor; OR DKR2_ENTITIES_RACERS for racer-only filtering. Initialize
every result record's size before calling. Slots are one-based handles while
the enumeration cursor is zero-based. Continue from the last returned slot,
not by blindly adding the requested capacity to a sparse list.

    Dkr2Entity racers[8];
    for (unsigned i=0; i<8; ++i) racers[i].size=sizeof(Dkr2Entity);
    int count=dkr2_entities(DKR2_ENTITIES_RACERS, racers, 8);
    if (count>0) {
        Dkr2Read query={racers[0].handle, DKR2_RACER_BANANAS, {.bits=0}, 0};
        int result=dkr2_read(&query,1);
        if(result==DKR2_OK && query.status==DKR2_OK) {
            /* query.value.integer is the signed byte expanded to 32 bits. */
        }
    }

Online inspector targets are copied only at agreed boundaries. A speculative
object birth must not become a command target and later alias after rollback.
The local view can therefore wait for a stable snapshot while the simulation
is predicting. Pausing through an agreed host command is useful for inspection.

REFLECTION AND PROPERTY TYPES
-----------------------------

Enumerate dkr2_property(index,&info). Initialize size. The returned metadata
contains ID, type, flags, minimum, maximum, phase, name and unit. Do not treat
an integer's bits as a float or write a float conversion into an ID field.
Dkr2Value is a union; .real, .integer and .bits select its interpretation.

Object properties include position, rotation, scale, opacity, velocity,
behaviour, object ID and segment. Position is intentionally read-only in the
generic editor. Use the teleport operation so segment and path bookkeeping
are updated. Behaviour, object ID and segment are also inspection-only.

Racer fields include forward/lateral speed, bananas, item/ammunition/level,
boost and shield timers/type, AI skill, throttle, brake and buoyancy. Player,
roster index, character, vehicle, lap, checkpoint, position and finished status
are inspection-only. They own more state than a single byte suggests.

Shading fields cover native object ambient/diffuse, primary light colour,
intensity and direction. They need a valid native shading record. Trigger
fields are exposed only for reviewed mode-change, bonus and trigger behaviours.
They do not make every unknown behaviour's private structure writable.

Float values must be finite and inside the published range. Angle16 fields
use signed native angles, not degrees. Direction uses its documented fixed
scale. Game speed units are native units, not a promise of metres per second.
The exact property table and all public constants ship with the SDK reference.

TRANSACTIONS, UNDO AND PERSISTENT OVERRIDES
-------------------------------------------

dkr2_edit accepts up to 32 unique entity/property pairs. It validates target
lifetimes, property types, ranges and duplicates before the first write.
One bad entry rejects the batch. An edit is not an arbitrary C function with
automatic rollback; native calls and manual memory patches are separate work.

    Dkr2Edit change={handle, DKR2_RACER_BANANAS, {.integer=10}};
    int result=dkr2_edit(&change,1);
    if(result==DKR2_STALE) { /* Select a fresh racer. */ }

For undo, capture the previous value first. Store before/after typed edits and
the current lifetime. Undo must still pass normal validation. If the target
has left the scene, an old undo must fail rather than find an address lookalike.
The example keeps 32 edits and clears its history on a scene change.

dkr2_override replaces this owner's complete persistent set, up to 32 fields.
It revalidates ownership before committing and rejects another API 2 owner's
claim to the same field. count=0 clears the set. Normal simulation frames
reapply it; original shading overrides also apply after the native shading
update so the engine does not immediately overwrite them. Unload clears them.

An override cannot turn a read-only field into a writable one. It is not a
generic native-function replacement manager, and it cannot protect you from
an unrelated trusted API 1 mod deliberately writing the same guest bytes.
Declare exclusive systems when those behaviours cannot reasonably compose.

TELEPORTING AND OBJECT OWNERSHIP
--------------------------------

DKR2_TELEPORT takes handle slot/generation and float-bit XYZ in its first five
words. It checks the destination, uses reviewed native movement, updates the
segment, clears motion/previous pose and repairs racer checkpoint selection.
Use it to return to a marked test position. Do not set X/Y/Z through raw memory
and assume collision, interpolation and the racing line will catch up.

DKR2_CLONE_SCENERY supports the reviewed scenery behaviour, not racers,
schedulers, doors, cameras or arbitrary resource owners. It copies the bounded
authored entry into owner-controlled storage and calls native spawn_object to
perform initialization. There are at most 32 tool spawns per package and the
native object capacity still applies. It is not memcpy of a live Object.

DKR2_DELETE_SPAWN removes only a matching tool-owned scenery spawn in the same
scene. Deletion is queued through native ownership; a queued delete cannot be
repeated to free an object twice. Another mod's or the track's original object
is not yours to delete. Use a dedicated adapter if a different behaviour needs
special setup, cross-reference repair or cleanup.

RACER, AI AND BOSS TUNING
-------------------------

dkr2_tune accepts a Dkr2Tune with a current racer handle. Mask bits select
weight, handling, acceleration curve and forward speed cap. Factors are
0.1..4. The cap is 0 for disabled or up to 100 native forward-speed units.
Zero mask resets the selected racer; zero handle plus zero mask resets this
owner's tuning. A scene unload drops tuning rather than leaking it into a menu.

The adapter scopes native weight/handling globals around the selected actor's
update and restores them before the next actor. Standard car/hovercraft/plane
acceleration uses a private copy of the reviewed native curve. Boss updates
support the reviewed weight/handling/cap path, not a guessed boss acceleration
array. Requesting an unsupported combination must not silently corrupt it.

dkr2_freeze_cpus skips only the reviewed AI racer update. Object ownership,
audio, graphics, input, transition and scheduler work still run. This is a
debugging tool, not a second global game pause. Resume CPUs before assessing
normal race behaviour. Only one racer-tuning2 provider may own the session.

The Dev Menu can change AI skill, bananas and ammunition; stop motion; maintain
boost, shield and bananas; and apply/reset selected-racer tuning. There is no
typed traction solver, arbitrary boss state-machine editor or extra-human-slot
API. Expert code can hook eligible game logic, but must own the resulting rules.
Vehicle and character identities are read-only here: changing them safely needs
their native scene/model/physics setup, not a live ID-byte switch.

TRACKS, ROUTES AND CHECKPOINTS
------------------------------

The course catalogue is frozen during offline or host preparation. It includes
admitted stock, enabled legacy and Track Lab courses, using logical IDs.
Custom course identity is not its position in Track Select. Menu rows can move;
the logical content identity remains what the match and documents refer to.
Clients verify the host's catalogue rather than building their own order.

dkr2_route reads the native checkpoint/racing line, including position, normal,
width, distance, main count and alternate route. A node includes an entity
handle for selecting it in the inspector. The reviewed native table is bounded
to 127 entries. This is the racing line, not every behaviour's private AI-node
format. Do not treat an alternate-route index as an editable heap pointer.

DKR2_EDIT_CHECKPOINT accepts its entity, float-bit XYZ, native yaw/pitch/roll
and float-bit scale. It validates the node, avoids coincident checkpoint
positions, moves it through native ownership and calls checkpoint_update_all.
That rebuilds the native planes, normals, distances and alternate-route data
together. Editing only a visible marker would leave race logic inconsistent.

Trigger radius/vehicle fields are reviewed for specific behaviours. Other
trigger/door/exit formats, route connectivity changes, new spawn-point layouts,
material remapping and structural meshes require the track authoring/reload
path or a dedicated reviewed code adapter. The inspector and symbol references
help develop that adapter; this release does not claim a universal live mesh
editor or writable material table.

COLLISION PROBES AND DEBUG OVERLAYS
-----------------------------------

dkr2_cast uses Dkr2Cast and Dkr2Hit. A radius of zero is a ray; positive radius
is a sphere sweep. Segment UINT_MAX searches intersected static segment bounds.
A specific segment restricts the query. Static scene triangles are bounded by
the work budget and per-segment limits. Exhausting fuel returns BUDGET, not an
apparently valid nearest hit from an incomplete scan.

Flag 1 adds ObjectInteraction spheres. Flag 2 skips static triangles; use 3
for dynamic interaction spheres only. These are actual native interaction
spheres, not moving-platform triangle meshes or a new physics engine. A
dynamic hit uses triangle UINT_MAX and texture as the bounded object ID.
A static hit's texture is its render batch texture ID, not a physics-material
classification. Keep those distinctions clear in an editor.

dkr2_debug publishes at most 64 line, box and ASCII-label primitives per
package. They are projected locally with the retained camera and viewport
clipping. No extra world render, GPU ray pass or renderer handle is exposed.
count=0 clears the list. Players can disable local debug display independently
without changing shared mod state or simulation fuel.

The Dev Menu shows nearby route lines/labels, selected-entity bounds and a probe
hit normal. It is a bounded overlay, not every collision triangle every frame.
For a large level, query a small useful area and refresh at a sensible cadence.
More diagnostic output should not make the game impossible to inspect.

COURSE LOADING, MENUS AND CUSTOM CUPS
-------------------------------------

DKR2_RESTART_SCENE and DKR2_WARP_COURSE use the normal level-property/transition
owners. Warp takes course-catalogue index, entrance and vehicle. The vehicle
must be supported by the admitted course. Warp allows entrance 0 only; other
courses have not constructed their spawn anchors yet. Restarting the current
scene can use an existing entrance with anchors for every current racer.
Do not guess an entrance index that has not been constructed.

DKR2_RETURN_MENU chooses Track Select, Character Select or Start menu. It uses
the normal pause-menu result/teardown path, not a direct jump into a menu
constructor. Pending transitions, menus and post-race viewports can reject
the request. Resume/pause state is reset as the transition takes ownership.

Custom trophy cups remain the API 1 trophies/racing adapter. Use the supplied
Custom Trophy Races example for cup selection, deterministic round order,
logical course selection, extended laps and result/HUD bounds. A runtime cup
picker must preserve that adapter's ownership rather than inventing a carrier
ID and jumping into the middle of trophy_flow.

Increasing only LevelHeader.laps is not enough. Native lap timings, result
cells and Time Trial records have fixed capacities. The reference adapter
owns an 8 by 100 timing table, registers the completed roster, handles the
finish sentinel and bounds result/HUD paths. Native Time Trial must stay off
for extended laps. Keep the exclusive race-laps and trophy-flow systems.

BUILDING YOUR OWN TOOLS UI
--------------------------

Declare tools-ui2: 2, add resources: {"tools-ui":"tools.json"}, and provide a
schema 2 document with pages. There may be 1..16 pages and 256 total fields.
Page IDs and field IDs must be valid, unique within their scope and stable.
Labels are bounded. Page labels allow 80 bytes; field labels allow 160.

    {
      "schema":2, "title":"My race tools",
      "pages":[{"id":"racer","label":"Racer","fields":[
        {"id":"status","type":"text","label":"Last action"},
        {"id":"bananas","type":"integer","label":"Bananas",
         "argument":2,"default":10,"min":0,"max":99},
        {"id":"apply","type":"button","label":"Apply bananas","action":10}
      ]}]
    }

Supported controls are text, button, number, integer, toggle, enum, vector,
colour, search, table and inspector. Diagnostics, authority, console, document,
camera, debug and reload provide reusable host widgets with their named module
requirements. They are not special cases for dkr_development_menu.

readonly makes a field inspection-only. visible names a published boolean,
so a mod can hide an unavailable operation. confirm requests a confirmation
for a button. help adds short guidance. table can select a supplied handle;
inspector can declare edit_action and racer_only. Numeric controls have finite
min/max/default values. Enum choices are an ordered bounded string list.

Use dedicated pages rather than deeply nested scroll areas. The host renders
one main page region with a fixed footer, centred actions, responsive page
navigation and on-screen keyboard access for text/numeric inputs. At narrow
widths the page selector becomes a dropdown. Keep labels short and put long
explanations in the guide, not under every button.

PUBLISHING VALUES AND RECEIVING ACTIONS
---------------------------------------

dkr2_ui_publish takes a JSON object up to 16 KiB, with at most 128 top-level
values. Keys match field IDs. Tables have at most 128 rows; each row may contain
name/detail and a slot/generation selection. Text fields can display strings or
other JSON values. Numeric fields must receive bounded finite numbers.
Visibility keys must be booleans. Malformed values return INVALID.

Publishing copies presentation data. It is throttled; END can mean no publish
on that tick. Do not branch gameplay on whether the local UI is open. Native
cost measurements and camera/debug visibility are not simulation inputs.
The mod should build the same valid report regardless of which peer reads it.

Action IDs are 1..65531. IDs 65532..65534 are reserved for document upload.
Each Dkr2Action is 64 bytes: owner, command ID, action and 13 words. Only the
first 12 argument words are author parameters. The final word is scene ownership
inserted by the host. Never use it for another slider value.

    DKR_CALLBACK(on_tool) void tools(void) {
        Dkr2Action command;
        int status=dkr2_action_next(&command);
        if(status!=DKR2_OK) return;
        if(command.action==10) {
            Dkr2Handle h={command.arguments[0],command.arguments[1]};
            Dkr2Edit e={h,DKR2_RACER_BANANAS,{.bits=command.arguments[2]}};
            int applied=dkr2_edit(&e,1);
            /* Publish applied as your status; do not assume a queue means success. */
        }
    }

This example requires a development tool session and a selected handle.
Parameters are staged in local UI draft state until Apply is pressed. UI fields
that intentionally share argument indices replace those words as the user
edits them; do not reuse an index for unrelated simultaneous controls.
For a larger tool, allocate arguments deliberately and document every action.

SETTINGS AND FROZEN CONFIGURATION
---------------------------------

Scalar settings use Number, Enum or String schemas and the existing launcher
Details editor. Number has finite min/max/default. Enum stores an option index;
String has its bounded text default. Keep IDs stable across versions. The
original dkr_setting_number/string imports remain the read interface.

API 1 settings are frozen for a running session. API 2 can use dkr2_setting_set
and reset for package-local Number/Enum overrides, applied in a tick. They
are checkpointed and cleared on relaunch. The frozen launch settings and
dependency identity are not rewritten. Strings and resource-changing settings
are not silently made live. Relaunch to prepare changed dependencies.

dkr2_catalogue("settings",...) reports this package's schema/current live
value. all-settings reports the canonical package_index and option_index for
every admitted code package, with api, live_allowed, overridden and live values.
API 1 options are always frozen. API 2 does not mean every option is live.

For a mod to accept live edits from a development tool, declare live-settings2
and list reviewed Number/Enum IDs in dkr.json live_options. The tool calls
dkr2_setting_apply(package_index,option_index,&value), or passes null to reset.
It needs a development session and the same ordered online command boundary
as other game edits. The target gets an optional owner-only on_live_setting
callback: option index, value bits, tag. Tag 0 is reset, 1 float, 2 enum. Read
the normal setting imports again when resetting; do not treat tag 0 as zero.
An unchanged override does not generate another callback. Private cached state
and the override both rewind. A genuine callback fault stops safely, not silently.

    /* dkr.json: "live_options": ["pace"] and "live-settings2": 2 */
    DKR_CALLBACK(on_live_setting)
    void changed(unsigned option, unsigned bits, unsigned tag) {
        (void)option; (void)bits; (void)tag;
        pace = dkr_setting_number("pace");
    }

Only opt in settings whose initialization/resource ownership really supports
this. String values, file choices, dependencies and asset-changing options
stay next-launch. A typed dependency export is still useful for more complex
cooperative configuration. Do not pretend the editor can guess it.

examples/live-ai-difficulty demonstrates that contract. It keeps the same
seven AI/boss pace settings and refreshes its private pace cache when the
callback arrives. It owns ai-pace, so do not enable it alongside the original
difficulty mod. The original API 1 example remains unchanged.

Structured configuration uses dkr_data_size/read: immutable canonical JSON
documents supplied at admission. Read in chunks no larger than 4096 bytes.
The trophy cup definitions are a practical example. Runtime development
documents are a separate user-approved channel, not a replacement for the
frozen launch configuration.

DOCUMENTS, SCENE PATCHES AND SHARING
------------------------------------

Declare documents2 and a document UI field with a scoped document ID and
import_action. The mod publishes a JSON payload no larger than 12,000 bytes.
The host wraps it with schema, package ID/digest, ROM revision, logical course
and document kind. The canonical wrapper must fit 16 KiB.

The player chooses a .dkrdev path, exports a new file or reads an import for
review. Exports do not overwrite an existing file. The host uses temporary
storage and only publishes the completed new file. File access is not performed
on the simulation thread and is not an arbitrary filesystem service for mods.

An approved import is bounded, checksummed and sent in ordered 40-byte chunks.
Online it uses the same host command stream as other tool actions. A maximum
size document can take several hundred simulation transport frames; this is
not the mod-installation download protocol. No client applies its file locally
while other peers continue with different state.

At the import action, call document_size/read in 4096-byte chunks, validate
the entire payload, resolve every target and only then apply one typed batch.
Call document_accept after processing. Treat missing, ambiguous or stale
targets as rejection. Do not apply the first half and discover the last target
belongs to a different object.

The example's scene-patch document contains deduplicated property edits and
a test-position bookmark. Racers resolve by native roster index; static objects
by ID/behaviour/original position. Missing or ambiguous identities reject the
whole batch. It does not export raw RAM, source track geometry, route topology,
ROM assets or someone's save. checkpoint-patch is a separate single-checkpoint
document: version, checkpoint index, total/main counts, position, angles in
yaw/pitch/roll order and scale. Import checks the counts and uses the native
checkpoint move/rebuild operation once. It does not combine unrelated property
and route commits into a falsely atomic operation. Prepare it from the inspected
checkpoint index; enter the same course and package edition before importing.
Authors can define additional scoped schemas
for reviewed adapters and must validate their own payloads just as carefully.

ONLINE DEVELOPMENT - HOST AUTHORITY
-----------------------------------

Use the v1.0.14 Rollback host mode for code mods. Unsupported engines do not acquire
shared code support just because a tools panel exists. Enable Development
lobby before hosting a session with development mods. Every racer must consent
before readying. All peers admit the same package editions, dependencies,
frozen configuration, courses and owned-engine contract before startup.

Clients can browse copied inspectors and use local presentation tools. A shared
edit becomes a request to the host. The host approves or declines it, or grants
that peer permission to request subsequent commands without another prompt.
Permission is revocable and scene-epoch scoped. A client request never writes
its local gameplay state immediately. Host-local buttons use the same queue.

The host assigns a fresh ordered command ID. Commands travel in final owner
input records, using the same acknowledgement, repair, conflict handling and
rollback as player input. Predicted commands are empty: a spawn is not repeated
because the next predicted player input looks similar. Commands wait for an
agreed prefix before resource/game-changing work is allowed.

Scene and entity generations reject stale requests. Pending requests and the
shared action audit are visible on Online Development. 'Queued' or 'ordered'
does not prove the operation succeeded; check the mod's returned status too.
Input transport and authenticated request traffic stay active during shared
pause. Step and timestep controls are agreed actions, not per-client cheats.

SDK 2 uses a new network contract. Keep every peer on the matching beta 7
package, not a mix of beta 5/6/7. Never suppress a real VM/native failure and
continue a potentially divergent race. The normal safe-stop remains required.

SAVE PROTECTION
---------------

Offline development packages use a separate saves/development namespace,
selected before retail initialization. Their paks are separate too. The route
does not switch halfway through the game or migrate your ordinary adventure
progress into a development profile behind your back.

An online Development lobby starts from the authenticated online seed, but
experimental progress is not committed to the ordinary durable online profile.
The original verified seed is retained for lobby return. This applies even
when a development lobby currently has no development package enabled.

This is a development workflow, not a recovery guarantee for malicious native
code. Keep your own backups. Do not ship a normal gameplay mod with development
enabled merely because it uses API 2: that flag changes save/admission policy.
Conversely, do not remove it from the Dev Menu to make debugging edits look
like an ordinary legitimate progression session.

LOCAL CAMERA AND CAMERA++
-------------------------

presentation2's camera widget asks before taking temporary control of a local
viewport. It does not silently replace ModernCamera++. Releasing control
restores the existing provider. Scene changes and scripted views return to
the normal camera. The retained presentation image owns the render resources;
the tools never write the shared simulation camera to move another peer's view.

The widget supports free/spectator motion, following a selected entity, XYZ,
yaw/pitch, FOV, speed, smoothing and a local position bookmark. Use the existing
camera look/zoom actions when the tools are closed. On a local multiplayer
screen choose the intended viewport. Online, ownership selects the local view.

The private draw closure currently bounds this camera to 1000 world units from
the local racer and the normal loaded-scene envelope. Leaving it restores the
normal camera. This is intentional resource safety, not an unlimited editor
flying anywhere through unloaded geometry. VR/headset support is not claimed
by this SDK/Dev Menu release. ModernCamera++ remains the supplied flat-screen
presentation mod, including the beta 6 shadow interpolation correction.

The camera screen offers Hide race HUD on this screen and Hide tools for
screenshot. HUD hiding is a local decoder scissor within the existing HUD draw
scope; native HUD updates, audio, DMA and task completion still run. It applies
to this machine's race HUD, not another peer's HUD. A new native draw is needed
to change a retained image, so step once if the simulation is paused. Nameplates
and user-enabled performance overlays are separate from the native race HUD.
Scene unload and session return restore visibility. No extra world rendering
or GPU ray tracing is used by these controls.

ENVIRONMENT, LIGHTING AND AUDIO
-------------------------------

SET_FOG adjusts the original game's viewport fog, with validated near/far and
RGB values. SET_WEATHER adjusts an already initialized scene weather system.
It does not instantiate rain/snow resources in a dry scene. That needs resource
preparation and reload. These APIs do not reintroduce the removed atmosphere,
dynamic-shadow or volumetric-lighting system.

Object ambient/diffuse/light fields describe native shading, not RTX lights.
Persistent overrides run after the reviewed native shading update. Select an
object that actually owns the corresponding shading record; unsupported
behaviours return UNAVAILABLE rather than interpreting another struct as light.

Audio operations are verified native sound, music, stop and fade commands.
IDs are checked against initialized native banks. The audio catalogue reports
sound/sequence counts; it is not a guessed list of names for custom imported
banks. Sequence IDs must fit the native path's width as well as its bank count.
Online audio still follows the confirmed owned PCM/effect pipeline.

No mod receives an SDL audio device, host mixer pointer or permission to run
a second audio thread. Local volume/mute belongs to the app's existing Sound
settings. A mod wanting continuous custom audio behaviour needs a reviewed
adapter and declared resources, not repeated device initialization calls.

THE DEV MENU - NINE SCREENS
---------------------------

Session shows current scene/tick, authority, save scope and operation result.
Pause/resume/single-update and speed presets use the retired control lane.
Restart and admitted-course warp use normal transitions; the return picker
chooses Track Select, Character Select or Start menu. Reset drops the example's
racer/property overrides. Check the returned status after every request.
An entrance-aware restart requires native spawn anchors for every current racer.
The lap override is limited to 1..3 (0 resets), ordinary races and before any
racer completes lap one. It refuses an admitted race-laps owner, including
Custom Trophy Races. That adapter owns extended timing and cup order; configure
its lineup before launch instead of competing with it mid-race.

Racers / AI selects a racer with the live inspector, changes bananas/skill/
ammunition, stops motion, freezes CPUs, maintains boost/shield/bananas and
applies selected-racer weight/handling/acceleration/cap tuning. A boss does
not magically gain the standard vehicle acceleration curve. Reset before
comparing behaviour against an AI difficulty mod that owns similar rules.

Objects shows typed properties and their units, with supported edits. It also
offers integer/float property actions, undo/redo, teleport, reviewed scenery
cloning and deletion of tool-owned spawns. Selecting a row is local. Applying
an edit is shared offline/host-authorized work. Stale targets need reselection.

Track / Routes lists the frozen course catalogue, native route pages and local
route/target/probe overlays. It moves reviewed checkpoints with native rebuild,
runs static/interaction-sphere probes and prepares property/bookmark/checkpoint documents
for export/import. It does not overwrite the installed .dkrmap or patch a ROM.
The scene inspector pages bounds, triangles, AI links, spawn anchors and triggers.
Highlight a segment box or one triangle's wireframe and normal. This is bounded
debug geometry, not an expensive all-track wireframe renderer.

Camera / Bookmarks marks and returns a gameplay target position, and separately
controls the local presentation camera. Those are different operations: target
teleport affects gameplay, local camera motion does not. Explicit handoff keeps
Camera++ ownership predictable.

Environment / Audio edits original fog, initialized weather and selected-object
shading. It exposes native sound/music/stop/fade controls. None is a replacement
RTX renderer, new atmosphere system or external audio player.

Mods / Values lists active code editions/modules, instruction/native-call fuel,
local physical costs, symbol/hook capabilities, recent deterministic events
and the offline compatible-reload panel. Full exact API references remain in
the SDK; a symbol being visible does not make its native call permitted.
The resource picker browses admitted character/model/header/texture/sprite/cup
identities, native audio IDs, hooks, callbacks and all settings. Apply only an
option marked live_allowed. Other settings require a relaunch.

Commands / History exposes registered actions, typed word arguments, recent
commands and a bounded scene-local macro recorder. Refresh, undo and redo use
the same normal command path as their buttons. Entity-based macros cannot be
replayed safely after a scene change and are disabled there.
Registered actions have completion from current tool fields, parameter hints
and pin-to-favourites. The footer Workspace opens presets and watches.

Online Development shows local/host authority, development save status,
pending client requests, host grant/revoke and recent shared actions. A normal
lobby is not silently promoted to a development lobby by opening this page.

THE COMMAND CONSOLE IS NOT A SHELL
----------------------------------

Enter a registered action ID followed by up to 12 words. Decimal integers,
0xHEX and f:float syntax are supported. Negative integers encode signed 32-bit
words. Floating arguments are stored as IEEE float bits. Action IDs are taken
from the immutable tools schema, not arbitrary function addresses.

    105 12 4 10

In the Dev Menu that means action 105 with slot 12, generation 4 and bananas
10. Those handle numbers are only an example. Select a real target first,
or inspect its current lifetime; do not copy a stale example into a live race.
An action with confirm still asks for confirmation when run through the console.

History holds at most 64 commands. A recorded macro holds at most 32 actions
and their parameters in the current tools session. Playback requests each
action through normal authority/lifetime checks. It is not an atomic batch of
32 unrelated native operations; an individual rejected command is still rejected.
The scene-patch document is the durable sharing format for the example's
property edits/bookmark. Session console history/macros are not exported as
an unrestricted script or automatically replayed on the next launch.

COMPATIBLE OFFLINE LIVE RELOAD
------------------------------

An API 2 development package can opt in with live_reload: "instruction-only"
and a nonzero state_schema. Declare reload2 for the host reload widget. Both
the running and incoming editions must opt in with the same schema and ID.
The player selects a fresh .nrm, inspects its identity and explicitly trusts it.
Reload is session-only; installed library files and the selected save route
are unchanged.

The loader relinks the set and requires equal owner/region/function layouts,
imports, hooks, events, profiles, capabilities, settings schema, resources
(apart from source-map), and initial non-executable bytes. Only instruction
bytes may differ. This is deliberately strict. Adding a global, changing a
string constant or altering tools/resources can require a full relaunch.

Cleanup and migration run through on_cleanup and on_reload, for that owner
only. The host preallocates rollback state first. Those callbacks may update
private VM state through core services. Game RAM and other owners' state are
read-only, and native/game/world service calls are forbidden. A callback fault
restores the old instructions, private state, RNG, heap and pending effects.
Do not free/spawn native objects from a cleanup callback.

Tool-owned spawns, active native scopes or a document upload prevent live
reload. Remove/finish them or relaunch. Online code replacement is not allowed:
return everyone to the lobby, import the new edition, readmit dependencies and
ready again. Do not relax the identity check to force a unilateral hot reload.

DIAGNOSTICS AND SOURCE MAPS
---------------------------

Build with --source-map to preserve callback source locations. The builder
uses debug-line information and canonical /mod and /sdk prefixes, avoiding
your local absolute development paths. tools.py source-map can also generate
a map from a suitable ELF. The package inspector prints identity, digest,
API, online/revision/module declarations and member counts.

VM errors include package and callback address; an included source map adds
the callback's source entry. It is not an exact failing instruction/line trace.
Keep the ELF/source version matching the reported package digest. A source
map from another edition can be worse than no source map.

The diagnostics widget reports resident/heap allocation, callbacks, executed
instructions, native calls and local CPU cost. Costs include replayed physical
work. They are local measurements, not checkpointed simulation clocks and not
values to use for deterministic gameplay decisions.

Inspect the first meaningful error, not just the final disconnect. INVALID
means check types/ranges/IDs. STALE means reacquire the entity. PHASE means use
the proper tick or confirmed transition. AUTHORITY means development/host/owner
permission. UNAVAILABLE means no reviewed adapter here. BUDGET means reduce
work. CONFLICT means ownership/duplicate work. RELOAD means prepare resources
or restart; it is not a request to bypass a guard.

WORK AND MEMORY BUDGETS
-----------------------

compact requests a 32 KiB heap, 100,000 instruction callback ceiling and 32
native calls. standard requests 64 KiB, 200,000 and 128. large requests 256 KiB,
500,000 and 256. API 1 keeps its existing allocation/limits. API 2 requests are
admitted before launch; the combined request ceiling is 2,000,000 instructions
and 1024 native calls, within the existing 8 MiB resident mod budget.

The game world also has a shared tick budget. Nested calls do not manufacture
fresh fuel. A package ceiling is not permission to consume that much for every
entity or import. Native work and collision scans are charged. Callback depth,
private VM/native stacks and the native call closure stay bounded.

Other important bounds: 512 handles per owner, 32 edits per batch, 32 persistent
property overrides, 32 tool spawns, 32 queued events, 64 debug primitives,
128 settings/options, 128 top-level published UI values, 16 pages, 256 fields,
16 KiB published UI/doc wrapper, 12,000-byte document payload and 4096-byte
resource/data/read chunks. The native race roster remains at most 8 and local
human input ownership remains at most 4. This is not an unlimited roster mod.

A package is at most 32 MiB compressed input and 64 MiB expanded, with at most
512 archive members. There are further symbol/code/relocation and dependency
limits. Treat a limit error as useful feedback. Avoid enormous per-frame JSON,
one import per triangle and full catalogue rebuilding every tick. Cache immutable
metadata in your private state and page large inspectors.

EXPERT GAME MEMORY AND HOOKS
----------------------------

dkr_game_symbol and symbol_size resolve reviewed named data. Unknown names
return no pointer. The SDK includes revision-specific symbol and decomp layout
references. Check the selected revision and field extent before using an expert
pointer. Never ship a raw address from one ROM as if it were revision-independent.

Named before/after callbacks and DKR_HOOK/DKR_HOOK_RETURN support eligible
native functions. DKR_REPLACE is exclusive replacement, not a cooperative
modifier. Declare incompatible systems/conflicts rather than depending on
load order. A replacement must preserve the actual calling convention and
native ownership invariants. A hook's argument registers are caller-saved;
an exit hook must not assume its entry arguments survived the original call.

DkrGameCall carries guest integer/FPU context, HI/LO and FCSR. Outgoing O32
arguments 5 onward are stack words; align the guest stack. dkr_call4 is the
convenience helper for four integer-word arguments, not a universal float/
struct-return ABI converter. Use the exact decomp signature for anything else.
The public headers document context access, skip-original and bounded repeat.

System/scheduler/thread/renderer/device and unsafe loader ownership stays
protected. Do not call initialization twice, replace an allocator while live
objects exist, free another system's object, or patch render buffers from a
gameplay callback. Expert access is powerful and trusted; it does not turn
arbitrary native code into a safe transaction.

FREESTANDING C++
----------------

Use tools.py new --language cpp for the optional C++ scaffold. The same public
C ABI remains underneath. development.hpp adds small handle/query/UI helpers;
it is not a host plugin framework. The builder uses C++17 without exceptions,
RTTI or thread-safe static initialization. Keep exported callbacks extern "C".

No host STL, host threads, filesystem, networking or dynamic library loading.
Avoid a dependency that quietly expects libc++, exceptions, global constructors
or an OS allocator. Use dkr_alloc/free for bounded guest allocations and the
included runtime helpers. Do not assume new/delete or a full C++ standard
library is present just because the source extension is .cpp.

Keep interchange records fixed-width O32 fields. No platform-sized long,
size_t or host pointer in a published record. Header static_asserts check
handle, edit, entity, action, collision, event, route, debug and tuning sizes
when compiled as C++. Preserve size fields and initialize them for queries.

DEPENDENCIES, SYSTEMS AND COOPERATIVE MODS
------------------------------------------

Declare package dependencies with the minimum compatible version. Resolution
creates a canonical order before activation. Missing dependencies, conflicting
packages, duplicate replacements and exclusive systems reject at preparation,
not halfway through a race. No process-global monkey patch should decide who
wins just because one machine loaded a mod first.

Use exported functions/events for cooperation. Let one owner validate a live
configuration change, then expose an event/result to dependents. Keep the event
payload a documented guest ABI. Do not pass a host pointer or expect a borrowed
temporary buffer to outlive its callback.

Only one simulation2 provider and one racer-tuning2 provider may own a session.
The Dev Menu also declares development-tools. Existing trophy race-laps and
trophy-flow ownership is retained. Typed persistent field ownership is checked
between API 2 override providers. API 1 expert mods still need honest conflict
metadata when they touch the same native systems.

WORKSPACES, FAVOURITES AND MULTI-TARGET WATCHES
-----------------------------------------------

Right - you do not need to set the same inspection layout up every time.
Open Workspace in the tools footer. It has one dedicated scroll area, the
current watch list, a path textbox with the on-screen keyboard and a fixed
Back action. Save different named .dkrtools files for different jobs.

A workspace stores the current page, numeric drafts, declared search fields,
up to 128 pinned actions and up to 32 watched fields. It is edition-scoped:
package ID/digest and schema must match. Export does not overwrite an existing
file. Reading a file shows a review before applying it. Import only changes
local UI drafts and watch definitions. It never starts a race, submits an edit,
runs a macro or changes an installed mod setting by itself.

Pin a typed field from the inspector. A watch keeps its racer index or static
object ID, behaviour and original XYZ, not a process pointer. Within the same
scene it keeps the actual handle generation. If that object dies, it stays
stale instead of attaching itself to the next allocation at the same address.
On a new scene or an imported workspace, a unique symbolic match can rebind.
Missing or ambiguous matches remain unresolved. You can explicitly rebind a
watch to the selected entity or remove it. Do not read a stale value as current.

Recorded command macros retain their original local session and scene token.
They are only restored from a workspace within that same session/scene. Old
macros containing entity handles are dropped on import into a later game run.
Favourites, numeric drafts and symbolic watches remain useful across runs.
This is intentional - a persistent macro is not permission to hit a different
object that happens to reuse slot 15 next time.

The exporter excludes file/reload paths, imported document contents, native
addresses, lobby secrets and private save data. Only schema-declared search
text is eligible. Workspaces are bounded to 32 KiB and every field is validated
before replacing local drafts. They are not executable scripts or a shell.

To save a camera/test position across runs, use the reviewed scene-patch
bookmark document or enter the coordinates as a local camera preset. A bare
handle is never a portable bookmark. Keep a copy of your original source track.

RESOURCE AND LOADED-SCENE CATALOGUE REFERENCE
---------------------------------------------

Use dkr2_catalogue(name,index,buffer,capacity). It returns the required byte
count including the terminating NUL, END past the list or a negative status.
Pass a bounded buffer and check the result before parsing. JSON records are
at most 4095 bytes, have explicit scope and use logical IDs. Enumerate a small
page, not every asset on every frame. Disabled/unadmitted packs are not secretly
loaded just because an editor wants their names.

courses includes stock, admitted legacy and modern Track Lab tracks with
carrier, vehicles and stable content identity. cups includes admitted cup
metadata. Start cups in Track Select so the normal trophy owner sets lineup,
rounds, order and timing. Do not manually write a trophy flow global.

characters includes the ten native characters and admitted custom characters.
character-assets describes admitted custom portrait/header/stage/model IDs.
These describe resources, not permission to swap a live racer's ID. Re-enter
Character Select or use a reviewed scene/model initialization adapter instead.

models, textures-3d, textures-2d, sprites, object-templates and level-models
are indexed records in the admitted boot namespace (or native base bank when
no legacy namespace exists). Each record includes its bank fingerprint, section,
count, index and scope. A global object-header index is not automatically a
level-entry spawn-template ID. Clone scenery uses a real current object entry,
not an arbitrary number typed from this catalogue. No ROM/texture bytes or host
source paths are exposed by the metadata browser.

sounds and sequences enumerate initialized native IDs. audio provides their
counts. resources lists the calling package's declared immutable resources;
packages, hooks and callbacks list actual admitted owners. all-settings includes
the exact live-edit opt-in contract. Native sound names are not guessed.

track-tools2 adds these current-scene lists:

    scene          Current map/race/vehicle/lap/fog and course-height metadata
    segments       Bounds plus vertex/triangle/batch counts for each segment
    triangles/N    A triangle in segment N: three vertices, normal, texture ID
    ai-nodes       Native graph node ID, elevation and cached adjacent links
    spawn-points   Racer anchor, entrance, vehicle, yaw, position and segment
    triggers       Reviewed trigger position, radius, vehicle and behaviour

These are bounded read-only snapshots of the loaded guest scene. Triangle
texture is a render-batch identity, not a claim to be a universal physics
material. The triangle inspector/highlight complements collision ray/sweep
results; it does not rewrite a moving platform's collision mesh. Water height,
wave parameters and material changes need the proper prepared resource path,
not a blind write to a field with a similar-looking name.

The Dev Menu highlights one segment box or one triangle wireframe/normal at
a time, together with target/probe markers. Switch back to Racing route to
restore checkpoint overlays. Lists and debug work stay within portable budgets.
There is no full-track extra render pass and no GPU ray-tracing dependency.

WHAT IS NOT A UNIVERSAL TYPED EDITOR
------------------------------------

The SDK gives broad expert hooks and game memory access plus reviewed typed
tools. It does not promise a safe setter for every decomp field. Character/
vehicle swapping, route-link topology, spawn-point table creation, arbitrary
boss state machines, moving-mesh collision and live structural geometry need
their own native lifecycle adapter or a prepared scene reload.

The catalogues cover admitted stock/legacy/Track Lab courses, custom cups,
characters, model/header/texture/sprite banks and initialized native audio,
alongside active code packages, hooks, callbacks and settings. They are not an
offline disk-library search for disabled packs, nor a symbolic name for every
sound in a modified ROM. Resource counts/IDs come from the admitted bank, not
invented placeholder names. The decomp references remain available for experts.

The example supplies a live typed inspector, multi-target watches, favourites,
reviewed workspaces, bounded event trace, history/macros and property/bookmark/
checkpoint documents. It does not claim an arbitrary persistent command script,
unlimited database, complete AI-nav editor or offline level-authoring suite. Use dedicated
tools for structural assets and extend reviewed public adapters for genuinely
new runtime behaviour. Unsupported work is explicit, never a hidden no-op.

MIGRATING AN EXISTING MOD
-------------------------

First build it unchanged with the compatibility headers and api: 1. Keep its
ID, scalar settings, data documents, exclusive systems and revision declarations.
Do not add development mode unless it is actually a development tool. Compare
your published ABI/manifest and retain the old edition for recovery.

Only opt into api: 2 when you need a new module. Add the exact requires entry,
choose a sensible profile, initialize size-tagged structs and handle status
returns. Replace stale raw entity pointers with reacquired handles where you
can. Move UI actions out of draw hooks and into deterministic tool commands.

Keep API 1 frozen settings semantics unless the author deliberately adopts
live numeric/enum overrides. New data formats need an explicit version and
validation/migration path. Never quietly overwrite imported cup definitions,
installed mods or a normal adventure save just to make an upgrade look seamless.

For a camera mod, stay in the local presentation domain. For a shared rules
mod, stay in the simulation domain and use admitted logical input/RNG. A single
package can cooperate with other packages, but it must not use one peer's
local camera or native profiling time as a shared gameplay input.

A SENSIBLE DEVELOPMENT WORKFLOW
-------------------------------

Start with one mod and one ordinary course. Get the small change right before
adding a dozen hooks. Log a short initialization message and meaningful failures,
not an unbounded report per racer per frame. Keep the current source, ELF,
manifest, package digest and guide together in version control.

Use a development save/session for tools. Confirm that leaving a scene clears
handles, undo selections, native scopes and spawned ownership. Read results
after operations. A UI click only queued a request; it did not bypass authority,
phase, memory or work validation.

Check both supported revisions if you advertise both. For online work, players
need matching app/package editions and admitted settings. Exercise host and
client requests, declined requests, scene changes, pause/resume and a late
input correction. Do not call a compile a multiplayer acceptance result.

Gameplay, visuals, performance, controller behaviour and multiplayer acceptance
for this playtest are carried out by the people running it. The distribution
records compiler/package checks separately. No game or mod was executed by
the build agent as part of those checks.

TROUBLESHOOTING WITHOUT GUESSWORK
---------------------------------

Missing import/module: check api, exact requires name/version, selected ROM and
backend. Unknown settings/resource: match stable logical IDs and include the
declared file. Package rejected: inspect archive paths, sizes, duplicate names,
manifest game_id and module requirements before weakening any guard.

Edit keeps reverting: the native behaviour may own that value every update.
Use a reviewed persistent override or a scoped behaviour hook, not a faster
UI timer. Teleport snaps back: you bypassed native path/segment bookkeeping or
another racing system owns the target. Use the teleport adapter and inspect
the returned status.

No route/weather/shading: the current scene/object may not own that system.
Enter the proper scene or use an adapter that prepares its resources. A normal
dry scene cannot safely acquire snow by writing one enable flag. A wrong object
struct cannot safely become a light just because the offsets look plausible.

Online action has no immediate effect: it may be waiting for host approval,
transport repair or the agreed prefix. Inspect authority/history and the mod's
result. If a scene changed, reselect the target and send a fresh request.
Never retry a spawn by applying it locally on only one peer.

Reload rejected: compare initial data, function/region layout, imports, hooks,
resources, options, profile and state_schema. Instruction-only is intentionally
narrow. Use a relaunch when the new edition changes those things. Do not change
the digest or receipt manually to make an incompatible image look compatible.

PUBLISHING YOUR MOD
-------------------

Ship the .nrm, source/license if appropriate, exact minimum application/API/
module versions, supported revisions, conflict/dependency notes and a short
explanation of what changes. Say whether it is a normal gameplay mod or an
isolated development tool. Give people a clear way to disable it and start again.

Inspect the final archive, not just the build directory. Exclude ROMs, saves,
private configuration, personal absolute paths and huge debug artifacts you
did not intend to publish. Keep source maps free of local path leaks. Testers
should report app version, mod digest/edition, ROM revision, backend and the
first useful runtime error.

Do not advertise 'changes absolutely anything safely' or 'bug free' just because
the VM admitted a package. The point of SDK 2.0 is that we can do a lot more,
with proper tools and explicit ownership. Keep the power, keep the limits
honest, and give the next modder enough information to build on your work.

WORKED EXAMPLE - A SETTING THAT CHANGES GAMEPLAY
------------------------------------------------

The kit's guide-starting-bananas is a deliberately small complete API 1 example.
It is useful even if your next project uses API 2, because it shows the whole
route from a launcher setting to an admitted deterministic native-roster edit.
Its dkr.json requires core, settings, game and racers at version 1, declares
both supported revisions and owns the starting-bananas system.

Put scalar settings in mod.toml under manifest.config_options. The builder
turns these into the package settings schema. Do not create a random settings
resource and expect the launcher to infer that it is a settings menu.

    [[manifest.config_options]]
    id = "bananas"
    name = "Starting bananas"
    type = "Number"
    default = 5.0
    min = 0.0
    max = 10.0

    [[manifest.config_options]]
    id = "affect"
    name = "Apply to"
    type = "Enum"
    options = ["Everyone", "Humans only", "AI only"]
    default = "Everyone"

    [[manifest.config_options]]
    id = "label"
    name = "Session label"
    type = "String"
    default = "Starting Bananas ready"

on_boot reads bananas through dkr_setting_number and rounds inside its declared
range. affect is the enum index, not its visible string. The label demonstrates
the length-query pattern: ask dkr_setting_string with a null destination, check
the required bytes including NUL, then read into a sufficiently large buffer.
Do not pass a 16-byte array for a possible 96-byte string.

    DKR_CALLBACK(after_track_setup_racers) void apply_start(void) {
        unsigned count=dkr_racer_count();
        for(unsigned i=0;i<count;++i) {
            DkrRacerState racer;
            if(!dkr_racer_get(i,&racer) || racer.finished) continue;
            if(affected==1 && racer.player<0) continue;
            if(affected==2 && racer.player>=0) continue;
            dkr_racer_set(i,DKR_RACER_BANANAS,starting_bananas);
        }
    }

The complete source defines affected and starting_bananas in on_boot. This is
a roster-setup teaching example: demos and menu rosters can also use that native
setup. A published race-only rule needs reviewed game-mode/race-type guards.
Do not copy a callback name and assume it runs only in the exact scene you had
in mind. Keep the original .nrm for comparison while developing a new edition.

REVIEWED OPERATION WORD REFERENCE
---------------------------------

dkr2_operation takes an operation ID plus two 16-word arrays. Zero unused
arguments. Floats are IEEE float bits; signed values occupy a 32-bit word.
Availability checks the selected CPU dispatch closure, then the call checks
phase, authority, scene data, ranges and budgets. A nonnegative availability
preview does not skip those runtime requirements.

    1 TELEPORT:       [0]slot [1]generation [2..4]float XYZ
    2 CLONE_SCENERY: [0]template slot [1]generation [2..4]float XYZ
    3 DELETE_SPAWN:  [0]tool-owned slot [1]generation
    4 CHECKPOINT:    [0]slot [1]generation [2..4]float XYZ
                     [5]yaw [6]pitch [7]roll [8]float scale
    10 FOG:          [0]viewport [1]near [2]far [3]R [4]G [5]B
    11 WEATHER:      [0]velocity X [1]Y [2]Z [3]intensity
                     [4]opacity [5]transition ticks
    20 SOUND:        [0]sound ID
    21 MUSIC:        [0]sequence ID
    22 STOP MUSIC:   no parameters
    23 FADE MUSIC:   [0]signed native fade value
    30 RESTART:      [1]entrance (0 or a fully anchored existing entrance)
    31 WARP:         [0]frozen course index [1]entrance 0 [2]vehicle
    32 RETURN MENU:  [0]0 Track Select / 1 Character Select / 2 Start menu
    40 RACE LAPS:    [0]1..3 laps; 0 resets this owner's override

Clone result[0] is an owner spawn-storage slot, not an immediately valid entity
handle. Re-enumerate after native initialization to obtain the lifetime handle.
Do not feed that storage index into DELETE_SPAWN as if generations were optional.
Restart uses the active playable course and default vehicle; it does not make
a new resource-changing vehicle/character choice live.
Race laps only changes an ordinary first-lap race with no race-laps mod owner.
It never increases the native three-slot result arrays or edits cup sequencing.

Fog viewport is 0..3, near 0..1018, far up to 1023 and at least five greater
than near. RGB is 0..255. Weather velocity is bounded to +/-1048576 native
units, intensity/opacity 0..65536 and transition 1..3600 ticks. Weather must
already be initialized. These are native units, not a graphics preset system.

Simulation control is a separate dkr2_control call: 0 resets/resumes, 1 pauses,
2 grants one update while paused, 3 sets retail logic units 1..4. This scales
the retail timestep; it does not simulate four extra fully subdivided physics
ticks or change audio pitch. Use bounded debugging speeds, not a timing rewrite.

INPUT, HUD AND IMMUTABLE RESOURCES
----------------------------------

API 1 dkr_input_get reads recorded logical held/pressed/released buttons and
stick axes for a native player port. These are the inputs the game owns, not
an independent SDL device read. Online decisions must use admitted logical
inputs or ordered commands, never whichever physical controller happens to be
connected to this computer.

dkr_hud_text works only inside an eligible drawing callback with an already
loaded font and the current menu/HUD list owner. Pass target, font, screen
coordinates, RRGGBBAA, alignment and printable ASCII. Text is at most 160 bytes
per call and 512 glyphs per package/frame. Do not recurse from inside draw_text
or invent a display-list pointer. A full custom texture/widget renderer needs
a dedicated rendering adapter, not a string converted to a GPU address.

dkr_resource_size/read accesses declared immutable resources by logical ID.
Query size, allocate a bounded buffer or stream in <=4096-byte chunks, and
validate your format before using it. dkr_data_size/read is similar but reads
the canonical frozen structured configuration, not the original resource file.
Both are stable admitted data, not live disk reads during a replay.

Use dkr_random_u32 for gameplay randomness. Its per-owner stream is checkpointed.
Taking an extra random number because a UI page is open changes the simulation;
do not do that. Use local presentation state for cosmetic UI randomness, or
derive a deterministic visual choice without changing the shared stream.

dkr_alloc uses the package's bounded guest heap and 16-byte alignment. Failed
allocation returns null. Check it. Free only a live allocation from that heap,
once. A native Object pointer is not an SDK heap allocation. A callback-local
array or temporary document buffer cannot be retained by another callback
unless its lifetime and ownership actually extend that far.

EXAMPLE SOURCE TOUR
-------------------

examples/dev-menu/mod.toml identifies the ordinary portable example and selects
main.c. dkr.json opts into API 2, development saves, the large portable profile,
its explicit version-2 modules and instruction-only reload state schema.
tools.json is the generated immutable nine-page tools resource.

main.c owns private command state, the current handle, property history,
bookmarks, event trace, route/probe snapshots and typed override selections.
The action switch is the public command protocol. on_tool consumes actions
and publishes bounded snapshots; on_frame applies the selected ordinary
gameplay overrides. Neither depends on an ImGui pointer or a secret host export.

documents.inl defines the example's small scene-patch payload and target
resolution. It validates all identities and values before dkr2_edit commits.
The source distinguishes static object identity from a racer roster index and
rejects ambiguous matches. Reuse that pattern rather than exporting raw RAM.
track.inl handles bounded geometry highlights and the separate checkpoint-patch
schema. The whole example compiles through the same public imports as your mod.

The native engine's generic tools renderer, input broker and service adapters
are implementation details, not extra functions a mod can call by package ID.
The shipped headers and named module policy are the supported interface. If a
new tool genuinely needs a new public service, add a versioned reviewed adapter
instead of reaching around the SDK from the example.

EXACT PUBLIC HEADER REFERENCE
-----------------------------

The following appendices are extracted from the same staged kit as the packaged
SDK 2.0. They include the original API, convenience game snapshots/setters,
new development services, C++ helpers, bounded JSON writer/reader and local
presentation ABI. Read the comments as part of the contract, not optional hints.

Use the revision references and example source shipped beside them for native
layouts and larger adapters. The Dev Menu is examples/dev-menu. The original
trophy and difficulty examples remain in the kit. The exact package hashes,
engine identity and compile/package evidence are recorded with the delivery.

SDK REFERENCE - magic_codes.h
-----------------------------
```c

#ifndef DKR_CUSTOM_MAGIC_CODES_H
#define DKR_CUSTOM_MAGIC_CODES_H
#ifdef __cplusplus
extern "C" {
#endif

#ifndef __mips__
#error "Custom Magic Codes use the portable MIPS O32 target, not a host DLL target."
#endif
#if __SIZEOF_POINTER__ != 4
#error "The DKR SDK requires 32-bit guest pointers."
#endif

#define DKR_SDK_API 1U
#define DKR_IMPORT __attribute__((noinline, weak, used, section(".recomp_import.*")))
#define DKR_CALLBACK(event) __attribute__((used, retain, section(".recomp_callback.*:" #event)))
#define DKR_EXPORT __attribute__((used, retain, section(".recomp_export")))
#define DKR_HOOK(function) __attribute__((used, retain, section(".recomp_hook." #function)))
#define DKR_HOOK_RETURN(function) __attribute__((used, retain, section(".recomp_hook_return." #function)))
#define DKR_REPLACE __attribute__((used, retain, section(".recomp_patch")))

typedef unsigned int DkrU32;
typedef unsigned long long DkrU64;
typedef int DkrS32;
/* O32 native calls are explicit. Stack contains 16 outgoing argument words;
 * arguments 5+ begin at stack[4]. Pointer fields are guest addresses only. */
typedef struct DkrGameCall {
    DkrU64 r[32],f[32],hi,lo;
    DkrU32 fcsr,padding;
} DkrGameCall;
DKR_IMPORT void* dkr_game_symbol(const char* name) {(void)name;return (void*)0;}
DKR_IMPORT DkrU32 dkr_game_symbol_size(const char* name) {(void)name;return 0;}
DKR_IMPORT DkrU32 dkr_game_call(const char* name,DkrGameCall* call) {(void)name;(void)call;return 0;}
DKR_IMPORT DkrU32 dkr_game_context_read(DkrGameCall* call) {(void)call;return 0;}
DKR_IMPORT DkrU32 dkr_game_context_write(const DkrGameCall* call) {(void)call;return 0;}
DKR_IMPORT DkrU32 dkr_game_skip_original(void) {return 0;}
/* after_hud_element_render only: request another original draw outside the
 * VM/native lease. At most four original draws are permitted per wrapper. */
DKR_IMPORT DkrU32 dkr_game_repeat_original(void) {return 0;}
/* racing v1 / exclusive system race-laps: 8 racers, 100 timing words each,
 * including a finish sentinel. Register the COMPLETED roster after
 * track_setup_racers, not obj_init_racer (racer IDs are assigned later).
 * The SDK binds the live completed roster on a first settling-tick access.
 * Pass NULL when leaving the custom race. Table and registrations rewind.
 * Native Time Trial must be off; its record/colour arrays are fixed-size.
 * A custom race must also bound/replace fixed native result lap arrays (see
 * custom-trophy-races), not merely increase LevelHeader.laps. */
DKR_IMPORT DkrU32 dkr_race_laps(DkrU32 table[8][100]) {(void)table;return 0;}
DKR_IMPORT DkrU32 dkr_race_register(void* racer) {(void)racer;return 0;}
typedef struct DkrTrophyRound {
    DkrU32 carrier,laps,vehicle;
    char name[164],course[164];
    DkrU32 reserved;
} DkrTrophyRound;
/* trophies v1: returns the admitted round count and optional 164-byte cup
   * name for the explicitly selected custom-cup tile, or zero for a vanilla
   * trophy/no selection. All admitted cups/order/assets are immutable; the
   * chosen menu identity is checkpointed with the owning game world. */
DKR_IMPORT DkrU32 dkr_trophy_info(char* name,DkrU32 capacity) {(void)name;(void)capacity;return 0;}
DKR_IMPORT DkrU32 dkr_trophy_round(DkrU32 index,DkrTrophyRound* round) {(void)index;(void)round;return 0;}
DKR_IMPORT DkrU32 dkr_course_select(const char* course,DkrU32 carrier) {(void)course;(void)carrier;return 0;}
/* Module game: named callbacks use DKR_CALLBACK(before_function) and
 * DKR_CALLBACK(after_function). They preserve port hooks and work across the
 * supported ROM revisions. System/scheduler callbacks are not exposed.
 * Unknown symbols return NULL; unavailable/protected native calls stop safely.
 * Use dkr_game_context_* only within these callbacks, never on_frame/on_boot. */
static inline DkrU64 dkr_call4(const char* name,DkrU32 a,DkrU32 b,DkrU32 c,DkrU32 d) {
#ifdef __cplusplus
    DkrGameCall call={};
#else
    DkrGameCall call={0};
#endif
    DkrU32 stack[16] __attribute__((aligned(16)))={0};
    call.r[4]=(DkrU64)(long long)(DkrS32)a;call.r[5]=(DkrU64)(long long)(DkrS32)b;
    call.r[6]=(DkrU64)(long long)(DkrS32)c;call.r[7]=(DkrU64)(long long)(DkrS32)d;
    call.r[29]=(DkrU64)(long long)(DkrS32)(DkrU32)stack;
    dkr_game_call(name,&call);return call.r[2];
}

/* These weak bodies are link metadata consumed by RecompModTool. The loader
 * resolves every import before activation; these bodies are never fallbacks. */
DKR_IMPORT DkrU32 dkr_sdk_version(void) { return 0; }
DKR_IMPORT DkrU32 dkr_random_u32(void) { return 0; }
DKR_IMPORT void* dkr_alloc(DkrU32 bytes) { (void)bytes; return (void*)0; }
DKR_IMPORT void dkr_free(void* address) { (void)address; }
DKR_IMPORT double dkr_setting_number(const char* key) { (void)key; return 0; }
/* Returns required bytes, INCLUDING NUL. Null destination queries length.
 * A non-null destination must fit the entire value. Maximum 4096 bytes. */
DKR_IMPORT DkrU32 dkr_setting_string(const char* key, char* destination, DkrU32 capacity) {
    (void)key; (void)destination; (void)capacity; return 0;
}
DKR_IMPORT DkrU32 dkr_resource_size(const char* name) { (void)name; return 0; }
/* Frozen structured configuration (canonical UTF-8 JSON, not a host file).
 * Unlike scalar String settings, a document may contain a complete cup.
 * Missing documents have size zero. Read in bounded 4096-byte chunks. */
DKR_IMPORT DkrU32 dkr_data_size(const char* name) { (void)name; return 0; }
DKR_IMPORT DkrU32 dkr_data_read(const char* name,DkrU32 offset,void* destination,DkrU32 bytes) {
    (void)name;(void)offset;(void)destination;(void)bytes;return 0;
}
/* Reads at most 4096 bytes per call. Resource names are logical IDs declared
 * in dkr.json; they are not host paths. Imported files are read-only. */
DKR_IMPORT DkrU32 dkr_resource_read(const char* name, DkrU32 offset, void* destination, DkrU32 bytes) {
    (void)name; (void)offset; (void)destination; (void)bytes; return 0;
}
/* Logs are queued effects. The host must publish them only after confirmation
 * online. A message is at most 512 UTF-8 bytes; the queue holds 128 effects. */
DKR_IMPORT void dkr_log(const char* message) { (void)message; }

#ifdef __cplusplus
}
#endif
#endif


```

SDK REFERENCE - game.h
----------------------
```c

#ifndef DKR_GAME_SDK_H
#define DKR_GAME_SDK_H
#ifdef __cplusplus
extern "C" {
#endif
#include "magic_codes.h"

/* Values are copied, not borrowed host pointers. Guest object/racer/camera
 * addresses are valid only in their current scene: reacquire after a load. */
typedef struct DkrRacerState {
    DkrU32 object,racer;DkrS32 player;
    DkrU32 index,character,vehicle,lap,count_lap,position,finished,bananas;
    float x,y,z,speed;DkrU32 reserved;
} DkrRacerState;
typedef struct DkrObjectState {
    DkrU32 object,behaviour,id;float x,y,z,scale;DkrU32 opacity,collision;
} DkrObjectState;
typedef struct DkrInput {DkrU32 held,pressed,released;DkrS32 stick_x,stick_y;} DkrInput;
typedef struct DkrCameraState {
    DkrU32 camera;float x,y,z;DkrS32 yaw,pitch,roll;
    float boom,shake;DkrU32 mode;
} DkrCameraState;
typedef struct DkrFloorQuery {DkrS32 segment;float x,ceiling_y,z;} DkrFloorQuery;
typedef struct DkrFloorHit {float y;DkrU32 segment,triangle;float normal_x,normal_y,normal_z;} DkrFloorHit;
enum {DKR_FIELD_X=0,DKR_FIELD_Y=1,DKR_FIELD_Z=2,DKR_RACER_SPEED=3,DKR_RACER_BANANAS=4,
      DKR_OBJECT_SCALE=3,DKR_CAMERA_BOOM=3,DKR_CAMERA_SHAKE=4};
/* modules racers/objects/input/cameras v1. Indices enumerate the CURRENT
 * native list, not a stable object identity. All state is ordinary guest RAM
 * and participates in online rewind. Non-finite writes are rejected. */
DKR_IMPORT DkrU32 dkr_racer_count(void) {return 0;}
DKR_IMPORT DkrU32 dkr_racer_get(DkrU32 index,DkrRacerState* out) {(void)index;(void)out;return 0;}
DKR_IMPORT DkrU32 dkr_racer_set(DkrU32 index,DkrU32 field,DkrU32 bits) {(void)index;(void)field;(void)bits;return 0;}
DKR_IMPORT DkrU32 dkr_object_count(void) {return 0;}
DKR_IMPORT DkrU32 dkr_object_get(DkrU32 index,DkrObjectState* out) {(void)index;(void)out;return 0;}
DKR_IMPORT DkrU32 dkr_object_set(DkrU32 index,DkrU32 field,DkrU32 bits) {(void)index;(void)field;(void)bits;return 0;}
DKR_IMPORT DkrU32 dkr_input_get(DkrU32 player,DkrInput* out) {(void)player;(void)out;return 0;}
DKR_IMPORT DkrU32 dkr_camera_get(DkrU32 index,DkrCameraState* out) {(void)index;(void)out;return 0;}
DKR_IMPORT DkrU32 dkr_camera_set(DkrU32 index,DkrU32 field,DkrU32 bits) {(void)index;(void)field;(void)bits;return 0;}
/* collision v1: closest static collision floor below ceiling_y in ONE native
 * segment. Does not invent walls/physics or include moving object collisions.
 * Returns 1 hit, 0 none, 0xffffffff if the bounded query budget is exceeded.
 * The original native multi-height function has no output capacity parameter;
 * this service never lends it an undersized output array. */
DKR_IMPORT DkrU32 dkr_collision_floor(const DkrFloorQuery* query,DkrFloorHit* out) {(void)query;(void)out;return 0;}
static inline DkrU32 dkr_float_bits(float value) {union {float f;DkrU32 bits;} v;v.f=value;return v.bits;}
enum {DKR_AUDIO_SOUND=0,DKR_AUDIO_MUSIC=1,DKR_AUDIO_STOP_MUSIC=2,DKR_AUDIO_FADE_MUSIC=3};
/* audio v1: routed into the owned guest audio engine. Online PCM/output is
 * still released only through the existing confirmed-frame audio journal.
 * Returns 0 before audio initialization or for an unavailable sound/sequence.
 * Fade value is signed (-4096..4096); sound handles are not lent to mods. */
DKR_IMPORT DkrU32 dkr_audio_command(DkrU32 command,DkrS32 value) {(void)command;(void)value;return 0;}
typedef struct DkrHudText {
    DkrU32 target,font;DkrS32 x,y;DkrU32 rgba,alignment;const char* text;
} DkrHudText;
enum {DKR_TEXT_MENU=0,DKR_TEXT_HUD=1};
/* hud v1: only in a before_/after_ draw callback, with an already loaded
 * font. target selects the current menu/HUD display-list owner; never pass a
 * fabricated display-list pointer. ASCII text, max 160 bytes; max 512 glyphs
 * per package/frame. rgba is RRGGBBAA. Returns 0 if the font/list is not ready.
 * Native font colour/position/background state is restored after drawing. */
DKR_IMPORT DkrU32 dkr_hud_text(const DkrHudText* text) {(void)text;return 0;}
_Static_assert(sizeof(DkrRacerState)==64,"Racer SDK ABI");
_Static_assert(sizeof(DkrObjectState)==36,"Object SDK ABI");
_Static_assert(sizeof(DkrInput)==20,"Input SDK ABI");
_Static_assert(sizeof(DkrCameraState)==40,"Camera SDK ABI");
_Static_assert(sizeof(DkrFloorQuery)==16&&sizeof(DkrFloorHit)==24,"Collision SDK ABI");
_Static_assert(sizeof(DkrHudText)==28,"HUD SDK ABI");
#ifdef __cplusplus
}
#endif
#endif


```

SDK REFERENCE - development.h
-----------------------------
```c

#ifndef DKR_DEVELOPMENT_H
#define DKR_DEVELOPMENT_H
#include "magic_codes.h"
#define DKR_SDK2_API 2U
#ifdef __cplusplus
extern "C" {
#endif
/* SDK 2 uses the same big-endian O32 ABI on every supported host. None of
 * these records contains a native process pointer or a platform-sized long. */
typedef enum Dkr2Status {
    DKR2_OK=0, DKR2_END=1, DKR2_INVALID=-1, DKR2_STALE=-2,
    DKR2_PHASE=-3, DKR2_AUTHORITY=-4, DKR2_UNAVAILABLE=-5,
    DKR2_BUDGET=-6, DKR2_CONFLICT=-7, DKR2_RELOAD=-8
} Dkr2Status;
typedef struct Dkr2Handle { unsigned slot, generation; } Dkr2Handle;
typedef enum Dkr2ValueType { DKR2_FLOAT=1, DKR2_SIGNED=2, DKR2_UNSIGNED=3 } Dkr2ValueType;
typedef union Dkr2Value { float real; int integer; unsigned bits; } Dkr2Value;
typedef enum Dkr2Property {
    DKR2_X=1, DKR2_Y, DKR2_Z, DKR2_PITCH, DKR2_YAW, DKR2_ROLL,
    DKR2_SCALE, DKR2_OPACITY, DKR2_VELOCITY_X, DKR2_VELOCITY_Y, DKR2_VELOCITY_Z,
    DKR2_BEHAVIOUR, DKR2_OBJECT_ID, DKR2_SEGMENT,
    DKR2_RACER_SPEED=100, DKR2_RACER_LATERAL_SPEED, DKR2_RACER_BANANAS,
    DKR2_RACER_ITEM, DKR2_RACER_AMMO, DKR2_RACER_ITEM_LEVEL,
    DKR2_RACER_BOOST_TICKS, DKR2_RACER_SHIELD_TICKS, DKR2_RACER_AI_SKILL,
    DKR2_RACER_THROTTLE, DKR2_RACER_BRAKE, DKR2_RACER_BUOYANCY,
    DKR2_RACER_PLAYER, DKR2_RACER_INDEX, DKR2_RACER_CHARACTER, DKR2_RACER_VEHICLE,
    DKR2_RACER_LAP, DKR2_RACER_CHECKPOINT, DKR2_RACER_PLACE, DKR2_RACER_FINISHED,
    DKR2_RACER_SHIELD_TYPE,
    DKR2_AMBIENT=200,DKR2_DIFFUSE,DKR2_LIGHT_R,DKR2_LIGHT_G,DKR2_LIGHT_B,
    DKR2_LIGHT_INTENSITY,DKR2_LIGHT_X,DKR2_LIGHT_Y,DKR2_LIGHT_Z,
    DKR2_TRIGGER_RADIUS=300,DKR2_TRIGGER_VEHICLE
} Dkr2Property;
enum { DKR2_PROPERTY_WRITE=1, DKR2_PROPERTY_RACER=2,
       DKR2_PHASE_READ=1, DKR2_PHASE_TICK=2, DKR2_PHASE_DRAW=4 };
typedef struct Dkr2PropertyInfo {
    unsigned size, id, type, flags;
    Dkr2Value minimum, maximum;
    unsigned phase, reserved;
    char name[48], unit[16];
} Dkr2PropertyInfo;
typedef struct Dkr2Entity {
    unsigned size;
    Dkr2Handle handle;
    unsigned behaviour, object_id, racer, reserved;
    float x,y,z;
} Dkr2Entity;
#define DKR2_ENTITIES_RACERS 0x80000000U
typedef struct Dkr2Read {
    Dkr2Handle handle;
    unsigned property;
    Dkr2Value value;
    int status;
} Dkr2Read;
/* A batch is all-or-nothing. Up to 32 edits, unique target/property pairs.
 * Mutations run at a deterministic tick callback, never a draw callback. */
typedef struct Dkr2Edit { Dkr2Handle handle; unsigned property; Dkr2Value value; } Dkr2Edit;
typedef struct Dkr2Session {
    unsigned size, api, revision, online;
    unsigned scene_generation, tick, entities, racers;
    unsigned development, phase, instruction_budget, native_budget;
} Dkr2Session;
/* arguments[0..11] are user values; arguments[12] is the expected scene
 * generation inserted by the host. Do not use the final word as a parameter. */
typedef struct Dkr2Action { unsigned owner,id,action,arguments[13]; } Dkr2Action;
typedef struct Dkr2Cast {
    unsigned size,segment,flags;
    float radius,from[3],to[3];
} Dkr2Cast;
typedef struct Dkr2Hit {
    unsigned size,hit;
    float fraction,point[3],normal[3];
    unsigned segment,triangle,texture;
} Dkr2Hit;
enum {DKR2_CAST_INTERACTION_SPHERES=1,DKR2_CAST_SKIP_STATIC=2};
typedef enum Dkr2EventType {
    DKR2_EVENT_SCENE=1, DKR2_EVENT_SPAWN=2, DKR2_EVENT_DESPAWN=3,
    DKR2_EVENT_LAP=4, DKR2_EVENT_CHECKPOINT=5, DKR2_EVENT_ITEM=6,
    DKR2_EVENT_RESULT=7, DKR2_EVENT_OVERFLOW=31
} Dkr2EventType;
typedef struct Dkr2Event {
    unsigned type,tick;Dkr2Handle handle;
    unsigned property,previous,value,auxiliary;
} Dkr2Event;
/* Subscribe with 1U << event_type. The bounded queue and previous racer values
 * are rewindable. Overflow is explicit, never an unbounded allocation. */
DKR_IMPORT int dkr2_events(unsigned mask) {(void)mask;return DKR2_UNAVAILABLE;}
DKR_IMPORT int dkr2_event_next(Dkr2Event* event) {(void)event;return DKR2_UNAVAILABLE;}
/* Segment UINT_MAX searches intersected segment bounds. Radius 0 is a ray;
 * positive radius sweeps a sphere against static scene triangles. Texture is
 * the render batch's texture ID, not a claimed physics surface type. */
DKR_IMPORT int dkr2_cast(const Dkr2Cast* query,Dkr2Hit* result) {(void)query;(void)result;return DKR2_UNAVAILABLE;}
typedef enum Dkr2Operation {
    DKR2_TELEPORT=1, DKR2_CLONE_SCENERY=2, DKR2_DELETE_SPAWN=3,DKR2_EDIT_CHECKPOINT=4,
    DKR2_SET_FOG=10, DKR2_SET_WEATHER=11,
    DKR2_SOUND=20, DKR2_MUSIC=21, DKR2_STOP_MUSIC=22, DKR2_FADE_MUSIC=23,
    DKR2_RESTART_SCENE=30, DKR2_WARP_COURSE=31,DKR2_RETURN_MENU=32,
    DKR2_SET_RACE_LAPS=40
} Dkr2Operation;
/* on_tool is a retired-boundary callback, also delivered while gameplay is
 * paused. 0 resets controls, 1 pauses, 2 grants exactly one update, 3 sets
 * retail logic units per update (1..4; normal owned rate is 2). Audio mixing
 * and network time continue. New scenes/menus reset control ownership. */
DKR_IMPORT int dkr2_control(unsigned operation,unsigned value) {(void)operation;(void)value;return DKR2_UNAVAILABLE;}
typedef struct Dkr2Route {
    unsigned size,index,count,main_count;Dkr2Handle object;
    float position[3],normal[3],width,distance;int alternate;unsigned reserved;
} Dkr2Route;
/* Native checkpoint/AI racing line, including alternate routes. Node edits use
 * EDIT_CHECKPOINT and rebuild all native planes/distances in one safe call.
 * This does not resize or silently overwrite the source track asset. */
DKR_IMPORT int dkr2_route(unsigned index,Dkr2Route* result) {(void)index;(void)result;return DKR2_UNAVAILABLE;}
/* Scoped scene documents. Publish a bounded JSON payload (<=12000 bytes).
 * Export/import is a user action in the tools panel; imports are edition,
 * revision and logical-course checked, uploaded through ordered commands,
 * and delivered as the document field's import_action. Read in <=4096-byte
 * chunks, validate the complete payload before editing, then accept it once.
 * No import may silently overwrite a source track or an ordinary save. */
DKR_IMPORT int dkr2_document_publish(const char* id,const char* json,unsigned bytes) {(void)id;(void)json;(void)bytes;return DKR2_UNAVAILABLE;}
DKR_IMPORT int dkr2_document_size(const char* id) {(void)id;return DKR2_UNAVAILABLE;}
DKR_IMPORT int dkr2_document_read(const char* id,unsigned offset,void* output,unsigned bytes) {(void)id;(void)offset;(void)output;(void)bytes;return DKR2_UNAVAILABLE;}
DKR_IMPORT int dkr2_document_accept(const char* id) {(void)id;return DKR2_UNAVAILABLE;}
typedef struct Dkr2Debug {unsigned size,kind,rgba,view;float a[3],b[3];char label[48];} Dkr2Debug;
enum {DKR2_DEBUG_LINE=1,DKR2_DEBUG_BOX=2,DKR2_DEBUG_LABEL=3};
/* At most 64 world-space primitives per package. Local retained-camera
 * projection and viewport clipping only; no new world render or GPU rays.
 * count=0 clears the local list. Labels are printable ASCII, max 48 bytes. */
DKR_IMPORT int dkr2_debug(const Dkr2Debug* primitives,unsigned count) {(void)primitives;(void)count;return DKR2_UNAVAILABLE;}
typedef struct Dkr2Tune {unsigned size;Dkr2Handle handle;float weight,handling,acceleration,speed_cap;unsigned mask;} Dkr2Tune;
enum {DKR2_TUNE_WEIGHT=1,DKR2_TUNE_HANDLING=2,DKR2_TUNE_ACCELERATION=4,DKR2_TUNE_SPEED_CAP=8};
/* Per-racer scene overrides. Weight/handling/acceleration factors 0.1..4;
 * speed cap 0 (off)..100. Acceleration curves are reviewed for vehicles 0..2;
 * boss updates support the other fields. Zero mask resets that racer; a zero
 * handle with zero mask resets this owner's overrides. Native scratch is
 * restored before the next actor. Only one tuning provider may own a session. */
DKR_IMPORT int dkr2_tune(const Dkr2Tune* tuning) {(void)tuning;return DKR2_UNAVAILABLE;}
/* Skips only the reviewed AI racer update, never object ownership, graphics,
 * audio, input, loader or scheduler work. Reset on a scene unload. */
DKR_IMPORT int dkr2_freeze_cpus(unsigned enabled) {(void)enabled;return DKR2_UNAVAILABLE;}
/* Commands use 16 O32 words. See the reference for each reviewed operation.
 * Unavailable handlers report DKR2_UNAVAILABLE, never a silent no-op. */
DKR_IMPORT int dkr2_operation_available(unsigned operation) {(void)operation;return 0;}
DKR_IMPORT int dkr2_operation(unsigned operation,const unsigned arguments[16],unsigned result[16]) {(void)operation;(void)arguments;(void)result;return DKR2_UNAVAILABLE;}
/* tools-ui is an immutable schema resource. Published values are copied,
 * throttled, local and read-only. Action polling uses on_tool (also delivered
 * while paused), or an ordinary deterministic tick. Online copied inspector
 * targets are published only at agreed boundaries, never speculative births. */
DKR_IMPORT int dkr2_ui_publish(const char* json,unsigned bytes) {(void)json;(void)bytes;return DKR2_UNAVAILABLE;}
DKR_IMPORT int dkr2_action_next(Dkr2Action* action) {(void)action;return DKR2_UNAVAILABLE;}
/* Live numeric/enum overrides are checkpointed, package-local and reset on
 * relaunch. Frozen launch settings and resource dependency identity stay put. */
DKR_IMPORT int dkr2_setting_set(const char* id,const Dkr2Value* value) {(void)id;(void)value;return DKR2_UNAVAILABLE;}
DKR_IMPORT int dkr2_setting_reset(const char* id) {(void)id;return DKR2_UNAVAILABLE;}
/* Development tools can edit another admitted API 2 package only when that
 * author declares live-settings2 and lists the option in dkr.json live_options.
 * The all-settings catalogue supplies canonical package/option indices. NULL
 * resets an override. API 1 and non-opted-in options return RELOAD. An optional
 * owner-only on_live_setting(option,bits,tag) callback refreshes cached state;
 * tag 0 reset / 1 float bits / 2 enum. All changes and callback state rewind. */
DKR_IMPORT int dkr2_setting_apply(unsigned package_index,unsigned option_index,const Dkr2Value* value) {(void)package_index;(void)option_index;(void)value;return DKR2_UNAVAILABLE;}
/* Track catalogues: scene, segments, triangles/<segment index>, ai-nodes,
 * spawn-points and triggers. Read-only, bounded current-scene metadata under
 * track-tools2. Triangle texture IDs do not claim universal physics materials.
 * SET_RACE_LAPS uses arguments[0] = 1..3 (0 reset), only on the first lap of
 * an ordinary race and without another race-laps owner. Longer races belong
 * to the existing extended-lap/trophy adapter. RESTART_SCENE accepts an
 * existing nonzero entrance only if it has anchors for every current racer.
 * Other courses can only use their admitted default entrance before loading.
 * JSON record copies. Returns required bytes including NUL, END when index
 * is past the list, or a negative status. Names: courses, packages, resources.
 * Course IDs are logical stock:/legacy:/dkrmap: identities, never menu rows. */
DKR_IMPORT int dkr2_catalogue(const char* list,unsigned index,char* output,unsigned capacity) {(void)list;(void)index;(void)output;(void)capacity;return DKR2_UNAVAILABLE;}
typedef struct Dkr2SymbolInfo {
    unsigned size,address,bytes,flags;
    char name[96];
} Dkr2SymbolInfo;
enum { DKR2_SYMBOL_FUNCTION=1, DKR2_SYMBOL_CALL=2, DKR2_SYMBOL_HOOK=4,
       DKR2_SYMBOL_PROTECTED=8 };
DKR_IMPORT unsigned dkr2_capability(const char* module) {(void)module;return 0;}
DKR_IMPORT int dkr2_session(Dkr2Session* result) {(void)result;return DKR2_UNAVAILABLE;}
DKR_IMPORT int dkr2_property(unsigned index,Dkr2PropertyInfo* result) {(void)index;(void)result;return DKR2_UNAVAILABLE;}
DKR_IMPORT int dkr2_entities(unsigned first,Dkr2Entity* result,unsigned capacity) {(void)first;(void)result;(void)capacity;return DKR2_UNAVAILABLE;}
DKR_IMPORT int dkr2_read(Dkr2Read* values,unsigned count) {(void)values;(void)count;return DKR2_UNAVAILABLE;}
DKR_IMPORT int dkr2_edit(const Dkr2Edit* values,unsigned count) {(void)values;(void)count;return DKR2_UNAVAILABLE;}
/* Replaces this owner's persistent property set (0..32). Validates all targets
 * and cross-package field ownership first. Reapplies on simulation frames;
 * original object shading fields also apply after the native shading update.
 * count=0 resets the set. Scene unload clears it. Movement still requires the
 * teleport/route adapters; readonly position fields cannot become overrides. */
DKR_IMPORT int dkr2_override(const Dkr2Edit* values,unsigned count) {(void)values;(void)count;return DKR2_UNAVAILABLE;}
DKR_IMPORT int dkr2_symbols(unsigned index,Dkr2SymbolInfo* result) {(void)index;(void)result;return DKR2_UNAVAILABLE;}
/* Read-only advanced inspection. Output is a copy; executable addresses and
 * the ordinary API 1 expert path retain their existing trust boundary. */
DKR_IMPORT int dkr2_memory_read(unsigned address,void* destination,unsigned bytes) {(void)address;(void)destination;(void)bytes;return DKR2_UNAVAILABLE;}
#ifdef __cplusplus
}
static_assert(sizeof(Dkr2Handle)==8,"O32 handle ABI");
static_assert(sizeof(Dkr2PropertyInfo)==96,"property ABI");
static_assert(sizeof(Dkr2Read)==20,"bulk query ABI");
static_assert(sizeof(Dkr2Edit)==16,"transaction ABI");
static_assert(sizeof(Dkr2Entity)==40,"entity ABI");
static_assert(sizeof(Dkr2Action)==64,"command ABI");
static_assert(sizeof(Dkr2Cast)==40,"cast ABI");
static_assert(sizeof(Dkr2Hit)==48,"hit ABI");
static_assert(sizeof(Dkr2Event)==32,"event ABI");
static_assert(sizeof(Dkr2Route)==64,"route ABI");
static_assert(sizeof(Dkr2Debug)==88,"debug primitive ABI");
static_assert(sizeof(Dkr2Tune)==32,"tuning ABI");
#endif
#endif


```

SDK REFERENCE - development.hpp
-------------------------------
```cpp

#ifndef DKR_SDK2_DEVELOPMENT_HPP
#define DKR_SDK2_DEVELOPMENT_HPP
#include "development.h"
#include "json.h"
/* Optional freestanding C++17 conveniences. No STL, exceptions, RTTI, native
 * allocations, threads, static constructors or host-sized ABI records. */
namespace dkr {
class Entity {
    Dkr2Handle handle_{};
public:
    constexpr Entity()=default;
    constexpr explicit Entity(Dkr2Handle value):handle_(value){}
    constexpr Dkr2Handle handle()const{return handle_;}
    int read(unsigned property,Dkr2Value& output)const {
        Dkr2Read value{};value.handle=handle_;value.property=property;
        const int status=dkr2_read(&value,1);if(status)return status;
        if(!value.status)output=value.value;return value.status;
    }
    int set(unsigned property,Dkr2Value value)const {
        const Dkr2Edit edit{handle_,property,value};return dkr2_edit(&edit,1);
    }
    int set_float(unsigned property,float real)const {Dkr2Value value{};value.real=real;return set(property,value);}
    int set_integer(unsigned property,int integer)const {Dkr2Value value{};value.integer=integer;return set(property,value);}
};
class Transaction {
    Dkr2Edit edits_[32]{};unsigned count_=0;int status_=0;
public:
    int add(Entity target,unsigned property,Dkr2Value value) {
        if(count_==32)return status_=DKR2_BUDGET;edits_[count_++]={target.handle(),property,value};return status_;
    }
    int real(Entity target,unsigned property,float value){Dkr2Value word{};word.real=value;return add(target,property,word);}
    int integer(Entity target,unsigned property,int value){Dkr2Value word{};word.integer=value;return add(target,property,word);}
    int commit(){return status_?status_:dkr2_edit(edits_,count_);}
    void reset(){count_=0;status_=0;}
    unsigned size()const{return count_;}
};
template<class T> class Buffer {
    static_assert(__is_trivially_copyable(T),"Guest buffers hold plain data, not objects needing constructors");
    static_assert(alignof(T)<=16,"Guest allocation alignment is 16 bytes");
    T* pointer_=nullptr;unsigned count_=0;
public:
    Buffer()=default;
    explicit Buffer(unsigned count):pointer_(count&&count<=65536/sizeof(T)?static_cast<T*>(dkr_alloc(count*sizeof(T))):nullptr),count_(pointer_?count:0){}
    Buffer(const Buffer&)=delete;Buffer& operator=(const Buffer&)=delete;
    Buffer(Buffer&& other)noexcept:pointer_(other.pointer_),count_(other.count_){other.pointer_=nullptr;other.count_=0;}
    Buffer& operator=(Buffer&& other)noexcept {if(this!=&other){if(pointer_)dkr_free(pointer_);pointer_=other.pointer_;count_=other.count_;other.pointer_=nullptr;other.count_=0;}return *this;}
    ~Buffer(){if(pointer_)dkr_free(pointer_);}
    explicit operator bool()const{return pointer_!=nullptr;}
    T* data(){return pointer_;}const T* data()const{return pointer_;}
    unsigned size()const{return count_;}
    // As with a C array, indexing is the author's responsibility; a buffer is
    // not permission to read past the allocation or construct host objects.
    T& operator[](unsigned index){return pointer_[index];}
};
class JsonWriter {
    DkrJson writer_;
public:
    JsonWriter(char* destination,unsigned capacity):writer_{destination,capacity,0,0}{if(capacity)destination[0]=0;}
    JsonWriter& raw(const char* value){dkr_json_raw(&writer_,value);return *this;}
    JsonWriter& text(const char* value){dkr_json_text(&writer_,value);return *this;}
    JsonWriter& key(const char* value){dkr_json_key(&writer_,value);return *this;}
    JsonWriter& integer(int value){dkr_json_integer(&writer_,value);return *this;}
    JsonWriter& real(float value){dkr_json_float(&writer_,value);return *this;}
    unsigned size()const{return writer_.used;}
    bool failed()const{return writer_.failed!=0;}
    int publish()const{return writer_.failed?DKR2_BUDGET:dkr2_ui_publish(writer_.bytes,writer_.used);}
};
}
#endif


```

SDK REFERENCE - json.h
----------------------
```c

#ifndef DKR_SDK2_JSON_H
#define DKR_SDK2_JSON_H
/* Freestanding bounded JSON writer for copied tools values and reports. It
 * needs no host libc, heap, compiler-runtime division or variadic formatter. */
typedef struct DkrJson {char* bytes;unsigned size,used;int failed;} DkrJson;
static inline void dkr_json_char(DkrJson* j,char c) {
    if(j->used+1>=j->size){j->failed=1;return;}j->bytes[j->used++]=c;j->bytes[j->used]=0;
}
static inline void dkr_json_raw(DkrJson* j,const char* s){while(*s)dkr_json_char(j,*s++);}
static inline void dkr_json_text(DkrJson* j,const char* s) {
    dkr_json_char(j,'"');while(*s) {
        unsigned char c=(unsigned char)*s++;
        if(c=='"'||c=='\\'){dkr_json_char(j,'\\');dkr_json_char(j,(char)c);}
        else if(c=='\n')dkr_json_raw(j,"\\n");else if(c=='\r')dkr_json_raw(j,"\\r");
        else if(c=='\t')dkr_json_raw(j,"\\t");else if(c>=32)dkr_json_char(j,(char)c);
    }dkr_json_char(j,'"');
}
static inline void dkr_json_unsigned(DkrJson* j,unsigned n) {
    char digits[10];unsigned used=0;do{digits[used++]=(char)('0'+n%10);n/=10;}while(n&&used<10);
    while(used)dkr_json_char(j,digits[--used]);
}
static inline void dkr_json_integer(DkrJson* j,int n) {
    if(n<0){dkr_json_char(j,'-');dkr_json_unsigned(j,0U-(unsigned)n);}else dkr_json_unsigned(j,(unsigned)n);
}
static inline void dkr_json_float(DkrJson* j,float n) {
    if(!(n>=-1e7F&&n<=1e7F)){dkr_json_raw(j,"null");return;}
    if(n<0){dkr_json_char(j,'-');n=-n;}
    unsigned whole=(unsigned)n;dkr_json_unsigned(j,whole);dkr_json_char(j,'.');
    unsigned part=(unsigned)((n-(float)whole)*1000.F);if(part>999)part=999;
    dkr_json_char(j,(char)('0'+part/100));dkr_json_char(j,(char)('0'+part/10%10));dkr_json_char(j,(char)('0'+part%10));
}
static inline void dkr_json_key(DkrJson* j,const char* key){dkr_json_text(j,key);dkr_json_char(j,':');}
#endif


```

SDK REFERENCE - json_read.h
---------------------------
```c

#ifndef DKR_SDK2_JSON_READ_H
#define DKR_SDK2_JSON_READ_H
/* Small, bounded JSON reader for data documents. It never allocates, uses a
 * host parser or accepts executable expressions. The caller owns all tokens.
 * Text is retained by offset; copy/unescape a string before displaying it.
 * Numeric conversion is locale-independent and rejects non-finite results. */
typedef struct DkrJsonToken {unsigned begin,end,child,next,type;} DkrJsonToken;
typedef struct DkrJsonReader {const char* bytes;unsigned length,at,count,capacity;DkrJsonToken* tokens;int failed;} DkrJsonReader;
enum {DKR_JSON_OBJECT=1,DKR_JSON_ARRAY=2,DKR_JSON_STRING=3,DKR_JSON_NUMBER=4,DKR_JSON_LITERAL=5};
static inline void dkr_json_space(DkrJsonReader* r){while(r->at<r->length&&(r->bytes[r->at]==' '||r->bytes[r->at]=='\n'||r->bytes[r->at]=='\r'||r->bytes[r->at]=='\t'))++r->at;}
static inline unsigned dkr_json_value(DkrJsonReader* r,unsigned depth) {
    dkr_json_space(r);if(depth>16||r->at==r->length||r->count==r->capacity){r->failed=1;return 0;}
    const unsigned index=r->count++;DkrJsonToken* t=&r->tokens[index];t->begin=r->at;t->child=0;
    const char first=r->bytes[r->at++];
    if(first=='{'||first=='[') {
        t->type=first=='{'?DKR_JSON_OBJECT:DKR_JSON_ARRAY;t->child=r->count;
        dkr_json_space(r);const char close=first=='{'?'}':']';
        if(r->at<r->length&&r->bytes[r->at]==close)++r->at;
        else while(!r->failed) {
            if(first=='{') {
                const unsigned key=dkr_json_value(r,depth+1);if(r->failed||r->tokens[key].type!=DKR_JSON_STRING){r->failed=1;break;}
                dkr_json_space(r);if(r->at==r->length||r->bytes[r->at++]!=':'){r->failed=1;break;}
            }
            dkr_json_value(r,depth+1);dkr_json_space(r);if(r->at==r->length){r->failed=1;break;}
            const char next=r->bytes[r->at++];if(next==close)break;if(next!=','){r->failed=1;break;}
        }
    }else if(first=='"') {
        t->type=DKR_JSON_STRING;int closed=0;
        while(r->at<r->length) {
            const unsigned char c=(unsigned char)r->bytes[r->at++];if(c=='"'){closed=1;break;}if(c<32){r->failed=1;break;}
            if(c=='\\') {
                if(r->at==r->length){r->failed=1;break;}const char escaped=r->bytes[r->at++];
                if(escaped=='u')for(unsigned i=0;i<4;++i){if(r->at==r->length){r->failed=1;break;}const char h=r->bytes[r->at++];if(!((h>='0'&&h<='9')||(h>='a'&&h<='f')||(h>='A'&&h<='F')))r->failed=1;}
                else if(escaped!='"'&&escaped!='\\'&&escaped!='/'&&escaped!='b'&&escaped!='f'&&escaped!='n'&&escaped!='r'&&escaped!='t')r->failed=1;
            }
        }if(!closed)r->failed=1;
    }else if(first=='-'||(first>='0'&&first<='9')) {
        t->type=DKR_JSON_NUMBER;r->at=t->begin;if(first=='-')++r->at;
        if(r->at==r->length){r->failed=1;return index;}
        if(r->bytes[r->at]=='0')++r->at;
        else if(r->bytes[r->at]>='1'&&r->bytes[r->at]<='9')while(r->at<r->length&&r->bytes[r->at]>='0'&&r->bytes[r->at]<='9')++r->at;
        else {r->failed=1;return index;}
        if(r->at<r->length&&r->bytes[r->at]=='.'){const unsigned begin=++r->at;while(r->at<r->length&&r->bytes[r->at]>='0'&&r->bytes[r->at]<='9')++r->at;if(begin==r->at)r->failed=1;}
        if(r->at<r->length&&(r->bytes[r->at]=='e'||r->bytes[r->at]=='E')){++r->at;if(r->at<r->length&&(r->bytes[r->at]=='+'||r->bytes[r->at]=='-'))++r->at;const unsigned begin=r->at;while(r->at<r->length&&r->bytes[r->at]>='0'&&r->bytes[r->at]<='9')++r->at;if(begin==r->at)r->failed=1;}
    }else {
        t->type=DKR_JSON_LITERAL;const char* expected=first=='t'?"rue":first=='f'?"alse":first=='n'?"ull":"!";
        for(unsigned i=0;expected[i];++i)if(r->at==r->length||r->bytes[r->at++]!=expected[i]){r->failed=1;break;}
    }
    t->end=r->at;t->next=r->count;return index;
}
static inline int dkr_json_parse(const char* bytes,unsigned length,DkrJsonToken* tokens,unsigned capacity) {
    DkrJsonReader r={bytes,length,0,0,capacity,tokens,0};dkr_json_value(&r,0);dkr_json_space(&r);return r.failed||r.at!=length?-1:(int)r.count;
}
static inline int dkr_json_equal(const char* bytes,const DkrJsonToken* token,const char* key) {
    if(token->type!=DKR_JSON_STRING)return 0;unsigned i=0;for(;key[i]&&token->begin+1+i<token->end-1;++i)if(bytes[token->begin+1+i]!=key[i])return 0;
    return !key[i]&&token->begin+1+i==token->end-1;
}
static inline int dkr_json_member(const char* bytes,const DkrJsonToken* tokens,unsigned object,const char* key) {
    if(tokens[object].type!=DKR_JSON_OBJECT)return -1;for(unsigned i=tokens[object].child;i<tokens[object].next;){const unsigned value=i+1;if(dkr_json_equal(bytes,&tokens[i],key))return (int)value;i=tokens[value].next;}return -1;
}
static inline int dkr_json_u32(const char* bytes,const DkrJsonToken* token,unsigned* value) {
    if(token->type!=DKR_JSON_NUMBER||token->begin==token->end)return 0;unsigned result=0;
    for(unsigned i=token->begin;i<token->end;++i){const unsigned c=(unsigned char)bytes[i]-'0';if(c>9||result>(0xffffffffU-c)/10)return 0;result=result*10+c;}*value=result;return 1;
}
static inline int dkr_json_real(const char* bytes,const DkrJsonToken* token,float* value) {
    if(token->type!=DKR_JSON_NUMBER)return 0;unsigned i=token->begin;float result=0,scale=1;int negative=0,exponent=0,exponent_negative=0;
    if(i<token->end&&bytes[i]=='-'){negative=1;++i;}unsigned digits=0;
    while(i<token->end&&bytes[i]>='0'&&bytes[i]<='9'){result=result*10+(bytes[i++]-'0');++digits;}
    if(!digits)return 0;if(i<token->end&&bytes[i]=='.'){++i;unsigned decimal=0;while(i<token->end&&bytes[i]>='0'&&bytes[i]<='9'){scale*=0.1f;result+=(bytes[i++]-'0')*scale;++decimal;}if(!decimal)return 0;}
    if(i<token->end&&(bytes[i]=='e'||bytes[i]=='E')){++i;if(i<token->end&&(bytes[i]=='-'||bytes[i]=='+'))exponent_negative=bytes[i++]=='-';unsigned n=0;while(i<token->end&&bytes[i]>='0'&&bytes[i]<='9'){exponent=exponent*10+bytes[i++]-'0';if(exponent>38)return 0;++n;}if(!n)return 0;}
    if(i!=token->end)return 0;for(int n=0;n<exponent;++n)result*=exponent_negative?0.1f:10.0f;if(!(result>=0&&result<=3.4e38f))return 0;*value=negative?-result:result;return 1;
}
#endif


```

SDK REFERENCE - presentation.h
------------------------------
```c

#ifndef DKR_PRESENTATION_SDK_H
#define DKR_PRESENTATION_SDK_H

/* Presentation v1. All lengths are bytes, angles are radians, distances are
 * game-world units and time is seconds. No field contains a guest/host pointer.
 * The host copies these values into a PRIVATE presentation VM. This API is
 * deliberately unavailable to gameplay callbacks, including online rollback.
 */
#ifdef DKR_PRESENTATION_HOST
#include <cstdint>
typedef std::uint32_t DkrViewU32;
typedef std::int32_t DkrViewS32;
#else
#include "magic_codes.h"
typedef DkrU32 DkrViewU32;
typedef DkrS32 DkrViewS32;
#endif

#define DKR_PRESENTATION_VERSION 1U
enum DkrViewCapability {
    DKR_VIEW_POSE=1U, DKR_VIEW_PROJECTION=2U, DKR_VIEW_REAR_COVERAGE=4U,
    DKR_VIEW_ORBIT_COVERAGE=8U, DKR_VIEW_HIDE_LOCAL_MODEL=16U,
    DKR_VIEW_HIDE_RIDER=32U, DKR_VIEW_STEREO=64U,
    DKR_VIEW_STATIC_COLLISION=128U, DKR_VIEW_DYNAMIC_COLLISION=256U
};
enum DkrViewState {
    DKR_VIEW_DRIVING=1U, DKR_VIEW_SCRIPTED=2U, DKR_VIEW_CUT=4U,
    DKR_VIEW_GROUNDED=8U, DKR_VIEW_DRIFTING=16U, DKR_VIEW_BOOSTING=32U,
    DKR_VIEW_IMPACT=64U, DKR_VIEW_ONLINE=128U, DKR_VIEW_INSPECTION_ALLOWED=256U
};
enum DkrViewAction {
    DKR_VIEW_LOOK_BACK=1U, DKR_VIEW_RECENTER=2U, DKR_VIEW_CYCLE=4U,
    DKR_VIEW_ZOOM_IN=8U, DKR_VIEW_ZOOM_OUT=16U,
    DKR_VIEW_FIRST_PERSON=32U, DKR_VIEW_XR_RECENTER=64U
};
enum DkrViewMode {
    DKR_VIEW_ORIGINAL=0U, DKR_VIEW_CHASE=1U, DKR_VIEW_BONNET=2U,
    DKR_VIEW_FIRST=3U, DKR_VIEW_COMFORT_FIRST=4U,
    DKR_VIEW_CINEMATIC=5U, DKR_VIEW_ORBIT=6U, DKR_VIEW_VR_DRIVER=7U
};
enum DkrViewVisibility {
    DKR_VIEW_SHOW_MODEL=0U, DKR_VIEW_HIDE_VEHICLE=1U, DKR_VIEW_HIDE_RIDER_ONLY=2U
};
enum DkrViewQueryStatus {
    DKR_VIEW_QUERY_NONE=0U, DKR_VIEW_QUERY_HIT=1U,
    DKR_VIEW_QUERY_UNSUPPORTED=2U, DKR_VIEW_QUERY_BUDGET=3U
};

typedef struct DkrViewSnapshot {
    DkrViewU32 api,size,capabilities,flags;
    DkrViewU32 scene,epoch,frame,view;
    DkrViewU32 racer_id,racer_generation,vehicle,character;
    float dt,time,aspect,speed;
    float position[3],velocity[3];
    float yaw,pitch,roll,steer;
    float original_position[3],original_yaw,original_pitch,original_roll;
    float original_fov,original_near,original_far,boost;
    DkrViewU32 reserved[4];
} DkrViewSnapshot;

typedef struct DkrViewInput {
    DkrViewU32 api,size,held,pressed,released,blocked;
    float orbit_x,orbit_y,zoom;
    DkrViewU32 reserved[3];
} DkrViewInput;

typedef struct DkrViewPose {
    DkrViewU32 api,size,view,mode;
    DkrViewU32 racer_id,racer_generation,visibility,cut;
    float position[3],yaw,pitch,roll;
    float vertical_fov,near_plane,far_plane;
    DkrViewU32 reserved[3];
} DkrViewPose;

typedef struct DkrViewQuery {
    DkrViewU32 api,size,view,exclude_racer;
    float origin[3],target[3],radius;
    DkrViewU32 include_dynamic,reserved[2];
} DkrViewQuery;

typedef struct DkrViewHit {
    DkrViewU32 api,size,status,reserved;
    float fraction,position[3],normal[3];
    DkrViewU32 reserved2;
} DkrViewHit;

enum DkrViewMath {DKR_VIEW_SIN=0U,DKR_VIEW_COS=1U,DKR_VIEW_ATAN2=2U,
    DKR_VIEW_EXP=3U,DKR_VIEW_SQRT=4U};

#ifdef DKR_PRESENTATION_HOST
static_assert(sizeof(DkrViewSnapshot)==160);
static_assert(sizeof(DkrViewInput)==48);
static_assert(sizeof(DkrViewPose)==80);
static_assert(sizeof(DkrViewQuery)==56);
static_assert(sizeof(DkrViewHit)==48);
#else
/* Exactly one on_presentation callback is required. It runs once for an
 * eligible local view, not once per gameplay tick or per eye. All writes are
 * confined to this package's private memory and validated output structures.
 * Extra callbacks, gameplay imports, raw references and native hooks are
 * rejected before activation. Tracking and graphics devices stay host-owned.
 */
DKR_IMPORT DkrU32 dkr_view_snapshot(DkrViewSnapshot* out) {(void)out;return 0;}
DKR_IMPORT DkrU32 dkr_view_input(DkrViewInput* out) {(void)out;return 0;}
DKR_IMPORT DkrU32 dkr_view_submit(const DkrViewPose* pose) {(void)pose;return 0;}
DKR_IMPORT DkrU32 dkr_view_query(const DkrViewQuery* query,DkrViewHit* hit) {(void)query;(void)hit;return 0;}
/* Float bits avoid platform-dependent O32 mixed floating/integer arguments. */
DKR_IMPORT DkrU32 dkr_view_math(DkrU32 operation,DkrU32 x,DkrU32 y) {(void)operation;(void)x;(void)y;return 0;}
static inline float dkr_view_real(DkrU32 bits) {union {DkrU32 u;float f;} v;v.u=bits;return v.f;}
static inline DkrU32 dkr_view_bits(float value) {union {DkrU32 u;float f;} v;v.f=value;return v.u;}
static inline float dkr_view_sin(float x) {return dkr_view_real(dkr_view_math(DKR_VIEW_SIN,dkr_view_bits(x),0));}
static inline float dkr_view_cos(float x) {return dkr_view_real(dkr_view_math(DKR_VIEW_COS,dkr_view_bits(x),0));}
static inline float dkr_view_atan2(float y,float x) {return dkr_view_real(dkr_view_math(DKR_VIEW_ATAN2,dkr_view_bits(y),dkr_view_bits(x)));}
static inline float dkr_view_exp(float x) {return dkr_view_real(dkr_view_math(DKR_VIEW_EXP,dkr_view_bits(x),0));}
static inline float dkr_view_sqrt(float x) {return dkr_view_real(dkr_view_math(DKR_VIEW_SQRT,dkr_view_bits(x),0));}
_Static_assert(sizeof(DkrViewSnapshot)==160,"presentation snapshot ABI");
_Static_assert(sizeof(DkrViewInput)==48,"presentation input ABI");
_Static_assert(sizeof(DkrViewPose)==80,"presentation pose ABI");
_Static_assert(sizeof(DkrViewQuery)==56,"presentation query ABI");
_Static_assert(sizeof(DkrViewHit)==48,"presentation hit ABI");
#endif
#endif

V1.0.12 ONLINE AND PROGRESS
The public host mode is Rollback; the old backend selector is removed.
Public discovery reuses Discord sign-in. Native content/code consent, host
authority, lifecycle/confirmed execution and development isolation still apply.
Spectators do not gain a racing port or authority to issue gameplay commands.
Watching/development do not award gameplay achievements. API 1/2 headers,
capability versions and package formats are unchanged by lobby discovery.
See docs/ONLINE_MULTIPLAYER.md, docs/SPECTATORS.md and docs/ACHIEVEMENTS.md.

```

## Analogue driving in v1.0.14

The additive analogue-driving version-1 capability exposes dkr/driving.h in
API 2. Existing DkrInput and API 1 layouts stay unchanged. DkrDrivingInput is
a separate 20-byte record: api, size, flags, accelerator and brake. Levels are
independent calibrated integers from 0 to 255; flags bit 0 means enabled.

dkr2_driving_input(player, output) reads the logical recorded sample for that
tick. dkr2_driving_apply(player) requests native effort caps for the same sample
from on_frame. Native coast, boosts and control restrictions retain ownership;
CPU and special boss vehicles are not changed. Replay uses recorded samples,
never physical-controller reads from simulation. Require analogue-driving 1
and use the complete examples/analogue-driving source and manifest.

The optional companion mod must be installed, approved and enabled. Its Details
contains bindings, calibration and live bars for the selected local player. In
Online the host enables the gameplay mod and every player keeps local controls.
Use matching compatible v1.0.14 application/package editions. Standalone Look
behind is a separate graphics option and does not require ModernCamera++.
