Files
libretro/schemas/install-manifest.schema.json
T
Abdessamad Derraz 329b9e1bda docs: describe every install manifest field
Nested fields and each omission reason code, read from the code that writes them.
2026-10-24 00:51:13 +02:00

1035 lines
37 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://abdess.github.io/retrobios/api/v1/schemas/install-manifest.schema.json",
"title": "RetroBIOS installer manifest",
"type": "object",
"additionalProperties": false,
"required": [
"manifest_version",
"source",
"regions",
"platform",
"display_name",
"version",
"generated",
"base_destination",
"detect",
"standalone_copies",
"total_files",
"total_size",
"total_omitted",
"omitted_files",
"files"
],
"properties": {
"manifest_version": {
"const": 2,
"description": "Format version of the manifest. install.py refuses a version it does not know."
},
"source": {
"enum": [
"full",
"platform",
"truth"
],
"description": "Which files the pack was built from: full (the platform's list plus the files its cores load), platform (the list alone) or truth (the cores' files alone)."
},
"regions": {
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true,
"description": "Region priority the manifest was narrowed to, best first. Empty when it was not narrowed."
},
"target": {
"type": "string",
"minLength": 1,
"description": "Hardware target this manifest was filtered for, absent when unfiltered"
},
"categorized": {
"description": "Every game data file carries category: game_data. Without it the list records no category, and the installer refuses --no-game-data.",
"const": true
},
"no_game_data": {
"description": "Built without game data, absent when the game data is listed",
"const": true
},
"platform": {
"type": "string",
"minLength": 1,
"description": "Registry key of the platform (batocera)."
},
"display_name": {
"type": "string",
"minLength": 1,
"description": "Name the site shows for the platform."
},
"version": {
"type": "string",
"description": "Version of the platform's own list this manifest was built from."
},
"generated": {
"type": "string",
"format": "date-time",
"description": "When the manifest was written."
},
"base_destination": {
"type": "string",
"description": "The BIOS folder entries are written below, as the platform names it. Empty when entries start one level higher (RetroDECK)."
},
"detect": {
"type": "array",
"items": {
"type": "object"
},
"description": "Rules install.py follows to find this setup and its BIOS folder on a machine, one per method and operating system."
},
"standalone_copies": {
"type": "array",
"items": {
"type": "object"
},
"description": "Files install.py also copies into the folders of standalone emulators it finds, or a note it prints when their folder exists."
},
"total_files": {
"type": "integer",
"minimum": 0,
"description": "Number of entries in files."
},
"total_size": {
"type": "integer",
"minimum": 0,
"description": "Bytes of the files in files."
},
"pack_files": {
"description": "Files the platform's pack holds once extracted: this list, the data directories and the pack's own documents.",
"type": "integer",
"minimum": 0
},
"pack_size": {
"description": "Bytes those files occupy once extracted.",
"type": "integer",
"minimum": 0
},
"total_omitted": {
"type": "integer",
"minimum": 0,
"description": "Number of entries in omitted_files."
},
"core_names": {
"description": "Each core a file names, with the other names it goes by upstream (the buildbot's mednafen_psx for beetle_psx).",
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"uniqueItems": true
}
},
"system_cores": {
"description": "Each platform system with the cores whose presence keeps it. A system absent here is kept whatever cores are available.",
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"uniqueItems": true
}
},
"system_titles": {
"description": "Each system id with the name a person reads for it, the one the site prints (Sony PlayStation). A system the site cannot name is absent.",
"type": "object",
"additionalProperties": {
"type": "string",
"minLength": 1
}
},
"system_names": {
"description": "Each platform system with the names the platform's own file gives it (native_system), which the installer accepts for --system.",
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"uniqueItems": true
}
},
"omitted_files": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"dest",
"name",
"system",
"required",
"reason",
"cores"
],
"properties": {
"dest": {
"type": "string",
"pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$",
"description": "Where the file would go, below base_destination, or below the save directory when root is save."
},
"name": {
"type": "string",
"minLength": 1,
"description": "File name the declaration gives."
},
"system": {
"type": "string",
"description": "System id of the declaration. Empty when the core's file names none."
},
"required": {
"type": "boolean",
"description": "The platform or the core needs the file to run, as the declaration says."
},
"reason": {
"enum": [
"hash_mismatch",
"not_found",
"external",
"user_provided",
"no_platform_slug",
"below_platform_folder",
"no_save_directory",
"no_core_folder",
"no_save_system"
],
"description": "Why the file is left out, as a code. enumDescriptions gives the meaning of each code, in the order of enum.",
"enumDescriptions": [
"The platform checks the hash and the only copy held contradicts the one it declares, so the frontend would refuse it.",
"The collection holds no copy the declaration accepts.",
"The declaration keeps the file outside the repository (storage: external). The pack builder downloads it from its own address, the installer does not.",
"The user supplies the file (storage: user_provided). The pack writes its instructions in its place.",
"The platform keeps one folder per system and has none for the system the core's file belongs to.",
"The platform lists each system folder flat (firmware_folder_flat) and the file would sit in a subfolder it does not read.",
"The core reads the file below the save directory and the platform has no save directory a pack can fill.",
"The frontend sorts saves by core and the core's profile does not name its folder (core_folder).",
"The launcher gives each system its own save folder and no system of the platform runs the core."
]
},
"category": {
"description": "game_data: a game's own data, not firmware. Absent on firmware.",
"const": "game_data"
},
"held": {
"description": "The pack builder holds this file and the installer cannot download it. Region narrowing counts it held, as the builder does, so it displaces the same fallback.",
"const": true
},
"cores": {
"oneOf": [
{
"type": "null"
},
{
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true
}
],
"description": "Profiles whose core wants the file. Null for a file the platform's own list declares."
},
"regions": {
"description": "The regions the emulator code selects the file for. Absent when it never competes.",
"type": "array",
"items": {
"type": "string",
"enum": [
"argentina",
"asia",
"asia-ntsc",
"asia-pal",
"australia",
"brazil",
"canada",
"china",
"denmark",
"europe",
"finland",
"france",
"germany",
"greece",
"hong-kong",
"india",
"italy",
"japan",
"latin-america",
"mexico",
"netherlands",
"new-zealand",
"north-america",
"norway",
"oceania",
"poland",
"portugal",
"russia",
"singapore",
"south-korea",
"spain",
"sweden",
"taiwan",
"uk",
"world"
]
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"region_groups": {
"description": "Groups the file competes in as a core extra, whatever systems are chosen.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"region_system_groups": {
"description": "Groups the file competes in as the platform's own declaration: present only when that system is kept.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"systems": {
"description": "Every platform system that declares the file, for the installer's --system.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"priority": {
"description": "The rank the code searches the file at, lower first: a world file withdraws an unmatched regional one only when it ranks no worse.",
"type": "integer"
},
"declared_in": {
"description": "The platform systems declaring the file, when fewer than `systems`, which also names the systems whose cores read it there.",
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"uniqueItems": true
},
"alternatives": {
"description": "Cores claiming this destination behind the platform's own declaration, in the order the pack builder tries them. A narrowing that drops every declaring system takes the first whose cores remain.",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"cores"
],
"properties": {
"cores": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true,
"description": "Profiles of the cores that claim the destination together."
}
}
}
},
"root": {
"description": "save: dest is relative to the save directory the frontend hands the core (RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY), not to the BIOS directory. Absent for a file of the BIOS tree.",
"const": "save"
},
"core_folder": {
"description": "The core's library_name, the folder a frontend sorting saves by core (RetroArch's sort_savefiles_enable) inserts between its save directory and dest. Only with root: save. Absent on a save entry when the core reads the same place sorted or not.",
"type": "string",
"minLength": 1,
"pattern": "^[^/\\\\]+$"
}
}
},
"description": "Files the pack needs and the repository cannot serve, with the reason."
},
"files": {
"type": "array",
"items": {
"$ref": "#/$defs/file"
},
"description": "Every file the installer fetches, one entry per destination."
},
"case_variants": {
"description": "Files the folded list serves under another case, installed only where the target directory keeps case.",
"type": "array",
"items": {
"$ref": "#/$defs/file"
}
},
"data_directories": {
"description": "Data trees the pack carries, rebuilt by the installer from their source archive. Each file is kept only when its sha1 matches.",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"ref",
"dest",
"url",
"type",
"systems",
"files"
],
"properties": {
"ref": {
"type": "string",
"description": "Key of the data directory in platforms/_data_dirs.yml."
},
"dest": {
"type": "string",
"pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).*$",
"description": "Folder below base_destination the tree is rebuilt in. Empty for base_destination itself."
},
"url": {
"type": "string",
"pattern": "^https://",
"description": "HTTPS address of the source archive the installer downloads."
},
"type": {
"enum": [
"zip",
"tarball"
],
"description": "Format of the source archive: zip, or tarball (gzip)."
},
"strip_components": {
"type": "integer",
"minimum": 0,
"description": "With type zip: leading path parts dropped from each member."
},
"source_path": {
"type": "string",
"description": "With type tarball: only members below this path are kept, relative to it."
},
"exclude": {
"type": "array",
"items": {
"type": "string"
},
"description": "First path parts left out of the tree."
},
"systems": {
"type": "array",
"items": {
"type": "string"
},
"description": "Platform systems whose cores read the tree."
},
"files": {
"type": "object",
"additionalProperties": {
"type": "object",
"additionalProperties": false,
"required": [
"sha1",
"size"
],
"properties": {
"sha1": {
"type": "string",
"pattern": "^[a-f0-9]{40}$",
"description": "SHA1 the file must have."
},
"size": {
"type": "integer",
"minimum": 0,
"description": "Bytes of the file."
}
}
},
"description": "Every file of the tree by its path below dest. A member whose sha1 differs is refused."
}
}
}
},
"save_directory": {
"description": "Where the platform keeps the save directory its frontend hands a core, present when an entry carries root: save. folder is the pack folder holding those files and the save directory's default place beside the BIOS directory, and by_core says the frontend appends each core's library_name (core_folder).",
"type": "object",
"additionalProperties": false,
"required": [
"folder",
"by_core"
],
"properties": {
"folder": {
"type": "string",
"pattern": "^[^/\\\\.][^/\\\\]*(/[^/\\\\.][^/\\\\]*)*$",
"description": "Pack folder holding the save entries, also the save directory's default place beside the BIOS directory."
},
"by_core": {
"type": "boolean",
"description": "true when the frontend inserts each core's library_name (core_folder) below the save directory."
},
"by_system": {
"description": "true when the platform's launcher hands each core <save directory>/<system> at each launch (Batocera, RetroBat). Each dest then starts with that system's folder, and no frontend config read at install time changes it.",
"const": true
}
}
}
},
"$defs": {
"file": {
"type": "object",
"additionalProperties": false,
"required": [
"dest",
"sha1",
"sha256",
"size",
"repo_path",
"cores"
],
"properties": {
"dest": {
"type": "string",
"pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$",
"description": "Path below base_destination, or below the save directory when root is save."
},
"sha1": {
"type": "string",
"pattern": "^[a-f0-9]{40}$",
"description": "SHA1 of the copy the repository serves. The installer checks it after download."
},
"sha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$",
"description": "SHA256 of the same copy."
},
"size": {
"type": "integer",
"minimum": 0,
"description": "Bytes of the same copy."
},
"repo_path": {
"type": "string",
"description": "Path in the repository. Fetched from `https://raw.githubusercontent.com/Abdess/retrobios/<ref>/<repo_path>`, ref being main unless RETROBIOS_REF names a tag."
},
"cores": {
"oneOf": [
{
"type": "null"
},
{
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true
}
],
"description": "Profiles whose core brings the file into the pack. Null for a file the platform's own list declares."
},
"storage": {
"const": "release",
"description": "release when the file is a large-files release asset."
},
"release_asset": {
"type": "string",
"pattern": "^[^/\\\\]+$",
"description": "Asset of the large-files release, fetched from `https://github.com/Abdess/retrobios/releases/download/large-files/<asset>` instead of repo_path."
},
"systems": {
"description": "Every platform system that declares the file, for the installer's --system.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"regions": {
"description": "The regions the emulator code selects the file for. Absent when it never competes.",
"type": "array",
"items": {
"type": "string",
"enum": [
"argentina",
"asia",
"asia-ntsc",
"asia-pal",
"australia",
"brazil",
"canada",
"china",
"denmark",
"europe",
"finland",
"france",
"germany",
"greece",
"hong-kong",
"india",
"italy",
"japan",
"latin-america",
"mexico",
"netherlands",
"new-zealand",
"north-america",
"norway",
"oceania",
"poland",
"portugal",
"russia",
"singapore",
"south-korea",
"spain",
"sweden",
"taiwan",
"uk",
"world"
]
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"region_groups": {
"description": "Groups the file competes in as a core extra, whatever systems are chosen.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"region_system_groups": {
"description": "Groups the file competes in as the platform's own declaration: present only when that system is kept.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"priority": {
"description": "The rank the code searches the file at, lower first: a world file withdraws an unmatched regional one only when it ranks no worse.",
"type": "integer"
},
"alternatives": {
"description": "Copies other cores read at this destination when they disagree on its content, in the order the pack builder tries them. A narrowing that drops the entry's cores takes the first whose cores remain.",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"sha1",
"sha256",
"size",
"repo_path",
"cores"
],
"properties": {
"sha1": {
"type": "string",
"pattern": "^[a-f0-9]{40}$",
"description": "SHA1 of this copy."
},
"sha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$",
"description": "SHA256 of this copy."
},
"size": {
"type": "integer",
"minimum": 0,
"description": "Bytes of this copy."
},
"repo_path": {
"type": "string",
"description": "Path of this copy in the repository, fetched as the entry's repo_path is."
},
"storage": {
"const": "release",
"description": "release when this copy is a large-files release asset."
},
"release_asset": {
"type": "string",
"pattern": "^[^/\\\\]+$",
"description": "Asset of the large-files release holding this copy, fetched instead of repo_path."
},
"cores": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true,
"description": "Profiles whose core reads this copy."
},
"executable": {
"type": "boolean",
"description": "The repository keeps this copy executable."
}
}
}
},
"slot_choices": {
"description": "On a platform file a core's own source contradicts while the frontend only checks the path: the copy each core's presence puts there, in the order the pack builder settles the slot. A narrowing serves the first whose cores remain. One naming only cores, or none remaining, leaves `declared_choices`.",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"cores"
],
"properties": {
"sha1": {
"type": "string",
"pattern": "^[a-f0-9]{40}$",
"description": "SHA1 of this copy."
},
"sha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$",
"description": "SHA256 of this copy."
},
"size": {
"type": "integer",
"minimum": 0,
"description": "Bytes of this copy."
},
"repo_path": {
"type": "string",
"description": "Path of this copy in the repository, fetched as the entry's repo_path is."
},
"storage": {
"const": "release",
"description": "release when this copy is a large-files release asset."
},
"release_asset": {
"type": "string",
"pattern": "^[^/\\\\]+$",
"description": "Asset of the large-files release holding this copy, fetched instead of repo_path."
},
"cores": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true,
"description": "Profiles whose presence puts this copy in the slot."
},
"executable": {
"type": "boolean",
"description": "The repository keeps this copy executable."
}
}
}
},
"declared_choices": {
"description": "The platform's own answer at the destination by the declaring systems that remain, in the order the pack builder takes the declarations. A narrowing serves the first whose systems remain.",
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"sha1",
"sha256",
"size",
"repo_path",
"systems"
],
"properties": {
"sha1": {
"type": "string",
"pattern": "^[a-f0-9]{40}$",
"description": "SHA1 of this copy."
},
"sha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$",
"description": "SHA256 of this copy."
},
"size": {
"type": "integer",
"minimum": 0,
"description": "Bytes of this copy."
},
"repo_path": {
"type": "string",
"description": "Path of this copy in the repository, fetched as the entry's repo_path is."
},
"storage": {
"const": "release",
"description": "release when this copy is a large-files release asset."
},
"release_asset": {
"type": "string",
"pattern": "^[^/\\\\]+$",
"description": "Asset of the large-files release holding this copy, fetched instead of repo_path."
},
"executable": {
"type": "boolean",
"description": "The repository keeps this copy executable."
},
"systems": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"minItems": 1,
"uniqueItems": true,
"description": "Platform systems whose declaration this copy answers."
}
}
}
},
"category": {
"description": "game_data: a game's own data, not firmware. Absent on firmware. The installer's --no-game-data leaves these out, as a pack built with --no-game-data does.",
"const": "game_data"
},
"executable": {
"description": "The repository keeps the file executable, and the installer gives it the execute bits the umask allows.",
"type": "boolean"
},
"declared_in": {
"description": "The platform systems declaring the file, when fewer than `systems`, which also names the systems whose cores read it there.",
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"uniqueItems": true
},
"absent_claims": {
"description": "Cores that want this destination and have nothing held, after the cores served here: a narrowing that keeps none of those takes the first named one as a file left out, as a pack built for it lists it.",
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"system",
"required",
"reason",
"cores"
],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "File name the declaration gives."
},
"system": {
"type": "string",
"description": "System id of the declaration. Empty when the core's file names none."
},
"required": {
"type": "boolean",
"description": "The platform or the core needs the file to run, as the declaration says."
},
"reason": {
"enum": [
"hash_mismatch",
"not_found",
"external",
"user_provided",
"no_platform_slug",
"below_platform_folder"
],
"description": "Why the file is left out, as a code. enumDescriptions gives the meaning of each code, in the order of enum.",
"enumDescriptions": [
"The platform checks the hash and the only copy held contradicts the one it declares, so the frontend would refuse it.",
"The collection holds no copy the declaration accepts.",
"The declaration keeps the file outside the repository (storage: external). The pack builder downloads it from its own address, the installer does not.",
"The user supplies the file (storage: user_provided). The pack writes its instructions in its place.",
"The platform keeps one folder per system and has none for the system the core's file belongs to.",
"The platform lists each system folder flat (firmware_folder_flat) and the file would sit in a subfolder it does not read."
]
},
"cores": {
"oneOf": [
{
"type": "null"
},
{
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true
}
],
"description": "Profiles whose core wants the file. Null for a file the platform's own list declares."
},
"regions": {
"description": "The regions the emulator code selects the file for. Absent when it never competes.",
"type": "array",
"items": {
"type": "string",
"enum": [
"argentina",
"asia",
"asia-ntsc",
"asia-pal",
"australia",
"brazil",
"canada",
"china",
"denmark",
"europe",
"finland",
"france",
"germany",
"greece",
"hong-kong",
"india",
"italy",
"japan",
"latin-america",
"mexico",
"netherlands",
"new-zealand",
"north-america",
"norway",
"oceania",
"poland",
"portugal",
"russia",
"singapore",
"south-korea",
"spain",
"sweden",
"taiwan",
"uk",
"world"
]
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"region_groups": {
"description": "Groups the file competes in as a core extra, whatever systems are chosen.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"region_system_groups": {
"description": "Groups the file competes in as the platform's own declaration: present only when that system is kept.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"systems": {
"description": "Every platform system that declares the file, for the installer's --system.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"priority": {
"description": "The rank the code searches the file at, lower first: a world file withdraws an unmatched regional one only when it ranks no worse.",
"type": "integer"
},
"declared_in": {
"description": "The platform systems declaring the file, when fewer than `systems`, which also names the systems whose cores read it there.",
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"uniqueItems": true
}
}
}
},
"root": {
"description": "save: dest is relative to the save directory the frontend hands the core (RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY), not to the BIOS directory. Absent for a file of the BIOS tree.",
"const": "save"
},
"core_folder": {
"description": "The core's library_name, the folder a frontend sorting saves by core (RetroArch's sort_savefiles_enable) inserts between its save directory and dest. Only with root: save. Absent on a save entry when the core reads the same place sorted or not.",
"type": "string",
"minLength": 1,
"pattern": "^[^/\\\\]+$"
}
},
"oneOf": [
{
"required": [
"release_asset"
]
},
{
"properties": {
"repo_path": {
"type": "string",
"pattern": "^bios/",
"description": "Without release_asset, the copy is served from the repository's bios/ tree."
}
},
"not": {
"required": [
"release_asset"
]
}
}
]
}
},
"description": "What a platform's pack installs, one file per entry, as install.py reads it. The platform's list says what the setup checks, this list says what the pack writes: the list's files plus every file the setup's cores load."
}