# Apollo PS4 Menu System Architecture: How `menu_option_t` and Callbacks Power the UI

> Discover Apollo PS4's menu system architecture. Learn how menu_option_t and callbacks drive the UI, allowing easy menu extension without touching rendering code. Boost your development today.

- Repository: [Damián Parrino/apollo-ps4](https://github.com/bucanero/apollo-ps4)
- Tags: architecture
- Published: 2026-02-26

---

**Apollo PS4 implements a data-driven UI architecture where the `menu_option_t` structure defines menu items and callback functions handle state changes, enabling developers to extend menus without modifying rendering code.**

Apollo PS4 is an open-source save game manager for the PlayStation 4 that utilizes a lightweight, data-driven menu system to handle all on-screen interfaces. The architectural design centers on the `menu_option_t` structure defined in [`include/settings.h`](https://github.com/bucanero/apollo-ps4/blob/main/include/settings.h) and a system of callback functions that bridge user input to application state. This separation of concerns allows new settings or features to be added by simply extending a static array and implementing a small callback, without touching the rendering pipeline.

## Core Data Structure – `menu_option_t`

The foundation of Apollo's menu architecture is the `menu_option_t` structure, which encapsulates everything needed to display and manage a single menu item.

### Structure Definition

Located in [`include/settings.h`](https://github.com/bucanero/apollo-ps4/blob/main/include/settings.h), the structure ties together display text, option values, data types, and function pointers:

```c
/* include/settings.h */
typedef struct
{
    const char * name;               // Text shown in the menu
    const char * * options;          // NULL for non-list types, otherwise array of strings
    enum app_option_type type;       // BOOL, LIST, INC, CALL, NONE
    uint8_t spacer;                  // Extra vertical space before this item
    uint8_t * value;                 // Pointer to stored setting (NULL for CALL)
    void(*callback)(int);            // Function executed on value change/activation
} menu_option_t;

```

*Source:* [[`include/settings.h`](https://github.com/bucanero/apollo-ps4/blob/main/include/settings.h) lines 19-27](https://github.com/bucanero/apollo-ps4/blob/main/include/settings.h#L19-L27)

### Option Types

The `type` field determines how the input handler and renderer treat the option:

- **`APP_OPTION_BOOL`**: On/off toggle using texture indices `opt_on_png_index` and `opt_off_png_index`
- **`APP_OPTION_LIST`**: Cycles through a predefined string array (`options` field)
- **`APP_OPTION_INC`**: Numeric increment/decrement control (e.g., "- 5 +")
- **`APP_OPTION_CALL`**: Executes a function without storing a value, used for actions like "Clear Cache"
- **`APP_OPTION_NONE`**: Reserved placeholder type

## Defining Menu Options

All visible menu options reside in [`source/settings.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c) as the global `menu_options[]` array. Each entry maps UI elements to their underlying storage in the `apollo_config` structure and their respective callbacks.

### The Global Options Array

```c
/* source/settings.c */
menu_option_t menu_options[] = {
    { .name = _i18n("Background Music"),
      .options = NULL,
      .type = APP_OPTION_BOOL,
      .value = &apollo_config.music,
      .callback = music_callback },

    { .name = _i18n("Menu Animations"),
      .options = NULL,
      .type = APP_OPTION_BOOL,
      .value = &apollo_config.doAni,
      .callback = ani_callback },

    { .name = _i18n("Sort Saves"),
      .options = sort_opt,
      .type = APP_OPTION_LIST,
      .value = &apollo_config.doSort,
      .callback = sort_callback },

    /* Additional options... */

    { .name = NULL }   // Sentinel terminates the array
};

```

*Source:* [[`source/settings.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c) lines 33-59](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c#L33-L59)

### Extending the Menu

Adding a new option requires only three steps without modifying rendering code:

```c
/* 1. Define value list if using LIST type */
static const char * theme_opt[] = { "Light", "Dark", NULL };

/* 2. Append to menu_options[] */
{ .name = _i18n("UI Theme"),
  .options = theme_opt,
  .type = APP_OPTION_LIST,
  .value = &apollo_config.theme,
  .callback = theme_callback },

/* 3. Implement callback */
void theme_callback(int sel) {
    apollo_config.theme = sel;
    apply_theme(sel);  // Immediate application
}

```

## Input Handling and Callback Dispatch

The input loop in [`source/menu_main.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_main.c) drives the menu interaction. The `doOptionsMenu()` function polls the PS4 controller via `orbisPad*` helpers and manages selection movement, value adjustment, and callback invocation.

### Navigation and Value Adjustment

When the user presses LEFT or RIGHT, the handler updates the underlying value and immediately triggers the callback:

```c
/* source/menu_main.c */
else if (orbisPadGetButtonHold(ORBIS_PAD_BUTTON_LEFT)) {
    if (menu_options[menu_sel].type == APP_OPTION_LIST) {
        if (*menu_options[menu_sel].value > 0)
            (*menu_options[menu_sel].value)--;
        else
            *menu_options[menu_sel].value = menu_options_maxsel[menu_sel] - 1;
    } else if (menu_options[menu_sel].type == APP_OPTION_INC) {
        (*menu_options[menu_sel].value)--;
    }
    if (menu_options[menu_sel].type != APP_OPTION_CALL)
        menu_options[menu_sel].callback(*menu_options[menu_sel].value);
}

```

*Source:* [[`source/menu_main.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_main.c) lines 94-106](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_main.c#L94-L106)

### Activation Handling

The CROSS button handles boolean toggles and call-type actions:

```c
/* source/menu_main.c */
else if (orbisPadGetButtonPressed(ORBIS_PAD_BUTTON_CROSS)) {
    if (menu_options[menu_sel].type == APP_OPTION_BOOL)
        menu_options[menu_sel].callback(*menu_options[menu_sel].value);
    else if (menu_options[menu_sel].type == APP_OPTION_CALL)
        menu_options[menu_sel].callback(0);
}

```

*Source:* [[`source/menu_main.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_main.c) lines 123-130](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_main.c#L123-L130)

### Callback Implementation Examples

Callbacks bridge UI changes to application state. The music toggle demonstrates the pattern:

```c
/* source/settings.c */
void music_callback(int sel) {
    apollo_config.music = !sel;
}

```

*Source:* [[`source/settings.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c) lines 6-9](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c#L6-L9)

A more complex example handles debug logging initialization:

```c
/* source/settings.c */
void log_callback(int sel) {
    apollo_config.dbglog = !sel;
    if (!apollo_config.dbglog) {
        dbglogger_stop();
        show_message(_("Debug Logging Disabled"));
    } else {
        dbglogger_init_mode(FILE_LOGGER, APOLLO_PATH "apollo.log", 0);
        show_message("%s\n\n%s", _("Debug Logging Enabled!"), APOLLO_PATH "apollo.log");
    }
}

```

*Source:* [[`source/settings.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c) lines 89-101](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c#L89-L101)

## Rendering Pipeline

The [`source/menu_options.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_options.c) file contains the generic rendering routine `_draw_OptionsMenu`. This function iterates over `menu_options[]` and draws the appropriate visual elements based on the `type` field, completely decoupling presentation from data definition.

### The Drawing Loop

```c
/* source/menu_options.c */
for (int ind = 0, y_off = 200; menu_options[ind].name; ind++, y_off += APP_LINE_OFFSET) {
    /* Label drawing... */
    
    switch (menu_options[ind].type) {
        case APP_OPTION_BOOL:
            c = (*menu_options[ind].value == 1) ? opt_on_png_index : opt_off_png_index;
            DrawTexture(&menu_textures[c], OPTION_ITEM_OFF - 29, y_off, 0,
                        menu_textures[c].width, menu_textures[c].height,
                        0xFFFFFF00 | alpha);
            break;
        case APP_OPTION_LIST:
            SetFontAlign(FONT_ALIGN_CENTER);
            DrawFormatString(OPTION_ITEM_OFF - 18, y_off,
                "< %s >", menu_options[ind].options[*menu_options[ind].value]);
            SetFontAlign(FONT_ALIGN_LEFT);
            break;
        case APP_OPTION_INC:
            SetFontAlign(FONT_ALIGN_CENTER);
            DrawFormatString(OPTION_ITEM_OFF - 18, y_off,
                "- %d +", *menu_options[ind].value);
            SetFontAlign(FONT_ALIGN_LEFT);
            break;
        /* APP_OPTION_CALL draws generic button icon */
    }
    
    /* Selection highlight */
    if (menu_sel == ind) {
        DrawTexture(&menu_textures[mark_line_png_index], 0, y_off, 0,
                    SCREEN_WIDTH, menu_textures[mark_line_png_index].height * 2,
                    0xFFFFFF00 | alpha);
        DrawTextureCenteredX(&menu_textures[mark_arrow_png_index],
                    MENU_ICON_OFF + MENU_TITLE_OFF, y_off, 0,
                    (2 * APP_LINE_OFFSET) / 3, APP_LINE_OFFSET + 2,
                    0xFFFFFF00 | alpha);
    }
}

```

*Source:* [[`source/menu_options.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_options.c) lines 16-56](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_options.c#L16-L56)

The rendering code remains completely agnostic of the semantic meaning of each option. It only interrogates the `type`, `value`, and `options` fields, enabling the data-driven architecture.

## Key Files in the Menu Subsystem

Understanding the file organization helps navigate the codebase when extending functionality:

| File | Role | Key Symbols |
|------|------|-------------|
| [`include/settings.h`](https://github.com/bucanero/apollo-ps4/blob/main/include/settings.h) | Structure definitions and enums | `menu_option_t`, `enum app_option_type`, `app_config_t` |
| [`source/settings.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c) | Menu definition array and callbacks | `menu_options[]`, `music_callback()`, `log_callback()` |
| [`source/menu_main.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_main.c) | Input processing and screen flow | `doOptionsMenu()`, `move_selection_fwd()`, `SetMenu()` |
| [`source/menu_options.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_options.c) | Generic rendering implementation | `_draw_OptionsMenu()`, texture indices |
| [`include/menu.h`](https://github.com/bucanero/apollo-ps4/blob/main/include/menu.h) | Rendering helper prototypes | `DrawTexture()`, `SetFontSize()`, `DrawFormatString()` |

## Summary

Apollo PS4's menu architecture demonstrates effective separation of concerns through a data-driven design:

- **`menu_option_t`** serves as the universal descriptor for menu items, encapsulating display text, data type, storage pointer, and callback function
- **Callback functions** bridge UI interactions to application state, executing immediately when values change or actions trigger
- **Input handling** in [`source/menu_main.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_main.c) operates generically on the `menu_options[]` array, requiring no modification when adding new options
- **Rendering** in [`source/menu_options.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_options.c) draws appropriate widgets based solely on the `type` field, maintaining visual consistency across all menus

This architecture enables rapid feature addition—developers extend `menu_options[]` with a new struct entry and implement a callback, while the existing input and rendering infrastructure handles the rest automatically.

## Frequently Asked Questions

### What is the `menu_option_t` structure in Apollo PS4?

The `menu_option_t` structure is the core data structure defined in [`include/settings.h`](https://github.com/bucanero/apollo-ps4/blob/main/include/settings.h) that describes every interactive element in Apollo's settings menus. It contains fields for the display name, option type (boolean, list, increment, or call), a pointer to the stored value, an optional list of string choices, and a callback function pointer that executes when the value changes.

### How do callbacks function in the Apollo PS4 menu system?

Callbacks in Apollo PS4 are function pointers stored in the `callback` field of `menu_option_t` that bridge user interface interactions to application logic. When a user changes a setting value using the directional buttons or activates a call-type option with the CROSS button, the input handler in [`source/menu_main.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_main.c) immediately invokes the associated callback with the new value as an argument, allowing real-time updates to the `apollo_config` state.

### Where is the menu rendering code located in Apollo PS4?

The generic menu rendering implementation resides in [`source/menu_options.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_options.c), specifically within the `_draw_OptionsMenu()` function. This file handles drawing all visual elements—including boolean toggle textures, list selection arrows, increment controls, and selection highlights—based solely on the metadata in the `menu_options[]` array. The rendering code remains completely decoupled from specific menu logic, using only the `type`, `value`, and `options` fields to determine visual presentation.

### How do I add a new option to the Apollo PS4 settings menu?

To add a new option, declare any necessary string arrays for list types, append a new `menu_option_t` entry to the `menu_options[]` array in [`source/settings.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c) with appropriate `.name`, `.type`, `.value`, and `.callback` fields, then implement the callback function to handle value changes. The existing input handling in [`source/menu_main.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_main.c) and rendering in [`source/menu_options.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_options.c) will automatically recognize and display the new option without requiring modifications.