Actions

SCHG

Sonic Riders/Memory Editing/Structures

From Sonic Retro

SCHG: Sonic Riders
Main Article
Memory Editing & Functions

Variables
Structures
Functions
Enumerables
Mathematical Formulae

Files
File Structure
Sound Editing
Voices

Disclaimer

The following structures are illustrated through the use of C# code in order to simplify maintenance of this SCHG. These have been exported from the following Github project: https://github.com/sewer56lol/Sewer56.SonicRiders and mirrored by hand.

Gameplay

Player

Found at address 006A4B80 and contains data on each of the individual racers. These racer details are an array and be accessed sequentially (i.e. 006A4B80 + 0x1200 would be the start of Player 2's struct).

[StructLayout(LayoutKind.Explicit, Size = 0x1200)]
public unsafe struct Player
{
    /// <summary>
    /// Pointer to the player input structure for the current player.
    /// </summary>
    [FieldOffset(0x00)]
    public PlayerInput* PlayerInput;

    /// <summary>
    /// The character currently assigned to the player.
    /// </summary>
    [FieldOffset(0xBA)]
    public Characters Character;

    /// <summary>
    /// The extreme gear that is currently assigned to the player.
    /// </summary>
    [FieldOffset(0xBB)]
    public Enums.ExtremeGear ExtremeGear;

    /// <summary>
    /// Toggles showing of tricks, trail/dirt visuals, enables/disables AI Inputs.
    /// </summary>
    [FieldOffset(0xBC)]
    public PlayerType PlayerController;

    /// <summary>
    /// Toggles 1P/2P etc indicators and map rendering in proximity of character.
    /// </summary>
    [FieldOffset(0xBD)]
    public PlayerType PlayerType;

    /// <summary>
    /// Crashes if changed in race.
    /// </summary>
    [FieldOffset(0xBE)]
    public GearType GearType;

    /// <summary>
    /// Controls the animations used for the current gear type.
    /// </summary>
    [FieldOffset(0xBF)]
    public GearType GearTypeAnimation_BF;

    /// <summary>
    /// Owned by DirectX?. Written to and accessed every frame.
    /// Increasing this stretches the Characters in an forward direction (X).
    /// </summary>
    [FieldOffset(0xE0)]
    public float RenderThing_StretchForward;

    /// <summary>
    /// Owned by DirectX?. Written to and accessed every frame.
    /// Increasing this stretches the Characters in an upward direction (Y).
    /// </summary>
    [FieldOffset(0xE4)]
    public float RenderThing_StretchUpward;

    /// <summary>
    /// Owned by DirectX?. Written to and accessed every frame.
    /// Increasing this stretches the Characters in a sideways direction (Z).
    /// </summary>
    [FieldOffset(0xE8)]
    public float RenderThing_StretchSideways;

    /// <summary>
    /// Owned by DirectX?. Written to and accessed every frame.
    /// </summary>
    [FieldOffset(0x100)]
    public float RenderPositionX;

    /// <summary>
    /// Owned by DirectX?. Written to and accessed every frame.
    /// </summary>
    [FieldOffset(0x104)]
    public float RenderPositionY;

    /// <summary>
    /// Owned by DirectX?. Written to and accessed every frame.
    /// </summary>
    [FieldOffset(0x108)]
    public float RenderPositionZ;

    [FieldOffset(0x240)]
    public float PositionX;

    [FieldOffset(0x244)]
    public float PositionY;

    [FieldOffset(0x248)]
    public float PositionZ;

    /// <summary>
    /// Measured in Pi. (3.14159265358...)
    /// Full Rotation: 2 Pi
    /// </summary>
    [FieldOffset(0x250)]
    public float RotationVertical;

    /// <summary>
    /// Measured in Pi. (3.14159265358...)
    /// Full Rotation: 2 Pi
    /// </summary>
    [FieldOffset(0x254)]
    public float RotationHorizontal;

    /// <summary>
    /// Measured in Pi. (3.14159265358...)
    /// Full Rotation: 2 Pi
    /// </summary>
    [FieldOffset(0x258)]
    public float PositionRoll;

    /// <summary>
    /// The amount of frames remaining until the individual player's boost ends.
    /// </summary>
    [FieldOffset(0x9D8)]
    public int BoostCountdown;

    /// <summary>
    /// Acceleration, Final Product of Character and Board Acceleration
    /// </summary>
    [FieldOffset(0xA1C)]
    public float Acceleration;

    /// <summary>
    /// Range 0 - 200,000
    /// </summary>
    [FieldOffset(0xAB8)]
    public int Air;

    /// <summary>
    /// Affects initial jump acceleration, rail grinding acceleration, Flight flying acceleration,
    /// start running acceleration, general board movement acceleration.
    /// </summary>
    [FieldOffset(0xBDC)]
    public float GeneralAcceleration;

    /// <summary>
    /// Speed, what is there to say.
    /// </summary>
    [FieldOffset(0xBE0)]
    public float SpeedHorizontal;

    /// <summary>
    /// Goes up if you go down slopes, and down up slopes.
    /// Affected by holding the jump button.
    /// Hard to judge in-game effect.
    /// </summary>
    [FieldOffset(0xBE4)]
    public float Momentum;

    /// <summary>
    /// General Speed Cap
    /// Applies on rails, tricks, during flight, auto/rotate sections and general movement.
    /// </summary>
    [FieldOffset(0xBE8)]
    public float GeneralSpeedCap;

    [FieldOffset(0xCCC)]
    public int Rings;

    /// <summary>
    /// [Flags]
    /// Seems to be affected by both player actions such as e.g. grinding on a rail but also 
    /// other settings and parameters e.g. whether to use cinematic camera.
    /// </summary>
    [FieldOffset(0xCDC)]
    public PlayerDisplayFlags PlayerDisplayFlags;

    /// <summary>
    /// Contains the current state of the player.
    /// </summary>
    [FieldOffset(0x11BC)]
    public PlayerState PlayerState;

    /// <summary>
    /// Contains the last state of the player.
    /// </summary>
    [FieldOffset(0x11BD)]
    public PlayerState LastPlayerState;

    /// <summary>
    /// Unconfirmed.
    /// Places various restrictions to gameplay on the individual player.
    /// </summary>
    [FieldOffset(0x11BF)]
    public PlayerRestrictions PlayerRestrictions;
}

Extreme Gear

Found at address 6575B0 and contains data on each of the individual extreme gears. This is yet another array and can be accessed with offsets of Extreme Gear (Enumerable).


[StructLayout(LayoutKind.Explicit, Size = 0x1D0)]
public struct ExtremeGear
{
    /// <summary>
    /// Determines who can select this specific extreme gear in question.
    /// </summary>
    [FieldOffset(0x0)]
    public CharactersFlags WhoCanSelect;

    /// <summary>
    /// Controls animations.
    /// Tends to crash if changed.
    /// </summary>
    [FieldOffset(0x4)]
    public GearType GearType;

    /// <summary>
    /// The model used for the current extreme gear.
    /// </summary>
    [FieldOffset(0x5)]
    public Enums.ExtremeGear GearModel;

    /// <summary>
    /// [Offset, Base Gear = 0]
    /// The acceleration of the current extreme gear.
    /// </summary>
    [FieldOffset(0xC)]
    public float Acceleration;

    /// <summary>
    /// [Offset, Base Gear = 0]
    /// The speed multiplier for this board with which handling is adjusted proportionally.
    /// </summary>
    [FieldOffset(0x10)]
    public float SpeedHandlingMultiplier;

    /// <summary>
    /// [Offset, Base Gear = 0]
    /// Affects acceleration/speed when the character goes off-road.
    /// </summary>
    [FieldOffset(0x14)]
    public float Field_14;

    /// <summary>
    /// [Offset, Base Gear = 0]
    /// The same as the speed and handling multiplier, does not affect handling, thus making it harder to steer at high speeds.
    /// This value however multiplies the final speed after it's been processed by the character and <see cref="SpeedHandlingMultiplier"/>.
    /// </summary>
    [FieldOffset(0x18)]
    public float SpeedMultiplier;

    /// <summary>
    /// Allows the board wielder to grind/fly/punch.
    /// </summary>
    [FieldOffset(0x20)]
    public FormationTypesFlags ExtraTypes;

    /// <summary>
    /// [Offset, Base Gear = 0]
    /// The same as the speed and handling multiplier, does not
    /// affect handling, thus making it harder to steer at high speeds.
    /// </summary>
    [FieldOffset(0x28)]
    public float TurnLowSpeedMultiplier;

    /// <summary>
    /// [Offset, Base Gear = 0]
    /// The acceleration used on turning, determines how fast the maximum turn radius is reached.
    /// </summary>
    [FieldOffset(0x2C)]
    public float TurnAcceleration;

    /// <summary>
    /// [Offset, Base Gear = 0]
    /// The maximum the character can turn.
    /// </summary>
    [FieldOffset(0x30)]
    public float TurnMaxRadius;

    /// <summary>
    /// [Offset, Base Gear = 0]
    /// The maximum the character can turn while drifting.
    /// </summary>
    [FieldOffset(0x34)]
    public float DriftMaximumTurnRadius;

    /// <summary>
    /// [Offset, Base Gear = 0]
    /// How much your momentum follows you during a drift.
    /// (Basically how much your current angle and velocity affects the drift by decreasing
    /// how much you can turn. Higher = turn less).
    /// </summary>
    [FieldOffset(0x38)]
    public float DriftMomentum;

    /// <summary>
    /// [Offset, Base Gear = 0]
    /// The minimum radius/angle that the character can drift at.
    /// Low values allow player to transfer drift to other side, or drift straight.
    /// </summary>
    [FieldOffset(0x3C)]
    public float DriftMinimumRadius;

    /// <summary>
    /// [Offset, Base Gear = 0]
    /// How fast the player can transfer between the minimum and maximum turning radius.
    /// Values too low can lead to the arrow keys/analog being reversed.
    /// </summary>
    [FieldOffset(0x40)]
    public float DriftAcceleration;

    /// <summary>
    /// [Offset] How many frames to add to the default drift dash time (60 frames).
    /// e.g. A value of 0 means it takes 60 frames of drift to boost.
    /// A value of -60 allows you to boost instantly by starting and letting off drift.
    /// </summary>
    [FieldOffset(0x50)]
    public int DriftBoostFramesOffset;

    /// <summary>
    /// Air gain multiplier (tricks).
    /// This value is an offset from 1.00 (100%), thus setting -0.05 would cause tricks
    /// to give 95% air.
    /// </summary>
    [FieldOffset(0x54)]
    public float AirGainTrickMultiplier;

    /// <summary>
    /// Air gain multiplier (shortcuts)
    /// This value is an offset from 1.00 (100%), thus setting -0.05 would cause shortcuts to give 95% air.
    /// </summary>
    [FieldOffset(0x58)]
    public float AirGainShortcutMultiplier;

    /// <summary>
    /// Air gain multiplier (control stick event)
    /// This value is an offset from 1.00 (100%), thus setting -0.05 would cause control stick events to give 95% air.
    /// </summary>
    [FieldOffset(0x5C)]
    public float AirGainAutorotateMultiplier;

    /// <summary>
    /// Default Value = 2 (200% Passive Drain)
    /// Air cost multiplier when charging jump.
    /// Multiplies the air cost of passive air drain when the jump button is held.
    /// </summary>
    [FieldOffset(0x60)]
    public ExtremeGearSpecialFlags SpecialFlags;

    /// <summary>
    /// Default Value = 2 (200% Passive Drain)
    /// Air cost multiplier when charging jump.
    /// Multiplies the air cost of passive air drain when the jump button is held.
    /// </summary>
    [FieldOffset(0x64)]
    public float JumpAirMultiplier;

    /// <summary>
    /// Gear Stats for Level 1.
    /// </summary>
    [FieldOffset(0x68)]
    public ExtremeGearLevelStats GearStatsLevel1;

    /// <summary>
    /// Gear Stats for Level 2.
    /// </summary>
    [FieldOffset(0x84)]
    public ExtremeGearLevelStats GearStatsLevel2;

    /// <summary>
    /// Gear Stats for Level 3.
    /// </summary>
    [FieldOffset(0xA0)]
    public ExtremeGearLevelStats GearStatsLevel3;

    /// <summary>
    /// Offset for the default, shown in menu dash stat.
    /// </summary>
    [FieldOffset(0xBC)]
    public sbyte StatDashOffset;

    /// <summary>
    /// Offset for the default, shown in menu limit stat..
    /// </summary>
    [FieldOffset(0xBD)]
    public sbyte StatLimitOffset;

    /// <summary>
    /// Offset for the default, shown in menu power stat..
    /// </summary>
    [FieldOffset(0xBE)]
    public sbyte StatPowerOffset;

    /// <summary>
    /// Offset for the default, shown in menu cornering stat..
    /// </summary>
    [FieldOffset(0xBF)]
    public sbyte StatCorneringOffset;

    [FieldOffset(0xC8)]
    public float ExhaustTrail1Width;

    [FieldOffset(0xCC)]
    public float ExhaustTrail2Width;

    [FieldOffset(0x108)]
    public Vector ExhaustTrail1PositionOffset;

    [FieldOffset(0x114)]
    public Vector ExhaustTrail2PositionOffset;

    [FieldOffset(0x14C)]
    public float ExhaustTrail1TrickWidth;

    [FieldOffset(0x150)]
    public float ExhaustTrail2TrickWidth;

    [FieldOffset(0x18C)]
    public Vector ExhaustTrail1TrickOffset;

    [FieldOffset(0x198)]
    public Vector ExhaustTrail2TrickOffset;
}

Running Physics 1

Found at address 005C30F8.

[StructLayout(LayoutKind.Sequential, Size = 0x20)]
public struct RunningPhysics
{
    /// <summary>
    /// Unknown.
    /// </summary>
    public float Field_00;

    /// <summary>
    /// [Note: Temp variable name]
    /// When high, letting go of inputs causes instant stop.
    /// Getting hit, doing tricks, item collects etc. 
    /// resets speed to max.
    /// </summary>
    public float Inertia;

    /// <summary>
    /// The acceleration applied when walking backwards without air.
    /// Backwards speed is limited by another variable.
    /// </summary>
    public float BackwardsWalkAccel;

    /// <summary>
    /// Acceleration applied when the player runs at above ~40 speed
    /// and holds down to stop, performing slide animation.
    /// </summary>
    public float SlidingBreakAccel;

    /// <summary>
    /// Unknown
    /// </summary>
    public float Field_10;

    /// <summary>
    /// Lowest value your speed can be (only updated when 
    /// inputting or speed hits 0).
    /// </summary>
    public float MinimumSpeed;

    /// <summary>
    /// Unknown
    /// </summary>
    public float Field_18;

    /// <summary>
    /// Unknown
    /// </summary>
    public float Field_1C;
}

Running Physics 2

Found at address 0065C534, the remainder of the running related physics.

public struct RunningPhysics2
{
    /// <summary>
    /// The maximum speed until a switch to gear 2 is performed.
    /// </summary>
    public float GearOneMaxSpeed;

    /// <summary>
    /// The maximum speed of gear 2.
    /// </summary>
    public float GearTwoMaxSpeed;

    /// <summary>
    /// Defines the speed limit for gear 2.
    /// </summary>
    public float MaxSpeed;

    /// <summary>
    /// Defines the acceleration of gear 1.
    /// </summary>
    public float GearOneAcceleration;

    /// <summary>
    /// Defines the acceleration of gear 2.
    /// </summary>
    public float GearTwoAcceleration;

    /// <summary>
    /// Defines the acceleration of gear 3. (When exceeding gearTwoMaxSpeed)
    /// </summary>
    public float GearThreeAcceleration;

    /// <summary>
    /// Unknown
    /// </summary>
    public float Field_18;
}

Extreme Gear Level Stats

Part of the Extreme Gear structure. Defines the statistics for a board at a particular level.

[StructLayout(LayoutKind.Sequential, Size = 0x1C)]
public struct ExtremeGearLevelStats
{
    public int MaxAir;

    /// <summary>
    /// Counted per frame.
    /// </summary>
    public int PassiveAirDrain;

    /// <summary>
    /// Counted per frame.
    /// </summary>
    public int DriftAirCost;

    public int BoostCost;
    public int TornadoCost;
    public int SpeedGainedFromDriftDash;
    public int BoostSpeed;
}


Character Type Properties

Found at 005BD4D8, it is an array of 3 consisting of Speed, Flight and Power so add 0x64 for Flight and add 0x64*2 for Power.

[StructLayout(LayoutKind.Explicit, Size = 0x64)]
public struct CharacterTypeStats
{
    /// <summary>
    /// Default 0.75 for Speed Type.
    /// Multiplier for the default maximum speed the character can have onboard.
    /// </summary>
    [FieldOffset(0x00)]
    public float MaxSpeedMultiplier;
}

Input

Player Input Structure

The pointer at the start of each player structure points here, albeit the location of this is also static either way. This is applicable to all 8 players and with switching player type to Human mid race can be used to control any player 0-7.

[StructLayout(LayoutKind.Explicit, Size = 0x30)]
public struct PlayerInput
{
    /// <summary>
    /// Continually counts up in frames, overflows and does it again.
    /// Resets when a button is pressed or released.
    /// </summary>
    [FieldOffset(0x0)]
    public int FrameCounter;

    /// <summary>
    /// Changes a lot between 0 and 3.
    /// </summary>
    [FieldOffset(0x4)]
    public int Field_04;

    [FieldOffset(0x8)]
    public short Field_08;

    [FieldOffset(0xA)]
    public Buttons Buttons;

    /// <summary>
    /// Sets new pressed button value at 0x08 for 1 frame on 
    /// input then flickers with inputs.
    /// </summary>
    [FieldOffset(0x14)]
    public Buttons Field_14;

    /// <summary>
    /// Range = 0 - 255
    /// </summary>
    [FieldOffset(0x18)]
    public byte LeftBumperPressure;

    /// <summary>
    /// Range = 0 - 255
    /// </summary>
    [FieldOffset(0x19)]
    public byte RightBumperPressure;

    /// <summary>
    /// Range = -100 to 100
    /// </summary>
    [FieldOffset(0x1A)]
    public sbyte AnalogStickX;

    /// <summary>
    /// Range = -100 to 100
    /// </summary>
    [FieldOffset(0x1B)]
    public sbyte AnalogStickY;
}

Menus

Main Menu

Memory address 16BF1D8 is a pointer to this structure.

i.e.
(MainMenu*)*(int*)0x16BF1D8


/// <summary>
/// Note: Size is a decent estimate, real size is not known.
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
public unsafe struct MainMenu
{
    /// <summary>
    /// See <see cref="Enums.MenuState"/>
    /// </summary>
    [FieldOffset(0x0)]
    public MenuCommon* MenuCommonPointer;

    /// <summary>
    /// See <see cref="Enums.MenuState"/>
    /// </summary>
    [FieldOffset(0xC)]
    public MenuState MenuState;

    [FieldOffset(0xD)]
    public byte CurrentSelection;

    /// <summary>
    /// See <see cref="Enums.MainMenuAction"/>
    /// </summary>
    [FieldOffset(0x1C)]
    public MainMenuAction MainMenuAction;

    /// <summary>
    /// Changes the colour of the main menu and all submenus.
    /// </summary>
    [FieldOffset(0x1E)]
    public MenuColour MenuColour;

    /// <summary>
    /// Seems to have more effect than changing the title sprite, but for now, such name will do.
    /// </summary>
    [FieldOffset(0x1F)]
    public byte MenuTitleSprite;

    /// <summary>
    /// Defined as this based off of observation.
    /// Something related to the last menu the user exited from.
    /// It is unknown what this is used for.
    /// </summary>
    [FieldOffset(0x28)]
    public int* LastMenuPointer;

    /// <summary>
    /// Used to determine the selectable options on screen by index and menus they will enter.
    /// See <see cref="Enums.MainMenuVariation"/> for more details.
    /// </summary>
    [FieldOffset(0x39)]
    public MainMenuVariation MainMenuVariation;
}

Character Select Menu

A pointer to this menu (may be null until menu opened) can be found at offset 0x80 of the Main Menu structure.

/// <summary>
/// Note: Size is a decent estimate, real size is not known.
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 0x54)]
public unsafe struct CharacterSelectMenu
{
    /// <summary>
    /// See <see cref="Enums.MenuState"/>
    /// </summary>
    [FieldOffset(0xC)]
    public MenuState MenuState;

    /// <summary>
    /// Set to true to show the "Are you Ready" prompt.
    /// Set to false to hide the prompt.
    /// </summary>
    [FieldOffset(0xF)]
    public bool AreYouReadyEnabled;

    /// <summary>
    /// Frame counter for the animation for the individual stars
    /// sliding in from the left side for the current gear.
    /// </summary>
    [FieldOffset(0x10)]
    public float StarsAnimationFrameCounterP1;

    /// <summary>
    /// Frame counter for the animation for the individual stars
    /// sliding in from the left side for the current gear.
    /// </summary>
    [FieldOffset(0x14)]
    public float StarsAnimationFrameCounterP2;

    /// <summary>
    /// Frame counter for the animation for the individual stars
    /// sliding in from the left side for the current gear.
    /// </summary>
    [FieldOffset(0x18)]
    public float StarsAnimationFrameCounterP3;

    /// <summary>
    /// Frame counter for the animation for the individual stars
    /// sliding in from the left side for the current gear.
    /// </summary>
    [FieldOffset(0x1C)]
    public float StarsAnimationFrameCounterP4;

    /// <summary>
    /// Defines the currently locked in Characters, these Characters
    /// cannot be re-selected.
    /// You may use <see cref="Structures.Enums.Characters"/> as in indexer here.
    /// </summary>
    [FieldOffset(0x20)]
    public fixed bool Characters[0x14];

    /// <summary>
    /// Points to something, right?
    /// </summary>
    [FieldOffset(0x34)]
    public int UnknownPointer;

    /// <summary>
    /// An array of <see cref="PlayerStatus"/> representing the player statuses.
    /// Note: Type is set to byte due to language limitations.
    /// </summary>
    [FieldOffset(0x38)]
    public fixed byte PlayerStatuses[0x4];

    /// <summary>
    /// The currently selected Characters on the character list for each player.
    /// </summary>
    [FieldOffset(0x3C)]
    public fixed byte PlayerMenuSelections[0x4];

    /// <summary>
    /// Defines the amount of currently active (joined in) players on the current menu.
    /// Setting this value to 0, exits the menu.
    /// </summary>
    [FieldOffset(0x49)]
    public byte CurrentlyActivePlayerCount;

    /// <summary>
    /// Defines the maximum number of players that are allowed to join the current game.
    /// </summary>
    [FieldOffset(0x4A)]
    public byte MaximumPlayerCount;

    /// <summary>
    /// 0xFF if a player can join in current slot, else 0x00.
    /// </summary>
    [FieldOffset(0x4B)]
    public fixed byte OpenPlayerSlots[0x4];
}

Menu Common

This structure appears to be common to all menus in the game. A pointer to this menu is part of the Main Menu structure. ControllingMenu is an enumerable defined on another page.

/// <summary>
/// Note: Size is a decent estimate, real size is not known.
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
public struct MenuCommon
{
    /// <summary>
    /// See <see cref="ControllingMenu"/>
    /// </summary>
    [FieldOffset(0x14)]
    public ControllingMenu currentlyControllingMenu;
}

Race Rules

Shares pointer with the Character Select Menu and is also thus found at offset 0x80 of the Main Menu structure.


/// <summary>
/// Note: Size is a decent estimate, real size is not known.
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
public unsafe struct RaceRules
{
    /// <summary>
    /// See <see cref="MenuState"/>
    /// </summary>
    [FieldOffset(0x1C)]
    public MenuState menuState;

    [FieldOffset(0x1D)]
    public byte currentHorizontalSelection;

    [FieldOffset(0x1E)]
    public byte currentVerticalSelection;

    [FieldOffset(0x1F)]
    public byte menuItemCount;

    /* Options */

    [FieldOffset(0x20)]
    public byte totalLapsCount;

    [FieldOffset(0x21)]
    public bool announcer;

    [FieldOffset(0x22)]
    public bool level;

    [FieldOffset(0x23)]
    public bool item;

    [FieldOffset(0x24)]
    public bool pit;

    [FieldOffset(0x25)]
    public AirLostActions airLostAction;

    /* Max Options */

    [FieldOffset(0x26)]
    public byte maxSelectionLapCount;

    [FieldOffset(0x27)]
    public byte maxSelectionAnnouncer;

    [FieldOffset(0x28)]
    public byte maxSelectionLevel;

    [FieldOffset(0x29)]
    public byte maxSelectionItem;

    [FieldOffset(0x2A)]
    public byte maxSelectionPit;

    [FieldOffset(0x2B)]
    public byte maxSelectionAirLostAction;
}

Stage Select

A pointer to this structure is located at address 16BF1CC when the menu is open.

/// <summary>
/// Note: Size is a decent estimate, real size is not known.
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
public unsafe struct StageSelect
{
    [FieldOffset(0x2C)]
    public MenuState menuState;

    [FieldOffset(0x2D)]
    public byte currentSelectionHorizontal;

    [FieldOffset(0x2E)]
    public byte currentSelectionVertical;

    [FieldOffset(0x2F)]
    public MenuState submenuState;

    /// <summary>
    /// Individual Track Order by Index. i.e. 1 2 3 4 5 6 7 8
    /// </summary>
    [FieldOffset(0x30)]
    public fixed byte stageOrder[8];

    /// <summary>
    /// Stage Select List Scroll Value.
    /// e.g. Setting 1 makes Splash Canyon on left.
    /// </summary>
    [FieldOffset(0x38)]
    public byte listScrollOffset;

    /// <summary>
    /// Vertical submenu selection.
    /// e.g. Setting 1 selects Night Chase in Metal City's submenu.
    /// </summary>
    [FieldOffset(0x3A)]
    public byte submenuSelection;

    /// <summary>
    /// Vertical submenu selection.
    /// e.g. Setting 1 selects Night Chase in Metal City's submenu.
    /// </summary>
    [FieldOffset(0x3C)]
    public byte trackSelectMode;
}

Title Screen

A pointer to this structure is located at address 16BF1CC when the menu is open.


/// <summary>
/// Note: Size is a decent estimate, real size is not known.
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 0x1C)]
public struct TitleScreen
{
    /// <summary>
    /// See <see cref="MenuState"/>
    /// </summary>
    [FieldOffset(0xC)]
    public MenuState menuState;

    [FieldOffset(0xD)]
    public byte currentSelection;
}

Misc

Various miscellaneous either non-game or arbitrary structures used to make manipulation of values in other structures easier.

Vector

[StructLayout(LayoutKind.Sequential, Size = 12)]
public struct Vector
{
    public float X;
    public float Y;
    public float Z;
}