How VentoyPlugson Parses JSON Plugin Configuration Files: A Deep Dive into the Source Code

VentoyPlugson parses JSON plugin configuration by loading ventoy.json into memory, tokenizing it into a recursive VTOY_JSON tree structure, and dispatching top-level blocks to specialized handlers via the ventoy_parse_json macro that maps configuration sections to internal data structures.

VentoyPlugson manages multiboot USB configurations through a centralized JSON-based plugin system. Understanding how VentoyPlugson parses JSON plugin configuration files reveals the architecture behind its flexible boot management and web-based configuration interface.

Overview of the JSON Parsing Pipeline

The parsing pipeline operates through three distinct layers that transform raw disk bytes into structured configuration data.

The Three-Layer Architecture

Layer Responsibility Main Source
I/O Layer Locate and read ventoy.json into memory ventoy_load_old_json() in [Plugson/src/Web/ventoy_http.c](https://github.com/ventoy/Ventoy/blob/master/Plugson/src/Web/ventoy_http.c#L5271-L5330)
Tokenization Layer Convert raw text into a linked node tree (VTOY_JSON) vtoy_json_parse*() in [Plugson/src/Core/ventoy_json.c](https://github.com/ventoy/Ventoy/blob/master/Plugson/src/Core/ventoy_json.c#L3324-L3398)
Domain Mapping Layer Walk the tree, match block names, fill internal structures ventoy_parse_json macro and ventoy_parse_*() functions in [Plugson/src/Web/ventoy_http.c](https://github.com/ventoy/Ventoy/blob/master/Plugson/src/Web/ventoy_http.c#L4085-L5340) and helper macros in [Plugson/src/Web/ventoy_http.h](https://github.com/ventoy/Ventoy/blob/master/Plugson/src/Web/ventoy_http.h#L389-L406)

Step 1: Loading the ventoy.json File

The entry point ventoy_load_old_json() handles file system interaction and initial buffer preparation.

int ventoy_load_old_json(const char *filename)
{
    /* read whole file → buffer */
    ventoy_read_file_to_buf(filename, 4, (void **)&buffer, &buflen);
    /* strip optional UTF‑8 BOM */
    /* … */
    /* create root node */
    json = vtoy_json_create();
    /* parse the text */
    vtoy_json_parse_ex(json, buffer + offset, buflen - offset);
    /* … iterate over children */
}

This function resides in [ventoy_http.c](https://github.com/ventoy/Ventoy/blob/master/Plugson/src/Web/ventoy_http.c#L5271-L5311) and serves as the bridge between persistent storage and the parser.

Step 2: JSON Tokenization and Tree Construction

Once loaded, the raw buffer undergoes recursive descent parsing to build an in-memory tree representation.

The VTOY_JSON Node Structure

Each node in the tree is defined in [Plugson/src/Core/ventoy_json.h](https://github.com/ventoy/Ventoy/blob/master/Plugson/src/Core/ventoy_json.h#L95-L111):

typedef struct tagVTOY_JSON {
    struct tagVTOY_JSON *pstPrev, *pstNext, *pstChild;
    JSON_TYPE enDataType;                 // number, string, object, array, bool, null
    union {
        char  *pcStrVal;   // for strings
        int    iNumVal;    // for numbers (int)
        uint64_t lValue;   // for numbers (uint64)
    } unData;
    char *pcName;                         // key name (NULL for root)
} VTOY_JSON;

Recursive Descent Parsing

The parser implements a hand-written recursive descent algorithm:

Step 3: Domain-Specific Configuration Parsing

After tokenization, the generic tree must be mapped to Ventoy's internal configuration structures.

The ventoy_parse_json Macro Dispatcher

The mapping relies on a sophisticated macro defined in [Plugson/src/Web/ventoy_http.h](https://github.com/ventoy/Ventoy/blob/master/Plugson/src/Web/ventoy_http.h#L389-L406):

#define ventoy_parse_json(name)                           \
{                                                         \
    int __len = (int)strlen(#name);                        \
    if (strncmp(#name, node->pcName, __len) == 0) {       \
        for (__loop = 0; __loop < bios_max; __loop++) {   \
            if (strcmp(g_json_title_postfix[__loop],        \
                       node->pcName + __len) == 0) {      \
                vlog("json parse <%s>\n", node->pcName);   \
                ventoy_parse_##name(node,                \
                    g_data_##name + __loop);              \
                break;                                    \
            }                                             \
        }                                                 \
    }                                                     \
}

This macro performs several critical functions:

  • Stringification: Uses #name to generate the block identifier (e.g., "control").
  • BIOS/UEFI Variant Detection: Compares against g_json_title_postfix[] (containing "_legacy", "_uefi", etc.) to handle mode-specific configurations.
  • Dispatch: Calls the concrete parser ventoy_parse_##name (e.g., ventoy_parse_control) and passes the appropriate storage slot from g_data_##name.

Block-Specific Handlers

Each configuration block implements a dedicated parser. For example, ventoy_parse_control() in [ventoy_http.c](https://github.com/ventoy/Ventoy/blob/master/Plugson/src/Web/ventoy_http.c#L4108-L4249) processes the control block:

static int ventoy_parse_control(VTOY_JSON *json, void *p)
{
    data_control *data = (data_control *)p;
    VTOY_JSON *node = json->pstChild;

    while (node) {
        if (strcmp(node->pcName, "VTOY_DEFAULT_MENU_MODE") == 0) {
            CONTROL_PARSE_INT_DEF_0(node, data->default_menu_mode);
        }
        /* … additional key processing … */
        node = node->pstNext;
    }
    return 0;
}

Helper macros like CONTROL_PARSE_INT_DEF_0 wrap the generic JSON getters (vtoy_json_get_int, vtoy_json_get_string) to populate the internal data structures with type safety and default value handling.

Runtime JSON Handling for Web UI

The same parsing infrastructure handles runtime configuration updates. When the Plugson web UI sends a POST request to /vtoy/json:

  1. The server reads the POST body into post_data_buf.
  2. Creates a fresh VTOY_JSON root via vtoy_json_create().
  3. Parses the payload using vtoy_json_parse().
  4. Dispatches to ventoy_json_handler(), which extracts the "method" field and routes to the appropriate API callback (e.g., ventoy_api_save_theme).

This ensures configuration changes made through the browser undergo the same validation and parsing as the static configuration file.

Practical Example: Reading Configuration Values

The following standalone example demonstrates how to reuse the Ventoy JSON library to read configuration values:

#include "ventoy_json.h"
#include <stdio.h>

int main(void)
{
    const char *filename = "ventoy.json";
    char *buf = NULL;
    int len, ret;
    VTOY_JSON *root = NULL;
    int timeout;

    /* 1. Load file into memory (simplified) */
    FILE *fp = fopen(filename, "rb");
    fseek(fp, 0, SEEK_END);
    len = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    buf = malloc(len + 1);
    fread(buf, 1, len, fp);
    buf[len] = '\0';
    fclose(fp);

    /* 2. Parse */
    root = vtoy_json_create();
    ret = vtoy_json_parse_ex(root, buf, len);
    if (ret != JSON_SUCCESS) {
        fprintf(stderr, "Parse error\n");
        return 1;
    }

    /* 3. Query a key inside the "control_uefi" block */
    VTOY_JSON *control = vtoy_json_find_item(root, JSON_TYPE_OBJECT, "control_uefi");
    if (control) {
        vtoy_json_get_int(control, "timeout", &timeout);
        printf("UEFI timeout = %d seconds\n", timeout);
    }

    vtoy_json_destroy(root);
    free(buf);
    return 0;
}

Key API functions demonstrated:

  • vtoy_json_create() – Allocates the root node.
  • vtoy_json_parse_ex() – Parses a raw buffer of known length.
  • vtoy_json_find_item() – Locates a specific object by name.
  • vtoy_json_get_int() – Extracts a numeric field.

Summary

  • VentoyPlugson stores configuration in a single ventoy.json file parsed through a three-layer pipeline.
  • File I/O is handled by ventoy_load_old_json() in Plugson/src/Web/ventoy_http.c, which reads the file and strips UTF-8 BOM markers.
  • Tokenization converts raw text into a linked VTOY_JSON tree using recursive descent parsing in Plugson/src/Core/ventoy_json.c.
  • Domain mapping uses the ventoy_parse_json macro in Plugson/src/Web/ventoy_http.h to dispatch top-level blocks (e.g., control_uefi) to specialized handlers like ventoy_parse_control().
  • Runtime updates reuse the same parser for POST requests from the web UI, ensuring consistent validation between file-based and interactive configuration.

Frequently Asked Questions

What file format does VentoyPlugson use for configuration?

VentoyPlugson uses a single JSON file named ventoy.json located in the Ventoy partition. This file contains top-level configuration blocks such as control, theme, menu_alias, and menu_class, with optional suffixes like _uefi or _legacy to support BIOS/UEFI-specific settings.

How does VentoyPlugson handle different BIOS/UEFI configurations?

The parser uses the ventoy_parse_json macro to detect suffixes defined in g_json_title_postfix[] (such as "_legacy" and "_uefi"). When parsing a key like control_uefi, the macro strips the suffix, identifies the base block type (control), and dispatches to ventoy_parse_control() while passing the appropriate storage slot for that boot mode.

Where is the JSON parser implementation located?

The core JSON tokenizer and tree builder reside in Plugson/src/Core/ventoy_json.c, with data structure definitions in Plugson/src/Core/ventoy_json.h. The domain-specific configuration dispatchers and block parsers are located in Plugson/src/Web/ventoy_http.c, with the dispatch macro defined in Plugson/src/Web/ventoy_http.h.

Can the JSON parser handle malformed or incomplete configuration files?

The parser implements strict validation during the tokenization phase in vtoy_json_parse_ex(). If the JSON structure is malformed (missing braces, invalid escape sequences, or trailing commas), the parser returns an error code before reaching the domain-specific mapping layer. However, individual configuration blocks may use default values when specific keys are missing, handled by macros like CONTROL_PARSE_INT_DEF_0 in the block-specific parsers.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →