Skip to content

03 08 Config game_info

evets17 edited this page Mar 31, 2026 · 2 revisions

03 08 Config game_info

This page explains game_info in Sprint Boost.

What game_info is (and is not)

game_info reads known in-game values from memory (for example score, lives, counters).

Important distinction:

  • By itself, game_info does not change gameplay experience.
  • It is a foundation that enables other features.

Why game_info matters

game_info is fundamental for:

  • Dynamic display values ({gi.<key>} in text/images)
  • play_stats tracking and persistence
  • Memory-based cheats (set_game_info / offset_game_info)

Without game_info, those systems cannot target meaningful game values.

Where game_info can be used

game_info is relevant for:

  • boost_mode = "enhanced"

In current Sprint Boost behavior, game_info is part of the enhanced/runtime flow.

How game_info works

At a high level:

  1. You define a value key (example: p1_lives)
  2. You provide the game memory address where that value lives
  3. You choose how to decode the raw memory
  4. (Optional) you extract packed bits from decoded value
  5. (Optional) you apply a transform for display-friendly value
  6. (Optional) you map numeric values to labels
  7. Sprint Boost exposes values as {gi.<key>} and {gi_label.<key>}

Why known memory addresses are required

game_info depends on known memory locations for each game.

Because each game stores values differently, game_info is almost always configured in a game-level *_boost.toml file.

If a game’s memory location for a desired value is unknown:

  • that value cannot be configured in game_info
  • and features that rely on that value (display/play-stats/cheats) cannot use it

So memory discovery is a prerequisite for game_info use.

Basic game_info example

[game_info]
poll_ms = 1000

[game_info.values.p1_lives]
address = "0x0194"
decode = "u16"
[game_info.values.p1_lives.transform]
op = "offset"
value = -1

[game_info.values.p1_score]
address = "0x0192"
decode = "u16"
[game_info.values.p1_score.transform]
op = "mul"
value = 100

Supported decode options

This is the full supported decode list:

  • u16: reads a 16-bit unsigned value.
  • u8_lo: reads the low 8-bit portion of a value.
  • u8_hi: reads the high 8-bit portion of a value.
  • bcd16: decodes a 16-bit BCD-encoded number.
  • lo_bytes_le: combines low bytes from adjacent addresses in little-endian order.

Use the decode that matches how that game stores the value in memory.

Supported transform options

This is the full supported transform list:

  • identity: no change (raw decoded value is used as-is).
  • offset: adds/subtracts a fixed amount.
  • mul: multiplies by a fixed amount.
  • div: divides by a fixed amount.

Transforms convert raw memory values into player-friendly display values.

Supported extract options

Packed fields are supported through extract on each value.

  • identity: no extraction.
  • bit_and: bitmask with value.
  • rshift: right-shift by value bits.
  • bit_slice: extract width bits starting at start.

Example:

[game_info.values.length]
address = "0x0182"
decode = "u16"
[game_info.values.length.extract]
op = "bit_and"
value = 0x07
[game_info.values.length.transform]
op = "offset"
value = 3

Equivalent bit_slice form:

[game_info.values.length.extract]
op = "bit_slice"
start = 0
width = 3

Labels mapping and {gi_label.*}

You can map numeric game_info values to text labels:

[game_info.values.condition]
address = "0x0182"
decode = "u16"
[game_info.values.condition.extract]
op = "bit_and"
value = 0x18

[game_info.values.condition.labels]
"0" = "dry"
"8" = "turf"
"16" = "mud"
default = "unknown"

Token behavior:

  • {gi.condition} returns numeric value (0, 8, 16, ...)
  • {gi_label.condition} returns mapped label (dry, turf, mud, ...)
  • if no label key matches and default is not set, {gi_label.condition} resolves to ?

Derived values

game_info can define derived values by combining base values.

Current support note:

  • op = "sum" is the only supported derived operation right now.

Example (sum two terms into one score):

[game_info.values.score_low]
address = "0x01A9"
decode = "lo_bytes_le"

[game_info.values.score_thousands]
address = "0x01AB"
decode = "u16"
[game_info.values.score_thousands.transform]
op = "mul"
value = 1000

[game_info.derived.p1_score]
op = "sum"
terms = ["score_low", "score_thousands"]

This is useful when a game stores one logical value in multiple memory parts.

Using game_info in display

Once defined, values can be used in substitutions:

[[display.layouts.hud.elements]]
type = "text"
text = "Lives: {gi.p1_lives}"
x = 20
y = 70
width = 280
height = 40
color = "#FFFFFF"
align = "left"
size = 26

[[display.layouts.hud.elements]]
type = "image"
source = "{gv.assets_images}/lives_{gi.p1_lives}.png"
fallback = "{gv.assets_images}/lives_unknown.png"
x = 20
y = 120
width = 140
height = 48
scaling = "fit"
alpha = 1.0

Label-based asset example:

[[display.layouts.hud.elements]]
type = "image"
source = "{gv.assets_images}/track_{gi_label.condition}.png"
fallback = "{gv.assets_images}/track_unknown.png"
x = 20
y = 180
width = 140
height = 48
scaling = "fit"
alpha = 1.0

Polling guidance

  • Default: poll_ms = 1000
  • Start with 1000 unless you need faster response
  • Lower values update faster but can add overhead

Override behavior

game_info follows normal config precedence:

  • Folder-level can define shared values
  • Game-level usually defines or overrides game-specific memory values

In practice, game-level game_info is most common because memory addresses are game-specific.

Practical tips

  • Start with one known value (for example lives) and verify it first.
  • Add more values incrementally.
  • Keep key names simple and stable (p1_lives, p1_score, timer).
  • Use derived values when games split one value across multiple addresses.
  • Use {gi.<key>} when numeric output is required (stats/math).
  • Use {gi_label.<key>} when label output is required (image/text naming).

Next step

Continue to:

Clone this wiki locally