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:
vtoy_json_parse_value()dispatches based on the first character ({,[,", digit,t/f/n) — located in [ventoy_json.c](https://github.com/ventoy/Ventoy/blob/master/Plugson/src/Core/ventoy_json.c#L3324-L3379).vtoy_json_parse_object()handles objects ({ … }) by creating child nodes for each key/value pair — found in [ventoy_json.c](https://github.com/ventoy/Ventoy/blob/master/Plugson/src/Core/ventoy_json.c#L3111-L3229).vtoy_json_parse_array()processes arrays ([ … ]) analogously — located in [ventoy_json.c](https://github.com/ventoy/Ventoy/blob/master/Plugson/src/Core/ventoy_json.c#L2870-L2919).
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
#nameto 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 fromg_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:
- The server reads the POST body into
post_data_buf. - Creates a fresh
VTOY_JSONroot viavtoy_json_create(). - Parses the payload using
vtoy_json_parse(). - 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.jsonfile parsed through a three-layer pipeline. - File I/O is handled by
ventoy_load_old_json()inPlugson/src/Web/ventoy_http.c, which reads the file and strips UTF-8 BOM markers. - Tokenization converts raw text into a linked
VTOY_JSONtree using recursive descent parsing inPlugson/src/Core/ventoy_json.c. - Domain mapping uses the
ventoy_parse_jsonmacro inPlugson/src/Web/ventoy_http.hto dispatch top-level blocks (e.g.,control_uefi) to specialized handlers likeventoy_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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →