mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 13:33:24 -05:00
Compare commits
83
Commits
8fe961a1ea
...
913ba09600
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
913ba09600 | ||
|
|
38655d1d23 | ||
|
|
9af89cb120 | ||
|
|
fbc92c99cf | ||
|
|
49b3cffa58 | ||
|
|
af4f980bab | ||
|
|
3582101af4 | ||
|
|
a4f3f016f8 | ||
|
|
a252e2d158 | ||
|
|
067d962210 | ||
|
|
0d81f19831 | ||
|
|
52d128bcf5 | ||
|
|
0b46880604 | ||
|
|
0f293b6c14 | ||
|
|
9ca14bb77e | ||
|
|
443639c33d | ||
|
|
0ea8840f14 | ||
|
|
53c5504330 | ||
|
|
7164e8a125 | ||
|
|
cea6e516a4 | ||
|
|
8d2df0b651 | ||
|
|
b20c38d1c1 | ||
|
|
45ede873b3 | ||
|
|
14e4dd919c | ||
|
|
dc5cdb8d70 | ||
|
|
87ef905c44 | ||
|
|
e3173b1fc5 | ||
|
|
514becd784 | ||
|
|
0cc6ab5990 | ||
|
|
4609102772 | ||
|
|
92594b1751 | ||
|
|
2499a0eabc | ||
|
|
91e113a7d3 | ||
|
|
e723f346f9 | ||
|
|
139bb99088 | ||
|
|
e35aa9a323 | ||
|
|
be2c325b53 | ||
|
|
b39b187243 | ||
|
|
6380a92e6c | ||
|
|
22f018995e | ||
|
|
5d5170dc12 | ||
|
|
64ed737ec6 | ||
|
|
87addda6e1 | ||
|
|
b8a7aa74ff | ||
|
|
d4ae1a76e8 | ||
|
|
eb8c7e5267 | ||
|
|
6748982d94 | ||
|
|
e335511b72 | ||
|
|
3088db7088 | ||
|
|
204637cfeb | ||
|
|
519c02646a | ||
|
|
72e47becb4 | ||
|
|
4a1a9dd861 | ||
|
|
d610aad990 | ||
|
|
a0af1b141e | ||
|
|
4d5340c5f4 | ||
|
|
4923873a7f | ||
|
|
ced7588d17 | ||
|
|
44cf638e06 | ||
|
|
a9b33484ed | ||
|
|
39d02b3b18 | ||
|
|
e5420a9dba | ||
|
|
9604e933ed | ||
|
|
dcc12e62b7 | ||
|
|
25e8eeb37b | ||
|
|
5d5befc002 | ||
|
|
c4e16e583e | ||
|
|
b0f2c3bb88 | ||
|
|
d89b193780 | ||
|
|
afdd38826c | ||
|
|
7ddb849c89 | ||
|
|
8e3f3fe057 | ||
|
|
af030fdd6d | ||
|
|
f159d35f18 | ||
|
|
dce43a1281 | ||
|
|
d7e7ec13ae | ||
|
|
098b2f0b70 | ||
|
|
6c9a41327b | ||
|
|
fb3c577521 | ||
|
|
821cbf4717 | ||
|
|
9c84b7990a | ||
|
|
4f8fbdfabb | ||
|
|
eccd0b0a28 |
No files matched your search
@@ -84,7 +84,7 @@ jobs:
|
|||||||
|
|
||||||
# mkdocs-material >= 9.7.5 caps mkdocs < 2; the upper bound keeps a major
|
# mkdocs-material >= 9.7.5 caps mkdocs < 2; the upper bound keeps a major
|
||||||
# theme release from changing the site without a deliberate bump here.
|
# theme release from changing the site without a deliberate bump here.
|
||||||
- run: pip install pyyaml jsonschema==4.26.0 "mkdocs-material>=9.7.5,<10" "pymdown-extensions>=10.14"
|
- run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0" "mkdocs-material>=9.7.5,<10" "pymdown-extensions>=10.14"
|
||||||
|
|
||||||
- name: Validate data contracts
|
- name: Validate data contracts
|
||||||
run: python scripts/validate_schemas.py
|
run: python scripts/validate_schemas.py
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ jobs:
|
|||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pip install pyyaml jsonschema==4.26.0
|
run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0"
|
||||||
|
|
||||||
- name: Validate platform configs and emulator profiles
|
- name: Validate platform configs and emulator profiles
|
||||||
run: python scripts/validate_schemas.py --source-only
|
run: python scripts/validate_schemas.py --source-only
|
||||||
@@ -127,7 +127,7 @@ jobs:
|
|||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pip install pyyaml jsonschema==4.26.0
|
run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0"
|
||||||
|
|
||||||
# The exporter fidelity cases compare what we hand back against the
|
# The exporter fidelity cases compare what we hand back against the
|
||||||
# file each platform publishes, so they need those files. They skip
|
# file each platform publishes, so they need those files. They skip
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ Every release ships `SHA256SUMS.txt` and a detached signature of it, checkable a
|
|||||||
| RetroBat | 1,466 | 4.4 GB | `bios/` | [Download](../../releases/latest) |
|
| RetroBat | 1,466 | 4.4 GB | `bios/` | [Download](../../releases/latest) |
|
||||||
| RetroDECK | 3,519 | 6.2 GB | `~/retrodeck/` | [Download](../../releases/latest) |
|
| RetroDECK | 3,519 | 6.2 GB | `~/retrodeck/` | [Download](../../releases/latest) |
|
||||||
| RetroPie * | 4,525 | 5.5 GB | `~/RetroPie/BIOS/` | [Download](../../releases/latest) |
|
| RetroPie * | 4,525 | 5.5 GB | `~/RetroPie/BIOS/` | [Download](../../releases/latest) |
|
||||||
| RomM | 617 | 1.3 GB | `bios/{platform_slug}/` | [Download](../../releases/latest) |
|
| RomM | 617 | 1.3 GB | `library/bios/` | [Download](../../releases/latest) |
|
||||||
|
|
||||||
The RetroDECK pack already contains its own `bios/` folder, so it extracts into `~/retrodeck/` rather than into the BIOS folder.
|
The RetroDECK pack already carries its own `bios/` folder, so it extracts into `~/retrodeck/` rather than into the BIOS folder.
|
||||||
|
|
||||||
\* Archived: the configuration is kept and packs are still built, but upstream is no longer scraped on a schedule.
|
\* Archived: the configuration is kept and packs are still built, but upstream is no longer scraped on a schedule.
|
||||||
|
|
||||||
@@ -72,9 +72,9 @@ Each file is checked the way your platform checks it. Most compare a checksum, t
|
|||||||
- **12 platforms** supported with platform-specific verification
|
- **12 platforms** supported with platform-specific verification
|
||||||
- **506 emulators** profiled from source (RetroArch cores + standalone)
|
- **506 emulators** profiled from source (RetroArch cores + standalone)
|
||||||
- **546 systems** handled by those emulators (NES, SNES, PlayStation, Saturn, Dreamcast, ...)
|
- **546 systems** handled by those emulators (NES, SNES, PlayStation, Saturn, Dreamcast, ...)
|
||||||
- **18,990 files in the collection**, each with its SHA1, MD5, SHA256, CRC32 and Adler-32 fingerprints: 5,252 system files, 2,810 arcade ROM sets, 10,928 game and engine data files. That is every platform and emulator together, so no pack holds them all: each carries what its own emulators load, counted in the download table
|
- **18,997 files in the collection**, each with its SHA1, MD5, SHA256, CRC32 and Adler-32 fingerprints: 5,261 system files, 2,808 arcade ROM sets, 10,928 game and engine data files. That is every platform and emulator together, so no pack holds them all: each carries what its own emulators load, counted in the download table
|
||||||
- **562 of 5,252 system files** matched to dump-preservation catalogs (No-Intro, Redump, TOSEC); arcade sets and engine data fall outside what those catalogs index
|
- **562 of 5,261 system files** matched to dump-preservation catalogs (No-Intro, Redump, TOSEC); arcade sets and engine data fall outside what those catalogs index
|
||||||
- **16672 MB** total collection size
|
- **16677 MB** total collection size
|
||||||
|
|
||||||
## Supported systems
|
## Supported systems
|
||||||
|
|
||||||
@@ -86,18 +86,18 @@ Full list with per-file details: **[https://abdess.github.io/retrobios/](https:/
|
|||||||
|
|
||||||
| Platform | On its BIOS list | Files its emulators load | Checked by |
|
| Platform | On its BIOS list | Files its emulators load | Checked by |
|
||||||
|----------|-----------------:|-------------------------:|------------|
|
|----------|-----------------:|-------------------------:|------------|
|
||||||
| Batocera | 353/353 | 12,307/12,738 | MD5 hash |
|
| Batocera | 353/353 | 12,306/12,737 | MD5 hash |
|
||||||
| BizHawk | 118/118 | 649/650 | SHA1 hash |
|
| BizHawk | 118/118 | 806/807 | SHA1 hash |
|
||||||
| EmuDeck | 168/168 | 381/383 | MD5 hash |
|
| EmuDeck | 168/168 | 431/433 | MD5 hash |
|
||||||
| Lakka | 527/527 | 5,263/5,293 | file presence |
|
| Lakka | 527/527 | 5,264/5,294 | file presence |
|
||||||
| MiSTer FPGA | 81/81 | - | MD5 hash |
|
| MiSTer FPGA | 81/81 | - | MD5 hash |
|
||||||
| ROCKNIX | 38/38 | 5,702/5,732 | MD5 hash |
|
| ROCKNIX | 38/38 | 5,698/5,728 | MD5 hash |
|
||||||
| Recalbox | 351/351 | 2,728/2,736 | MD5 hash |
|
| Recalbox | 351/351 | 2,730/2,738 | MD5 hash |
|
||||||
| RetroArch | 527/527 | 5,263/5,293 | file presence |
|
| RetroArch | 527/527 | 5,264/5,294 | file presence |
|
||||||
| RetroBat | 348/348 | 11,749/12,179 | MD5 hash |
|
| RetroBat | 348/348 | 11,889/12,319 | MD5 hash |
|
||||||
| RetroDECK | 2,008/2,008 | 5,296/5,346 | MD5 hash |
|
| RetroDECK | 2,008/2,008 | 5,407/5,457 | MD5 hash |
|
||||||
| RetroPie * | 527/527 | 9,182/9,324 | file presence |
|
| RetroPie * | 527/527 | 9,183/9,325 | file presence |
|
||||||
| RomM | 381/381 | 624/630 | MD5 hash |
|
| RomM | 381/381 | 751/757 | MD5 hash |
|
||||||
|
|
||||||
Each fraction is what the pack has over what is needed, counting required and optional files alike since both ship. The first column is the BIOS list the platform publishes. The second counts files its emulators load that this list never mentions, found by reading their source code, and it is routinely several times larger. A short fraction means files are still missing, and they are named in the [gap analysis](https://abdess.github.io/retrobios/gaps/).
|
Each fraction is what the pack has over what is needed, counting required and optional files alike since both ship. The first column is the BIOS list the platform publishes. The second counts files its emulators load that this list never mentions, found by reading their source code, and it is routinely several times larger. A short fraction means files are still missing, and they are named in the [gap analysis](https://abdess.github.io/retrobios/gaps/).
|
||||||
That second number is a floor, not a ceiling: an emulator that accepts any file handed to it names none in its code, so nothing there can be counted.
|
That second number is a floor, not a ceiling: an emulator that accepts any file handed to it names none in its code, so nothing there can be counted.
|
||||||
@@ -180,4 +180,4 @@ The scripts and tooling are released under the [MIT License](LICENSE).
|
|||||||
The BIOS and firmware files are not covered by that license: they are third-party system software, preserved and provided for personal backup, archival, and interoperability with emulation software. [NOTICE](NOTICE) sets out their status and how to ask for a file to be removed.
|
The BIOS and firmware files are not covered by that license: they are third-party system software, preserved and provided for personal backup, archival, and interoperability with emulation software. [NOTICE](NOTICE) sets out their status and how to ask for a file to be removed.
|
||||||
The reasoning, and where it is weakest, is in the [FAQ](https://abdess.github.io/retrobios/wiki/faq/#is-this-legal).
|
The reasoning, and where it is weakest, is in the [FAQ](https://abdess.github.io/retrobios/wiki/faq/#is-this-legal).
|
||||||
|
|
||||||
*Auto-generated on 2026-10-06T03:31:49Z*
|
*Auto-generated on 2026-10-06T11:05:39Z*
|
||||||
File renamed without changes.
File renamed without changes.
File diff suppressed because it is too large.
Load diff
Binary file not shown.
@@ -0,0 +1,498 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||||
|
// SPDX-License-Identifier: GPL-3.0+
|
||||||
|
|
||||||
|
#ifndef FXAA_HLSL
|
||||||
|
#define FXAA_HLSL 0
|
||||||
|
#endif
|
||||||
|
#ifndef FXAA_GLSL_130
|
||||||
|
#define FXAA_GLSL_130 0
|
||||||
|
#endif
|
||||||
|
#ifndef FXAA_GLSL_VK
|
||||||
|
#define FXAA_GLSL_VK 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define UHQ_FXAA 1 //High Quality Fast Approximate Anti Aliasing. Adapted for GS from Timothy Lottes FXAA 3.11.
|
||||||
|
#define FxaaSubpixMax 0.0 //[0.00 to 1.00] Amount of subpixel aliasing removal. 0.00: Edge only antialiasing (no blurring)
|
||||||
|
#define FxaaEarlyExit 1 //[0 or 1] Use Fxaa early exit pathing. When disabled, the entire scene is antialiased(FSAA). 0 is off, 1 is on.
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
[GLOBALS|FUNCTIONS]
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
#if (FXAA_GLSL_130 == 1)
|
||||||
|
|
||||||
|
in vec2 PSin_t;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 SV_Target0;
|
||||||
|
layout(binding = 0) uniform sampler2D TextureSampler;
|
||||||
|
|
||||||
|
#elif (FXAA_GLSL_VK == 1)
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 PSin_t;
|
||||||
|
layout(location = 0) out vec4 SV_Target0;
|
||||||
|
layout(set = 0, binding = 0) uniform sampler2D TextureSampler;
|
||||||
|
|
||||||
|
#elif (FXAA_HLSL == 1)
|
||||||
|
Texture2D Texture : register(t0);
|
||||||
|
SamplerState TextureSampler : register(s0);
|
||||||
|
|
||||||
|
struct VS_INPUT
|
||||||
|
{
|
||||||
|
float4 p : POSITION;
|
||||||
|
float2 t : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VS_OUTPUT
|
||||||
|
{
|
||||||
|
float4 p : SV_Position;
|
||||||
|
float2 t : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PS_OUTPUT
|
||||||
|
{
|
||||||
|
float4 c : SV_Target0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif defined(__METAL_VERSION__)
|
||||||
|
static constexpr sampler MAIN_SAMPLER(coord::normalized, address::clamp_to_edge, filter::linear);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
[FXAA CODE SECTION]
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if (FXAA_HLSL == 1)
|
||||||
|
struct FxaaTex { SamplerState smpl; Texture2D tex; };
|
||||||
|
#define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0)
|
||||||
|
#define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o)
|
||||||
|
#define FxaaDiscard clip(-1)
|
||||||
|
#define FxaaSat(x) saturate(x)
|
||||||
|
|
||||||
|
#elif (FXAA_GLSL_130 == 1 || FXAA_GLSL_VK == 1)
|
||||||
|
#define int2 ivec2
|
||||||
|
#define float2 vec2
|
||||||
|
#define float3 vec3
|
||||||
|
#define float4 vec4
|
||||||
|
#define FxaaDiscard discard
|
||||||
|
#define FxaaSat(x) clamp(x, 0.0, 1.0)
|
||||||
|
#define FxaaTex sampler2D
|
||||||
|
#define FxaaTexTop(t, p) textureLod(t, p, 0.0)
|
||||||
|
#define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o)
|
||||||
|
|
||||||
|
#elif defined(__METAL_VERSION__)
|
||||||
|
#define FxaaTex texture2d<float>
|
||||||
|
#define FxaaTexTop(t, p) t.sample(MAIN_SAMPLER, p)
|
||||||
|
#define FxaaTexOff(t, p, o, r) t.sample(MAIN_SAMPLER, p, o)
|
||||||
|
#define FxaaDiscard discard_fragment()
|
||||||
|
#define FxaaSat(x) saturate(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FxaaEdgeThreshold 0.063
|
||||||
|
#define FxaaEdgeThresholdMin 0.00
|
||||||
|
#define FXAA_QUALITY_P0 1.0
|
||||||
|
#define FXAA_QUALITY_P1 1.5
|
||||||
|
#define FXAA_QUALITY_P2 2.0
|
||||||
|
#define FXAA_QUALITY_P3 2.0
|
||||||
|
#define FXAA_QUALITY_P4 2.0
|
||||||
|
#define FXAA_QUALITY_P5 2.0
|
||||||
|
#define FXAA_QUALITY_P6 2.0
|
||||||
|
#define FXAA_QUALITY_P7 2.0
|
||||||
|
#define FXAA_QUALITY_P8 2.0
|
||||||
|
#define FXAA_QUALITY_P9 2.0
|
||||||
|
#define FXAA_QUALITY_P10 4.0
|
||||||
|
#define FXAA_QUALITY_P11 8.0
|
||||||
|
#define FXAA_QUALITY_P12 8.0
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
[GAMMA PREPASS CODE SECTION]
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
float RGBLuminance(float3 color)
|
||||||
|
{
|
||||||
|
const float3 lumCoeff = float3(0.2126729, 0.7151522, 0.0721750);
|
||||||
|
return dot(color.rgb, lumCoeff);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 RGBGammaToLinear(float3 color, float gamma)
|
||||||
|
{
|
||||||
|
color = FxaaSat(color);
|
||||||
|
color.r = (color.r <= 0.0404482362771082) ?
|
||||||
|
color.r / 12.92 : pow((color.r + 0.055) / 1.055, gamma);
|
||||||
|
color.g = (color.g <= 0.0404482362771082) ?
|
||||||
|
color.g / 12.92 : pow((color.g + 0.055) / 1.055, gamma);
|
||||||
|
color.b = (color.b <= 0.0404482362771082) ?
|
||||||
|
color.b / 12.92 : pow((color.b + 0.055) / 1.055, gamma);
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 LinearToRGBGamma(float3 color, float gamma)
|
||||||
|
{
|
||||||
|
color = FxaaSat(color);
|
||||||
|
color.r = (color.r <= 0.00313066844250063) ?
|
||||||
|
color.r * 12.92 : 1.055 * pow(color.r, 1.0 / gamma) - 0.055;
|
||||||
|
color.g = (color.g <= 0.00313066844250063) ?
|
||||||
|
color.g * 12.92 : 1.055 * pow(color.g, 1.0 / gamma) - 0.055;
|
||||||
|
color.b = (color.b <= 0.00313066844250063) ?
|
||||||
|
color.b * 12.92 : 1.055 * pow(color.b, 1.0 / gamma) - 0.055;
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 PreGammaPass(float4 color)
|
||||||
|
{
|
||||||
|
const float GammaConst = 2.233;
|
||||||
|
color.rgb = RGBGammaToLinear(color.rgb, GammaConst);
|
||||||
|
color.rgb = LinearToRGBGamma(color.rgb, GammaConst);
|
||||||
|
color.a = RGBLuminance(color.rgb);
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
[FXAA CODE SECTION]
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
float FxaaLuma(float4 rgba)
|
||||||
|
{
|
||||||
|
rgba.w = RGBLuminance(rgba.xyz);
|
||||||
|
return rgba.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 FxaaPixelShader(float2 pos, FxaaTex tex, float2 fxaaRcpFrame, float fxaaSubpix, float fxaaEdgeThreshold, float fxaaEdgeThresholdMin)
|
||||||
|
{
|
||||||
|
float2 posM = pos;
|
||||||
|
float4 rgbyM = FxaaTexTop(tex, posM);
|
||||||
|
rgbyM.w = RGBLuminance(rgbyM.xyz);
|
||||||
|
#define lumaM rgbyM.w
|
||||||
|
|
||||||
|
float lumaS = FxaaLuma(FxaaTexOff(tex, posM, int2( 0, 1), fxaaRcpFrame.xy));
|
||||||
|
float lumaE = FxaaLuma(FxaaTexOff(tex, posM, int2( 1, 0), fxaaRcpFrame.xy));
|
||||||
|
float lumaN = FxaaLuma(FxaaTexOff(tex, posM, int2( 0,-1), fxaaRcpFrame.xy));
|
||||||
|
float lumaW = FxaaLuma(FxaaTexOff(tex, posM, int2(-1, 0), fxaaRcpFrame.xy));
|
||||||
|
|
||||||
|
float maxSM = max(lumaS, lumaM);
|
||||||
|
float minSM = min(lumaS, lumaM);
|
||||||
|
float maxESM = max(lumaE, maxSM);
|
||||||
|
float minESM = min(lumaE, minSM);
|
||||||
|
float maxWN = max(lumaN, lumaW);
|
||||||
|
float minWN = min(lumaN, lumaW);
|
||||||
|
|
||||||
|
float rangeMax = max(maxWN, maxESM);
|
||||||
|
float rangeMin = min(minWN, minESM);
|
||||||
|
float range = rangeMax - rangeMin;
|
||||||
|
float rangeMaxScaled = rangeMax * fxaaEdgeThreshold;
|
||||||
|
float rangeMaxClamped = max(fxaaEdgeThresholdMin, rangeMaxScaled);
|
||||||
|
|
||||||
|
#if (FxaaEarlyExit == 1)
|
||||||
|
// Potential optimization, early exit.
|
||||||
|
if (range < rangeMaxClamped)
|
||||||
|
return rgbyM;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float lumaNW = FxaaLuma(FxaaTexOff(tex, posM, int2(-1,-1), fxaaRcpFrame.xy));
|
||||||
|
float lumaSE = FxaaLuma(FxaaTexOff(tex, posM, int2( 1, 1), fxaaRcpFrame.xy));
|
||||||
|
float lumaNE = FxaaLuma(FxaaTexOff(tex, posM, int2( 1,-1), fxaaRcpFrame.xy));
|
||||||
|
float lumaSW = FxaaLuma(FxaaTexOff(tex, posM, int2(-1, 1), fxaaRcpFrame.xy));
|
||||||
|
|
||||||
|
float lumaNS = lumaN + lumaS;
|
||||||
|
float lumaWE = lumaW + lumaE;
|
||||||
|
float subpixRcpRange = 1.0/range;
|
||||||
|
float subpixNSWE = lumaNS + lumaWE;
|
||||||
|
float edgeHorz1 = (-2.0 * lumaM) + lumaNS;
|
||||||
|
float edgeVert1 = (-2.0 * lumaM) + lumaWE;
|
||||||
|
float lumaNESE = lumaNE + lumaSE;
|
||||||
|
float lumaNWNE = lumaNW + lumaNE;
|
||||||
|
float edgeHorz2 = (-2.0 * lumaE) + lumaNESE;
|
||||||
|
float edgeVert2 = (-2.0 * lumaN) + lumaNWNE;
|
||||||
|
|
||||||
|
float lumaNWSW = lumaNW + lumaSW;
|
||||||
|
float lumaSWSE = lumaSW + lumaSE;
|
||||||
|
float edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2);
|
||||||
|
float edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2);
|
||||||
|
float edgeHorz3 = (-2.0 * lumaW) + lumaNWSW;
|
||||||
|
float edgeVert3 = (-2.0 * lumaS) + lumaSWSE;
|
||||||
|
float edgeHorz = abs(edgeHorz3) + edgeHorz4;
|
||||||
|
float edgeVert = abs(edgeVert3) + edgeVert4;
|
||||||
|
|
||||||
|
float subpixNWSWNESE = lumaNWSW + lumaNESE;
|
||||||
|
float lengthSign = fxaaRcpFrame.x;
|
||||||
|
bool horzSpan = edgeHorz >= edgeVert;
|
||||||
|
float subpixA = subpixNSWE * 2.0 + subpixNWSWNESE;
|
||||||
|
if(!horzSpan) lumaN = lumaW;
|
||||||
|
if(!horzSpan) lumaS = lumaE;
|
||||||
|
if(horzSpan) lengthSign = fxaaRcpFrame.y;
|
||||||
|
float subpixB = (subpixA * (1.0/12.0)) - lumaM;
|
||||||
|
|
||||||
|
float gradientN = lumaN - lumaM;
|
||||||
|
float gradientS = lumaS - lumaM;
|
||||||
|
float lumaNN = lumaN + lumaM;
|
||||||
|
float lumaSS = lumaS + lumaM;
|
||||||
|
bool pairN = abs(gradientN) >= abs(gradientS);
|
||||||
|
float gradient = max(abs(gradientN), abs(gradientS));
|
||||||
|
if(pairN) lengthSign = -lengthSign;
|
||||||
|
float subpixC = FxaaSat(abs(subpixB) * subpixRcpRange);
|
||||||
|
|
||||||
|
float2 posB;
|
||||||
|
posB.x = posM.x;
|
||||||
|
posB.y = posM.y;
|
||||||
|
float2 offNP;
|
||||||
|
offNP.x = (!horzSpan) ? 0.0 : fxaaRcpFrame.x;
|
||||||
|
offNP.y = ( horzSpan) ? 0.0 : fxaaRcpFrame.y;
|
||||||
|
if(!horzSpan) posB.x += lengthSign * 0.5;
|
||||||
|
if( horzSpan) posB.y += lengthSign * 0.5;
|
||||||
|
|
||||||
|
float2 posN;
|
||||||
|
posN.x = posB.x - offNP.x * FXAA_QUALITY_P0;
|
||||||
|
posN.y = posB.y - offNP.y * FXAA_QUALITY_P0;
|
||||||
|
float2 posP;
|
||||||
|
posP.x = posB.x + offNP.x * FXAA_QUALITY_P0;
|
||||||
|
posP.y = posB.y + offNP.y * FXAA_QUALITY_P0;
|
||||||
|
float subpixD = ((-2.0)*subpixC) + 3.0;
|
||||||
|
float lumaEndN = FxaaLuma(FxaaTexTop(tex, posN));
|
||||||
|
float subpixE = subpixC * subpixC;
|
||||||
|
float lumaEndP = FxaaLuma(FxaaTexTop(tex, posP));
|
||||||
|
|
||||||
|
if(!pairN) lumaNN = lumaSS;
|
||||||
|
float gradientScaled = gradient * 1.0/4.0;
|
||||||
|
float lumaMM = lumaM - lumaNN * 0.5;
|
||||||
|
float subpixF = subpixD * subpixE;
|
||||||
|
bool lumaMLTZero = lumaMM < 0.0;
|
||||||
|
lumaEndN -= lumaNN * 0.5;
|
||||||
|
lumaEndP -= lumaNN * 0.5;
|
||||||
|
bool doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
bool doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P1;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P1;
|
||||||
|
bool doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P1;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P1;
|
||||||
|
|
||||||
|
if(doneNP) {
|
||||||
|
if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||||
|
if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||||
|
if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
|
||||||
|
if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
|
||||||
|
doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P2;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P2;
|
||||||
|
doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P2;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P2;
|
||||||
|
|
||||||
|
if(doneNP) {
|
||||||
|
if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||||
|
if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||||
|
if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
|
||||||
|
if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
|
||||||
|
doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P3;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P3;
|
||||||
|
doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P3;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P3;
|
||||||
|
|
||||||
|
if(doneNP) {
|
||||||
|
if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||||
|
if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||||
|
if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
|
||||||
|
if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
|
||||||
|
doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P4;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P4;
|
||||||
|
doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P4;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P4;
|
||||||
|
|
||||||
|
if(doneNP) {
|
||||||
|
if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||||
|
if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||||
|
if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
|
||||||
|
if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
|
||||||
|
doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P5;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P5;
|
||||||
|
doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P5;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P5;
|
||||||
|
|
||||||
|
if(doneNP) {
|
||||||
|
if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||||
|
if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||||
|
if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
|
||||||
|
if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
|
||||||
|
doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P6;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P6;
|
||||||
|
doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P6;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P6;
|
||||||
|
|
||||||
|
if(doneNP) {
|
||||||
|
if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||||
|
if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||||
|
if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
|
||||||
|
if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
|
||||||
|
doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P7;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P7;
|
||||||
|
doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P7;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P7;
|
||||||
|
|
||||||
|
if(doneNP) {
|
||||||
|
if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||||
|
if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||||
|
if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
|
||||||
|
if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
|
||||||
|
doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P8;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P8;
|
||||||
|
doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P8;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P8;
|
||||||
|
|
||||||
|
if(doneNP) {
|
||||||
|
if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||||
|
if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||||
|
if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
|
||||||
|
if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
|
||||||
|
doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P9;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P9;
|
||||||
|
doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P9;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P9;
|
||||||
|
|
||||||
|
if(doneNP) {
|
||||||
|
if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||||
|
if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||||
|
if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
|
||||||
|
if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
|
||||||
|
doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P10;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P10;
|
||||||
|
doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P10;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P10;
|
||||||
|
|
||||||
|
if(doneNP) {
|
||||||
|
if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||||
|
if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||||
|
if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
|
||||||
|
if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
|
||||||
|
doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P11;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P11;
|
||||||
|
doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P11;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P11;
|
||||||
|
|
||||||
|
if(doneNP) {
|
||||||
|
if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||||
|
if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||||
|
if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
|
||||||
|
if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
|
||||||
|
doneN = abs(lumaEndN) >= gradientScaled;
|
||||||
|
doneP = abs(lumaEndP) >= gradientScaled;
|
||||||
|
if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P12;
|
||||||
|
if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P12;
|
||||||
|
doneNP = (!doneN) || (!doneP);
|
||||||
|
if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P12;
|
||||||
|
if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P12;
|
||||||
|
}}}}}}}}}}}
|
||||||
|
|
||||||
|
float dstN = posM.x - posN.x;
|
||||||
|
float dstP = posP.x - posM.x;
|
||||||
|
if(!horzSpan) dstN = posM.y - posN.y;
|
||||||
|
if(!horzSpan) dstP = posP.y - posM.y;
|
||||||
|
|
||||||
|
bool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero;
|
||||||
|
float spanLength = (dstP + dstN);
|
||||||
|
bool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero;
|
||||||
|
float spanLengthRcp = 1.0/spanLength;
|
||||||
|
|
||||||
|
bool directionN = dstN < dstP;
|
||||||
|
float dst = min(dstN, dstP);
|
||||||
|
bool goodSpan = directionN ? goodSpanN : goodSpanP;
|
||||||
|
float subpixG = subpixF * subpixF;
|
||||||
|
float pixelOffset = (dst * (-spanLengthRcp)) + 0.5;
|
||||||
|
float subpixH = subpixG * fxaaSubpix;
|
||||||
|
|
||||||
|
float pixelOffsetGood = goodSpan ? pixelOffset : 0.0;
|
||||||
|
float pixelOffsetSubpix = max(pixelOffsetGood, subpixH);
|
||||||
|
if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign;
|
||||||
|
if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign;
|
||||||
|
|
||||||
|
return float4(FxaaTexTop(tex, posM).xyz, lumaM);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (FXAA_GLSL_130 == 1 || FXAA_GLSL_VK == 1)
|
||||||
|
float4 FxaaPass(float4 FxaaColor, float2 uv0)
|
||||||
|
#elif (FXAA_HLSL == 1)
|
||||||
|
float4 FxaaPass(float4 FxaaColor : COLOR0, float2 uv0 : TEXCOORD0)
|
||||||
|
#elif defined(__METAL_VERSION__)
|
||||||
|
float4 FxaaPass(float4 FxaaColor, float2 uv0, texture2d<float> tex)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
|
||||||
|
#if (FXAA_HLSL == 1)
|
||||||
|
FxaaTex tex;
|
||||||
|
tex.tex = Texture;
|
||||||
|
tex.smpl = TextureSampler;
|
||||||
|
|
||||||
|
float2 PixelSize;
|
||||||
|
Texture.GetDimensions(PixelSize.x, PixelSize.y);
|
||||||
|
FxaaColor = FxaaPixelShader(uv0, tex, 1.0/PixelSize.xy, FxaaSubpixMax, FxaaEdgeThreshold, FxaaEdgeThresholdMin);
|
||||||
|
|
||||||
|
#elif (FXAA_GLSL_130 == 1 || FXAA_GLSL_VK == 1)
|
||||||
|
vec2 PixelSize = vec2(textureSize(TextureSampler, 0));
|
||||||
|
FxaaColor = FxaaPixelShader(uv0, TextureSampler, 1.0/PixelSize.xy, FxaaSubpixMax, FxaaEdgeThreshold, FxaaEdgeThresholdMin);
|
||||||
|
#elif defined(__METAL_VERSION__)
|
||||||
|
float2 PixelSize = float2(tex.get_width(), tex.get_height());
|
||||||
|
FxaaColor = FxaaPixelShader(uv0, tex, 1.f/PixelSize, FxaaSubpixMax, FxaaEdgeThreshold, FxaaEdgeThresholdMin);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return FxaaColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
[MAIN() & COMBINE PASS CODE SECTION]
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
#if (FXAA_GLSL_130 == 1 || FXAA_GLSL_VK == 1)
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 color = texture(TextureSampler, PSin_t);
|
||||||
|
color = PreGammaPass(color);
|
||||||
|
color = FxaaPass(color, PSin_t);
|
||||||
|
|
||||||
|
SV_Target0 = float4(color.rgb, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif (FXAA_HLSL == 1)
|
||||||
|
PS_OUTPUT main(VS_OUTPUT input)
|
||||||
|
{
|
||||||
|
PS_OUTPUT output;
|
||||||
|
|
||||||
|
float4 color = Texture.Sample(TextureSampler, input.t);
|
||||||
|
|
||||||
|
color = PreGammaPass(color);
|
||||||
|
color = FxaaPass(color, input.t);
|
||||||
|
|
||||||
|
output.c = float4(color.rgb, 1.0);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metal main function in in fxaa.metal
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,470 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
||||||
|
// SPDX-License-Identifier: GPL-3.0+
|
||||||
|
|
||||||
|
//#version 420 // Keep it for text editor detection
|
||||||
|
|
||||||
|
layout(std140, binding = 1) uniform cb20
|
||||||
|
{
|
||||||
|
vec2 VertexScale;
|
||||||
|
vec2 VertexOffset;
|
||||||
|
|
||||||
|
vec2 TextureScale;
|
||||||
|
vec2 TextureOffset;
|
||||||
|
|
||||||
|
vec2 PointSize;
|
||||||
|
|
||||||
|
uint MaxDepth;
|
||||||
|
float LineAA1Width;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef VERTEX_SHADER
|
||||||
|
|
||||||
|
#ifndef VS_EXPAND_NONE
|
||||||
|
#define VS_EXPAND_NONE 0
|
||||||
|
#define VS_EXPAND_POINT 1
|
||||||
|
#define VS_EXPAND_LINE 2
|
||||||
|
#define VS_EXPAND_SPRITE 3
|
||||||
|
#define VS_EXPAND_LINE_AA1 4
|
||||||
|
#define VS_EXPAND_TRIANGLE_AA1 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
out SHADER
|
||||||
|
{
|
||||||
|
vec4 t_float;
|
||||||
|
vec4 t_int;
|
||||||
|
#if VS_IIP != 0
|
||||||
|
vec4 c;
|
||||||
|
#else
|
||||||
|
flat vec4 c;
|
||||||
|
#endif
|
||||||
|
float inv_cov; // We use the inverse to make it simpler to interpolate.
|
||||||
|
flat uint interior; // 1 for triangle interior; 0 for edge.
|
||||||
|
} VSout;
|
||||||
|
|
||||||
|
const float exp_min32 = exp2(-32.0f);
|
||||||
|
|
||||||
|
#if VS_EXPAND == VS_EXPAND_NONE
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 i_st;
|
||||||
|
layout(location = 2) in vec4 i_c;
|
||||||
|
layout(location = 3) in float i_q;
|
||||||
|
layout(location = 4) in uvec2 i_p;
|
||||||
|
layout(location = 5) in uint i_z;
|
||||||
|
layout(location = 6) in uvec2 i_uv;
|
||||||
|
layout(location = 7) in vec4 i_f;
|
||||||
|
|
||||||
|
void texture_coord()
|
||||||
|
{
|
||||||
|
vec2 uv = vec2(i_uv) - TextureOffset;
|
||||||
|
vec2 st = i_st - TextureOffset;
|
||||||
|
|
||||||
|
// Float coordinate
|
||||||
|
VSout.t_float.xy = st;
|
||||||
|
VSout.t_float.w = i_q;
|
||||||
|
|
||||||
|
// Integer coordinate => normalized
|
||||||
|
VSout.t_int.xy = uv * TextureScale;
|
||||||
|
#if VS_FST
|
||||||
|
// Integer coordinate => integral
|
||||||
|
VSout.t_int.zw = uv;
|
||||||
|
#else
|
||||||
|
// Some games uses float coordinate for post-processing effect
|
||||||
|
VSout.t_int.zw = st / TextureScale;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void vs_main()
|
||||||
|
{
|
||||||
|
// Clamp to max depth, gs doesn't wrap
|
||||||
|
highp uint z = min(i_z, MaxDepth);
|
||||||
|
|
||||||
|
// pos -= 0.05 (1/320 pixel) helps avoiding rounding problems (integral part of pos is usually 5 digits, 0.05 is about as low as we can go)
|
||||||
|
// example: ceil(afterseveralvertextransformations(y = 133)) => 134 => line 133 stays empty
|
||||||
|
// input granularity is 1/16 pixel, anything smaller than that won't step drawing up/left by one pixel
|
||||||
|
// example: 133.0625 (133 + 1/16) should start from line 134, ceil(133.0625 - 0.05) still above 133
|
||||||
|
gl_Position.xy = vec2(i_p) - vec2(0.05f, 0.05f);
|
||||||
|
gl_Position.xy = gl_Position.xy * VertexScale - VertexOffset;
|
||||||
|
|
||||||
|
#if HAS_CLIP_CONTROL
|
||||||
|
gl_Position.z = float(z) * exp_min32;
|
||||||
|
#else
|
||||||
|
gl_Position.z = (float(z) * exp_min32) * 2.0f - 1.0f;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gl_Position.w = 1.0f;
|
||||||
|
|
||||||
|
texture_coord();
|
||||||
|
|
||||||
|
VSout.c = i_c;
|
||||||
|
VSout.t_float.z = i_f.x; // pack for with texture
|
||||||
|
|
||||||
|
#if VS_POINT_SIZE
|
||||||
|
gl_PointSize = PointSize.x;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // VS_EXPAND
|
||||||
|
|
||||||
|
struct RawVertex
|
||||||
|
{
|
||||||
|
vec2 ST;
|
||||||
|
uint RGBA;
|
||||||
|
float Q;
|
||||||
|
uint XY;
|
||||||
|
uint Z;
|
||||||
|
uint UV;
|
||||||
|
uint FOG;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(std140, binding = 4) uniform cb22
|
||||||
|
{
|
||||||
|
uint BaseVertex;
|
||||||
|
uint BaseIndex;
|
||||||
|
uint pad_cb22_0;
|
||||||
|
uint pad_cb22_1;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(std140, binding = 2) readonly buffer VertexBuffer {
|
||||||
|
RawVertex vertex_buffer[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Warning: use std430 instead of std140 so that the ints are tightly packed.
|
||||||
|
layout(std430, binding = 3) readonly buffer IndexBuffer {
|
||||||
|
uint index_buffer[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProcessedVertex
|
||||||
|
{
|
||||||
|
vec4 p;
|
||||||
|
vec4 t_float;
|
||||||
|
vec4 t_int;
|
||||||
|
vec4 c;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint load_index(uint _i)
|
||||||
|
{
|
||||||
|
uint i = _i + BaseIndex;
|
||||||
|
// i is even => load lower 16 bits; i odd => load upper 16 bits.
|
||||||
|
uint shift = (i & 1u) << 4u;
|
||||||
|
return (index_buffer[i >> 1u] >> shift) & 0xFFFFu;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessedVertex load_vertex(uint index)
|
||||||
|
{
|
||||||
|
RawVertex rvtx = vertex_buffer[BaseVertex + index];
|
||||||
|
|
||||||
|
vec2 i_st = rvtx.ST;
|
||||||
|
vec4 i_c = vec4(uvec4(bitfieldExtract(rvtx.RGBA, 0, 8), bitfieldExtract(rvtx.RGBA, 8, 8),
|
||||||
|
bitfieldExtract(rvtx.RGBA, 16, 8), bitfieldExtract(rvtx.RGBA, 24, 8)));
|
||||||
|
float i_q = rvtx.Q;
|
||||||
|
uvec2 i_p = uvec2(bitfieldExtract(rvtx.XY, 0, 16), bitfieldExtract(rvtx.XY, 16, 16));
|
||||||
|
uint i_z = rvtx.Z;
|
||||||
|
uvec2 i_uv = uvec2(bitfieldExtract(rvtx.UV, 0, 16), bitfieldExtract(rvtx.UV, 16, 16));
|
||||||
|
vec4 i_f = unpackUnorm4x8(rvtx.FOG);
|
||||||
|
|
||||||
|
ProcessedVertex vtx;
|
||||||
|
|
||||||
|
uint z = min(i_z, MaxDepth);
|
||||||
|
vtx.p.xy = vec2(i_p) - vec2(0.05f, 0.05f);
|
||||||
|
vtx.p.xy = vtx.p.xy * VertexScale - VertexOffset;
|
||||||
|
|
||||||
|
#if HAS_CLIP_CONTROL
|
||||||
|
vtx.p.z = float(z) * exp_min32;
|
||||||
|
#else
|
||||||
|
vtx.p.z = (float(z) * exp_min32) * 2.0f - 1.0f;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vtx.p.w = 1.0f;
|
||||||
|
|
||||||
|
vec2 uv = vec2(i_uv) - TextureOffset;
|
||||||
|
vec2 st = i_st - TextureOffset;
|
||||||
|
|
||||||
|
vtx.t_float.xy = st;
|
||||||
|
vtx.t_float.w = i_q;
|
||||||
|
|
||||||
|
vtx.t_int.xy = uv * TextureScale;
|
||||||
|
#if VS_FST
|
||||||
|
vtx.t_int.zw = uv;
|
||||||
|
#else
|
||||||
|
vtx.t_int.zw = st / TextureScale;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vtx.c = i_c;
|
||||||
|
vtx.t_float.z = i_f.x;
|
||||||
|
|
||||||
|
return vtx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert XY from NDC to GS pixel coordinates (i.e. 1.0 = 1 GS pixel).
|
||||||
|
vec2 get_xy_unscaled(vec2 xy)
|
||||||
|
{
|
||||||
|
return round(xy / VertexScale) / 16.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the XY deltas in GS pixel coordinates, using first vertex as the origin.
|
||||||
|
mat2 get_xy_deltas_unscaled(ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2)
|
||||||
|
{
|
||||||
|
vec2 xy0 = get_xy_unscaled(v0.p.xy);
|
||||||
|
vec2 xy1 = get_xy_unscaled(v1.p.xy);
|
||||||
|
vec2 xy2 = get_xy_unscaled(v2.p.xy);
|
||||||
|
return mat2(xy1 - xy0, xy2 - xy0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the AA1 outward expand direction to the edge formed by the first two vertices.
|
||||||
|
// This is up or down for shallow (X dominant) edges, and right or left for steep (Y dominant) edges.
|
||||||
|
// Similar expansion to line AA1 except instead of expanding on both sides of the line,
|
||||||
|
// expand on on the side towards the outside of the triangle.
|
||||||
|
vec2 get_aa1_triangle_expand_dir(ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2)
|
||||||
|
{
|
||||||
|
mat2 xy_deltas = get_xy_deltas_unscaled(v0, v1, v2);
|
||||||
|
vec2 line_delta = xy_deltas[0];
|
||||||
|
vec2 line_opposite = xy_deltas[1];
|
||||||
|
|
||||||
|
vec2 line_normal = vec2(line_delta.y, -line_delta.x);
|
||||||
|
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 1.0f) : vec2(1.0f, 0.0f);
|
||||||
|
|
||||||
|
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(line_opposite, line_normal) >= 0.0f))
|
||||||
|
{
|
||||||
|
// Expand direction point towards the interior so flip it.
|
||||||
|
line_expand = -line_expand;
|
||||||
|
}
|
||||||
|
|
||||||
|
return line_expand;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat2 get_inverse(mat2 mat, float det)
|
||||||
|
{
|
||||||
|
return mat2(mat[1][1], -mat[0][1], -mat[1][0], mat[0][0]) * (1.0f / det);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extrapolate triangle attributes from the first vertex along the given direction.
|
||||||
|
// dp_mat is derived from the input vertices, it is passed in to avoid recomputing.
|
||||||
|
void extrapolate_aa1_triangle_edge(inout ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2, mat2 dp_mat, vec2 dp)
|
||||||
|
{
|
||||||
|
// Get texture deltas
|
||||||
|
#if VS_TME
|
||||||
|
#if VS_FST
|
||||||
|
mat2 dt = mat2(v1.t_int.zw - v0.t_int.zw, v2.t_int.zw - v0.t_int.zw);
|
||||||
|
#else
|
||||||
|
mat2 dt = mat2(v1.t_float.xy - v0.t_float.xy, v2.t_float.xy - v0.t_float.xy);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get color delta if interpolating
|
||||||
|
#if VS_IIP
|
||||||
|
mat2x4 dc = mat2x4(v1.c - v0.c, v2.c - v0.c);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vec2 dz = vec2(v1.p.z - v0.p.z, v2.p.z - v0.p.z); // Z deltas
|
||||||
|
|
||||||
|
vec2 df = vec2(v1.t_float.z - v0.t_float.z, v2.t_float.z - v0.t_float.z); // Fog deltas
|
||||||
|
|
||||||
|
vec2 dq = vec2(v1.t_float.w - v0.t_float.w, v2.t_float.w - v0.t_float.w); // Q deltas
|
||||||
|
|
||||||
|
// To prevent unstable extrapolation, do not extrapolate if the
|
||||||
|
// minimum perpendicular length of the triangle is < 2 pixels.
|
||||||
|
float dp_det = determinant(dp_mat); // Twice signed triangle area.
|
||||||
|
float len0 = length(dp_mat[0]);
|
||||||
|
float len1 = length(dp_mat[1]);
|
||||||
|
float len2 = length(dp_mat[1] - dp_mat[0]);
|
||||||
|
float min_perp_length = abs(dp_det) / max(max(len0, len1), len2);
|
||||||
|
|
||||||
|
// Get the position -> barycentric weight matrix
|
||||||
|
mat2 inv_dp_mat = get_inverse(dp_mat, dp_det);
|
||||||
|
|
||||||
|
vec2 weights = min_perp_length < 2.0f ? vec2(0) : inv_dp_mat * dp;
|
||||||
|
|
||||||
|
v0.p.xy += dp * PointSize; // Extrapolate position
|
||||||
|
|
||||||
|
// Extrapolate texture coords
|
||||||
|
#if VS_TME
|
||||||
|
#if VS_FST
|
||||||
|
v0.t_int.zw += dt * weights;
|
||||||
|
v0.t_int.xy = v0.t_int.zw * TextureScale;
|
||||||
|
#else
|
||||||
|
v0.t_float.xy += dt * weights;
|
||||||
|
v0.t_int.zw = v0.t_float.xy / TextureScale;
|
||||||
|
v0.t_float.w += dot(dq, weights);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Extrapolate and clamp color
|
||||||
|
#if VS_IIP
|
||||||
|
v0.c += dc * weights;
|
||||||
|
v0.c = clamp(v0.c, vec4(0), vec4(255));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
v0.p.z += dot(dz, weights); // Extrapolate depth
|
||||||
|
|
||||||
|
v0.t_float.z += dot(df, weights); // Extrapolate fog
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
ProcessedVertex vtx;
|
||||||
|
|
||||||
|
uint vid = uint(gl_VertexID);
|
||||||
|
|
||||||
|
#if VS_EXPAND == VS_EXPAND_POINT
|
||||||
|
|
||||||
|
vtx = load_vertex(vid >> 2);
|
||||||
|
|
||||||
|
vtx.p.x += ((vid & 1u) != 0u) ? PointSize.x : 0.0f;
|
||||||
|
vtx.p.y += ((vid & 2u) != 0u) ? PointSize.y : 0.0f;
|
||||||
|
|
||||||
|
#elif (VS_EXPAND == VS_EXPAND_LINE) || (VS_EXPAND == VS_EXPAND_LINE_AA1)
|
||||||
|
|
||||||
|
uint vid_base = vid >> 2;
|
||||||
|
bool is_bottom = (vid & 2u) != 0u;
|
||||||
|
bool is_right = (vid & 1u) != 0u;
|
||||||
|
uint vid_other = is_bottom ? vid_base - 1u : vid_base + 1u;
|
||||||
|
vtx = load_vertex(vid_base);
|
||||||
|
ProcessedVertex other = load_vertex(vid_other);
|
||||||
|
|
||||||
|
// Use bottom minus top for delta regardless of which vertex we are expanding.
|
||||||
|
vec2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
|
||||||
|
vec2 line_vector = normalize(line_delta / VertexScale);
|
||||||
|
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
|
||||||
|
#if VS_EXPAND == VS_EXPAND_LINE_AA1
|
||||||
|
line_expand *= 2.0f * LineAA1Width;
|
||||||
|
#endif
|
||||||
|
vec2 line_width = (line_expand * PointSize) / 2.0f;
|
||||||
|
vec2 offset = is_right ? line_width : -line_width;
|
||||||
|
vtx.p.xy += offset;
|
||||||
|
|
||||||
|
#if VS_EXPAND == VS_EXPAND_LINE_AA1
|
||||||
|
VSout.inv_cov = is_right ? 1.0f : -1.0f;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Lines will be run as (0 1 2) (1 2 3)
|
||||||
|
// This means that both triangles will have a point based off the top line point as their first point
|
||||||
|
// So we don't have to do anything for !IIP
|
||||||
|
|
||||||
|
#elif VS_EXPAND == VS_EXPAND_SPRITE
|
||||||
|
|
||||||
|
// Sprite points are always in pairs
|
||||||
|
uint vid_base = vid >> 1;
|
||||||
|
uint vid_lt = vid_base & ~1u;
|
||||||
|
uint vid_rb = vid_base | 1u;
|
||||||
|
|
||||||
|
ProcessedVertex lt = load_vertex(vid_lt);
|
||||||
|
ProcessedVertex rb = load_vertex(vid_rb);
|
||||||
|
vtx = rb;
|
||||||
|
|
||||||
|
bool is_right = ((vid & 1u) != 0u);
|
||||||
|
vtx.p.x = is_right ? lt.p.x : vtx.p.x;
|
||||||
|
vtx.t_float.x = is_right ? lt.t_float.x : vtx.t_float.x;
|
||||||
|
vtx.t_int.xz = is_right ? lt.t_int.xz : vtx.t_int.xz;
|
||||||
|
|
||||||
|
bool is_bottom = ((vid & 2u) != 0u);
|
||||||
|
vtx.p.y = is_bottom ? lt.p.y : vtx.p.y;
|
||||||
|
vtx.t_float.y = is_bottom ? lt.t_float.y : vtx.t_float.y;
|
||||||
|
vtx.t_int.yw = is_bottom ? lt.t_int.yw : vtx.t_int.yw;
|
||||||
|
|
||||||
|
#elif VS_EXPAND == VS_EXPAND_TRIANGLE_AA1
|
||||||
|
|
||||||
|
// Triangles with AA1 are expanded as follows:
|
||||||
|
// - Vertices 0-2: Interior of triangle (1 triangle).
|
||||||
|
// - Vertices 3-8: First edge expanded (2 triangles).
|
||||||
|
// - Vertices 9-14: Second edge expanded (2 triangles).
|
||||||
|
// - Vertices 15-20: Third edge expanded (2 triangles).
|
||||||
|
// - Vertices 21-26: First corner cap (2 triangles).
|
||||||
|
// - Vertices 27-32: Second corner cap (2 triangles).
|
||||||
|
// - Vertices 33-38: Third corner cap (2 triangles).
|
||||||
|
|
||||||
|
uint prim_id = vid / 39u;
|
||||||
|
uint prim_offset = vid - 39u * prim_id; // range: 0-38
|
||||||
|
bool interior = prim_offset < 3u;
|
||||||
|
bool edge = 3u <= prim_offset && prim_offset < 21u;
|
||||||
|
|
||||||
|
if (interior)
|
||||||
|
{
|
||||||
|
vtx = load_vertex(load_index(3u * prim_id + prim_offset));
|
||||||
|
VSout.inv_cov = 0.0f; // Full coverage
|
||||||
|
VSout.interior = 1u;
|
||||||
|
}
|
||||||
|
else if (edge)
|
||||||
|
{
|
||||||
|
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
|
||||||
|
uint prim_offset_edges = prim_offset - 3u; // range: 0-17
|
||||||
|
uint i0 = prim_offset_edges / 6u;
|
||||||
|
uint i1 = (i0 >= 2u) ? i0 - 2u : i0 + 1u;
|
||||||
|
uint i2 = (i0 >= 1u) ? i0 - 1u : i0 + 2u;
|
||||||
|
uint edge_offset = prim_offset_edges - 6u * i0; // range: 0-5
|
||||||
|
|
||||||
|
// Note: order of top/bottom, inside/outside is arbitrary,
|
||||||
|
// as long as it assembles into two triangles forming a quad.
|
||||||
|
bool is_bottom = (2u <= edge_offset) && (edge_offset <= 4u);
|
||||||
|
bool is_outside = (edge_offset & 1u) != 0u;
|
||||||
|
|
||||||
|
vtx = load_vertex(load_index(3u * prim_id + (is_bottom ? i1 : i0)));
|
||||||
|
ProcessedVertex other = load_vertex(load_index(3u * prim_id + (is_bottom ? i0 : i1)));
|
||||||
|
ProcessedVertex opposite = load_vertex(load_index(3u * prim_id + i2));
|
||||||
|
|
||||||
|
mat2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||||
|
|
||||||
|
vec2 expand_dir = is_outside ? get_aa1_triangle_expand_dir(vtx, other, opposite) : vec2(0);
|
||||||
|
|
||||||
|
// Do actual extrapolation, or no-op if expand_dir == 0.
|
||||||
|
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||||
|
|
||||||
|
VSout.inv_cov = is_outside ? 1.0f : 0.0f; // No coverage on outside, otherwise full.
|
||||||
|
|
||||||
|
VSout.interior = 0u;
|
||||||
|
}
|
||||||
|
else // Corner cap
|
||||||
|
{
|
||||||
|
// Vertex indices for this cap. We need all 3 for determining exterior/interior.
|
||||||
|
uint prim_offset_cap = prim_offset - 21u; // range: 0-17
|
||||||
|
uint i0 = prim_offset_cap / 6u;
|
||||||
|
uint i1 = (i0 >= 2u) ? i0 - 2u : i0 + 1u;
|
||||||
|
uint i2 = (i0 >= 1u) ? i0 - 1u : i0 + 2u;
|
||||||
|
uint cap_offset = prim_offset_cap - 6u * i0; // range: 0-5
|
||||||
|
|
||||||
|
bool is_near_corner = cap_offset == 0u || cap_offset == 3u;
|
||||||
|
bool is_far_corner = cap_offset == 2u || cap_offset == 5u;
|
||||||
|
bool is_first_tri = cap_offset < 3u;
|
||||||
|
|
||||||
|
// First triangle is on the side of vertex i1 and second is on the side of vertex i2.
|
||||||
|
vtx = load_vertex(load_index(3u * prim_id + i0));
|
||||||
|
ProcessedVertex other = load_vertex(load_index(3u * prim_id + (is_first_tri ? i1 : i2)));
|
||||||
|
ProcessedVertex opposite = load_vertex(load_index(3u * prim_id + (is_first_tri ? i2 : i1)));
|
||||||
|
|
||||||
|
mat2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||||
|
|
||||||
|
// Get the edge expansion directions of both incident edges.
|
||||||
|
vec2 edge_expand_dir_0 = get_aa1_triangle_expand_dir(vtx, other, opposite);
|
||||||
|
vec2 edge_expand_dir_1 = get_aa1_triangle_expand_dir(vtx, opposite, other);
|
||||||
|
|
||||||
|
// Check if the corner is already filled by the expanded edges.
|
||||||
|
// This happens if the expand directions are the same.
|
||||||
|
// If so we output a degenerate triangle at this corner.
|
||||||
|
bool corner_filled = all(equal(edge_expand_dir_0, edge_expand_dir_1));
|
||||||
|
|
||||||
|
// Nothing if corner is filled, otherwise opposite to the bisector of the corner angle.
|
||||||
|
vec2 far_corner_dir = corner_filled ? vec2(0) : -normalize((pos_deltas[0] + pos_deltas[1]) / 2.0f);
|
||||||
|
|
||||||
|
// Determine the expand direction.
|
||||||
|
vec2 expand_dir = is_near_corner ? vec2(0) : // No extrapolation
|
||||||
|
is_far_corner ? far_corner_dir : // Opposite to the angle bisector of corner
|
||||||
|
edge_expand_dir_0; // Standard AA1 edge expansion
|
||||||
|
|
||||||
|
// Do the actual extrapolation (no-op if expand_dir == 0).
|
||||||
|
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||||
|
|
||||||
|
VSout.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
|
||||||
|
|
||||||
|
VSout.interior = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gl_Position = vtx.p;
|
||||||
|
VSout.t_float = vtx.t_float;
|
||||||
|
VSout.t_int = vtx.t_int;
|
||||||
|
VSout.c = vtx.c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // VS_EXPAND
|
||||||
|
|
||||||
|
#endif // VERTEX_SHADER
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+273
-108
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"generated_at": "2026-10-06T03:26:07Z",
|
"generated_at": "2026-10-06T10:11:17Z",
|
||||||
"total_files": 18990,
|
"total_files": 18997,
|
||||||
"total_size": 17482351547,
|
"total_size": 17487414657,
|
||||||
"files": {
|
"files": {
|
||||||
"738c7f5fd0c8b57ee2e87ae7a97bf8e21a821d07": {
|
"738c7f5fd0c8b57ee2e87ae7a97bf8e21a821d07": {
|
||||||
"path": "bios/3D Realms/Duke Nukem 3D/DUKE.RTS",
|
"path": "bios/3D Realms/Duke Nukem 3D/DUKE.RTS",
|
||||||
@@ -9040,26 +9040,6 @@
|
|||||||
"crc32": "772caf31",
|
"crc32": "772caf31",
|
||||||
"adler32": "7fd6387e"
|
"adler32": "7fd6387e"
|
||||||
},
|
},
|
||||||
"d6cedce971a2a4ab2588e39285a9d2fb37b62924": {
|
|
||||||
"path": "bios/Arcade/Arcade/switch/prod.keys",
|
|
||||||
"name": "prod.keys",
|
|
||||||
"size": 15039,
|
|
||||||
"sha1": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
|
||||||
"md5": "4ed853d4a52e6b9b9e11954f155ecb8a",
|
|
||||||
"sha256": "76df55f85c6628cd924f227a72a301bbb731833c0a0f00c440e01e6b34480990",
|
|
||||||
"crc32": "6d74253f",
|
|
||||||
"adler32": "c8696e75"
|
|
||||||
},
|
|
||||||
"8e6c2873d671f482d3fe0f7b96e18e0f84d02c10": {
|
|
||||||
"path": "bios/Arcade/Arcade/switch/title.keys",
|
|
||||||
"name": "title.keys",
|
|
||||||
"size": 19584,
|
|
||||||
"sha1": "8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
|
||||||
"md5": "b5890c3d737880f6dd3751ffec5b128b",
|
|
||||||
"sha256": "dc6e31aae8daa9067dafcf2bca6d9d3aa691a63474eb698ad8c54be3e360753b",
|
|
||||||
"crc32": "04387ea8",
|
|
||||||
"adler32": "74fffc45"
|
|
||||||
},
|
|
||||||
"1f424e6329bc1dfc254a60b752e0c2ad1d1f6126": {
|
"1f424e6329bc1dfc254a60b752e0c2ad1d1f6126": {
|
||||||
"path": "bios/Arcade/Arcade/sx1541.zip",
|
"path": "bios/Arcade/Arcade/sx1541.zip",
|
||||||
"name": "sx1541.zip",
|
"name": "sx1541.zip",
|
||||||
@@ -129419,6 +129399,26 @@
|
|||||||
"crc32": "4c866029",
|
"crc32": "4c866029",
|
||||||
"adler32": "a75265c4"
|
"adler32": "a75265c4"
|
||||||
},
|
},
|
||||||
|
"d6cedce971a2a4ab2588e39285a9d2fb37b62924": {
|
||||||
|
"path": "bios/Nintendo/Switch/.variants/prod.keys.d6cedce9",
|
||||||
|
"name": "prod.keys",
|
||||||
|
"size": 15039,
|
||||||
|
"sha1": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
||||||
|
"md5": "4ed853d4a52e6b9b9e11954f155ecb8a",
|
||||||
|
"sha256": "76df55f85c6628cd924f227a72a301bbb731833c0a0f00c440e01e6b34480990",
|
||||||
|
"crc32": "6d74253f",
|
||||||
|
"adler32": "c8696e75"
|
||||||
|
},
|
||||||
|
"8e6c2873d671f482d3fe0f7b96e18e0f84d02c10": {
|
||||||
|
"path": "bios/Nintendo/Switch/.variants/title.keys.8e6c2873",
|
||||||
|
"name": "title.keys",
|
||||||
|
"size": 19584,
|
||||||
|
"sha1": "8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
||||||
|
"md5": "b5890c3d737880f6dd3751ffec5b128b",
|
||||||
|
"sha256": "dc6e31aae8daa9067dafcf2bca6d9d3aa691a63474eb698ad8c54be3e360753b",
|
||||||
|
"crc32": "04387ea8",
|
||||||
|
"adler32": "74fffc45"
|
||||||
|
},
|
||||||
"9d4488178fe93d053d44f836e849d3d6b3e5974e": {
|
"9d4488178fe93d053d44f836e849d3d6b3e5974e": {
|
||||||
"path": "bios/Nintendo/Switch/Firmware_batocera43.zip",
|
"path": "bios/Nintendo/Switch/Firmware_batocera43.zip",
|
||||||
"name": "Firmware_batocera43.zip",
|
"name": "Firmware_batocera43.zip",
|
||||||
@@ -135666,6 +135666,36 @@
|
|||||||
"crc32": "ca72899c",
|
"crc32": "ca72899c",
|
||||||
"adler32": "f932ede3"
|
"adler32": "f932ede3"
|
||||||
},
|
},
|
||||||
|
"2d9878fdd70979ecf6dd0da7652efd3797c26039": {
|
||||||
|
"path": "bios/Other/emucorex/resources/GameIndex.yaml",
|
||||||
|
"name": "GameIndex.yaml",
|
||||||
|
"size": 2697551,
|
||||||
|
"sha1": "2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||||
|
"md5": "602ea57b1d141e81b2a96f20a4b8098c",
|
||||||
|
"sha256": "d5558a0e0aa033482b098cc8a6449e78137f5069beabf421ab60d9eccbb62d8f",
|
||||||
|
"crc32": "e8038ec9",
|
||||||
|
"adler32": "933aaa5d"
|
||||||
|
},
|
||||||
|
"a3d939af27fe0929e61b8e20d49ef58316c0a66c": {
|
||||||
|
"path": "bios/Other/emucorex/resources/patches.zip",
|
||||||
|
"name": "patches.zip",
|
||||||
|
"size": 2091144,
|
||||||
|
"sha1": "a3d939af27fe0929e61b8e20d49ef58316c0a66c",
|
||||||
|
"md5": "ed4d4e8f81d9707dbf4931a786b6b56d",
|
||||||
|
"sha256": "aa14a46908c1114e0c0f1c06accbfa7efc83c1768fb501699e043cbdb1af797f",
|
||||||
|
"crc32": "8820b708",
|
||||||
|
"adler32": "ec343d8e"
|
||||||
|
},
|
||||||
|
"81c56a54bde692fb15fca4efe6db508b7d09bf6e": {
|
||||||
|
"path": "bios/Other/emucorex/resources/shaders/common/fxaa.fx",
|
||||||
|
"name": "fxaa.fx",
|
||||||
|
"size": 17432,
|
||||||
|
"sha1": "81c56a54bde692fb15fca4efe6db508b7d09bf6e",
|
||||||
|
"md5": "efa6392d3dd6e66abf94542159889e4c",
|
||||||
|
"sha256": "817eb2a258e44bb730b94ab77b00cac2480c2e27449078949e2897bad3cb3bc3",
|
||||||
|
"crc32": "ea1fd68a",
|
||||||
|
"adler32": "f2f86cbf"
|
||||||
|
},
|
||||||
"cb0d4276f9947762af0841a8af75efa6cbe9109c": {
|
"cb0d4276f9947762af0841a8af75efa6cbe9109c": {
|
||||||
"path": "bios/Other/emucorex/resources/shaders/opengl/cas.glsl",
|
"path": "bios/Other/emucorex/resources/shaders/opengl/cas.glsl",
|
||||||
"name": "cas.glsl",
|
"name": "cas.glsl",
|
||||||
@@ -135736,6 +135766,26 @@
|
|||||||
"crc32": "54c435f2",
|
"crc32": "54c435f2",
|
||||||
"adler32": "c42ff6d7"
|
"adler32": "c42ff6d7"
|
||||||
},
|
},
|
||||||
|
"f1c97200cb5b571a1ed6d33b5a2f854cebbfb528": {
|
||||||
|
"path": "bios/Other/emucorex/resources/shaders/opengl/tfx_fs.glsl",
|
||||||
|
"name": "tfx_fs.glsl",
|
||||||
|
"size": 37479,
|
||||||
|
"sha1": "f1c97200cb5b571a1ed6d33b5a2f854cebbfb528",
|
||||||
|
"md5": "0ae47ac28b7183aca6e74cc18559ee8d",
|
||||||
|
"sha256": "7194fda8019aeee6d74c70876bf39586996e594e5c85b53136d6cce3118e4a23",
|
||||||
|
"crc32": "9a1419e3",
|
||||||
|
"adler32": "7c34a5b0"
|
||||||
|
},
|
||||||
|
"cb02fa6503183c313aa8d7c278bb9fbfe3d1a943": {
|
||||||
|
"path": "bios/Other/emucorex/resources/shaders/opengl/tfx_vgs.glsl",
|
||||||
|
"name": "tfx_vgs.glsl",
|
||||||
|
"size": 14156,
|
||||||
|
"sha1": "cb02fa6503183c313aa8d7c278bb9fbfe3d1a943",
|
||||||
|
"md5": "9e537d9c29b5278a47412ace793a648d",
|
||||||
|
"sha256": "1f63be246613c1459891ea1cf9faca74161bf97a44ad75d305a035c319d478b3",
|
||||||
|
"crc32": "68bbb4a2",
|
||||||
|
"adler32": "87949cf8"
|
||||||
|
},
|
||||||
"005cecf1a5e20b05407d55d0aad22cf81ad66c31": {
|
"005cecf1a5e20b05407d55d0aad22cf81ad66c31": {
|
||||||
"path": "bios/Other/emucorex/resources/shaders/vulkan/cas.glsl",
|
"path": "bios/Other/emucorex/resources/shaders/vulkan/cas.glsl",
|
||||||
"name": "cas.glsl",
|
"name": "cas.glsl",
|
||||||
@@ -135806,6 +135856,16 @@
|
|||||||
"crc32": "69dc25d7",
|
"crc32": "69dc25d7",
|
||||||
"adler32": "9f4b472e"
|
"adler32": "9f4b472e"
|
||||||
},
|
},
|
||||||
|
"373792e3bc3c89226bff1852dca3eaf5a8ae4a80": {
|
||||||
|
"path": "bios/Other/emucorex/resources/shaders/vulkan/tfx.glsl",
|
||||||
|
"name": "tfx.glsl",
|
||||||
|
"size": 52705,
|
||||||
|
"sha1": "373792e3bc3c89226bff1852dca3eaf5a8ae4a80",
|
||||||
|
"md5": "f88711bf5b4476782fab23bef3f7aa98",
|
||||||
|
"sha256": "db3c84c36729bd6a321566fae796f2ef3645f9889188f2af7e223d5f42d46755",
|
||||||
|
"crc32": "348f482e",
|
||||||
|
"adler32": "257a6143"
|
||||||
|
},
|
||||||
"407f0fde89ac1a628fed0329072dfae1f8d304b1": {
|
"407f0fde89ac1a628fed0329072dfae1f8d304b1": {
|
||||||
"path": "bios/Other/epsxe/.variants/libopenglplugin.so.0e506076",
|
"path": "bios/Other/epsxe/.variants/libopenglplugin.so.0e506076",
|
||||||
"name": "libopenglplugin.so",
|
"name": "libopenglplugin.so",
|
||||||
@@ -154911,6 +154971,16 @@
|
|||||||
"crc32": "94468d79",
|
"crc32": "94468d79",
|
||||||
"adler32": "97dfa90c"
|
"adler32": "97dfa90c"
|
||||||
},
|
},
|
||||||
|
"681e3362f0251d2e26e6bc2e06157c027f5f3116": {
|
||||||
|
"path": "bios/Other/supermodel-dojo/Config/Games.xml",
|
||||||
|
"name": "Games.xml",
|
||||||
|
"size": 152643,
|
||||||
|
"sha1": "681e3362f0251d2e26e6bc2e06157c027f5f3116",
|
||||||
|
"md5": "d5c1f69e9910622b4ca6beae4c23d68a",
|
||||||
|
"sha256": "a73b9f1c37d64532bf82d1d790a3e0cb81a9b2accb04d866901bb7ae1970473a",
|
||||||
|
"crc32": "a7b8bc61",
|
||||||
|
"adler32": "03e1bc30"
|
||||||
|
},
|
||||||
"bc283d9ad8ad3bb99b632ca5b194a5bf27df1c22": {
|
"bc283d9ad8ad3bb99b632ca5b194a5bf27df1c22": {
|
||||||
"path": "bios/Other/swf-player/assets/META-INF/AIR/application.xml",
|
"path": "bios/Other/swf-player/assets/META-INF/AIR/application.xml",
|
||||||
"name": "application.xml",
|
"name": "application.xml",
|
||||||
@@ -195970,8 +196040,6 @@
|
|||||||
"733af5c6422ec0d77423047c1401b8f1": "60c6e393295188b8937bf975394b799c64d87e70",
|
"733af5c6422ec0d77423047c1401b8f1": "60c6e393295188b8937bf975394b799c64d87e70",
|
||||||
"3ec5ebb9a9d65cccedb73d5d326194eb": "85acdc4646039d919c6125d715109685865376ab",
|
"3ec5ebb9a9d65cccedb73d5d326194eb": "85acdc4646039d919c6125d715109685865376ab",
|
||||||
"3088997dfc4798cfc86a7eb357d2186c": "05b91dc8dd6fdbbf982388fd0673073363be9ce6",
|
"3088997dfc4798cfc86a7eb357d2186c": "05b91dc8dd6fdbbf982388fd0673073363be9ce6",
|
||||||
"4ed853d4a52e6b9b9e11954f155ecb8a": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
|
||||||
"b5890c3d737880f6dd3751ffec5b128b": "8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
|
||||||
"601203108bc1c3c8e46d08eb247a92fc": "1f424e6329bc1dfc254a60b752e0c2ad1d1f6126",
|
"601203108bc1c3c8e46d08eb247a92fc": "1f424e6329bc1dfc254a60b752e0c2ad1d1f6126",
|
||||||
"20c241b5bf924f2338d79dd1d37769d5": "fbba87e9d6852862252d31dd919f943c8edd7eb7",
|
"20c241b5bf924f2338d79dd1d37769d5": "fbba87e9d6852862252d31dd919f943c8edd7eb7",
|
||||||
"8c11c3ead979857c38177a270382bd84": "721621a353051fea6de484c26b534cdec4de616e",
|
"8c11c3ead979857c38177a270382bd84": "721621a353051fea6de484c26b534cdec4de616e",
|
||||||
@@ -207772,6 +207840,8 @@
|
|||||||
"8ecd73eb4edf7ed7e81aef1be80031d5": "e5b2922ca137051059e4269b236d07a22c07bc84",
|
"8ecd73eb4edf7ed7e81aef1be80031d5": "e5b2922ca137051059e4269b236d07a22c07bc84",
|
||||||
"6d507dcf3b61ce45a5717421cdd0ae95": "f282b3aaf98f8423dab7d77f1aa0192be630f2fb",
|
"6d507dcf3b61ce45a5717421cdd0ae95": "f282b3aaf98f8423dab7d77f1aa0192be630f2fb",
|
||||||
"a43cbd0ab271990f434412fc5c500eff": "369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de",
|
"a43cbd0ab271990f434412fc5c500eff": "369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de",
|
||||||
|
"4ed853d4a52e6b9b9e11954f155ecb8a": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
||||||
|
"b5890c3d737880f6dd3751ffec5b128b": "8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
||||||
"89049432c168d28f649348cd31c057cd": "9d4488178fe93d053d44f836e849d3d6b3e5974e",
|
"89049432c168d28f649348cd31c057cd": "9d4488178fe93d053d44f836e849d3d6b3e5974e",
|
||||||
"31fda6c3678507b12631dfd01b0415ef": "c146b651696d25287241f4bab18226e488bc45b3",
|
"31fda6c3678507b12631dfd01b0415ef": "c146b651696d25287241f4bab18226e488bc45b3",
|
||||||
"f65367650f88124362922994b78ede77": "bfee8e708dc4c8f27bfe51a12d57caaa0b7f74de",
|
"f65367650f88124362922994b78ede77": "bfee8e708dc4c8f27bfe51a12d57caaa0b7f74de",
|
||||||
@@ -208396,6 +208466,9 @@
|
|||||||
"22e156b807f3147bb7dcafadc52ab721": "4d0f8dfc5980195ee905fa39e125cd418b8a9599",
|
"22e156b807f3147bb7dcafadc52ab721": "4d0f8dfc5980195ee905fa39e125cd418b8a9599",
|
||||||
"f4ae6809bd8c31573370e8da72514012": "e18b9aabbda0cc178436855ef8d04bce96533049",
|
"f4ae6809bd8c31573370e8da72514012": "e18b9aabbda0cc178436855ef8d04bce96533049",
|
||||||
"bf5e583b038933c5f7bc4f0a6f70617c": "c549a2088bc9b678c8c79fa987ac5f4e6ff4772c",
|
"bf5e583b038933c5f7bc4f0a6f70617c": "c549a2088bc9b678c8c79fa987ac5f4e6ff4772c",
|
||||||
|
"602ea57b1d141e81b2a96f20a4b8098c": "2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||||
|
"ed4d4e8f81d9707dbf4931a786b6b56d": "a3d939af27fe0929e61b8e20d49ef58316c0a66c",
|
||||||
|
"efa6392d3dd6e66abf94542159889e4c": "81c56a54bde692fb15fca4efe6db508b7d09bf6e",
|
||||||
"b56eb507fac5ac64a7079514c02b8ca7": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
|
"b56eb507fac5ac64a7079514c02b8ca7": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
|
||||||
"b8f058ec01942f596c890a060a32f08b": "35910b67070342cdd6ec4d68eb2e56b39d350543",
|
"b8f058ec01942f596c890a060a32f08b": "35910b67070342cdd6ec4d68eb2e56b39d350543",
|
||||||
"9338b9a25372178b4ecbf0215697e9be": "ca149058af89d8f612aaa529661e8267d9569875",
|
"9338b9a25372178b4ecbf0215697e9be": "ca149058af89d8f612aaa529661e8267d9569875",
|
||||||
@@ -208403,6 +208476,8 @@
|
|||||||
"1cbf28d65b0d6fe727b571d3e46871b8": "c8eb9c1926baad8bbba25f97be54abae233fe715",
|
"1cbf28d65b0d6fe727b571d3e46871b8": "c8eb9c1926baad8bbba25f97be54abae233fe715",
|
||||||
"480b029f6c357fb683a6720e2b78d990": "4abe45e59dc9286321a115c76c31cc7fe85e58d5",
|
"480b029f6c357fb683a6720e2b78d990": "4abe45e59dc9286321a115c76c31cc7fe85e58d5",
|
||||||
"23a5e91beb2072f48e1011e7a9b6e901": "03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564",
|
"23a5e91beb2072f48e1011e7a9b6e901": "03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564",
|
||||||
|
"0ae47ac28b7183aca6e74cc18559ee8d": "f1c97200cb5b571a1ed6d33b5a2f854cebbfb528",
|
||||||
|
"9e537d9c29b5278a47412ace793a648d": "cb02fa6503183c313aa8d7c278bb9fbfe3d1a943",
|
||||||
"deb7394a31184990c78c9753a293ff60": "005cecf1a5e20b05407d55d0aad22cf81ad66c31",
|
"deb7394a31184990c78c9753a293ff60": "005cecf1a5e20b05407d55d0aad22cf81ad66c31",
|
||||||
"669f55ffa59d7b754ab11f96c42f206f": "705d20d39a910d58a070fb86780ac58c8007591c",
|
"669f55ffa59d7b754ab11f96c42f206f": "705d20d39a910d58a070fb86780ac58c8007591c",
|
||||||
"b79725bff4fecb48d68ad575e13950d1": "276d984150c2fd2fb5ba287f2e5ceb7262dc5a95",
|
"b79725bff4fecb48d68ad575e13950d1": "276d984150c2fd2fb5ba287f2e5ceb7262dc5a95",
|
||||||
@@ -208410,6 +208485,7 @@
|
|||||||
"8fbeeb5c09477bcaf614087003df61a4": "808b7b46bfb033c7fe4d94d0dbf5a271de30dedc",
|
"8fbeeb5c09477bcaf614087003df61a4": "808b7b46bfb033c7fe4d94d0dbf5a271de30dedc",
|
||||||
"b0bdec53b1c50873ba45539565f1327c": "a848aad1840c7d85bfcdd552521a7181b00331cc",
|
"b0bdec53b1c50873ba45539565f1327c": "a848aad1840c7d85bfcdd552521a7181b00331cc",
|
||||||
"ec6b58f723279afe732b7f9660acf27d": "9b2704655a893ff1675d28df4bd9ffc9e15d3dd5",
|
"ec6b58f723279afe732b7f9660acf27d": "9b2704655a893ff1675d28df4bd9ffc9e15d3dd5",
|
||||||
|
"f88711bf5b4476782fab23bef3f7aa98": "373792e3bc3c89226bff1852dca3eaf5a8ae4a80",
|
||||||
"0e506076654afda89187c33481b1bfef": "407f0fde89ac1a628fed0329072dfae1f8d304b1",
|
"0e506076654afda89187c33481b1bfef": "407f0fde89ac1a628fed0329072dfae1f8d304b1",
|
||||||
"1ca91fb5e46147945fd924b13e868d97": "f0168fbad73f8c0589c070f3e2071c1d62967a40",
|
"1ca91fb5e46147945fd924b13e868d97": "f0168fbad73f8c0589c070f3e2071c1d62967a40",
|
||||||
"b28dd2574fb0d7dea4950bde882bd537": "e17ecabba25dc797bf7a0e9979b18e5189e410e6",
|
"b28dd2574fb0d7dea4950bde882bd537": "e17ecabba25dc797bf7a0e9979b18e5189e410e6",
|
||||||
@@ -210317,6 +210393,7 @@
|
|||||||
"fc26e2deaa532794d91fe33ca73f8e2c": "b41150f8cff409dde89939b80a980a20e853df0b",
|
"fc26e2deaa532794d91fe33ca73f8e2c": "b41150f8cff409dde89939b80a980a20e853df0b",
|
||||||
"a468e45e005caa89356bd67b70e0d37d": "6c38cc6fba14863bfbdc730e53d3274fd79a4544",
|
"a468e45e005caa89356bd67b70e0d37d": "6c38cc6fba14863bfbdc730e53d3274fd79a4544",
|
||||||
"e8da2d033e90d6fefa884ee78690b4e0": "cb6be8206308470ecaa8b9cc882d82abe0ef3562",
|
"e8da2d033e90d6fefa884ee78690b4e0": "cb6be8206308470ecaa8b9cc882d82abe0ef3562",
|
||||||
|
"d5c1f69e9910622b4ca6beae4c23d68a": "681e3362f0251d2e26e6bc2e06157c027f5f3116",
|
||||||
"de36d0dc1b74a39be2313749138d4d00": "bc283d9ad8ad3bb99b632ca5b194a5bf27df1c22",
|
"de36d0dc1b74a39be2313749138d4d00": "bc283d9ad8ad3bb99b632ca5b194a5bf27df1c22",
|
||||||
"39c997db73db15a6807b35f148fcc85a": "0aaf20633888d2d0a2da38e82773cb5d44e840eb",
|
"39c997db73db15a6807b35f148fcc85a": "0aaf20633888d2d0a2da38e82773cb5d44e840eb",
|
||||||
"834f85979115fd740943a782887d0ab0": "d27da38e4d37727a44bf04d7690a09135fad7115",
|
"834f85979115fd740943a782887d0ab0": "d27da38e4d37727a44bf04d7690a09135fad7115",
|
||||||
@@ -216839,14 +216916,6 @@
|
|||||||
"firmare installation.txt": [
|
"firmare installation.txt": [
|
||||||
"05b91dc8dd6fdbbf982388fd0673073363be9ce6"
|
"05b91dc8dd6fdbbf982388fd0673073363be9ce6"
|
||||||
],
|
],
|
||||||
"prod.keys": [
|
|
||||||
"d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
|
||||||
"c146b651696d25287241f4bab18226e488bc45b3"
|
|
||||||
],
|
|
||||||
"title.keys": [
|
|
||||||
"8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
|
||||||
"bfee8e708dc4c8f27bfe51a12d57caaa0b7f74de"
|
|
||||||
],
|
|
||||||
"sx1541.zip": [
|
"sx1541.zip": [
|
||||||
"1f424e6329bc1dfc254a60b752e0c2ad1d1f6126"
|
"1f424e6329bc1dfc254a60b752e0c2ad1d1f6126"
|
||||||
],
|
],
|
||||||
@@ -246560,6 +246629,14 @@
|
|||||||
"sgb_boot.bin": [
|
"sgb_boot.bin": [
|
||||||
"369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de"
|
"369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de"
|
||||||
],
|
],
|
||||||
|
"prod.keys": [
|
||||||
|
"d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
||||||
|
"c146b651696d25287241f4bab18226e488bc45b3"
|
||||||
|
],
|
||||||
|
"title.keys": [
|
||||||
|
"8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
||||||
|
"bfee8e708dc4c8f27bfe51a12d57caaa0b7f74de"
|
||||||
|
],
|
||||||
"Firmware_batocera43.zip": [
|
"Firmware_batocera43.zip": [
|
||||||
"9d4488178fe93d053d44f836e849d3d6b3e5974e"
|
"9d4488178fe93d053d44f836e849d3d6b3e5974e"
|
||||||
],
|
],
|
||||||
@@ -247590,6 +247667,7 @@
|
|||||||
],
|
],
|
||||||
"fxaa.fx": [
|
"fxaa.fx": [
|
||||||
"9d7012f8bb7bb4599ed2518f12ed540655cdd8d8",
|
"9d7012f8bb7bb4599ed2518f12ed540655cdd8d8",
|
||||||
|
"81c56a54bde692fb15fca4efe6db508b7d09bf6e",
|
||||||
"3ac5f68049ed00d8ff3d2811bd09ef0c036efd0a"
|
"3ac5f68049ed00d8ff3d2811bd09ef0c036efd0a"
|
||||||
],
|
],
|
||||||
"imgui.fx": [
|
"imgui.fx": [
|
||||||
@@ -247739,6 +247817,7 @@
|
|||||||
],
|
],
|
||||||
"tfx.glsl": [
|
"tfx.glsl": [
|
||||||
"229d91d96b6dc338421e696fe8cdcfcaa093f40d",
|
"229d91d96b6dc338421e696fe8cdcfcaa093f40d",
|
||||||
|
"373792e3bc3c89226bff1852dca3eaf5a8ae4a80",
|
||||||
"aa06cffa086126bb4972674562a5f5c8dc6082f3",
|
"aa06cffa086126bb4972674562a5f5c8dc6082f3",
|
||||||
"1c3a966a42cefcb9c26ce2fa77e4cd90f2d50329",
|
"1c3a966a42cefcb9c26ce2fa77e4cd90f2d50329",
|
||||||
"b4ebf3f7f20fc88f91cda033552a7489a9ff179c",
|
"b4ebf3f7f20fc88f91cda033552a7489a9ff179c",
|
||||||
@@ -247746,12 +247825,14 @@
|
|||||||
],
|
],
|
||||||
"tfx_fs.glsl": [
|
"tfx_fs.glsl": [
|
||||||
"d67bfc4ab5c7e550ea41a0491f45b95f395bacac",
|
"d67bfc4ab5c7e550ea41a0491f45b95f395bacac",
|
||||||
|
"f1c97200cb5b571a1ed6d33b5a2f854cebbfb528",
|
||||||
"00eb0418e7a25c89aaf7c526922fe45d83243ec5",
|
"00eb0418e7a25c89aaf7c526922fe45d83243ec5",
|
||||||
"919c3308c8850852d529d0127ce94e873375bfd2",
|
"919c3308c8850852d529d0127ce94e873375bfd2",
|
||||||
"554160d31bb97aca0e9f367f4de811dd8ad1fb21"
|
"554160d31bb97aca0e9f367f4de811dd8ad1fb21"
|
||||||
],
|
],
|
||||||
"tfx_vgs.glsl": [
|
"tfx_vgs.glsl": [
|
||||||
"f8200a0f4bd284670f0f0191fa9340684e94f66d",
|
"f8200a0f4bd284670f0f0191fa9340684e94f66d",
|
||||||
|
"cb02fa6503183c313aa8d7c278bb9fbfe3d1a943",
|
||||||
"0e9e98f766c9250b465af776d0077f49f797fed2",
|
"0e9e98f766c9250b465af776d0077f49f797fed2",
|
||||||
"fc40fc49de1cd8ed7d60df0394dd6367ae82b85f"
|
"fc40fc49de1cd8ed7d60df0394dd6367ae82b85f"
|
||||||
],
|
],
|
||||||
@@ -248407,6 +248488,22 @@
|
|||||||
"games.db": [
|
"games.db": [
|
||||||
"0e2ee74d1f9ebc3a6faf9dbaebbacc2ff053f4df"
|
"0e2ee74d1f9ebc3a6faf9dbaebbacc2ff053f4df"
|
||||||
],
|
],
|
||||||
|
"GameIndex.yaml": [
|
||||||
|
"2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||||
|
"997fdfb1c1873c1a43972b2984ac0da61fd47b14",
|
||||||
|
"fca26d4f7c1fe0abf6534b470da2ee5ad461eba5",
|
||||||
|
"58fb1f8ea6cf2d526904349640453e682fe388a1",
|
||||||
|
"7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||||
|
"57369df9ca82a460e596cea7835c5a0797dee668",
|
||||||
|
"b22389650f6c0a1e276b48213fed8a9c1c6476ce",
|
||||||
|
"6eddec30056cde7c664a0cf508dcad29353a12bb",
|
||||||
|
"40ecf6138c99a0aba775ef93240b295025a45500"
|
||||||
|
],
|
||||||
|
"patches.zip": [
|
||||||
|
"a3d939af27fe0929e61b8e20d49ef58316c0a66c",
|
||||||
|
"2562e542d66db945ccf158d7a221c2a28e3b6be4",
|
||||||
|
"541a74165496fb7ef35ce6ec03f711ebbe263e11"
|
||||||
|
],
|
||||||
"libopenglplugin.so": [
|
"libopenglplugin.so": [
|
||||||
"407f0fde89ac1a628fed0329072dfae1f8d304b1",
|
"407f0fde89ac1a628fed0329072dfae1f8d304b1",
|
||||||
"f0168fbad73f8c0589c070f3e2071c1d62967a40",
|
"f0168fbad73f8c0589c070f3e2071c1d62967a40",
|
||||||
@@ -249537,16 +249634,6 @@
|
|||||||
"3rdparty.html": [
|
"3rdparty.html": [
|
||||||
"7743bf1d7e6e09a02f1b2972aa63bd8a9f26050d"
|
"7743bf1d7e6e09a02f1b2972aa63bd8a9f26050d"
|
||||||
],
|
],
|
||||||
"GameIndex.yaml": [
|
|
||||||
"997fdfb1c1873c1a43972b2984ac0da61fd47b14",
|
|
||||||
"fca26d4f7c1fe0abf6534b470da2ee5ad461eba5",
|
|
||||||
"58fb1f8ea6cf2d526904349640453e682fe388a1",
|
|
||||||
"7efb2ed721cf0c0f0677324e09be311a8337e793",
|
|
||||||
"57369df9ca82a460e596cea7835c5a0797dee668",
|
|
||||||
"b22389650f6c0a1e276b48213fed8a9c1c6476ce",
|
|
||||||
"6eddec30056cde7c664a0cf508dcad29353a12bb",
|
|
||||||
"40ecf6138c99a0aba775ef93240b295025a45500"
|
|
||||||
],
|
|
||||||
"cheats_ni.zip": [
|
"cheats_ni.zip": [
|
||||||
"0b219dc8b3b98c1705b14758bb7950064b928ebb",
|
"0b219dc8b3b98c1705b14758bb7950064b928ebb",
|
||||||
"31cbff66380f255d359ff9ee09b6edc06102579a",
|
"31cbff66380f255d359ff9ee09b6edc06102579a",
|
||||||
@@ -253818,6 +253905,7 @@
|
|||||||
"Games.xml": [
|
"Games.xml": [
|
||||||
"ccbc64e2f5b9aefd89ff9acd7227eff230aed8ed",
|
"ccbc64e2f5b9aefd89ff9acd7227eff230aed8ed",
|
||||||
"b41150f8cff409dde89939b80a980a20e853df0b",
|
"b41150f8cff409dde89939b80a980a20e853df0b",
|
||||||
|
"681e3362f0251d2e26e6bc2e06157c027f5f3116",
|
||||||
"6871a47de90ca67f9ff8e4e280416851a8164563",
|
"6871a47de90ca67f9ff8e4e280416851a8164563",
|
||||||
"57500ad35b883acb5e7766c04f5b42e65185715c",
|
"57500ad35b883acb5e7766c04f5b42e65185715c",
|
||||||
"fa334ab5ba6366bd28168860c913e6365b9fec96"
|
"fa334ab5ba6366bd28168860c913e6365b9fec96"
|
||||||
@@ -262936,10 +263024,6 @@
|
|||||||
"1d3caaed7411425c16aee8c35db47b928e31f60b",
|
"1d3caaed7411425c16aee8c35db47b928e31f60b",
|
||||||
"62f5a0b54a174daf2bf66634b86c6f2a3cd297ce"
|
"62f5a0b54a174daf2bf66634b86c6f2a3cd297ce"
|
||||||
],
|
],
|
||||||
"patches.zip": [
|
|
||||||
"2562e542d66db945ccf158d7a221c2a28e3b6be4",
|
|
||||||
"541a74165496fb7ef35ce6ec03f711ebbe263e11"
|
|
||||||
],
|
|
||||||
"ps2-dvd-110u.bin": [
|
"ps2-dvd-110u.bin": [
|
||||||
"0bc9143cb6839471d62b8fdd0b2fc05e941527d3"
|
"0bc9143cb6839471d62b8fdd0b2fc05e941527d3"
|
||||||
],
|
],
|
||||||
@@ -271760,8 +271844,6 @@
|
|||||||
"621a057a": "60c6e393295188b8937bf975394b799c64d87e70",
|
"621a057a": "60c6e393295188b8937bf975394b799c64d87e70",
|
||||||
"4860e4b4": "85acdc4646039d919c6125d715109685865376ab",
|
"4860e4b4": "85acdc4646039d919c6125d715109685865376ab",
|
||||||
"772caf31": "05b91dc8dd6fdbbf982388fd0673073363be9ce6",
|
"772caf31": "05b91dc8dd6fdbbf982388fd0673073363be9ce6",
|
||||||
"6d74253f": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
|
||||||
"04387ea8": "8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
|
||||||
"9145508f": "1f424e6329bc1dfc254a60b752e0c2ad1d1f6126",
|
"9145508f": "1f424e6329bc1dfc254a60b752e0c2ad1d1f6126",
|
||||||
"450b9cb4": "fbba87e9d6852862252d31dd919f943c8edd7eb7",
|
"450b9cb4": "fbba87e9d6852862252d31dd919f943c8edd7eb7",
|
||||||
"d1821125": "721621a353051fea6de484c26b534cdec4de616e",
|
"d1821125": "721621a353051fea6de484c26b534cdec4de616e",
|
||||||
@@ -283562,6 +283644,8 @@
|
|||||||
"cb176e45": "e5b2922ca137051059e4269b236d07a22c07bc84",
|
"cb176e45": "e5b2922ca137051059e4269b236d07a22c07bc84",
|
||||||
"3ad83b6e": "f282b3aaf98f8423dab7d77f1aa0192be630f2fb",
|
"3ad83b6e": "f282b3aaf98f8423dab7d77f1aa0192be630f2fb",
|
||||||
"4c866029": "369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de",
|
"4c866029": "369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de",
|
||||||
|
"6d74253f": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
||||||
|
"04387ea8": "8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
||||||
"01008403": "9d4488178fe93d053d44f836e849d3d6b3e5974e",
|
"01008403": "9d4488178fe93d053d44f836e849d3d6b3e5974e",
|
||||||
"a65c13ca": "c146b651696d25287241f4bab18226e488bc45b3",
|
"a65c13ca": "c146b651696d25287241f4bab18226e488bc45b3",
|
||||||
"77cacffb": "bfee8e708dc4c8f27bfe51a12d57caaa0b7f74de",
|
"77cacffb": "bfee8e708dc4c8f27bfe51a12d57caaa0b7f74de",
|
||||||
@@ -284186,6 +284270,9 @@
|
|||||||
"bf24835a": "4d0f8dfc5980195ee905fa39e125cd418b8a9599",
|
"bf24835a": "4d0f8dfc5980195ee905fa39e125cd418b8a9599",
|
||||||
"e1232465": "e18b9aabbda0cc178436855ef8d04bce96533049",
|
"e1232465": "e18b9aabbda0cc178436855ef8d04bce96533049",
|
||||||
"ca72899c": "c549a2088bc9b678c8c79fa987ac5f4e6ff4772c",
|
"ca72899c": "c549a2088bc9b678c8c79fa987ac5f4e6ff4772c",
|
||||||
|
"e8038ec9": "2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||||
|
"8820b708": "a3d939af27fe0929e61b8e20d49ef58316c0a66c",
|
||||||
|
"ea1fd68a": "81c56a54bde692fb15fca4efe6db508b7d09bf6e",
|
||||||
"13eed5a5": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
|
"13eed5a5": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
|
||||||
"bb25c312": "35910b67070342cdd6ec4d68eb2e56b39d350543",
|
"bb25c312": "35910b67070342cdd6ec4d68eb2e56b39d350543",
|
||||||
"342d0e13": "ca149058af89d8f612aaa529661e8267d9569875",
|
"342d0e13": "ca149058af89d8f612aaa529661e8267d9569875",
|
||||||
@@ -284193,6 +284280,8 @@
|
|||||||
"ae991348": "c8eb9c1926baad8bbba25f97be54abae233fe715",
|
"ae991348": "c8eb9c1926baad8bbba25f97be54abae233fe715",
|
||||||
"67a7b03b": "4abe45e59dc9286321a115c76c31cc7fe85e58d5",
|
"67a7b03b": "4abe45e59dc9286321a115c76c31cc7fe85e58d5",
|
||||||
"54c435f2": "03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564",
|
"54c435f2": "03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564",
|
||||||
|
"9a1419e3": "f1c97200cb5b571a1ed6d33b5a2f854cebbfb528",
|
||||||
|
"68bbb4a2": "cb02fa6503183c313aa8d7c278bb9fbfe3d1a943",
|
||||||
"e00e7479": "005cecf1a5e20b05407d55d0aad22cf81ad66c31",
|
"e00e7479": "005cecf1a5e20b05407d55d0aad22cf81ad66c31",
|
||||||
"fd02bb00": "705d20d39a910d58a070fb86780ac58c8007591c",
|
"fd02bb00": "705d20d39a910d58a070fb86780ac58c8007591c",
|
||||||
"c9511683": "276d984150c2fd2fb5ba287f2e5ceb7262dc5a95",
|
"c9511683": "276d984150c2fd2fb5ba287f2e5ceb7262dc5a95",
|
||||||
@@ -284200,6 +284289,7 @@
|
|||||||
"51cef730": "808b7b46bfb033c7fe4d94d0dbf5a271de30dedc",
|
"51cef730": "808b7b46bfb033c7fe4d94d0dbf5a271de30dedc",
|
||||||
"50fb46e4": "a848aad1840c7d85bfcdd552521a7181b00331cc",
|
"50fb46e4": "a848aad1840c7d85bfcdd552521a7181b00331cc",
|
||||||
"69dc25d7": "9b2704655a893ff1675d28df4bd9ffc9e15d3dd5",
|
"69dc25d7": "9b2704655a893ff1675d28df4bd9ffc9e15d3dd5",
|
||||||
|
"348f482e": "373792e3bc3c89226bff1852dca3eaf5a8ae4a80",
|
||||||
"9a76f6f2": "407f0fde89ac1a628fed0329072dfae1f8d304b1",
|
"9a76f6f2": "407f0fde89ac1a628fed0329072dfae1f8d304b1",
|
||||||
"f3a06766": "f0168fbad73f8c0589c070f3e2071c1d62967a40",
|
"f3a06766": "f0168fbad73f8c0589c070f3e2071c1d62967a40",
|
||||||
"cb72da07": "e17ecabba25dc797bf7a0e9979b18e5189e410e6",
|
"cb72da07": "e17ecabba25dc797bf7a0e9979b18e5189e410e6",
|
||||||
@@ -286107,6 +286197,7 @@
|
|||||||
"b2ea6948": "b41150f8cff409dde89939b80a980a20e853df0b",
|
"b2ea6948": "b41150f8cff409dde89939b80a980a20e853df0b",
|
||||||
"2d012d14": "6c38cc6fba14863bfbdc730e53d3274fd79a4544",
|
"2d012d14": "6c38cc6fba14863bfbdc730e53d3274fd79a4544",
|
||||||
"94468d79": "cb6be8206308470ecaa8b9cc882d82abe0ef3562",
|
"94468d79": "cb6be8206308470ecaa8b9cc882d82abe0ef3562",
|
||||||
|
"a7b8bc61": "681e3362f0251d2e26e6bc2e06157c027f5f3116",
|
||||||
"2d08ea82": "bc283d9ad8ad3bb99b632ca5b194a5bf27df1c22",
|
"2d08ea82": "bc283d9ad8ad3bb99b632ca5b194a5bf27df1c22",
|
||||||
"21a26259": "0aaf20633888d2d0a2da38e82773cb5d44e840eb",
|
"21a26259": "0aaf20633888d2d0a2da38e82773cb5d44e840eb",
|
||||||
"e251326a": "d27da38e4d37727a44bf04d7690a09135fad7115",
|
"e251326a": "d27da38e4d37727a44bf04d7690a09135fad7115",
|
||||||
@@ -290752,8 +290843,6 @@
|
|||||||
"ebb770368645197d340c4c618e243425e64ab444f128b5a7ab84761564bf5d89": "60c6e393295188b8937bf975394b799c64d87e70",
|
"ebb770368645197d340c4c618e243425e64ab444f128b5a7ab84761564bf5d89": "60c6e393295188b8937bf975394b799c64d87e70",
|
||||||
"58fb6bdd20229c3a1d442d416aa1d4c120eebf402bfb408a245ef406fbb84a81": "85acdc4646039d919c6125d715109685865376ab",
|
"58fb6bdd20229c3a1d442d416aa1d4c120eebf402bfb408a245ef406fbb84a81": "85acdc4646039d919c6125d715109685865376ab",
|
||||||
"1cf244001bcc42008d93669cdf7bb37a2b343045cd81485527111c5dec2be092": "05b91dc8dd6fdbbf982388fd0673073363be9ce6",
|
"1cf244001bcc42008d93669cdf7bb37a2b343045cd81485527111c5dec2be092": "05b91dc8dd6fdbbf982388fd0673073363be9ce6",
|
||||||
"76df55f85c6628cd924f227a72a301bbb731833c0a0f00c440e01e6b34480990": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
|
||||||
"dc6e31aae8daa9067dafcf2bca6d9d3aa691a63474eb698ad8c54be3e360753b": "8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
|
||||||
"3e1cfb8ce0475c8088d8393aeb75341edbed5f30d7c023aac673fe549c0545fe": "1f424e6329bc1dfc254a60b752e0c2ad1d1f6126",
|
"3e1cfb8ce0475c8088d8393aeb75341edbed5f30d7c023aac673fe549c0545fe": "1f424e6329bc1dfc254a60b752e0c2ad1d1f6126",
|
||||||
"f3bb5f2101534172ec49f6e7b6eb66bb2a2116c953d64517fc098a51f042002a": "fbba87e9d6852862252d31dd919f943c8edd7eb7",
|
"f3bb5f2101534172ec49f6e7b6eb66bb2a2116c953d64517fc098a51f042002a": "fbba87e9d6852862252d31dd919f943c8edd7eb7",
|
||||||
"5fc6a17d793b5b647c7590f650ee3f21c5210cca7e05e51734a3c3d20182698d": "721621a353051fea6de484c26b534cdec4de616e",
|
"5fc6a17d793b5b647c7590f650ee3f21c5210cca7e05e51734a3c3d20182698d": "721621a353051fea6de484c26b534cdec4de616e",
|
||||||
@@ -302554,6 +302643,8 @@
|
|||||||
"c172498a23d1176672931bab33b629c7d28f914a43dca9e540b8af1b37ccf2c6": "e5b2922ca137051059e4269b236d07a22c07bc84",
|
"c172498a23d1176672931bab33b629c7d28f914a43dca9e540b8af1b37ccf2c6": "e5b2922ca137051059e4269b236d07a22c07bc84",
|
||||||
"8a65465a9da7ec657726a671da9a85963cf19d2c975e499aea6b97eb37e0b6ea": "f282b3aaf98f8423dab7d77f1aa0192be630f2fb",
|
"8a65465a9da7ec657726a671da9a85963cf19d2c975e499aea6b97eb37e0b6ea": "f282b3aaf98f8423dab7d77f1aa0192be630f2fb",
|
||||||
"b60d493a7944ccf74c81f1e7b6bf38c2c7029e296648ea0e74cb22b10dd1fcb8": "369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de",
|
"b60d493a7944ccf74c81f1e7b6bf38c2c7029e296648ea0e74cb22b10dd1fcb8": "369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de",
|
||||||
|
"76df55f85c6628cd924f227a72a301bbb731833c0a0f00c440e01e6b34480990": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
||||||
|
"dc6e31aae8daa9067dafcf2bca6d9d3aa691a63474eb698ad8c54be3e360753b": "8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
||||||
"f00797dd1ca5a077881fd47ac99febbd14930a5877d8aae99d85b2cd2ae7f57e": "9d4488178fe93d053d44f836e849d3d6b3e5974e",
|
"f00797dd1ca5a077881fd47ac99febbd14930a5877d8aae99d85b2cd2ae7f57e": "9d4488178fe93d053d44f836e849d3d6b3e5974e",
|
||||||
"039895c6e93a0c6208c3af27da8c3fc408083873a96f2320dc334a6fa252c112": "c146b651696d25287241f4bab18226e488bc45b3",
|
"039895c6e93a0c6208c3af27da8c3fc408083873a96f2320dc334a6fa252c112": "c146b651696d25287241f4bab18226e488bc45b3",
|
||||||
"3ed66f7e82df79922739c6c2d34de90bc36a48d13acdff24f9d51013bd8c0677": "bfee8e708dc4c8f27bfe51a12d57caaa0b7f74de",
|
"3ed66f7e82df79922739c6c2d34de90bc36a48d13acdff24f9d51013bd8c0677": "bfee8e708dc4c8f27bfe51a12d57caaa0b7f74de",
|
||||||
@@ -303178,6 +303269,9 @@
|
|||||||
"bdff3e5659d67e67def05b33f749683b9376ae819d65d3dd62ac4640b3aaef48": "4d0f8dfc5980195ee905fa39e125cd418b8a9599",
|
"bdff3e5659d67e67def05b33f749683b9376ae819d65d3dd62ac4640b3aaef48": "4d0f8dfc5980195ee905fa39e125cd418b8a9599",
|
||||||
"306b53ecfb182a504dd8a7446093c316387d2fd8dc350d0792ed1753fe0996cd": "e18b9aabbda0cc178436855ef8d04bce96533049",
|
"306b53ecfb182a504dd8a7446093c316387d2fd8dc350d0792ed1753fe0996cd": "e18b9aabbda0cc178436855ef8d04bce96533049",
|
||||||
"cdefaf8efd47045f6820928eba84db5bed7557539328952b5f828315485e02ee": "c549a2088bc9b678c8c79fa987ac5f4e6ff4772c",
|
"cdefaf8efd47045f6820928eba84db5bed7557539328952b5f828315485e02ee": "c549a2088bc9b678c8c79fa987ac5f4e6ff4772c",
|
||||||
|
"d5558a0e0aa033482b098cc8a6449e78137f5069beabf421ab60d9eccbb62d8f": "2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||||
|
"aa14a46908c1114e0c0f1c06accbfa7efc83c1768fb501699e043cbdb1af797f": "a3d939af27fe0929e61b8e20d49ef58316c0a66c",
|
||||||
|
"817eb2a258e44bb730b94ab77b00cac2480c2e27449078949e2897bad3cb3bc3": "81c56a54bde692fb15fca4efe6db508b7d09bf6e",
|
||||||
"53e5ddc6825ecde7b502acb5fae3a173ba6696ec5b8bb7445f378f7b0ad1beb8": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
|
"53e5ddc6825ecde7b502acb5fae3a173ba6696ec5b8bb7445f378f7b0ad1beb8": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
|
||||||
"fcb651c2ff39102a33ece3716579e3aee7d8b92d4700e52b56b6cf14baa8b8d1": "35910b67070342cdd6ec4d68eb2e56b39d350543",
|
"fcb651c2ff39102a33ece3716579e3aee7d8b92d4700e52b56b6cf14baa8b8d1": "35910b67070342cdd6ec4d68eb2e56b39d350543",
|
||||||
"ea82ce9731de4a0b5af3e66e47e048af569e0e98c5739b32f268fb54caa7603a": "ca149058af89d8f612aaa529661e8267d9569875",
|
"ea82ce9731de4a0b5af3e66e47e048af569e0e98c5739b32f268fb54caa7603a": "ca149058af89d8f612aaa529661e8267d9569875",
|
||||||
@@ -303185,6 +303279,8 @@
|
|||||||
"654281fb002aa1a086ee979d694e9d6443864bdc8740d20188baf8e125e386fa": "c8eb9c1926baad8bbba25f97be54abae233fe715",
|
"654281fb002aa1a086ee979d694e9d6443864bdc8740d20188baf8e125e386fa": "c8eb9c1926baad8bbba25f97be54abae233fe715",
|
||||||
"e31c519e6b22683cebefc5c2358b2b7ddad10964a1e478fa3eba01f3ab5adb17": "4abe45e59dc9286321a115c76c31cc7fe85e58d5",
|
"e31c519e6b22683cebefc5c2358b2b7ddad10964a1e478fa3eba01f3ab5adb17": "4abe45e59dc9286321a115c76c31cc7fe85e58d5",
|
||||||
"15ff418a0fc877886015cc781c9c3b0810a870bee5803d76741bbcbcc540b9e5": "03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564",
|
"15ff418a0fc877886015cc781c9c3b0810a870bee5803d76741bbcbcc540b9e5": "03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564",
|
||||||
|
"7194fda8019aeee6d74c70876bf39586996e594e5c85b53136d6cce3118e4a23": "f1c97200cb5b571a1ed6d33b5a2f854cebbfb528",
|
||||||
|
"1f63be246613c1459891ea1cf9faca74161bf97a44ad75d305a035c319d478b3": "cb02fa6503183c313aa8d7c278bb9fbfe3d1a943",
|
||||||
"ea3f65c6d1b18abe23b42a3d9ddf48d787df287feef6cfac52ca6109d8852d97": "005cecf1a5e20b05407d55d0aad22cf81ad66c31",
|
"ea3f65c6d1b18abe23b42a3d9ddf48d787df287feef6cfac52ca6109d8852d97": "005cecf1a5e20b05407d55d0aad22cf81ad66c31",
|
||||||
"ac3d88c299e304faf76cda2b305510d5a6c19c06319e53ee54fdc18b028a860f": "705d20d39a910d58a070fb86780ac58c8007591c",
|
"ac3d88c299e304faf76cda2b305510d5a6c19c06319e53ee54fdc18b028a860f": "705d20d39a910d58a070fb86780ac58c8007591c",
|
||||||
"2d765ffb2bef04082138e29e8ba7f7cc1c788c6415226d1aab1b7eb5bbd0431f": "276d984150c2fd2fb5ba287f2e5ceb7262dc5a95",
|
"2d765ffb2bef04082138e29e8ba7f7cc1c788c6415226d1aab1b7eb5bbd0431f": "276d984150c2fd2fb5ba287f2e5ceb7262dc5a95",
|
||||||
@@ -303192,6 +303288,7 @@
|
|||||||
"2e6883d4304c5de25c464a9fd97fcd2f642d8d2af2db9c2b13882e67f0f35b93": "808b7b46bfb033c7fe4d94d0dbf5a271de30dedc",
|
"2e6883d4304c5de25c464a9fd97fcd2f642d8d2af2db9c2b13882e67f0f35b93": "808b7b46bfb033c7fe4d94d0dbf5a271de30dedc",
|
||||||
"2db47ffca9510468e37717266838576f96dccd9ed6b7f00c0a721c9868338b19": "a848aad1840c7d85bfcdd552521a7181b00331cc",
|
"2db47ffca9510468e37717266838576f96dccd9ed6b7f00c0a721c9868338b19": "a848aad1840c7d85bfcdd552521a7181b00331cc",
|
||||||
"27ab08e76e08fa8a1023b076f3e6c23bc6841732658942f30cea634394f40729": "9b2704655a893ff1675d28df4bd9ffc9e15d3dd5",
|
"27ab08e76e08fa8a1023b076f3e6c23bc6841732658942f30cea634394f40729": "9b2704655a893ff1675d28df4bd9ffc9e15d3dd5",
|
||||||
|
"db3c84c36729bd6a321566fae796f2ef3645f9889188f2af7e223d5f42d46755": "373792e3bc3c89226bff1852dca3eaf5a8ae4a80",
|
||||||
"deb7b69072b63f2916e74a44a8dc80d668f8010081009b667562f9f48203faae": "407f0fde89ac1a628fed0329072dfae1f8d304b1",
|
"deb7b69072b63f2916e74a44a8dc80d668f8010081009b667562f9f48203faae": "407f0fde89ac1a628fed0329072dfae1f8d304b1",
|
||||||
"75093b914808931350c0ae0daa120ce1c12493cb4927a7e3698c8e492525a6ec": "f0168fbad73f8c0589c070f3e2071c1d62967a40",
|
"75093b914808931350c0ae0daa120ce1c12493cb4927a7e3698c8e492525a6ec": "f0168fbad73f8c0589c070f3e2071c1d62967a40",
|
||||||
"10636657cd2e9b76da55e041007f9b34e08daa99affd932ef2d9a88ae0e7b1e9": "e17ecabba25dc797bf7a0e9979b18e5189e410e6",
|
"10636657cd2e9b76da55e041007f9b34e08daa99affd932ef2d9a88ae0e7b1e9": "e17ecabba25dc797bf7a0e9979b18e5189e410e6",
|
||||||
@@ -305099,6 +305196,7 @@
|
|||||||
"6d79dba0ad101cdaf3311dc70c94ecd163856236b6883cb242ed2cf4e07bfc80": "b41150f8cff409dde89939b80a980a20e853df0b",
|
"6d79dba0ad101cdaf3311dc70c94ecd163856236b6883cb242ed2cf4e07bfc80": "b41150f8cff409dde89939b80a980a20e853df0b",
|
||||||
"400e334a2b49b14a9f585f3305d8e0f9a14c80e71680818abc9844efb91c92dc": "6c38cc6fba14863bfbdc730e53d3274fd79a4544",
|
"400e334a2b49b14a9f585f3305d8e0f9a14c80e71680818abc9844efb91c92dc": "6c38cc6fba14863bfbdc730e53d3274fd79a4544",
|
||||||
"c128994d60990b4ecff6840c9f0117be52cec93fd3a1e3329e791ed25c1cb900": "cb6be8206308470ecaa8b9cc882d82abe0ef3562",
|
"c128994d60990b4ecff6840c9f0117be52cec93fd3a1e3329e791ed25c1cb900": "cb6be8206308470ecaa8b9cc882d82abe0ef3562",
|
||||||
|
"a73b9f1c37d64532bf82d1d790a3e0cb81a9b2accb04d866901bb7ae1970473a": "681e3362f0251d2e26e6bc2e06157c027f5f3116",
|
||||||
"b4bbb22c62ce21e0447d74f3e6fd966b06c6ee16b629395021ea9f3d299f652a": "bc283d9ad8ad3bb99b632ca5b194a5bf27df1c22",
|
"b4bbb22c62ce21e0447d74f3e6fd966b06c6ee16b629395021ea9f3d299f652a": "bc283d9ad8ad3bb99b632ca5b194a5bf27df1c22",
|
||||||
"59ca6249806f63805df4dddf997e7405b9d2e542ba71a9dad7d4fd6ee7d1bf2c": "0aaf20633888d2d0a2da38e82773cb5d44e840eb",
|
"59ca6249806f63805df4dddf997e7405b9d2e542ba71a9dad7d4fd6ee7d1bf2c": "0aaf20633888d2d0a2da38e82773cb5d44e840eb",
|
||||||
"5d0a70211529b39745b6bfb7e9abf7cfa8fe7a61bf7882acdf712812aa9b2e70": "d27da38e4d37727a44bf04d7690a09135fad7115",
|
"5d0a70211529b39745b6bfb7e9abf7cfa8fe7a61bf7882acdf712812aa9b2e70": "d27da38e4d37727a44bf04d7690a09135fad7115",
|
||||||
@@ -314459,24 +314557,6 @@
|
|||||||
"switch/firmare installation.txt": [
|
"switch/firmare installation.txt": [
|
||||||
"05b91dc8dd6fdbbf982388fd0673073363be9ce6"
|
"05b91dc8dd6fdbbf982388fd0673073363be9ce6"
|
||||||
],
|
],
|
||||||
"Arcade/Arcade/switch/prod.keys": [
|
|
||||||
"d6cedce971a2a4ab2588e39285a9d2fb37b62924"
|
|
||||||
],
|
|
||||||
"Arcade/switch/prod.keys": [
|
|
||||||
"d6cedce971a2a4ab2588e39285a9d2fb37b62924"
|
|
||||||
],
|
|
||||||
"switch/prod.keys": [
|
|
||||||
"d6cedce971a2a4ab2588e39285a9d2fb37b62924"
|
|
||||||
],
|
|
||||||
"Arcade/Arcade/switch/title.keys": [
|
|
||||||
"8e6c2873d671f482d3fe0f7b96e18e0f84d02c10"
|
|
||||||
],
|
|
||||||
"Arcade/switch/title.keys": [
|
|
||||||
"8e6c2873d671f482d3fe0f7b96e18e0f84d02c10"
|
|
||||||
],
|
|
||||||
"switch/title.keys": [
|
|
||||||
"8e6c2873d671f482d3fe0f7b96e18e0f84d02c10"
|
|
||||||
],
|
|
||||||
"Arcade/Arcade/sx1541.zip": [
|
"Arcade/Arcade/sx1541.zip": [
|
||||||
"1f424e6329bc1dfc254a60b752e0c2ad1d1f6126"
|
"1f424e6329bc1dfc254a60b752e0c2ad1d1f6126"
|
||||||
],
|
],
|
||||||
@@ -466549,6 +466629,24 @@
|
|||||||
"Super Game Boy/sgb_boot.bin": [
|
"Super Game Boy/sgb_boot.bin": [
|
||||||
"369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de"
|
"369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de"
|
||||||
],
|
],
|
||||||
|
"Nintendo/Switch/.variants/prod.keys.d6cedce9": [
|
||||||
|
"d6cedce971a2a4ab2588e39285a9d2fb37b62924"
|
||||||
|
],
|
||||||
|
"Switch/.variants/prod.keys.d6cedce9": [
|
||||||
|
"d6cedce971a2a4ab2588e39285a9d2fb37b62924"
|
||||||
|
],
|
||||||
|
".variants/prod.keys.d6cedce9": [
|
||||||
|
"d6cedce971a2a4ab2588e39285a9d2fb37b62924"
|
||||||
|
],
|
||||||
|
"Nintendo/Switch/.variants/title.keys.8e6c2873": [
|
||||||
|
"8e6c2873d671f482d3fe0f7b96e18e0f84d02c10"
|
||||||
|
],
|
||||||
|
"Switch/.variants/title.keys.8e6c2873": [
|
||||||
|
"8e6c2873d671f482d3fe0f7b96e18e0f84d02c10"
|
||||||
|
],
|
||||||
|
".variants/title.keys.8e6c2873": [
|
||||||
|
"8e6c2873d671f482d3fe0f7b96e18e0f84d02c10"
|
||||||
|
],
|
||||||
"Nintendo/Switch/Firmware_batocera43.zip": [
|
"Nintendo/Switch/Firmware_batocera43.zip": [
|
||||||
"9d4488178fe93d053d44f836e849d3d6b3e5974e"
|
"9d4488178fe93d053d44f836e849d3d6b3e5974e"
|
||||||
],
|
],
|
||||||
@@ -472917,6 +473015,42 @@
|
|||||||
"emucorex/pcsx2/resources/fonts/NotoSansHebrew-Regular.ttf": [
|
"emucorex/pcsx2/resources/fonts/NotoSansHebrew-Regular.ttf": [
|
||||||
"c549a2088bc9b678c8c79fa987ac5f4e6ff4772c"
|
"c549a2088bc9b678c8c79fa987ac5f4e6ff4772c"
|
||||||
],
|
],
|
||||||
|
"Other/emucorex/resources/GameIndex.yaml": [
|
||||||
|
"2d9878fdd70979ecf6dd0da7652efd3797c26039"
|
||||||
|
],
|
||||||
|
"emucorex/resources/GameIndex.yaml": [
|
||||||
|
"2d9878fdd70979ecf6dd0da7652efd3797c26039"
|
||||||
|
],
|
||||||
|
"resources/GameIndex.yaml": [
|
||||||
|
"2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||||
|
"40ecf6138c99a0aba775ef93240b295025a45500"
|
||||||
|
],
|
||||||
|
"Other/emucorex/resources/patches.zip": [
|
||||||
|
"a3d939af27fe0929e61b8e20d49ef58316c0a66c"
|
||||||
|
],
|
||||||
|
"emucorex/resources/patches.zip": [
|
||||||
|
"a3d939af27fe0929e61b8e20d49ef58316c0a66c"
|
||||||
|
],
|
||||||
|
"resources/patches.zip": [
|
||||||
|
"a3d939af27fe0929e61b8e20d49ef58316c0a66c"
|
||||||
|
],
|
||||||
|
"Other/emucorex/resources/shaders/common/fxaa.fx": [
|
||||||
|
"81c56a54bde692fb15fca4efe6db508b7d09bf6e"
|
||||||
|
],
|
||||||
|
"emucorex/resources/shaders/common/fxaa.fx": [
|
||||||
|
"81c56a54bde692fb15fca4efe6db508b7d09bf6e"
|
||||||
|
],
|
||||||
|
"resources/shaders/common/fxaa.fx": [
|
||||||
|
"81c56a54bde692fb15fca4efe6db508b7d09bf6e"
|
||||||
|
],
|
||||||
|
"shaders/common/fxaa.fx": [
|
||||||
|
"81c56a54bde692fb15fca4efe6db508b7d09bf6e",
|
||||||
|
"3ac5f68049ed00d8ff3d2811bd09ef0c036efd0a"
|
||||||
|
],
|
||||||
|
"common/fxaa.fx": [
|
||||||
|
"81c56a54bde692fb15fca4efe6db508b7d09bf6e",
|
||||||
|
"3ac5f68049ed00d8ff3d2811bd09ef0c036efd0a"
|
||||||
|
],
|
||||||
"Other/emucorex/resources/shaders/opengl/cas.glsl": [
|
"Other/emucorex/resources/shaders/opengl/cas.glsl": [
|
||||||
"cb0d4276f9947762af0841a8af75efa6cbe9109c"
|
"cb0d4276f9947762af0841a8af75efa6cbe9109c"
|
||||||
],
|
],
|
||||||
@@ -472959,6 +473093,46 @@
|
|||||||
"emucorex/resources/shaders/opengl/shadeboost.glsl": [
|
"emucorex/resources/shaders/opengl/shadeboost.glsl": [
|
||||||
"03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564"
|
"03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564"
|
||||||
],
|
],
|
||||||
|
"Other/emucorex/resources/shaders/opengl/tfx_fs.glsl": [
|
||||||
|
"f1c97200cb5b571a1ed6d33b5a2f854cebbfb528"
|
||||||
|
],
|
||||||
|
"emucorex/resources/shaders/opengl/tfx_fs.glsl": [
|
||||||
|
"f1c97200cb5b571a1ed6d33b5a2f854cebbfb528"
|
||||||
|
],
|
||||||
|
"resources/shaders/opengl/tfx_fs.glsl": [
|
||||||
|
"f1c97200cb5b571a1ed6d33b5a2f854cebbfb528"
|
||||||
|
],
|
||||||
|
"shaders/opengl/tfx_fs.glsl": [
|
||||||
|
"f1c97200cb5b571a1ed6d33b5a2f854cebbfb528",
|
||||||
|
"00eb0418e7a25c89aaf7c526922fe45d83243ec5",
|
||||||
|
"919c3308c8850852d529d0127ce94e873375bfd2",
|
||||||
|
"554160d31bb97aca0e9f367f4de811dd8ad1fb21"
|
||||||
|
],
|
||||||
|
"opengl/tfx_fs.glsl": [
|
||||||
|
"f1c97200cb5b571a1ed6d33b5a2f854cebbfb528",
|
||||||
|
"00eb0418e7a25c89aaf7c526922fe45d83243ec5",
|
||||||
|
"919c3308c8850852d529d0127ce94e873375bfd2",
|
||||||
|
"554160d31bb97aca0e9f367f4de811dd8ad1fb21"
|
||||||
|
],
|
||||||
|
"Other/emucorex/resources/shaders/opengl/tfx_vgs.glsl": [
|
||||||
|
"cb02fa6503183c313aa8d7c278bb9fbfe3d1a943"
|
||||||
|
],
|
||||||
|
"emucorex/resources/shaders/opengl/tfx_vgs.glsl": [
|
||||||
|
"cb02fa6503183c313aa8d7c278bb9fbfe3d1a943"
|
||||||
|
],
|
||||||
|
"resources/shaders/opengl/tfx_vgs.glsl": [
|
||||||
|
"cb02fa6503183c313aa8d7c278bb9fbfe3d1a943"
|
||||||
|
],
|
||||||
|
"shaders/opengl/tfx_vgs.glsl": [
|
||||||
|
"cb02fa6503183c313aa8d7c278bb9fbfe3d1a943",
|
||||||
|
"0e9e98f766c9250b465af776d0077f49f797fed2",
|
||||||
|
"fc40fc49de1cd8ed7d60df0394dd6367ae82b85f"
|
||||||
|
],
|
||||||
|
"opengl/tfx_vgs.glsl": [
|
||||||
|
"cb02fa6503183c313aa8d7c278bb9fbfe3d1a943",
|
||||||
|
"0e9e98f766c9250b465af776d0077f49f797fed2",
|
||||||
|
"fc40fc49de1cd8ed7d60df0394dd6367ae82b85f"
|
||||||
|
],
|
||||||
"Other/emucorex/resources/shaders/vulkan/cas.glsl": [
|
"Other/emucorex/resources/shaders/vulkan/cas.glsl": [
|
||||||
"005cecf1a5e20b05407d55d0aad22cf81ad66c31"
|
"005cecf1a5e20b05407d55d0aad22cf81ad66c31"
|
||||||
],
|
],
|
||||||
@@ -473012,6 +473186,29 @@
|
|||||||
"emucorex/resources/shaders/vulkan/shadeboost.glsl": [
|
"emucorex/resources/shaders/vulkan/shadeboost.glsl": [
|
||||||
"9b2704655a893ff1675d28df4bd9ffc9e15d3dd5"
|
"9b2704655a893ff1675d28df4bd9ffc9e15d3dd5"
|
||||||
],
|
],
|
||||||
|
"Other/emucorex/resources/shaders/vulkan/tfx.glsl": [
|
||||||
|
"373792e3bc3c89226bff1852dca3eaf5a8ae4a80"
|
||||||
|
],
|
||||||
|
"emucorex/resources/shaders/vulkan/tfx.glsl": [
|
||||||
|
"373792e3bc3c89226bff1852dca3eaf5a8ae4a80"
|
||||||
|
],
|
||||||
|
"resources/shaders/vulkan/tfx.glsl": [
|
||||||
|
"373792e3bc3c89226bff1852dca3eaf5a8ae4a80"
|
||||||
|
],
|
||||||
|
"shaders/vulkan/tfx.glsl": [
|
||||||
|
"373792e3bc3c89226bff1852dca3eaf5a8ae4a80",
|
||||||
|
"aa06cffa086126bb4972674562a5f5c8dc6082f3",
|
||||||
|
"1c3a966a42cefcb9c26ce2fa77e4cd90f2d50329",
|
||||||
|
"b4ebf3f7f20fc88f91cda033552a7489a9ff179c",
|
||||||
|
"0a16f66d7816d73af3b28dea2bb292415695f6f7"
|
||||||
|
],
|
||||||
|
"vulkan/tfx.glsl": [
|
||||||
|
"373792e3bc3c89226bff1852dca3eaf5a8ae4a80",
|
||||||
|
"aa06cffa086126bb4972674562a5f5c8dc6082f3",
|
||||||
|
"1c3a966a42cefcb9c26ce2fa77e4cd90f2d50329",
|
||||||
|
"b4ebf3f7f20fc88f91cda033552a7489a9ff179c",
|
||||||
|
"0a16f66d7816d73af3b28dea2bb292415695f6f7"
|
||||||
|
],
|
||||||
"Other/epsxe/.variants/libopenglplugin.so.0e506076": [
|
"Other/epsxe/.variants/libopenglplugin.so.0e506076": [
|
||||||
"407f0fde89ac1a628fed0329072dfae1f8d304b1"
|
"407f0fde89ac1a628fed0329072dfae1f8d304b1"
|
||||||
],
|
],
|
||||||
@@ -477236,12 +477433,6 @@
|
|||||||
"assets/shaders/common/fxaa.fx": [
|
"assets/shaders/common/fxaa.fx": [
|
||||||
"3ac5f68049ed00d8ff3d2811bd09ef0c036efd0a"
|
"3ac5f68049ed00d8ff3d2811bd09ef0c036efd0a"
|
||||||
],
|
],
|
||||||
"shaders/common/fxaa.fx": [
|
|
||||||
"3ac5f68049ed00d8ff3d2811bd09ef0c036efd0a"
|
|
||||||
],
|
|
||||||
"common/fxaa.fx": [
|
|
||||||
"3ac5f68049ed00d8ff3d2811bd09ef0c036efd0a"
|
|
||||||
],
|
|
||||||
"Other/nethersx2/assets/shaders/opengl/cas.glsl": [
|
"Other/nethersx2/assets/shaders/opengl/cas.glsl": [
|
||||||
"1a69a0fc522caff98fc3829ebf3f12a6a006a058"
|
"1a69a0fc522caff98fc3829ebf3f12a6a006a058"
|
||||||
],
|
],
|
||||||
@@ -477325,16 +477516,6 @@
|
|||||||
"00eb0418e7a25c89aaf7c526922fe45d83243ec5",
|
"00eb0418e7a25c89aaf7c526922fe45d83243ec5",
|
||||||
"919c3308c8850852d529d0127ce94e873375bfd2"
|
"919c3308c8850852d529d0127ce94e873375bfd2"
|
||||||
],
|
],
|
||||||
"shaders/opengl/tfx_fs.glsl": [
|
|
||||||
"00eb0418e7a25c89aaf7c526922fe45d83243ec5",
|
|
||||||
"919c3308c8850852d529d0127ce94e873375bfd2",
|
|
||||||
"554160d31bb97aca0e9f367f4de811dd8ad1fb21"
|
|
||||||
],
|
|
||||||
"opengl/tfx_fs.glsl": [
|
|
||||||
"00eb0418e7a25c89aaf7c526922fe45d83243ec5",
|
|
||||||
"919c3308c8850852d529d0127ce94e873375bfd2",
|
|
||||||
"554160d31bb97aca0e9f367f4de811dd8ad1fb21"
|
|
||||||
],
|
|
||||||
"Other/nethersx2/assets/shaders/opengl/tfx_vgs.glsl": [
|
"Other/nethersx2/assets/shaders/opengl/tfx_vgs.glsl": [
|
||||||
"0e9e98f766c9250b465af776d0077f49f797fed2"
|
"0e9e98f766c9250b465af776d0077f49f797fed2"
|
||||||
],
|
],
|
||||||
@@ -477344,14 +477525,6 @@
|
|||||||
"assets/shaders/opengl/tfx_vgs.glsl": [
|
"assets/shaders/opengl/tfx_vgs.glsl": [
|
||||||
"0e9e98f766c9250b465af776d0077f49f797fed2"
|
"0e9e98f766c9250b465af776d0077f49f797fed2"
|
||||||
],
|
],
|
||||||
"shaders/opengl/tfx_vgs.glsl": [
|
|
||||||
"0e9e98f766c9250b465af776d0077f49f797fed2",
|
|
||||||
"fc40fc49de1cd8ed7d60df0394dd6367ae82b85f"
|
|
||||||
],
|
|
||||||
"opengl/tfx_vgs.glsl": [
|
|
||||||
"0e9e98f766c9250b465af776d0077f49f797fed2",
|
|
||||||
"fc40fc49de1cd8ed7d60df0394dd6367ae82b85f"
|
|
||||||
],
|
|
||||||
"Other/nethersx2/assets/shaders/vulkan/cas.glsl": [
|
"Other/nethersx2/assets/shaders/vulkan/cas.glsl": [
|
||||||
"709fd5393188f7cff0fe4230d0e3e8b69a82f5b9"
|
"709fd5393188f7cff0fe4230d0e3e8b69a82f5b9"
|
||||||
],
|
],
|
||||||
@@ -477420,18 +477593,6 @@
|
|||||||
"1c3a966a42cefcb9c26ce2fa77e4cd90f2d50329",
|
"1c3a966a42cefcb9c26ce2fa77e4cd90f2d50329",
|
||||||
"b4ebf3f7f20fc88f91cda033552a7489a9ff179c"
|
"b4ebf3f7f20fc88f91cda033552a7489a9ff179c"
|
||||||
],
|
],
|
||||||
"shaders/vulkan/tfx.glsl": [
|
|
||||||
"aa06cffa086126bb4972674562a5f5c8dc6082f3",
|
|
||||||
"1c3a966a42cefcb9c26ce2fa77e4cd90f2d50329",
|
|
||||||
"b4ebf3f7f20fc88f91cda033552a7489a9ff179c",
|
|
||||||
"0a16f66d7816d73af3b28dea2bb292415695f6f7"
|
|
||||||
],
|
|
||||||
"vulkan/tfx.glsl": [
|
|
||||||
"aa06cffa086126bb4972674562a5f5c8dc6082f3",
|
|
||||||
"1c3a966a42cefcb9c26ce2fa77e4cd90f2d50329",
|
|
||||||
"b4ebf3f7f20fc88f91cda033552a7489a9ff179c",
|
|
||||||
"0a16f66d7816d73af3b28dea2bb292415695f6f7"
|
|
||||||
],
|
|
||||||
"Other/nethersx2/assets/sounds/achievements/unlock.wav": [
|
"Other/nethersx2/assets/sounds/achievements/unlock.wav": [
|
||||||
"eb488a76217629f620e40287567d7e2b30337680"
|
"eb488a76217629f620e40287567d7e2b30337680"
|
||||||
],
|
],
|
||||||
@@ -494743,6 +494904,7 @@
|
|||||||
],
|
],
|
||||||
"Config/Games.xml": [
|
"Config/Games.xml": [
|
||||||
"b41150f8cff409dde89939b80a980a20e853df0b",
|
"b41150f8cff409dde89939b80a980a20e853df0b",
|
||||||
|
"681e3362f0251d2e26e6bc2e06157c027f5f3116",
|
||||||
"6871a47de90ca67f9ff8e4e280416851a8164563"
|
"6871a47de90ca67f9ff8e4e280416851a8164563"
|
||||||
],
|
],
|
||||||
"Other/super3/Config/Supermodel.ini": [
|
"Other/super3/Config/Supermodel.ini": [
|
||||||
@@ -494763,6 +494925,12 @@
|
|||||||
"Config/gamecontrollerdb.txt": [
|
"Config/gamecontrollerdb.txt": [
|
||||||
"cb6be8206308470ecaa8b9cc882d82abe0ef3562"
|
"cb6be8206308470ecaa8b9cc882d82abe0ef3562"
|
||||||
],
|
],
|
||||||
|
"Other/supermodel-dojo/Config/Games.xml": [
|
||||||
|
"681e3362f0251d2e26e6bc2e06157c027f5f3116"
|
||||||
|
],
|
||||||
|
"supermodel-dojo/Config/Games.xml": [
|
||||||
|
"681e3362f0251d2e26e6bc2e06157c027f5f3116"
|
||||||
|
],
|
||||||
"Other/swf-player/assets/META-INF/AIR/application.xml": [
|
"Other/swf-player/assets/META-INF/AIR/application.xml": [
|
||||||
"bc283d9ad8ad3bb99b632ca5b194a5bf27df1c22"
|
"bc283d9ad8ad3bb99b632ca5b194a5bf27df1c22"
|
||||||
],
|
],
|
||||||
@@ -544215,9 +544383,6 @@
|
|||||||
"PlayStation 2/resources/GameIndex.yaml": [
|
"PlayStation 2/resources/GameIndex.yaml": [
|
||||||
"40ecf6138c99a0aba775ef93240b295025a45500"
|
"40ecf6138c99a0aba775ef93240b295025a45500"
|
||||||
],
|
],
|
||||||
"resources/GameIndex.yaml": [
|
|
||||||
"40ecf6138c99a0aba775ef93240b295025a45500"
|
|
||||||
],
|
|
||||||
"Sony/PlayStation 2/resources/sounds/achievements/message.wav": [
|
"Sony/PlayStation 2/resources/sounds/achievements/message.wav": [
|
||||||
"c54f04b63ac6f04e0a0944ef260b5497e4494dbd"
|
"c54f04b63ac6f04e0a0944ef260b5497e4494dbd"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ files:
|
|||||||
- name: colecovision.rom
|
- name: colecovision.rom
|
||||||
system: colecovision
|
system: colecovision
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "990bf1956f10207d8781b619eb74f89b00d921c8d45c95c334c16c8cceca09ad"
|
sha256: "990bf1956f10207d8781b619eb74f89b00d921c8d45c95c334c16c8cceca09ad"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "ColecoVision BIOS"
|
description: "ColecoVision BIOS"
|
||||||
@@ -103,7 +102,6 @@ files:
|
|||||||
- name: disksys.rom
|
- name: disksys.rom
|
||||||
system: famicom-disk-system
|
system: famicom-disk-system
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "fdc1a76e654feea993fcb38366e05ee5f4eb641f86fe6bebaeefd412e112dd72"
|
sha256: "fdc1a76e654feea993fcb38366e05ee5f4eb641f86fe6bebaeefd412e112dd72"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Famicom Disk System BIOS"
|
description: "Famicom Disk System BIOS"
|
||||||
@@ -113,7 +111,6 @@ files:
|
|||||||
- name: gba_bios.bin
|
- name: gba_bios.bin
|
||||||
system: game-boy-advance
|
system: game-boy-advance
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570"
|
sha256: "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "GBA BIOS"
|
description: "GBA BIOS"
|
||||||
@@ -123,7 +120,6 @@ files:
|
|||||||
- name: bios.gg
|
- name: bios.gg
|
||||||
system: game-gear
|
system: game-gear
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
sha256: "8c8a21335038285cfa03dc076100c1f0bfadf3e4ff70796f11f3dfaaab60eee2"
|
sha256: "8c8a21335038285cfa03dc076100c1f0bfadf3e4ff70796f11f3dfaaab60eee2"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Game Gear BIOS"
|
description: "Game Gear BIOS"
|
||||||
@@ -134,7 +130,6 @@ files:
|
|||||||
system: master-system
|
system: master-system
|
||||||
region: [north-america]
|
region: [north-america]
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
sha256: "477617917a12a30f9f43844909dc2de6e6a617430f5c9a36306c86414a670d50"
|
sha256: "477617917a12a30f9f43844909dc2de6e6a617430f5c9a36306c86414a670d50"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Master System BIOS (US)"
|
description: "Master System BIOS (US)"
|
||||||
@@ -144,7 +139,6 @@ files:
|
|||||||
system: master-system
|
system: master-system
|
||||||
region: [japan]
|
region: [japan]
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
sha256: "67846e26764bd862f19179294347f7353a4166b62ac4198a5ec32933b7da486e"
|
sha256: "67846e26764bd862f19179294347f7353a4166b62ac4198a5ec32933b7da486e"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Master System BIOS (Japan)"
|
description: "Master System BIOS (Japan)"
|
||||||
@@ -154,7 +148,6 @@ files:
|
|||||||
system: master-system
|
system: master-system
|
||||||
region: [europe]
|
region: [europe]
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
sha256: "477617917a12a30f9f43844909dc2de6e6a617430f5c9a36306c86414a670d50"
|
sha256: "477617917a12a30f9f43844909dc2de6e6a617430f5c9a36306c86414a670d50"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Master System BIOS (Europe), same binary as US"
|
description: "Master System BIOS (Europe), same binary as US"
|
||||||
@@ -166,7 +159,6 @@ files:
|
|||||||
system: mega-cd
|
system: mega-cd
|
||||||
region: [north-america]
|
region: [north-america]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "fb477cdbf94c84424c2feca4fe40656d85393fe7b7b401911b45ad2eb991258c"
|
sha256: "fb477cdbf94c84424c2feca4fe40656d85393fe7b7b401911b45ad2eb991258c"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Mega CD / Sega CD BIOS (US)"
|
description: "Mega CD / Sega CD BIOS (US)"
|
||||||
@@ -176,7 +168,6 @@ files:
|
|||||||
system: mega-cd
|
system: mega-cd
|
||||||
region: [japan]
|
region: [japan]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "7133fc2dd2fe5b7d0acd53a5f10f3d00b5d31270239ad20d74ef32393e24af88"
|
sha256: "7133fc2dd2fe5b7d0acd53a5f10f3d00b5d31270239ad20d74ef32393e24af88"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Mega CD BIOS (Japan)"
|
description: "Mega CD BIOS (Japan)"
|
||||||
@@ -186,7 +177,6 @@ files:
|
|||||||
system: mega-cd
|
system: mega-cd
|
||||||
region: [europe]
|
region: [europe]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "fe608a2a07676a23ab5fd5eee2f53c9e2526d69a28aa16ccd85c0ec42e6933cb"
|
sha256: "fe608a2a07676a23ab5fd5eee2f53c9e2526d69a28aa16ccd85c0ec42e6933cb"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Mega CD BIOS (Europe)"
|
description: "Mega CD BIOS (Europe)"
|
||||||
@@ -197,7 +187,6 @@ files:
|
|||||||
system: mega-ld
|
system: mega-ld
|
||||||
region: [north-america]
|
region: [north-america]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "e89b5a319f66406611ec82fe5c4aa6827c175a05135bd7bd177366cba0465021"
|
sha256: "e89b5a319f66406611ec82fe5c4aa6827c175a05135bd7bd177366cba0465021"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "LaserActive SEGA PAC BIOS (US), v1.04"
|
description: "LaserActive SEGA PAC BIOS (US), v1.04"
|
||||||
@@ -207,7 +196,6 @@ files:
|
|||||||
system: mega-ld
|
system: mega-ld
|
||||||
region: [japan]
|
region: [japan]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "dca942d977217f703d8d1c6eb1aeb6b32c78ecc421486bbb46c459d385161c94"
|
sha256: "dca942d977217f703d8d1c6eb1aeb6b32c78ecc421486bbb46c459d385161c94"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "LaserActive SEGA PAC BIOS (Japan), v1.02"
|
description: "LaserActive SEGA PAC BIOS (Japan), v1.02"
|
||||||
@@ -217,7 +205,6 @@ files:
|
|||||||
- name: MSX.ROM
|
- name: MSX.ROM
|
||||||
system: msx
|
system: msx
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "413a2b601a94b3792e054be2439cc77a1819cceadbfa9542f88d51c7480f2ef0"
|
sha256: "413a2b601a94b3792e054be2439cc77a1819cceadbfa9542f88d51c7480f2ef0"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "MSX BIOS ROM (Japan)"
|
description: "MSX BIOS ROM (Japan)"
|
||||||
@@ -227,7 +214,6 @@ files:
|
|||||||
- name: MSX2.ROM
|
- name: MSX2.ROM
|
||||||
system: msx2
|
system: msx2
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "0c672d86ead61a97f49a583b88b7c1905da120645cd44f0c9f2baf4f4631e0b1"
|
sha256: "0c672d86ead61a97f49a583b88b7c1905da120645cd44f0c9f2baf4f4631e0b1"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "MSX2 main BIOS ROM (Japan)"
|
description: "MSX2 main BIOS ROM (Japan)"
|
||||||
@@ -236,7 +222,6 @@ files:
|
|||||||
- name: MSX2EXT.ROM
|
- name: MSX2EXT.ROM
|
||||||
system: msx2
|
system: msx2
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "6c6f421a10c428d960b7ecc990f99af1c638147f747bddca7b0bf0e2ab738300"
|
sha256: "6c6f421a10c428d960b7ecc990f99af1c638147f747bddca7b0bf0e2ab738300"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "MSX2 sub ROM (Japan)"
|
description: "MSX2 sub ROM (Japan)"
|
||||||
@@ -246,7 +231,6 @@ files:
|
|||||||
- name: neo-epo.bin
|
- name: neo-epo.bin
|
||||||
system: neo-geo
|
system: neo-geo
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
description: "Neo Geo AES BIOS. Accepts neogeo.zip (extracts neo-epo.bin) or direct file. Byte-swapped on load."
|
description: "Neo Geo AES BIOS. Accepts neogeo.zip (extracts neo-epo.bin) or direct file. Byte-swapped on load."
|
||||||
source_ref: "desktop-ui/emulator/neo-geo-aes.cpp:14, mia/system/neo-geo-aes.cpp:16"
|
source_ref: "desktop-ui/emulator/neo-geo-aes.cpp:14, mia/system/neo-geo-aes.cpp:16"
|
||||||
|
|
||||||
@@ -254,7 +238,6 @@ files:
|
|||||||
- name: sp-45.sp1
|
- name: sp-45.sp1
|
||||||
system: neo-geo
|
system: neo-geo
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
description: "Neo Geo MVS BIOS. Accepts neogeo.zip (extracts sp-45.sp1) or direct file. Byte-swapped on load."
|
description: "Neo Geo MVS BIOS. Accepts neogeo.zip (extracts sp-45.sp1) or direct file. Byte-swapped on load."
|
||||||
source_ref: "desktop-ui/emulator/neo-geo-mvs.cpp:15, mia/system/neo-geo-mvs.cpp:16"
|
source_ref: "desktop-ui/emulator/neo-geo-mvs.cpp:15, mia/system/neo-geo-mvs.cpp:16"
|
||||||
|
|
||||||
@@ -262,7 +245,6 @@ files:
|
|||||||
- name: ngp_bios.rom
|
- name: ngp_bios.rom
|
||||||
system: neo-geo-pocket
|
system: neo-geo-pocket
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "0293555b21c4fac516d25199df7809b26beeae150e1d4504a050db32264a6ad7"
|
sha256: "0293555b21c4fac516d25199df7809b26beeae150e1d4504a050db32264a6ad7"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Neo Geo Pocket BIOS"
|
description: "Neo Geo Pocket BIOS"
|
||||||
@@ -272,7 +254,6 @@ files:
|
|||||||
- name: ngpc_bios.rom
|
- name: ngpc_bios.rom
|
||||||
system: neo-geo-pocket-color
|
system: neo-geo-pocket-color
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "8fb845a2f71514cec20728e2f0fecfade69444f8d50898b92c2259f1ba63e10d"
|
sha256: "8fb845a2f71514cec20728e2f0fecfade69444f8d50898b92c2259f1ba63e10d"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Neo Geo Pocket Color BIOS"
|
description: "Neo Geo Pocket Color BIOS"
|
||||||
@@ -283,7 +264,6 @@ files:
|
|||||||
system: nintendo-64dd
|
system: nintendo-64dd
|
||||||
region: [japan]
|
region: [japan]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "806400ec0df94b0755de6c5b8249d6b6a9866124c5ddbdac198bde22499bfb8b"
|
sha256: "806400ec0df94b0755de6c5b8249d6b6a9866124c5ddbdac198bde22499bfb8b"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Nintendo 64DD IPL ROM (Japan retail)"
|
description: "Nintendo 64DD IPL ROM (Japan retail)"
|
||||||
@@ -293,7 +273,6 @@ files:
|
|||||||
system: nintendo-64dd
|
system: nintendo-64dd
|
||||||
region: [north-america]
|
region: [north-america]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "e9fec87a45fba02399e88064b9e2f8cf0f2106e351c58279a87f05da5bc984ad"
|
sha256: "e9fec87a45fba02399e88064b9e2f8cf0f2106e351c58279a87f05da5bc984ad"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Nintendo 64DD IPL ROM (US dev kit)"
|
description: "Nintendo 64DD IPL ROM (US dev kit)"
|
||||||
@@ -302,7 +281,6 @@ files:
|
|||||||
- name: 64dd_ipl_DEV.bin
|
- name: 64dd_ipl_DEV.bin
|
||||||
system: nintendo-64dd
|
system: nintendo-64dd
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "9c2962a8b994a29e4cd04b3a6e4ed730a751414655ab6a9799ebf5fc08b79d44"
|
sha256: "9c2962a8b994a29e4cd04b3a6e4ed730a751414655ab6a9799ebf5fc08b79d44"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Nintendo 64DD IPL ROM (development)"
|
description: "Nintendo 64DD IPL ROM (development)"
|
||||||
@@ -312,7 +290,6 @@ files:
|
|||||||
- name: syscard1.pce
|
- name: syscard1.pce
|
||||||
system: pc-engine-cd
|
system: pc-engine-cd
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "afe9f27f91ac918348555b86298b4f984643eafa2773196f2c5441ea84f0c3bb"
|
sha256: "afe9f27f91ac918348555b86298b4f984643eafa2773196f2c5441ea84f0c3bb"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "PC Engine CD-ROM2 System Card v1.0 (Japan). Also used by PC Engine LD."
|
description: "PC Engine CD-ROM2 System Card v1.0 (Japan). Also used by PC Engine LD."
|
||||||
@@ -321,7 +298,6 @@ files:
|
|||||||
- name: syscard3.pce
|
- name: syscard3.pce
|
||||||
system: pc-engine-cd
|
system: pc-engine-cd
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce"
|
sha256: "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Super CD-ROM2 System Card v3.0 (Japan). Also used by SuperGrafx CD as Arcade Card."
|
description: "Super CD-ROM2 System Card v3.0 (Japan). Also used by SuperGrafx CD as Arcade Card."
|
||||||
@@ -330,7 +306,6 @@ files:
|
|||||||
- name: syscard3u.pce
|
- name: syscard3u.pce
|
||||||
system: pc-engine-cd
|
system: pc-engine-cd
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "cadac2725711b3c442bcf237b02f5a5210c96f17625c35fa58f009e0ed39e4db"
|
sha256: "cadac2725711b3c442bcf237b02f5a5210c96f17625c35fa58f009e0ed39e4db"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "TurboGrafx-CD Super System Card v3.0 (US)"
|
description: "TurboGrafx-CD Super System Card v3.0 (US)"
|
||||||
@@ -339,7 +314,6 @@ files:
|
|||||||
- name: games_express.pce
|
- name: games_express.pce
|
||||||
system: pc-engine-cd
|
system: pc-engine-cd
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d"
|
sha256: "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "Games Express CD Card (Japan). Also used by PC Engine LD."
|
description: "Games Express CD Card (Japan). Also used by PC Engine LD."
|
||||||
@@ -349,7 +323,6 @@ files:
|
|||||||
- name: pac-n10.pce
|
- name: pac-n10.pce
|
||||||
system: pc-engine-ld
|
system: pc-engine-ld
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "0e87a3385a27b3a4cac51934819b7eefa5b3d690768d2495633838488cd0e2e4"
|
sha256: "0e87a3385a27b3a4cac51934819b7eefa5b3d690768d2495633838488cd0e2e4"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "NEC PAC-N10 LaserActive module (US)"
|
description: "NEC PAC-N10 LaserActive module (US)"
|
||||||
@@ -358,7 +331,6 @@ files:
|
|||||||
- name: pac-n1.pce
|
- name: pac-n1.pce
|
||||||
system: pc-engine-ld
|
system: pc-engine-ld
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "459325690a458baebd77495c91e37c4dddfdd542ba13a821ce954e5bb245627f"
|
sha256: "459325690a458baebd77495c91e37c4dddfdd542ba13a821ce954e5bb245627f"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "NEC PAC-N1 LaserActive module (Japan)"
|
description: "NEC PAC-N1 LaserActive module (Japan)"
|
||||||
@@ -367,7 +339,6 @@ files:
|
|||||||
- name: pce-lp1.pce
|
- name: pce-lp1.pce
|
||||||
system: pc-engine-ld
|
system: pc-engine-ld
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "3f43b3b577117d84002e99cb0baeb97b0d65b1d70b4adadc68817185c6a687f0"
|
sha256: "3f43b3b577117d84002e99cb0baeb97b0d65b1d70b4adadc68817185c6a687f0"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "NEC PCE-LP1 LaserActive module (Japan)"
|
description: "NEC PCE-LP1 LaserActive module (Japan)"
|
||||||
@@ -377,7 +348,6 @@ files:
|
|||||||
- name: scph5501.bin
|
- name: scph5501.bin
|
||||||
system: sony-playstation
|
system: sony-playstation
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "11052b6499e466bbf0a709b1f9cb6834a9418e66680387912451e971cf8a1fef"
|
sha256: "11052b6499e466bbf0a709b1f9cb6834a9418e66680387912451e971cf8a1fef"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "PlayStation BIOS (US)"
|
description: "PlayStation BIOS (US)"
|
||||||
@@ -386,7 +356,6 @@ files:
|
|||||||
- name: scph5500.bin
|
- name: scph5500.bin
|
||||||
system: sony-playstation
|
system: sony-playstation
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "9c0421858e217805f4abe18698afea8d5aa36ff0727eb8484944e00eb5e7eadb"
|
sha256: "9c0421858e217805f4abe18698afea8d5aa36ff0727eb8484944e00eb5e7eadb"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "PlayStation BIOS (Japan)"
|
description: "PlayStation BIOS (Japan)"
|
||||||
@@ -395,7 +364,6 @@ files:
|
|||||||
- name: scph5502.bin
|
- name: scph5502.bin
|
||||||
system: sony-playstation
|
system: sony-playstation
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
sha256: "1faaa18fa820a0225e488d9f086296b8e6c46df739666093987ff7d8fd352c09"
|
sha256: "1faaa18fa820a0225e488d9f086296b8e6c46df739666093987ff7d8fd352c09"
|
||||||
validation: [sha256]
|
validation: [sha256]
|
||||||
description: "PlayStation BIOS (Europe)"
|
description: "PlayStation BIOS (Europe)"
|
||||||
@@ -406,7 +374,6 @@ files:
|
|||||||
system: saturn
|
system: saturn
|
||||||
region: [north-america]
|
region: [north-america]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
description: "Sega Saturn BIOS (US)"
|
description: "Sega Saturn BIOS (US)"
|
||||||
source_ref: "desktop-ui/emulator/saturn.cpp:14"
|
source_ref: "desktop-ui/emulator/saturn.cpp:14"
|
||||||
|
|
||||||
@@ -414,7 +381,6 @@ files:
|
|||||||
system: saturn
|
system: saturn
|
||||||
region: [japan]
|
region: [japan]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
description: "Sega Saturn BIOS (Japan)"
|
description: "Sega Saturn BIOS (Japan)"
|
||||||
source_ref: "desktop-ui/emulator/saturn.cpp:15"
|
source_ref: "desktop-ui/emulator/saturn.cpp:15"
|
||||||
|
|
||||||
@@ -422,7 +388,6 @@ files:
|
|||||||
system: saturn
|
system: saturn
|
||||||
region: [europe]
|
region: [europe]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
description: "Sega Saturn BIOS (Europe)"
|
description: "Sega Saturn BIOS (Europe)"
|
||||||
source_ref: "desktop-ui/emulator/saturn.cpp:16"
|
source_ref: "desktop-ui/emulator/saturn.cpp:16"
|
||||||
|
|
||||||
@@ -430,7 +395,6 @@ files:
|
|||||||
- name: SGB1.sfc
|
- name: SGB1.sfc
|
||||||
system: super-famicom
|
system: super-famicom
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
category: game_data
|
category: game_data
|
||||||
description: "Super Game Boy cartridge ROM. Loaded via CLI as SFC cartridge alongside GB game."
|
description: "Super Game Boy cartridge ROM. Loaded via CLI as SFC cartridge alongside GB game."
|
||||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||||
@@ -438,7 +402,6 @@ files:
|
|||||||
- name: SGB2.sfc
|
- name: SGB2.sfc
|
||||||
system: super-famicom
|
system: super-famicom
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
category: game_data
|
category: game_data
|
||||||
description: "Super Game Boy 2 cartridge ROM. Loaded via CLI as SFC cartridge alongside GB game."
|
description: "Super Game Boy 2 cartridge ROM. Loaded via CLI as SFC cartridge alongside GB game."
|
||||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||||
@@ -446,7 +409,6 @@ files:
|
|||||||
- name: STBIOS.bin
|
- name: STBIOS.bin
|
||||||
system: super-famicom
|
system: super-famicom
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
category: game_data
|
category: game_data
|
||||||
description: "SuFami Turbo adapter BIOS. Loaded via CLI as SFC cartridge alongside SuFami games."
|
description: "SuFami Turbo adapter BIOS. Loaded via CLI as SFC cartridge alongside SuFami games."
|
||||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||||
@@ -454,7 +416,6 @@ files:
|
|||||||
- name: BS-X.bin
|
- name: BS-X.bin
|
||||||
system: super-famicom
|
system: super-famicom
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
category: game_data
|
category: game_data
|
||||||
description: "Satellaview BS-X Town cartridge ROM. Loaded via CLI as SFC cartridge for BS games."
|
description: "Satellaview BS-X Town cartridge ROM. Loaded via CLI as SFC cartridge for BS games."
|
||||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||||
@@ -101,7 +101,6 @@ files:
|
|||||||
|
|
||||||
- name: "psxonpsp660.bin"
|
- name: "psxonpsp660.bin"
|
||||||
description: "PSP embedded PS1 BIOS (region-free override)"
|
description: "PSP embedded PS1 BIOS (region-free override)"
|
||||||
region: [world]
|
|
||||||
required: false
|
required: false
|
||||||
size: 524288
|
size: 524288
|
||||||
sha1: "96880d1ca92a016ff054be5159bb06fe03cb4e14"
|
sha1: "96880d1ca92a016ff054be5159bb06fe03cb4e14"
|
||||||
@@ -114,7 +113,6 @@ files:
|
|||||||
|
|
||||||
- name: "ps1_rom.bin"
|
- name: "ps1_rom.bin"
|
||||||
description: "PS3 embedded PS1 BIOS, first 512KB extracted (region-free override)"
|
description: "PS3 embedded PS1 BIOS, first 512KB extracted (region-free override)"
|
||||||
region: [world]
|
|
||||||
required: false
|
required: false
|
||||||
sha1: "c40146361eb8cf670b19fdc9759190257803cab7"
|
sha1: "c40146361eb8cf670b19fdc9759190257803cab7"
|
||||||
md5: "81bbe60ba7a3d1cea1d48c14cbcc647b"
|
md5: "81bbe60ba7a3d1cea1d48c14cbcc647b"
|
||||||
@@ -126,7 +124,6 @@ files:
|
|||||||
|
|
||||||
- name: "openbios.bin"
|
- name: "openbios.bin"
|
||||||
description: "OpenBIOS (open-source PS1 HLE BIOS, region-free override)"
|
description: "OpenBIOS (open-source PS1 HLE BIOS, region-free override)"
|
||||||
region: [world]
|
|
||||||
required: false
|
required: false
|
||||||
size: 524288
|
size: 524288
|
||||||
mode: libretro
|
mode: libretro
|
||||||
|
|||||||
+6
-5
@@ -14,13 +14,14 @@ notes: >
|
|||||||
Doom 3 source port based on dhewm3 (GPL Doom 3 engine). The core accepts
|
Doom 3 source port based on dhewm3 (GPL Doom 3 engine). The core accepts
|
||||||
.pk4 extensions (retro_get_system_info sets valid_extensions = "pk4").
|
.pk4 extensions (retro_get_system_info sets valid_extensions = "pk4").
|
||||||
Content is loaded directly from the .pk4 file path - the engine extracts
|
Content is loaded directly from the .pk4 file path - the engine extracts
|
||||||
the grandparent directory as fs_basepath (libretro.cpp:1097,1135-1136
|
the grandparent directory as fs_basepath (libretro.cpp:2381,2449-2450
|
||||||
via extract_directory called twice on info->path) and expects the standard
|
via extract_directory called twice on info->path) and expects the standard
|
||||||
Doom 3 directory layout with base/ containing pak000.pk4 through pak008.pk4.
|
Doom 3 directory layout with base/ containing pak000.pk4 through pak008.pk4.
|
||||||
BASE_GAMEDIR is hardcoded to "base" (Licensee.h:58). The boom3_xp variant
|
BASE_GAMEDIR is hardcoded to "base" (Licensee.h:58). One binary serves both
|
||||||
compiles with _D3XP defined, which passes "+set fs_game d3xp" at startup
|
games: content in a directory named d3xp, or the doom_game core option,
|
||||||
(libretro.cpp:538-544) to load the Resurrection of Evil expansion from a
|
passes "+set fs_game d3xp" (libretro.cpp:1635-1643, 2384-2405) to load the
|
||||||
d3xp/ subdirectory alongside base/. The filesystem code (FileSystem.cpp:2125)
|
Resurrection of Evil expansion from a d3xp/ subdirectory alongside base/.
|
||||||
|
The filesystem code (FileSystem.cpp:2207)
|
||||||
scans for .pk4 files in the game directory and loads them in order, with
|
scans for .pk4 files in the game directory and loads them in order, with
|
||||||
higher-numbered paks overriding lower ones. The core requires OpenGL
|
higher-numbered paks overriding lower ones. The core requires OpenGL
|
||||||
(RETRO_ENVIRONMENT_SET_HW_RENDER) and XRGB8888 pixel format. All required
|
(RETRO_ENVIRONMENT_SET_HW_RENDER) and XRGB8888 pixel format. All required
|
||||||
|
|||||||
@@ -25,21 +25,18 @@ files:
|
|||||||
- name: "prod.keys"
|
- name: "prod.keys"
|
||||||
required: true
|
required: true
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:655-656"
|
source_ref: "src/core/crypto/key_manager.cpp:655-656"
|
||||||
|
|
||||||
- name: "title.keys"
|
- name: "title.keys"
|
||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:659-660"
|
source_ref: "src/core/crypto/key_manager.cpp:659-660"
|
||||||
|
|
||||||
- name: "console.keys"
|
- name: "console.keys"
|
||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Console-specific keys (BIS, SD seed)"
|
note: "Console-specific keys (BIS, SD seed)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:661-662"
|
source_ref: "src/core/crypto/key_manager.cpp:661-662"
|
||||||
|
|
||||||
@@ -47,7 +44,6 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
size: 160
|
size: 160
|
||||||
mode: standalone
|
|
||||||
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
||||||
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:274-292"
|
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:274-292"
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ files:
|
|||||||
size: 2097152
|
size: 2097152
|
||||||
crc32: "54c0c963"
|
crc32: "54c0c963"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:58-65"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:58-65"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -75,7 +74,6 @@ files:
|
|||||||
crc32: "79434781"
|
crc32: "79434781"
|
||||||
required: true
|
required: true
|
||||||
aliases: [basic2.rom]
|
aliases: [basic2.rom]
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:78-85"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:78-85"
|
||||||
|
|
||||||
- name: os12.rom
|
- name: os12.rom
|
||||||
@@ -84,7 +82,6 @@ files:
|
|||||||
size: 16384
|
size: 16384
|
||||||
crc32: "3c14fc70"
|
crc32: "3c14fc70"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:436-443"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:436-443"
|
||||||
|
|
||||||
- name: dfs09.rom
|
- name: dfs09.rom
|
||||||
@@ -94,7 +91,6 @@ files:
|
|||||||
crc32: "3ce609cf"
|
crc32: "3ce609cf"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: only when using 8271 disk interface"
|
note: "Conditional: only when using 8271 disk interface"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:444-451"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:444-451"
|
||||||
|
|
||||||
- name: dfs-2.26.rom
|
- name: dfs-2.26.rom
|
||||||
@@ -104,7 +100,6 @@ files:
|
|||||||
crc32: "5ae33e94"
|
crc32: "5ae33e94"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for 1770 DFS or tube processor"
|
note: "Conditional: needed for 1770 DFS or tube processor"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:452-459"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:452-459"
|
||||||
|
|
||||||
- name: adfs-1.30.rom
|
- name: adfs-1.30.rom
|
||||||
@@ -114,7 +109,6 @@ files:
|
|||||||
crc32: "d3855588"
|
crc32: "d3855588"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for ADFS support"
|
note: "Conditional: needed for ADFS support"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:460-467"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:460-467"
|
||||||
|
|
||||||
- name: ADT-1.40.rom
|
- name: ADT-1.40.rom
|
||||||
@@ -124,7 +118,6 @@ files:
|
|||||||
crc32: "8314fed0"
|
crc32: "8314fed0"
|
||||||
required: false
|
required: false
|
||||||
note: "Optional: fetched separately, silently skipped if absent"
|
note: "Optional: fetched separately, silently skipped if absent"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:468-475"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:468-475"
|
||||||
|
|
||||||
- name: TUBE110.rom
|
- name: TUBE110.rom
|
||||||
@@ -134,7 +127,6 @@ files:
|
|||||||
crc32: "9ec2dbd0"
|
crc32: "9ec2dbd0"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for 6502 tube processor"
|
note: "Conditional: needed for 6502 tube processor"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:476-483"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:476-483"
|
||||||
|
|
||||||
- name: Z80_122.rom
|
- name: Z80_122.rom
|
||||||
@@ -144,7 +136,6 @@ files:
|
|||||||
crc32: "b5851efc"
|
crc32: "b5851efc"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for Z80 tube processor"
|
note: "Conditional: needed for Z80 tube processor"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:484-491"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:484-491"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -156,7 +147,6 @@ files:
|
|||||||
size: 16384
|
size: 16384
|
||||||
crc32: "79434781"
|
crc32: "79434781"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:90-97"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:90-97"
|
||||||
|
|
||||||
- name: os.rom
|
- name: os.rom
|
||||||
@@ -165,7 +155,6 @@ files:
|
|||||||
size: 16384
|
size: 16384
|
||||||
crc32: "bf63fb1f"
|
crc32: "bf63fb1f"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
note: >
|
note: >
|
||||||
The catalogue accepts five images: 406a42ce, 0886741b, bf63fb1f, 6fea0978
|
The catalogue accepts five images: 406a42ce, 0886741b, bf63fb1f, 6fea0978
|
||||||
and d61135d7. They differ only in the credits text, which lives on the IO
|
and d61135d7. They differ only in the credits text, which lives on the IO
|
||||||
@@ -179,7 +168,6 @@ files:
|
|||||||
crc32: "51523993"
|
crc32: "51523993"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for PRES ADFS"
|
note: "Conditional: needed for PRES ADFS"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:98-105"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:98-105"
|
||||||
|
|
||||||
- name: ADFS-E00_2.rom
|
- name: ADFS-E00_2.rom
|
||||||
@@ -189,7 +177,6 @@ files:
|
|||||||
crc32: "8d17de0e"
|
crc32: "8d17de0e"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for PRES ADFS"
|
note: "Conditional: needed for PRES ADFS"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:106-113"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:106-113"
|
||||||
|
|
||||||
- name: adfs.rom
|
- name: adfs.rom
|
||||||
@@ -199,7 +186,6 @@ files:
|
|||||||
crc32: "3289bdc6"
|
crc32: "3289bdc6"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for Acorn ADFS"
|
note: "Conditional: needed for Acorn ADFS"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:114-121"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:114-121"
|
||||||
|
|
||||||
- name: DFS-1770-2.20.rom
|
- name: DFS-1770-2.20.rom
|
||||||
@@ -209,7 +195,6 @@ files:
|
|||||||
crc32: "f3dc9bc5"
|
crc32: "f3dc9bc5"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for DFS disk images"
|
note: "Conditional: needed for DFS disk images"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:122-129"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:122-129"
|
||||||
|
|
||||||
- name: ELK130E.rom
|
- name: ELK130E.rom
|
||||||
@@ -219,7 +204,6 @@ files:
|
|||||||
crc32: "a923368d"
|
crc32: "a923368d"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for IDE interface"
|
note: "Conditional: needed for IDE interface"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:130-137"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:130-137"
|
||||||
|
|
||||||
- name: AP6v133.rom
|
- name: AP6v133.rom
|
||||||
@@ -229,7 +213,6 @@ files:
|
|||||||
crc32: "e0013cfc"
|
crc32: "e0013cfc"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for AP6 commands. The catalogue records crc32 e0013cfc, which is the 7772-byte RetroHardware Plus 1 Support 1.33c build of 16 Jun 2020, and declares 8 KiB: Request::Node::validate() resizes the loaded image to 8192 bytes and the fetcher reads any length."
|
note: "Conditional: needed for AP6 commands. The catalogue records crc32 e0013cfc, which is the 7772-byte RetroHardware Plus 1 Support 1.33c build of 16 Jun 2020, and declares 8 KiB: Request::Node::validate() resizes the loaded image to 8192 bytes and the fetcher reads any length."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:138-145, Machines/Utility/ROMCatalogue.cpp:1484-1499, OSBindings/SDL/main.cpp:756-783"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:138-145, Machines/Utility/ROMCatalogue.cpp:1484-1499, OSBindings/SDL/main.cpp:756-783"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -241,7 +224,6 @@ files:
|
|||||||
size: 262144
|
size: 262144
|
||||||
crc32: "c4f0f55f"
|
crc32: "c4f0f55f"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:193-200"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:193-200"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -255,7 +237,6 @@ files:
|
|||||||
crc32: "815752df"
|
crc32: "815752df"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for CPC 464 model."
|
note: "Built into binary. Required for CPC 464 model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:261-268, Machines/Utility/ROMLibrary.cpp:14535"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:261-268, Machines/Utility/ROMLibrary.cpp:14535"
|
||||||
|
|
||||||
- name: basic464.rom
|
- name: basic464.rom
|
||||||
@@ -265,7 +246,6 @@ files:
|
|||||||
crc32: "7d9a3bac"
|
crc32: "7d9a3bac"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for CPC 464 model."
|
note: "Built into binary. Required for CPC 464 model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:269-276, Machines/Utility/ROMLibrary.cpp:14532"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:269-276, Machines/Utility/ROMLibrary.cpp:14532"
|
||||||
|
|
||||||
- name: os664.rom
|
- name: os664.rom
|
||||||
@@ -275,7 +255,6 @@ files:
|
|||||||
crc32: "3f5a6dc4"
|
crc32: "3f5a6dc4"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for CPC 664 model."
|
note: "Built into binary. Required for CPC 664 model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:277-283, Machines/Utility/ROMLibrary.cpp:14537"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:277-283, Machines/Utility/ROMLibrary.cpp:14537"
|
||||||
|
|
||||||
- name: basic664.rom
|
- name: basic664.rom
|
||||||
@@ -285,7 +264,6 @@ files:
|
|||||||
crc32: "32fee492"
|
crc32: "32fee492"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for CPC 664 model."
|
note: "Built into binary. Required for CPC 664 model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:284-291, Machines/Utility/ROMLibrary.cpp:14534"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:284-291, Machines/Utility/ROMLibrary.cpp:14534"
|
||||||
|
|
||||||
- name: os6128.rom
|
- name: os6128.rom
|
||||||
@@ -295,7 +273,6 @@ files:
|
|||||||
crc32: "0219bb74"
|
crc32: "0219bb74"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for CPC 6128 model."
|
note: "Built into binary. Required for CPC 6128 model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:292-299, Machines/Utility/ROMLibrary.cpp:14536"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:292-299, Machines/Utility/ROMLibrary.cpp:14536"
|
||||||
|
|
||||||
- name: basic6128.rom
|
- name: basic6128.rom
|
||||||
@@ -305,7 +282,6 @@ files:
|
|||||||
crc32: "ca6af63d"
|
crc32: "ca6af63d"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for CPC 6128 model."
|
note: "Built into binary. Required for CPC 6128 model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:300-307, Machines/Utility/ROMLibrary.cpp:14533"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:300-307, Machines/Utility/ROMLibrary.cpp:14533"
|
||||||
|
|
||||||
- name: amsdos.rom
|
- name: amsdos.rom
|
||||||
@@ -315,7 +291,6 @@ files:
|
|||||||
crc32: "1fe22ecd"
|
crc32: "1fe22ecd"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Needed for CPC 664/6128 disk support."
|
note: "Built into binary. Needed for CPC 664/6128 disk support."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:253-260, Machines/Utility/ROMLibrary.cpp:14531"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:253-260, Machines/Utility/ROMLibrary.cpp:14531"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -328,7 +303,6 @@ files:
|
|||||||
crc32: "ba210588"
|
crc32: "ba210588"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for original Apple II model"
|
note: "Required for original Apple II model"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:336-343"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:336-343"
|
||||||
|
|
||||||
- name: apple2.rom
|
- name: apple2.rom
|
||||||
@@ -338,7 +312,6 @@ files:
|
|||||||
crc32: "f66f9c26"
|
crc32: "f66f9c26"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for Apple II+ model"
|
note: "Required for Apple II+ model"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:328-335"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:328-335"
|
||||||
|
|
||||||
- name: apple2eu.rom
|
- name: apple2eu.rom
|
||||||
@@ -348,7 +321,6 @@ files:
|
|||||||
crc32: "e12be18d"
|
crc32: "e12be18d"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for Apple IIe model"
|
note: "Required for Apple IIe model"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:320-327"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:320-327"
|
||||||
|
|
||||||
- name: apple2e.rom
|
- name: apple2e.rom
|
||||||
@@ -358,7 +330,6 @@ files:
|
|||||||
crc32: "65989942"
|
crc32: "65989942"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for Enhanced IIe model"
|
note: "Required for Enhanced IIe model"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:312-319"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:312-319"
|
||||||
|
|
||||||
- name: apple2-character.rom
|
- name: apple2-character.rom
|
||||||
@@ -368,7 +339,6 @@ files:
|
|||||||
crc32: "64f415c6"
|
crc32: "64f415c6"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for Apple II and II+ models"
|
note: "Required for Apple II and II+ models"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:344-351"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:344-351"
|
||||||
|
|
||||||
- name: apple2eu-character.rom
|
- name: apple2eu-character.rom
|
||||||
@@ -378,7 +348,6 @@ files:
|
|||||||
crc32: "816a86f1"
|
crc32: "816a86f1"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for Apple IIe model"
|
note: "Required for Apple IIe model"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:352-359"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:352-359"
|
||||||
|
|
||||||
- name: apple2e-character.rom
|
- name: apple2e-character.rom
|
||||||
@@ -388,7 +357,6 @@ files:
|
|||||||
crc32: "2651014d"
|
crc32: "2651014d"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for Enhanced IIe model. Also used by Apple IIgs."
|
note: "Required for Enhanced IIe model. Also used by Apple IIgs."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:360-367"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:360-367"
|
||||||
|
|
||||||
- name: scsi.rom
|
- name: scsi.rom
|
||||||
@@ -398,7 +366,6 @@ files:
|
|||||||
crc32: "5aff85d3"
|
crc32: "5aff85d3"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for SCSI controller"
|
note: "Conditional: needed for SCSI controller"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:368-375"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:368-375"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -411,7 +378,6 @@ files:
|
|||||||
crc32: "42f124b0"
|
crc32: "42f124b0"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for ROM01 model"
|
note: "Required for ROM01 model"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:380-387"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:380-387"
|
||||||
|
|
||||||
- name: apple2gs.rom2
|
- name: apple2gs.rom2
|
||||||
@@ -421,7 +387,6 @@ files:
|
|||||||
crc32: "de7ddf29"
|
crc32: "de7ddf29"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for ROM03 model (default)"
|
note: "Required for ROM03 model (default)"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:388-395"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:388-395"
|
||||||
|
|
||||||
- name: apple2gs.chr
|
- name: apple2gs.chr
|
||||||
@@ -430,7 +395,6 @@ files:
|
|||||||
size: 4096
|
size: 4096
|
||||||
crc32: "91e53cd8"
|
crc32: "91e53cd8"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:396-403"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:396-403"
|
||||||
|
|
||||||
- name: "341s0632-2"
|
- name: "341s0632-2"
|
||||||
@@ -439,7 +403,6 @@ files:
|
|||||||
size: 4096
|
size: 4096
|
||||||
crc32: "e1c11fb0"
|
crc32: "e1c11fb0"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:404-411"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:404-411"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -451,7 +414,6 @@ files:
|
|||||||
size: 196608
|
size: 196608
|
||||||
crc32: "1a586c64"
|
crc32: "1a586c64"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:416-423"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:416-423"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -463,7 +425,6 @@ files:
|
|||||||
size: 8192
|
size: 8192
|
||||||
crc32: "3aa93ef3"
|
crc32: "3aa93ef3"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:496-503"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:496-503"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -477,7 +438,6 @@ files:
|
|||||||
crc32: "718d42b1"
|
crc32: "718d42b1"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed when VIC-20 has 1540 drive"
|
note: "Conditional: needed when VIC-20 has 1540 drive"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:508-515"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:508-515"
|
||||||
|
|
||||||
- name: 1541.bin
|
- name: 1541.bin
|
||||||
@@ -487,7 +447,6 @@ files:
|
|||||||
crc32: "fb760019"
|
crc32: "fb760019"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed when Plus/4 has 1541 drive"
|
note: "Conditional: needed when Plus/4 has 1541 drive"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:516-523"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:516-523"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -501,7 +460,6 @@ files:
|
|||||||
crc32: "ce7144f6"
|
crc32: "ce7144f6"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for 16-sector disk controller"
|
note: "Conditional: needed for 16-sector disk controller"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:528-535"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:528-535"
|
||||||
|
|
||||||
- name: state-machine-16.rom
|
- name: state-machine-16.rom
|
||||||
@@ -518,7 +476,6 @@ files:
|
|||||||
other image is read as the unadulterated P6 PROM (9796a238), its address
|
other image is read as the unadulterated P6 PROM (9796a238), its address
|
||||||
lines and the top nibble of each value remapped into that order. Both give
|
lines and the top nibble of each value remapped into that order. Both give
|
||||||
the same internal table.
|
the same internal table.
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:536-543, Components/DiskII/DiskII.cpp:187-239"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:536-543, Components/DiskII/DiskII.cpp:187-239"
|
||||||
|
|
||||||
- name: boot-13.rom
|
- name: boot-13.rom
|
||||||
@@ -528,7 +485,6 @@ files:
|
|||||||
crc32: "d34eb2ff"
|
crc32: "d34eb2ff"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for 13-sector disk controller"
|
note: "Conditional: needed for 13-sector disk controller"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:544-551"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:544-551"
|
||||||
|
|
||||||
- name: state-machine-13.rom
|
- name: state-machine-13.rom
|
||||||
@@ -538,7 +494,6 @@ files:
|
|||||||
crc32: "62e22620"
|
crc32: "62e22620"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for 13-sector disk controller"
|
note: "Conditional: needed for 13-sector disk controller"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:552-559"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:552-559"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -552,7 +507,6 @@ files:
|
|||||||
required: true
|
required: true
|
||||||
aliases: ["Exos (198x)(Enterprise).bin"]
|
aliases: ["Exos (198x)(Enterprise).bin"]
|
||||||
note: "One EXOS version required. Selected by target or any available."
|
note: "One EXOS version required. Selected by target or any available."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:600-607"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:600-607"
|
||||||
|
|
||||||
- name: exos20.bin
|
- name: exos20.bin
|
||||||
@@ -563,7 +517,6 @@ files:
|
|||||||
required: true
|
required: true
|
||||||
aliases: ["Expandible OS v2.0 (1984)(Intelligent Software).bin"]
|
aliases: ["Expandible OS v2.0 (1984)(Intelligent Software).bin"]
|
||||||
note: "Alternative EXOS version"
|
note: "Alternative EXOS version"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:611-618"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:611-618"
|
||||||
|
|
||||||
- name: exos21.bin
|
- name: exos21.bin
|
||||||
@@ -574,7 +527,6 @@ files:
|
|||||||
required: true
|
required: true
|
||||||
aliases: ["Expandible OS v2.1 (1985)(Intelligent Software).bin"]
|
aliases: ["Expandible OS v2.1 (1985)(Intelligent Software).bin"]
|
||||||
note: "Alternative EXOS version"
|
note: "Alternative EXOS version"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:622-629"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:622-629"
|
||||||
|
|
||||||
- name: exos23.bin
|
- name: exos23.bin
|
||||||
@@ -585,7 +537,6 @@ files:
|
|||||||
required: true
|
required: true
|
||||||
aliases: ["Expandible OS v2.3 (1987)(Intelligent Software).bin"]
|
aliases: ["Expandible OS v2.3 (1987)(Intelligent Software).bin"]
|
||||||
note: "Alternative EXOS version"
|
note: "Alternative EXOS version"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:633-640"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:633-640"
|
||||||
|
|
||||||
- name: basic10.bin
|
- name: basic10.bin
|
||||||
@@ -595,7 +546,6 @@ files:
|
|||||||
crc32: "d62e4fb7"
|
crc32: "d62e4fb7"
|
||||||
required: true
|
required: true
|
||||||
note: "One BASIC version required. v1.0 single-file version."
|
note: "One BASIC version required. v1.0 single-file version."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:645-652"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:645-652"
|
||||||
|
|
||||||
- name: "BASIC 1.0 - EPROM 1-2 (198x)(Enterprise).bin"
|
- name: "BASIC 1.0 - EPROM 1-2 (198x)(Enterprise).bin"
|
||||||
@@ -605,7 +555,6 @@ files:
|
|||||||
crc32: "37bf48e1"
|
crc32: "37bf48e1"
|
||||||
required: false
|
required: false
|
||||||
note: "Alternative: both parts needed together as substitute for basic10.bin"
|
note: "Alternative: both parts needed together as substitute for basic10.bin"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:653-660"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:653-660"
|
||||||
|
|
||||||
- name: "BASIC 1.0 - EPROM 2-2 (198x)(Enterprise).bin"
|
- name: "BASIC 1.0 - EPROM 2-2 (198x)(Enterprise).bin"
|
||||||
@@ -615,7 +564,6 @@ files:
|
|||||||
crc32: "c5298c79"
|
crc32: "c5298c79"
|
||||||
required: false
|
required: false
|
||||||
note: "Alternative: both parts needed together as substitute for basic10.bin"
|
note: "Alternative: both parts needed together as substitute for basic10.bin"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:661-668"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:661-668"
|
||||||
|
|
||||||
- name: basic11.bin
|
- name: basic11.bin
|
||||||
@@ -625,7 +573,6 @@ files:
|
|||||||
crc32: "683cf455"
|
crc32: "683cf455"
|
||||||
required: true
|
required: true
|
||||||
note: "Alternative BASIC version"
|
note: "Alternative BASIC version"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:669-676"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:669-676"
|
||||||
|
|
||||||
- name: "BASIC 1.1 - EPROM 1.1 (198x)(Enterprise).bin"
|
- name: "BASIC 1.1 - EPROM 1.1 (198x)(Enterprise).bin"
|
||||||
@@ -635,7 +582,6 @@ files:
|
|||||||
crc32: "c96b7602"
|
crc32: "c96b7602"
|
||||||
required: false
|
required: false
|
||||||
note: "Alternative format of BASIC v1.1 with trailing byte"
|
note: "Alternative format of BASIC v1.1 with trailing byte"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:677-684"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:677-684"
|
||||||
|
|
||||||
- name: basic21.bin
|
- name: basic21.bin
|
||||||
@@ -648,7 +594,6 @@ files:
|
|||||||
- "BASIC Interpreter v2.1 (1985)(Intelligent Software).bin"
|
- "BASIC Interpreter v2.1 (1985)(Intelligent Software).bin"
|
||||||
- "BASIC Interpreter v2.1 (1985)(Intelligent Software)[a].bin"
|
- "BASIC Interpreter v2.1 (1985)(Intelligent Software)[a].bin"
|
||||||
note: "Alternative BASIC version"
|
note: "Alternative BASIC version"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:685-693"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:685-693"
|
||||||
|
|
||||||
- name: epdos.bin
|
- name: epdos.bin
|
||||||
@@ -659,7 +604,6 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
aliases: ["EPDOS v1.7 (19xx)(Haluska, Laszlo).bin"]
|
aliases: ["EPDOS v1.7 (19xx)(Haluska, Laszlo).bin"]
|
||||||
note: "Conditional: needed for EPDOS disk interface"
|
note: "Conditional: needed for EPDOS disk interface"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:698-705"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:698-705"
|
||||||
|
|
||||||
- name: exdos.bin
|
- name: exdos.bin
|
||||||
@@ -670,7 +614,6 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
aliases: ["EX-DOS EPROM (198x)(Enterprise).bin"]
|
aliases: ["EX-DOS EPROM (198x)(Enterprise).bin"]
|
||||||
note: "Conditional: needed when DOS is set to EXDOS"
|
note: "Conditional: needed when DOS is set to EXDOS"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:709-716"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:709-716"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -683,7 +626,6 @@ files:
|
|||||||
crc32: "6d0c8a28"
|
crc32: "6d0c8a28"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for Macintosh 128k model"
|
note: "Required for Macintosh 128k model"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:724-731"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:724-731"
|
||||||
|
|
||||||
- name: mac512k.rom
|
- name: mac512k.rom
|
||||||
@@ -693,7 +635,6 @@ files:
|
|||||||
crc32: "cf759e0d"
|
crc32: "cf759e0d"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for Macintosh 512k model"
|
note: "Required for Macintosh 512k model"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:732-739"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:732-739"
|
||||||
|
|
||||||
- name: macplus.rom
|
- name: macplus.rom
|
||||||
@@ -708,7 +649,6 @@ files:
|
|||||||
checksum 4D1F8172 of the third Mac Plus revision. The
|
checksum 4D1F8172 of the third Mac Plus revision. The
|
||||||
machine copies whichever is supplied into its 128 KiB ROM without
|
machine copies whichever is supplied into its 128 KiB ROM without
|
||||||
reading the revision.
|
reading the revision.
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:740-751, Machines/Apple/Macintosh/Macintosh.cpp:127-144"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:740-751, Machines/Apple/Macintosh/Macintosh.cpp:127-144"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -722,7 +662,6 @@ files:
|
|||||||
crc32: "0072ed54"
|
crc32: "0072ed54"
|
||||||
required: false
|
required: false
|
||||||
note: "Optional. Boots cartridge directly if absent."
|
note: "Optional. Boots cartridge directly if absent."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:764-771"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:764-771"
|
||||||
|
|
||||||
- name: japanese-bios.sms
|
- name: japanese-bios.sms
|
||||||
@@ -733,7 +672,6 @@ files:
|
|||||||
crc32: "48d44a13"
|
crc32: "48d44a13"
|
||||||
required: false
|
required: false
|
||||||
note: "Optional. Used for Japan region. Boots without if absent."
|
note: "Optional. Used for Japan region. Boots without if absent."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:756-763"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:756-763"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -747,7 +685,6 @@ files:
|
|||||||
crc32: "94ee12f3"
|
crc32: "94ee12f3"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for MSX1. Region bytes patched in memory if regional BIOS absent."
|
note: "Required for MSX1. Region bytes patched in memory if regional BIOS absent."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:777-784"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:777-784"
|
||||||
|
|
||||||
- name: msx-japanese.rom
|
- name: msx-japanese.rom
|
||||||
@@ -758,7 +695,6 @@ files:
|
|||||||
crc32: "ee229390"
|
crc32: "ee229390"
|
||||||
required: false
|
required: false
|
||||||
note: "Alternative to msx.rom for Japanese region"
|
note: "Alternative to msx.rom for Japanese region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:785-792"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:785-792"
|
||||||
|
|
||||||
- name: msx-american.rom
|
- name: msx-american.rom
|
||||||
@@ -768,7 +704,6 @@ files:
|
|||||||
size: 32768
|
size: 32768
|
||||||
required: false
|
required: false
|
||||||
note: "Alternative to msx.rom for American region"
|
note: "Alternative to msx.rom for American region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:793-800"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:793-800"
|
||||||
|
|
||||||
- name: msx-european.rom
|
- name: msx-european.rom
|
||||||
@@ -778,7 +713,6 @@ files:
|
|||||||
size: 32768
|
size: 32768
|
||||||
required: false
|
required: false
|
||||||
note: "Alternative to msx.rom for European region"
|
note: "Alternative to msx.rom for European region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:801-808"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:801-808"
|
||||||
|
|
||||||
- name: disk.rom
|
- name: disk.rom
|
||||||
@@ -788,7 +722,6 @@ files:
|
|||||||
crc32: "721f61df"
|
crc32: "721f61df"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for disk drive support"
|
note: "Conditional: needed for disk drive support"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:809-816"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:809-816"
|
||||||
|
|
||||||
- name: msx2.rom
|
- name: msx2.rom
|
||||||
@@ -799,7 +732,6 @@ files:
|
|||||||
crc32: "6cdaf3a5"
|
crc32: "6cdaf3a5"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for MSX2"
|
note: "Required for MSX2"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:818-825"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:818-825"
|
||||||
|
|
||||||
- name: msx2ext.rom
|
- name: msx2ext.rom
|
||||||
@@ -810,7 +742,6 @@ files:
|
|||||||
crc32: "66237ecf"
|
crc32: "66237ecf"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for MSX2"
|
note: "Required for MSX2"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:826-833"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:826-833"
|
||||||
|
|
||||||
- name: fmpac.rom
|
- name: fmpac.rom
|
||||||
@@ -820,7 +751,6 @@ files:
|
|||||||
crc32: "0e84505d"
|
crc32: "0e84505d"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for MSX-Music support"
|
note: "Conditional: needed for MSX-Music support"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:834-841"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:834-841"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -833,7 +763,6 @@ files:
|
|||||||
crc32: "f18710b4"
|
crc32: "f18710b4"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for Oric-1 model"
|
note: "Required for Oric-1 model"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:870-877"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:870-877"
|
||||||
|
|
||||||
- name: basic11.rom
|
- name: basic11.rom
|
||||||
@@ -843,7 +772,6 @@ files:
|
|||||||
crc32: "c3a92bef"
|
crc32: "c3a92bef"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for Oric Atmos model"
|
note: "Required for Oric Atmos model"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:878-885"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:878-885"
|
||||||
|
|
||||||
- name: pravetz.rom
|
- name: pravetz.rom
|
||||||
@@ -853,7 +781,6 @@ files:
|
|||||||
crc32: "58079502"
|
crc32: "58079502"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for Pravetz 8D model"
|
note: "Required for Pravetz 8D model"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:886-893"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:886-893"
|
||||||
|
|
||||||
- name: colour.rom
|
- name: colour.rom
|
||||||
@@ -863,7 +790,6 @@ files:
|
|||||||
crc32: "d50fca65"
|
crc32: "d50fca65"
|
||||||
required: false
|
required: false
|
||||||
note: "Optional. Enables colour video output."
|
note: "Optional. Enables colour video output."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:846-853"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:846-853"
|
||||||
|
|
||||||
- name: ORIC.PAL-PROM-TBP24S10-1ab9b572.bin
|
- name: ORIC.PAL-PROM-TBP24S10-1ab9b572.bin
|
||||||
@@ -873,7 +799,6 @@ files:
|
|||||||
crc32: "1ab9b572"
|
crc32: "1ab9b572"
|
||||||
required: false
|
required: false
|
||||||
note: "Optional. Alternative colour ROM."
|
note: "Optional. Alternative colour ROM."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:854-861"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:854-861"
|
||||||
|
|
||||||
- name: PRAVETZ-8D.PAL-PROM-82S129-1576a69b.bin
|
- name: PRAVETZ-8D.PAL-PROM-82S129-1576a69b.bin
|
||||||
@@ -883,7 +808,6 @@ files:
|
|||||||
crc32: "1576a69b"
|
crc32: "1576a69b"
|
||||||
required: false
|
required: false
|
||||||
note: "Optional. Colour ROM for Pravetz model."
|
note: "Optional. Colour ROM for Pravetz model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:862-869"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:862-869"
|
||||||
|
|
||||||
- name: bd500.rom
|
- name: bd500.rom
|
||||||
@@ -893,7 +817,6 @@ files:
|
|||||||
crc32: "61952e34"
|
crc32: "61952e34"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for Byte Drive 500 disk interface"
|
note: "Conditional: needed for Byte Drive 500 disk interface"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:894-901"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:894-901"
|
||||||
|
|
||||||
- name: jasmin.rom
|
- name: jasmin.rom
|
||||||
@@ -903,7 +826,6 @@ files:
|
|||||||
crc32: "37220e89"
|
crc32: "37220e89"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for Jasmin disk interface"
|
note: "Conditional: needed for Jasmin disk interface"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:902-909"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:902-909"
|
||||||
|
|
||||||
- name: microdisc.rom
|
- name: microdisc.rom
|
||||||
@@ -913,7 +835,6 @@ files:
|
|||||||
crc32: "a9664a9c"
|
crc32: "a9664a9c"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for Microdisc disk interface"
|
note: "Conditional: needed for Microdisc disk interface"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:910-917"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:910-917"
|
||||||
|
|
||||||
- name: 8dos.rom
|
- name: 8dos.rom
|
||||||
@@ -923,7 +844,6 @@ files:
|
|||||||
crc32: "49a74c06"
|
crc32: "49a74c06"
|
||||||
required: false
|
required: false
|
||||||
note: "Conditional: needed for Pravetz 8-DOS disk interface"
|
note: "Conditional: needed for Pravetz 8-DOS disk interface"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:918-925"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:918-925"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -936,7 +856,6 @@ files:
|
|||||||
crc32: "9576944c"
|
crc32: "9576944c"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for XT-class emulation"
|
note: "Required for XT-class emulation"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:930-937"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:930-937"
|
||||||
|
|
||||||
- name: GLaTICK_0.8.5_AT.ROM
|
- name: GLaTICK_0.8.5_AT.ROM
|
||||||
@@ -946,7 +865,6 @@ files:
|
|||||||
crc32: "371ea3f1"
|
crc32: "371ea3f1"
|
||||||
required: false
|
required: false
|
||||||
note: "XT-class RTC option ROM"
|
note: "XT-class RTC option ROM"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:938-945"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:938-945"
|
||||||
|
|
||||||
- name: "Phoenix 80286 ROM BIOS Version 3.05.bin"
|
- name: "Phoenix 80286 ROM BIOS Version 3.05.bin"
|
||||||
@@ -956,7 +874,6 @@ files:
|
|||||||
crc32: "8d0d318a"
|
crc32: "8d0d318a"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for AT-class emulation (alternative to IBM AT BIOS)"
|
note: "Required for AT-class emulation (alternative to IBM AT BIOS)"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:946-953"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:946-953"
|
||||||
|
|
||||||
- name: at-bios.bin
|
- name: at-bios.bin
|
||||||
@@ -966,7 +883,6 @@ files:
|
|||||||
crc32: "674426be"
|
crc32: "674426be"
|
||||||
required: true
|
required: true
|
||||||
note: "AT-class: single-file IBM AT BIOS (alternative to split U27/U47)"
|
note: "AT-class: single-file IBM AT BIOS (alternative to split U27/U47)"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:954-961"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:954-961"
|
||||||
|
|
||||||
- name: BIOS_5170_15NOV85_U27_61X9266_27256.BIN
|
- name: BIOS_5170_15NOV85_U27_61X9266_27256.BIN
|
||||||
@@ -976,7 +892,6 @@ files:
|
|||||||
crc32: "4995be7a"
|
crc32: "4995be7a"
|
||||||
required: false
|
required: false
|
||||||
note: "AT-class: split BIOS, used with U47 chip"
|
note: "AT-class: split BIOS, used with U47 chip"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:962-969"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:962-969"
|
||||||
|
|
||||||
- name: BIOS_5170_15NOV85_U47_61X9265_27256.BIN
|
- name: BIOS_5170_15NOV85_U47_61X9265_27256.BIN
|
||||||
@@ -986,7 +901,6 @@ files:
|
|||||||
crc32: "c32713e4"
|
crc32: "c32713e4"
|
||||||
required: false
|
required: false
|
||||||
note: "AT-class: split BIOS, used with U27 chip"
|
note: "AT-class: split BIOS, used with U27 chip"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:970-977"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:970-977"
|
||||||
|
|
||||||
- name: CGA.F08
|
- name: CGA.F08
|
||||||
@@ -996,7 +910,6 @@ files:
|
|||||||
crc32: "a362ffe6"
|
crc32: "a362ffe6"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for CGA video adapter"
|
note: "Required for CGA video adapter"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:978-985"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:978-985"
|
||||||
|
|
||||||
- name: EUMDA9.F14
|
- name: EUMDA9.F14
|
||||||
@@ -1006,7 +919,6 @@ files:
|
|||||||
crc32: "7754882a"
|
crc32: "7754882a"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for MDA video adapter"
|
note: "Required for MDA video adapter"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:986-993"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:986-993"
|
||||||
|
|
||||||
- name: ibm_6277356_ega_card_u44_27128.bin
|
- name: ibm_6277356_ega_card_u44_27128.bin
|
||||||
@@ -1016,7 +928,6 @@ files:
|
|||||||
crc32: "2f2fbc40"
|
crc32: "2f2fbc40"
|
||||||
required: false
|
required: false
|
||||||
note: "Needed for EGA video adapter"
|
note: "Needed for EGA video adapter"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:994-1001"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:994-1001"
|
||||||
|
|
||||||
- name: ibm_vga.bin
|
- name: ibm_vga.bin
|
||||||
@@ -1026,7 +937,6 @@ files:
|
|||||||
crc32: "03b3f90d"
|
crc32: "03b3f90d"
|
||||||
required: false
|
required: false
|
||||||
note: "Needed for VGA video adapter"
|
note: "Needed for VGA video adapter"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1002-1009"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1002-1009"
|
||||||
|
|
||||||
- name: ibm-basic-1.10.rom
|
- name: ibm-basic-1.10.rom
|
||||||
@@ -1036,7 +946,6 @@ files:
|
|||||||
crc32: "ebacb791"
|
crc32: "ebacb791"
|
||||||
required: false
|
required: false
|
||||||
note: "Required for AT-class with IBM BIOS (not needed with Phoenix)"
|
note: "Required for AT-class with IBM BIOS (not needed with Phoenix)"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1010-1017"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1010-1017"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -1048,7 +957,6 @@ files:
|
|||||||
size: 16384
|
size: 16384
|
||||||
crc32: "74eaae87"
|
crc32: "74eaae87"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1046-1053"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1046-1053"
|
||||||
|
|
||||||
- name: kernal.318004-05.bin
|
- name: kernal.318004-05.bin
|
||||||
@@ -1058,7 +966,6 @@ files:
|
|||||||
crc32: "71c07bd4"
|
crc32: "71c07bd4"
|
||||||
required: true
|
required: true
|
||||||
note: "Default kernel version requested by machine code"
|
note: "Default kernel version requested by machine code"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1038-1045"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1038-1045"
|
||||||
|
|
||||||
- name: kernal.318004-03.bin
|
- name: kernal.318004-03.bin
|
||||||
@@ -1068,7 +975,6 @@ files:
|
|||||||
crc32: "77bab934"
|
crc32: "77bab934"
|
||||||
required: false
|
required: false
|
||||||
note: "Alternative kernel version (in catalogue but not default)"
|
note: "Alternative kernel version (in catalogue but not default)"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1022-1029"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1022-1029"
|
||||||
|
|
||||||
- name: kernal.318004-04.bin
|
- name: kernal.318004-04.bin
|
||||||
@@ -1078,7 +984,6 @@ files:
|
|||||||
crc32: "be54ed79"
|
crc32: "be54ed79"
|
||||||
required: false
|
required: false
|
||||||
note: "Alternative kernel version (in catalogue but not default)"
|
note: "Alternative kernel version (in catalogue but not default)"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1030-1037"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1030-1037"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -1090,7 +995,6 @@ files:
|
|||||||
size: 16384
|
size: 16384
|
||||||
crc32: "f62d2f3a"
|
crc32: "f62d2f3a"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1157-1164"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1157-1164"
|
||||||
|
|
||||||
- name: mo5-v1.1.rom
|
- name: mo5-v1.1.rom
|
||||||
@@ -1099,7 +1003,6 @@ files:
|
|||||||
size: 16384
|
size: 16384
|
||||||
crc32: "237c60bf"
|
crc32: "237c60bf"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1165-1172"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1165-1172"
|
||||||
|
|
||||||
- name: mo6-v1.rom
|
- name: mo6-v1.rom
|
||||||
@@ -1108,7 +1011,6 @@ files:
|
|||||||
size: 65536
|
size: 65536
|
||||||
crc32: "45e859c6"
|
crc32: "45e859c6"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1173-1180"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1173-1180"
|
||||||
|
|
||||||
- name: mo6-v2.rom
|
- name: mo6-v2.rom
|
||||||
@@ -1117,7 +1019,6 @@ files:
|
|||||||
size: 65536
|
size: 65536
|
||||||
crc32: "b4430a3b"
|
crc32: "b4430a3b"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1181-1188"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1181-1188"
|
||||||
|
|
||||||
- name: mo6-v3.rom
|
- name: mo6-v3.rom
|
||||||
@@ -1126,7 +1027,6 @@ files:
|
|||||||
size: 65536
|
size: 65536
|
||||||
crc32: "7a012322"
|
crc32: "7a012322"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1189-1196"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1189-1196"
|
||||||
|
|
||||||
- name: pc128.rom
|
- name: pc128.rom
|
||||||
@@ -1135,7 +1035,6 @@ files:
|
|||||||
size: 65536
|
size: 65536
|
||||||
crc32: "fc173504"
|
crc32: "fc173504"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1197-1204"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1197-1204"
|
||||||
|
|
||||||
- name: cd90-640.rom
|
- name: cd90-640.rom
|
||||||
@@ -1145,7 +1044,6 @@ files:
|
|||||||
crc32: "5114c0a5"
|
crc32: "5114c0a5"
|
||||||
required: false
|
required: false
|
||||||
note: "Requested on top of the machine ROM when a disk is attached."
|
note: "Requested on top of the machine ROM when a disk is attached."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1206-1213"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1206-1213"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -1157,7 +1055,6 @@ files:
|
|||||||
size: 8192
|
size: 8192
|
||||||
crc32: "db4c43c1"
|
crc32: "db4c43c1"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1218-1225"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1218-1225"
|
||||||
|
|
||||||
- name: characters-english.bin
|
- name: characters-english.bin
|
||||||
@@ -1168,7 +1065,6 @@ files:
|
|||||||
crc32: "83e032a6"
|
crc32: "83e032a6"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for English region"
|
note: "Required for English region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1226-1233"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1226-1233"
|
||||||
|
|
||||||
- name: kernel-pal.bin
|
- name: kernel-pal.bin
|
||||||
@@ -1179,7 +1075,6 @@ files:
|
|||||||
crc32: "4be07cb4"
|
crc32: "4be07cb4"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for English PAL region"
|
note: "Required for English PAL region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1234-1241"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1234-1241"
|
||||||
|
|
||||||
- name: kernel-ntsc.bin
|
- name: kernel-ntsc.bin
|
||||||
@@ -1190,7 +1085,6 @@ files:
|
|||||||
crc32: "e5e7c174"
|
crc32: "e5e7c174"
|
||||||
required: true
|
required: true
|
||||||
note: "Required for English NTSC region"
|
note: "Required for English NTSC region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1242-1249"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1242-1249"
|
||||||
|
|
||||||
- name: characters-danish.bin
|
- name: characters-danish.bin
|
||||||
@@ -1201,7 +1095,6 @@ files:
|
|||||||
crc32: "7fc11454"
|
crc32: "7fc11454"
|
||||||
required: false
|
required: false
|
||||||
note: "Required for Danish region"
|
note: "Required for Danish region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1250-1257"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1250-1257"
|
||||||
|
|
||||||
- name: kernel-danish.bin
|
- name: kernel-danish.bin
|
||||||
@@ -1212,7 +1105,6 @@ files:
|
|||||||
crc32: "02adaf16"
|
crc32: "02adaf16"
|
||||||
required: false
|
required: false
|
||||||
note: "Required for Danish region"
|
note: "Required for Danish region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1258-1265"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1258-1265"
|
||||||
|
|
||||||
- name: characters-japanese.bin
|
- name: characters-japanese.bin
|
||||||
@@ -1223,7 +1115,6 @@ files:
|
|||||||
crc32: "fcfd8a4b"
|
crc32: "fcfd8a4b"
|
||||||
required: false
|
required: false
|
||||||
note: "Required for Japanese region"
|
note: "Required for Japanese region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1266-1273"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1266-1273"
|
||||||
|
|
||||||
- name: kernel-japanese.bin
|
- name: kernel-japanese.bin
|
||||||
@@ -1234,7 +1125,6 @@ files:
|
|||||||
crc32: "336900d7"
|
crc32: "336900d7"
|
||||||
required: false
|
required: false
|
||||||
note: "Required for Japanese region"
|
note: "Required for Japanese region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1274-1281"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1274-1281"
|
||||||
|
|
||||||
- name: characters-swedish.bin
|
- name: characters-swedish.bin
|
||||||
@@ -1245,7 +1135,6 @@ files:
|
|||||||
crc32: "d808551d"
|
crc32: "d808551d"
|
||||||
required: false
|
required: false
|
||||||
note: "Required for Swedish region"
|
note: "Required for Swedish region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1282-1289"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1282-1289"
|
||||||
|
|
||||||
- name: kernel-swedish.bin
|
- name: kernel-swedish.bin
|
||||||
@@ -1256,7 +1145,6 @@ files:
|
|||||||
crc32: "b2a60662"
|
crc32: "b2a60662"
|
||||||
required: false
|
required: false
|
||||||
note: "Required for Swedish region"
|
note: "Required for Swedish region"
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1290-1297"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1290-1297"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -1270,7 +1158,6 @@ files:
|
|||||||
crc32: "ddee531f"
|
crc32: "ddee531f"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for 16K/48K model."
|
note: "Built into binary. Required for 16K/48K model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1302-1309, Machines/Utility/ROMLibrary.cpp:14539"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1302-1309, Machines/Utility/ROMLibrary.cpp:14539"
|
||||||
|
|
||||||
- name: 128.rom
|
- name: 128.rom
|
||||||
@@ -1280,7 +1167,6 @@ files:
|
|||||||
crc32: "2cbe8995"
|
crc32: "2cbe8995"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for 128K model."
|
note: "Built into binary. Required for 128K model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1310-1317, Machines/Utility/ROMLibrary.cpp:14540"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1310-1317, Machines/Utility/ROMLibrary.cpp:14540"
|
||||||
|
|
||||||
- name: plus2.rom
|
- name: plus2.rom
|
||||||
@@ -1290,7 +1176,6 @@ files:
|
|||||||
crc32: "e7a517dc"
|
crc32: "e7a517dc"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for +2 model."
|
note: "Built into binary. Required for +2 model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1318-1325, Machines/Utility/ROMLibrary.cpp:14541"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1318-1325, Machines/Utility/ROMLibrary.cpp:14541"
|
||||||
|
|
||||||
- name: plus3.rom
|
- name: plus3.rom
|
||||||
@@ -1300,7 +1185,6 @@ files:
|
|||||||
crc32: "96e3c17a"
|
crc32: "96e3c17a"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for +2a/+3 model."
|
note: "Built into binary. Required for +2a/+3 model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1326-1336, Machines/Utility/ROMLibrary.cpp:14542"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1326-1336, Machines/Utility/ROMLibrary.cpp:14542"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
@@ -1314,7 +1198,6 @@ files:
|
|||||||
crc32: "4c7fc597"
|
crc32: "4c7fc597"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for ZX80 model."
|
note: "Built into binary. Required for ZX80 model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1341-1348, Machines/Utility/ROMLibrary.cpp:14544"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1341-1348, Machines/Utility/ROMLibrary.cpp:14544"
|
||||||
|
|
||||||
- name: zx81.rom
|
- name: zx81.rom
|
||||||
@@ -1324,5 +1207,4 @@ files:
|
|||||||
crc32: "4b1dd6eb"
|
crc32: "4b1dd6eb"
|
||||||
required: false
|
required: false
|
||||||
note: "Built into binary. Required for ZX81 model."
|
note: "Built into binary. Required for ZX81 model."
|
||||||
mode: standalone
|
|
||||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1349-1356, Machines/Utility/ROMLibrary.cpp:14545"
|
source_ref: "Machines/Utility/ROMCatalogue.cpp:1349-1356, Machines/Utility/ROMLibrary.cpp:14545"
|
||||||
@@ -28,11 +28,11 @@ notes: |
|
|||||||
- ROMEXT[0] (upper ROM slot 0, 16KB): second half of cpc6128_bin (BASIC)
|
- ROMEXT[0] (upper ROM slot 0, 16KB): second half of cpc6128_bin (BASIC)
|
||||||
- ROMEXT[7] (upper ROM slot 7, 16KB): romdisc_bin (AMSDOS)
|
- ROMEXT[7] (upper ROM slot 7, 16KB): romdisc_bin (AMSDOS)
|
||||||
|
|
||||||
Called from HardResetCPC() (crocods-core/platform.c:1009-1020) using
|
Called from HardResetCPC() (crocods-core/platform.c:1024-1035) using
|
||||||
the extern byte arrays directly. No filesystem ROM loading exists in
|
the extern byte arrays directly. No filesystem ROM loading exists in
|
||||||
the libretro path.
|
the libretro path.
|
||||||
|
|
||||||
A Multiface ROM loader exists (crocods-core/multiface.c:51-61) that
|
A Multiface ROM loader exists (crocods-core/multiface.c:53-64) that
|
||||||
reads LocRomMulti via fopen(), but InitMultiface() is never called
|
reads LocRomMulti via fopen(), but InitMultiface() is never called
|
||||||
from the libretro build or platform.c. Dead code in libretro context.
|
from the libretro build or platform.c. Dead code in libretro context.
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ files: []
|
|||||||
|
|
||||||
platform_details:
|
platform_details:
|
||||||
bios_mapping:
|
bios_mapping:
|
||||||
source_ref: "crocods-core/vga.c:185-200, crocods-core/platform.c:1009-1020"
|
source_ref: "crocods-core/vga.c:185-200, crocods-core/platform.c:1024-1035"
|
||||||
note: |
|
note: |
|
||||||
All ROMs are embedded. InitMemCPC() copies cpc6128_bin and romdisc_bin
|
All ROMs are embedded. InitMemCPC() copies cpc6128_bin and romdisc_bin
|
||||||
into ROMINF and ROMEXT arrays via memcpy. No filesystem lookup occurs.
|
into ROMINF and ROMEXT arrays via memcpy. No filesystem lookup occurs.
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "BootMii NAND backup. Can be imported to populate Wii NAND with channels, saves, system menu"
|
note: "BootMii NAND backup. Can be imported to populate Wii NAND with channels, saves, system menu"
|
||||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:26-39,42-89"
|
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:27-45,47-92"
|
||||||
|
|
||||||
- name: "keys.bin"
|
- name: "keys.bin"
|
||||||
path: null
|
path: null
|
||||||
@@ -156,7 +156,7 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "OTP/SEEPROM dump (Wii encryption keys). Needed if not appended to nand.bin for NAND import"
|
note: "OTP/SEEPROM dump (Wii encryption keys). Needed if not appended to nand.bin for NAND import"
|
||||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:20, Source/Core/DiscIO/NANDImporter.cpp:82-94, Source/Core/DiscIO/NANDImporter.h:46"
|
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:20, Source/Core/DiscIO/NANDImporter.cpp:77-91, Source/Core/DiscIO/NANDImporter.h:35-38"
|
||||||
|
|
||||||
# -- Wii SD Card Image --
|
# -- Wii SD Card Image --
|
||||||
- name: "WiiSD.raw"
|
- name: "WiiSD.raw"
|
||||||
@@ -189,21 +189,21 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "SSL client certificate. Auto-extracted from IOS13 content during NAND import"
|
note: "SSL client certificate. Auto-extracted from IOS13 content during NAND import"
|
||||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:239-323"
|
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:220-304"
|
||||||
|
|
||||||
- name: "clientcakey.pem"
|
- name: "clientcakey.pem"
|
||||||
path: "Wii/clientcakey.pem"
|
path: "Wii/clientcakey.pem"
|
||||||
required: false
|
required: false
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "SSL client private key. Auto-extracted from IOS13 content during NAND import"
|
note: "SSL client private key. Auto-extracted from IOS13 content during NAND import"
|
||||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:275"
|
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:256"
|
||||||
|
|
||||||
- name: "rootca.pem"
|
- name: "rootca.pem"
|
||||||
path: "Wii/rootca.pem"
|
path: "Wii/rootca.pem"
|
||||||
required: false
|
required: false
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "SSL root CA certificate. Auto-extracted from IOS13 content during NAND import"
|
note: "SSL root CA certificate. Auto-extracted from IOS13 content during NAND import"
|
||||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:276"
|
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:257"
|
||||||
|
|
||||||
# -- Realtek Bluetooth firmware (Wiimote passthrough) --
|
# -- Realtek Bluetooth firmware (Wiimote passthrough) --
|
||||||
# Required for real Wiimote connectivity via USB Bluetooth adapters with Realtek chipsets.
|
# Required for real Wiimote connectivity via USB Bluetooth adapters with Realtek chipsets.
|
||||||
|
|||||||
@@ -28,28 +28,24 @@ files:
|
|||||||
- name: "prod.keys"
|
- name: "prod.keys"
|
||||||
required: true
|
required: true
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:574-575, 853"
|
source_ref: "src/core/crypto/key_manager.cpp:574-575, 853"
|
||||||
|
|
||||||
- name: "dev.keys"
|
- name: "dev.keys"
|
||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Development-unit keys, read instead of prod.keys when use_dev_keys is set"
|
note: "Development-unit keys, read instead of prod.keys when use_dev_keys is set"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:570-571, 852"
|
source_ref: "src/core/crypto/key_manager.cpp:570-571, 852"
|
||||||
|
|
||||||
- name: "title.keys"
|
- name: "title.keys"
|
||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:577-578, 850"
|
source_ref: "src/core/crypto/key_manager.cpp:577-578, 850"
|
||||||
|
|
||||||
- name: "console.keys"
|
- name: "console.keys"
|
||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Console-specific keys (BIS, SD seed)"
|
note: "Console-specific keys (BIS, SD seed)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:579-580"
|
source_ref: "src/core/crypto/key_manager.cpp:579-580"
|
||||||
|
|
||||||
@@ -57,7 +53,6 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
size: 160
|
size: 160
|
||||||
mode: standalone
|
|
||||||
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
||||||
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:281-302"
|
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:281-302"
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ files:
|
|||||||
description: "Sega CD BIOS (US)"
|
description: "Sega CD BIOS (US)"
|
||||||
region: [north-america]
|
region: [north-america]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
size: 131072
|
size: 131072
|
||||||
validation: [size]
|
validation: [size]
|
||||||
source_ref: "frontend/jgenesis-native-driver/src/mainloop/genesis.rs:39-49 (read), frontend/jgenesis-native-driver/src/mainloop/genesis.rs:238-244 (bios_path_for_region), backend/segacd-core/src/api.rs:77-79 (BIOS_LEN check)"
|
source_ref: "frontend/jgenesis-native-driver/src/mainloop/genesis.rs:39-49 (read), frontend/jgenesis-native-driver/src/mainloop/genesis.rs:238-244 (bios_path_for_region), backend/segacd-core/src/api.rs:77-79 (BIOS_LEN check)"
|
||||||
@@ -90,7 +89,6 @@ files:
|
|||||||
description: "Mega CD BIOS (Europe)"
|
description: "Mega CD BIOS (Europe)"
|
||||||
region: [europe]
|
region: [europe]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
size: 131072
|
size: 131072
|
||||||
validation: [size]
|
validation: [size]
|
||||||
source_ref: "frontend/jgenesis-native-driver/src/mainloop/genesis.rs:238-244 (eu_bios_path), backend/segacd-core/src/api.rs:77-79"
|
source_ref: "frontend/jgenesis-native-driver/src/mainloop/genesis.rs:238-244 (eu_bios_path), backend/segacd-core/src/api.rs:77-79"
|
||||||
@@ -100,7 +98,6 @@ files:
|
|||||||
description: "Mega CD BIOS (Japan)"
|
description: "Mega CD BIOS (Japan)"
|
||||||
region: [japan]
|
region: [japan]
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
size: 131072
|
size: 131072
|
||||||
validation: [size]
|
validation: [size]
|
||||||
source_ref: "frontend/jgenesis-native-driver/src/mainloop/genesis.rs:238-244 (jp_bios_path), backend/segacd-core/src/api.rs:77-79"
|
source_ref: "frontend/jgenesis-native-driver/src/mainloop/genesis.rs:238-244 (jp_bios_path), backend/segacd-core/src/api.rs:77-79"
|
||||||
@@ -112,7 +109,6 @@ files:
|
|||||||
system: nintendo-gba
|
system: nintendo-gba
|
||||||
description: "Game Boy Advance BIOS"
|
description: "Game Boy Advance BIOS"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
size: 16384
|
size: 16384
|
||||||
validation: [size]
|
validation: [size]
|
||||||
source_ref: "frontend/jgenesis-native-driver/src/mainloop/gba.rs:29-33 (GbaNoBios), backend/gba-core/src/memory.rs:10,102-107 (BIOS_ROM_LEN check)"
|
source_ref: "frontend/jgenesis-native-driver/src/mainloop/gba.rs:29-33 (GbaNoBios), backend/gba-core/src/memory.rs:10,102-107 (BIOS_ROM_LEN check)"
|
||||||
@@ -126,7 +122,6 @@ files:
|
|||||||
system: nintendo-snes
|
system: nintendo-snes
|
||||||
description: "NEC uPD77C25 firmware for DSP-1"
|
description: "NEC uPD77C25 firmware for DSP-1"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
size: 8192
|
size: 8192
|
||||||
source_ref: "backend/snes-core/src/memory/cartridge.rs:336-353 (MissingDsp1Rom), backend/snes-coprocessors/src/upd77c25.rs:28-31,35-38,382-390"
|
source_ref: "backend/snes-core/src/memory/cartridge.rs:336-353 (MissingDsp1Rom), backend/snes-coprocessors/src/upd77c25.rs:28-31,35-38,382-390"
|
||||||
note: "0x1800 program plus 0x800 data. Also used by DSP-1B titles, which have no separate slot. Layout is sliced at a fixed offset, size is not checked."
|
note: "0x1800 program plus 0x800 data. Also used by DSP-1B titles, which have no separate slot. Layout is sliced at a fixed offset, size is not checked."
|
||||||
@@ -136,7 +131,6 @@ files:
|
|||||||
system: nintendo-snes
|
system: nintendo-snes
|
||||||
description: "NEC uPD77C25 firmware for DSP-2"
|
description: "NEC uPD77C25 firmware for DSP-2"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
size: 8192
|
size: 8192
|
||||||
source_ref: "backend/snes-core/src/memory/cartridge.rs:340-342 (MissingDsp2Rom), :929 (Dungeon Master CRC32 match), backend/snes-coprocessors/src/upd77c25.rs:382-390"
|
source_ref: "backend/snes-core/src/memory/cartridge.rs:340-342 (MissingDsp2Rom), :929 (Dungeon Master CRC32 match), backend/snes-coprocessors/src/upd77c25.rs:382-390"
|
||||||
|
|
||||||
@@ -145,7 +139,6 @@ files:
|
|||||||
system: nintendo-snes
|
system: nintendo-snes
|
||||||
description: "NEC uPD77C25 firmware for DSP-3"
|
description: "NEC uPD77C25 firmware for DSP-3"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
size: 8192
|
size: 8192
|
||||||
source_ref: "backend/snes-core/src/memory/cartridge.rs:343-345 (MissingDsp3Rom), :931 (SD Gundam GX CRC32 match), backend/snes-coprocessors/src/upd77c25.rs:382-390"
|
source_ref: "backend/snes-core/src/memory/cartridge.rs:343-345 (MissingDsp3Rom), :931 (SD Gundam GX CRC32 match), backend/snes-coprocessors/src/upd77c25.rs:382-390"
|
||||||
|
|
||||||
@@ -154,7 +147,6 @@ files:
|
|||||||
system: nintendo-snes
|
system: nintendo-snes
|
||||||
description: "NEC uPD77C25 firmware for DSP-4"
|
description: "NEC uPD77C25 firmware for DSP-4"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
size: 8192
|
size: 8192
|
||||||
source_ref: "backend/snes-core/src/memory/cartridge.rs:346-348 (MissingDsp4Rom), :933 (Top Gear 3000 CRC32 match), backend/snes-coprocessors/src/upd77c25.rs:382-390"
|
source_ref: "backend/snes-core/src/memory/cartridge.rs:346-348 (MissingDsp4Rom), :933 (Top Gear 3000 CRC32 match), backend/snes-coprocessors/src/upd77c25.rs:382-390"
|
||||||
|
|
||||||
@@ -163,7 +155,6 @@ files:
|
|||||||
system: nintendo-snes
|
system: nintendo-snes
|
||||||
description: "NEC uPD96050 firmware for ST010"
|
description: "NEC uPD96050 firmware for ST010"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
size: 53248
|
size: 53248
|
||||||
source_ref: "backend/snes-core/src/memory/cartridge.rs:294-305 (MissingSt010Rom), :938-943 (variant detection), backend/snes-coprocessors/src/upd77c25.rs:28-31,35-38"
|
source_ref: "backend/snes-core/src/memory/cartridge.rs:294-305 (MissingSt010Rom), :938-943 (variant detection), backend/snes-coprocessors/src/upd77c25.rs:28-31,35-38"
|
||||||
note: "0xC000 program plus 0x1000 data. Used by F1 ROC II."
|
note: "0xC000 program plus 0x1000 data. Used by F1 ROC II."
|
||||||
@@ -173,7 +164,6 @@ files:
|
|||||||
system: nintendo-snes
|
system: nintendo-snes
|
||||||
description: "NEC uPD96050 firmware for ST011"
|
description: "NEC uPD96050 firmware for ST011"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
size: 53248
|
size: 53248
|
||||||
source_ref: "backend/snes-core/src/memory/cartridge.rs:298-300 (MissingSt011Rom), :938-943 (Hayazashi Nidan Morita Shougi CRC32 match)"
|
source_ref: "backend/snes-core/src/memory/cartridge.rs:298-300 (MissingSt011Rom), :938-943 (Hayazashi Nidan Morita Shougi CRC32 match)"
|
||||||
|
|
||||||
@@ -182,7 +172,6 @@ files:
|
|||||||
system: nintendo-snes
|
system: nintendo-snes
|
||||||
description: "ARM firmware for ST018"
|
description: "ARM firmware for ST018"
|
||||||
required: true
|
required: true
|
||||||
mode: standalone
|
|
||||||
min_size: 163840
|
min_size: 163840
|
||||||
validation: [size]
|
validation: [size]
|
||||||
source_ref: "backend/snes-core/src/memory/cartridge.rs:310-320 (MissingSt018Rom), backend/snes-coprocessors/src/st018.rs:18-22,346-352 (TOTAL_ROM_LEN check)"
|
source_ref: "backend/snes-core/src/memory/cartridge.rs:310-320 (MissingSt018Rom), backend/snes-coprocessors/src/st018.rs:18-22,346-352 (TOTAL_ROM_LEN check)"
|
||||||
@@ -195,7 +184,6 @@ files:
|
|||||||
system: sega-mastersystem
|
system: sega-mastersystem
|
||||||
description: "Master System BIOS"
|
description: "Master System BIOS"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
source_ref: "frontend/jgenesis-native-driver/src/mainloop/smsgg.rs:27-33,50-56,74-80,112-122, backend/smsgg-core/src/memory.rs:101-106,148-168"
|
source_ref: "frontend/jgenesis-native-driver/src/mainloop/smsgg.rs:27-33,50-56,74-80,112-122, backend/smsgg-core/src/memory.rs:101-106,148-168"
|
||||||
note: "Read only when sms_boot_from_bios is enabled or the emulator runs without a cartridge, both off by default. One slot accepts any regional dump, no size or content check."
|
note: "Read only when sms_boot_from_bios is enabled or the emulator runs without a cartridge, both off by default. One slot accepts any regional dump, no size or content check."
|
||||||
|
|
||||||
@@ -203,7 +191,6 @@ files:
|
|||||||
system: sega-gamegear
|
system: sega-gamegear
|
||||||
description: "Game Gear BIOS"
|
description: "Game Gear BIOS"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
source_ref: "frontend/jgenesis-native-driver/src/mainloop/smsgg.rs:27-33,50-56,74-80,112-122"
|
source_ref: "frontend/jgenesis-native-driver/src/mainloop/smsgg.rs:27-33,50-56,74-80,112-122"
|
||||||
note: "Read only when gg_boot_from_bios is enabled or the emulator runs without a cartridge, both off by default."
|
note: "Read only when gg_boot_from_bios is enabled or the emulator runs without a cartridge, both off by default."
|
||||||
|
|
||||||
@@ -214,7 +201,6 @@ files:
|
|||||||
system: nintendo-gb
|
system: nintendo-gb
|
||||||
description: "Game Boy boot ROM"
|
description: "Game Boy boot ROM"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
size: 256
|
size: 256
|
||||||
validation: [size]
|
validation: [size]
|
||||||
source_ref: "frontend/jgenesis-native-driver/src/mainloop/gb.rs:29-33,77-89, backend/gb-core/src/memory.rs:9,45-63 (DMG_BOOT_ROM_LEN check)"
|
source_ref: "frontend/jgenesis-native-driver/src/mainloop/gb.rs:29-33,77-89, backend/gb-core/src/memory.rs:9,45-63 (DMG_BOOT_ROM_LEN check)"
|
||||||
@@ -224,7 +210,6 @@ files:
|
|||||||
system: nintendo-gbc
|
system: nintendo-gbc
|
||||||
description: "Game Boy Color boot ROM"
|
description: "Game Boy Color boot ROM"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
min_size: 2048
|
min_size: 2048
|
||||||
max_size: 2304
|
max_size: 2304
|
||||||
validation: [size]
|
validation: [size]
|
||||||
@@ -239,7 +224,6 @@ files:
|
|||||||
system: nintendo-nes
|
system: nintendo-nes
|
||||||
description: "External NES color palette"
|
description: "External NES color palette"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
min_size: 192
|
min_size: 192
|
||||||
max_size: 1536
|
max_size: 1536
|
||||||
validation: [size]
|
validation: [size]
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ notes: |
|
|||||||
the JVM default soundbank in place. VlcDir points at a VLC installation that
|
the JVM default soundbank in place. VlcDir points at a VLC installation that
|
||||||
vlcj scans for libvlc.
|
vlcj scans for libvlc.
|
||||||
ref: src/media/emulator/media/EmulatorMIDI.java:26-36,
|
ref: src/media/emulator/media/EmulatorMIDI.java:26-36,
|
||||||
src/main/emulator/ui/swt/Property.java:1860-1901
|
src/main/emulator/ui/swt/Property.java:1863-1904
|
||||||
|
|
||||||
files:
|
files:
|
||||||
- name: M7_EmuSmw7.dll
|
- name: M7_EmuSmw7.dll
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ emulator: Kronos
|
|||||||
type: libretro
|
type: libretro
|
||||||
core_classification: official_port
|
core_classification: official_port
|
||||||
source: "https://github.com/libretro/yabause"
|
source: "https://github.com/libretro/yabause"
|
||||||
|
source_branch: kronos
|
||||||
upstream: "https://github.com/FCare/Kronos"
|
upstream: "https://github.com/FCare/Kronos"
|
||||||
profiled_date: "2026-03-24"
|
profiled_date: "2026-03-24"
|
||||||
source_commit: "8926b0c6c347f8c5c755911ddb0ac695420ffbf8"
|
source_commit: "3791ffb2221fda61d70aec5cca376418caf05669"
|
||||||
core_version: "v2.6.0"
|
core_version: "v2.6.0"
|
||||||
display_name: "Sega - Saturn/ST-V (Kronos)"
|
display_name: "Sega - Saturn/ST-V (Kronos)"
|
||||||
cores:
|
cores:
|
||||||
@@ -244,7 +245,7 @@ files:
|
|||||||
size: 2097152
|
size: 2097152
|
||||||
validation: [size]
|
validation: [size]
|
||||||
note: "ROM cartridge for The King of Fighters '95. Auto-detected via game DB."
|
note: "ROM cartridge for The King of Fighters '95. Auto-detected via game DB."
|
||||||
source_ref: "yabause/src/utils/src/db.c:43-44, yabause/src/cs0.c:1207-1216"
|
source_ref: "yabause/src/utils/src/db.c:43-44, yabause/src/sys/memory/src/cs0.c:1526-1535"
|
||||||
|
|
||||||
- name: "mpr-19367-mx.ic1"
|
- name: "mpr-19367-mx.ic1"
|
||||||
system: sega-saturn
|
system: sega-saturn
|
||||||
@@ -252,4 +253,4 @@ files:
|
|||||||
size: 2097152
|
size: 2097152
|
||||||
validation: [size]
|
validation: [size]
|
||||||
note: "ROM cartridge for Ultraman: Hikari no Kyojin Densetsu. Auto-detected via game DB."
|
note: "ROM cartridge for Ultraman: Hikari no Kyojin Densetsu. Auto-detected via game DB."
|
||||||
source_ref: "yabause/src/utils/src/db.c:45, yabause/src/cs0.c:1207-1216"
|
source_ref: "yabause/src/utils/src/db.c:45, yabause/src/sys/memory/src/cs0.c:1526-1535"
|
||||||
+5
-5
@@ -36,7 +36,7 @@ files:
|
|||||||
min_size: 4194304
|
min_size: 4194304
|
||||||
max_size: 8388608
|
max_size: 8388608
|
||||||
validation: [size]
|
validation: [size]
|
||||||
source_ref: "pcsx2/ps2/BiosTools.cpp:231-254,267-322"
|
source_ref: "pcsx2/ps2/BiosTools.cpp:33-34,63-180,249-274,286-348"
|
||||||
note: >
|
note: >
|
||||||
Scans pcsx2/bios/ for any file between 4 MB and 8 MB. Validates via romdir
|
Scans pcsx2/bios/ for any file between 4 MB and 8 MB. Validates via romdir
|
||||||
structure parsing (RESET + ROMVER entries). User selects BIOS via core option
|
structure parsing (RESET + ROMVER entries). User selects BIOS via core option
|
||||||
@@ -47,7 +47,7 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
max_size: 4194304
|
max_size: 4194304
|
||||||
description: "DVD player ROM"
|
description: "DVD player ROM"
|
||||||
source_ref: "pcsx2/ps2/BiosTools.cpp:190-211,314"
|
source_ref: "pcsx2/ps2/BiosTools.cpp:189-229,339"
|
||||||
note: >
|
note: >
|
||||||
DVD player ROM. Tries <biospath>.rom1 (appended) then <biosbase>.rom1
|
DVD player ROM. Tries <biospath>.rom1 (appended) then <biosbase>.rom1
|
||||||
(extension replaced). Silently skipped if not found.
|
(extension replaced). Silently skipped if not found.
|
||||||
@@ -57,7 +57,7 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
max_size: 524288
|
max_size: 524288
|
||||||
description: "Chinese ROM extension"
|
description: "Chinese ROM extension"
|
||||||
source_ref: "pcsx2/ps2/BiosTools.cpp:190-211,315"
|
source_ref: "pcsx2/ps2/BiosTools.cpp:189-229,340"
|
||||||
note: >
|
note: >
|
||||||
Chinese region ROM extension. Same naming convention as rom1.
|
Chinese region ROM extension. Same naming convention as rom1.
|
||||||
Only present on Chinese region consoles.
|
Only present on Chinese region consoles.
|
||||||
@@ -68,7 +68,7 @@ files:
|
|||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
size: 1024
|
size: 1024
|
||||||
description: "NVRAM / EEPROM data"
|
description: "NVRAM / EEPROM data"
|
||||||
source_ref: "pcsx2/CDVD/CDVD.cpp:224-267"
|
source_ref: "pcsx2/CDVD/CDVD.cpp:196-227"
|
||||||
note: >
|
note: >
|
||||||
Console EEPROM data (language, timezone, region, iLink ID, OSD settings).
|
Console EEPROM data (language, timezone, region, iLink ID, OSD settings).
|
||||||
Path derived from BIOS path with extension replaced to .nvm.
|
Path derived from BIOS path with extension replaced to .nvm.
|
||||||
@@ -80,7 +80,7 @@ files:
|
|||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
size: 4
|
size: 4
|
||||||
description: "Mechacon version"
|
description: "Mechacon version"
|
||||||
source_ref: "pcsx2/CDVD/CDVD.cpp:255-266"
|
source_ref: "pcsx2/CDVD/CDVD.cpp:229-245"
|
||||||
note: >
|
note: >
|
||||||
Mechacon (disc drive controller) version as u32.
|
Mechacon (disc drive controller) version as u32.
|
||||||
Auto-created with default 0x00020603 if missing.
|
Auto-created with default 0x00020603 if missing.
|
||||||
|
|||||||
+1
-1
@@ -3183,7 +3183,7 @@ files:
|
|||||||
required: true
|
required: true
|
||||||
category: bios_zip
|
category: bios_zip
|
||||||
system: casio-loopy
|
system: casio-loopy
|
||||||
source_ref: "src/mame/casio/casloopy.cpp:2105-2462"
|
source_ref: "src/mame/casio/casloopy.cpp:2097-2105"
|
||||||
contents:
|
contents:
|
||||||
- name: hd6437021.lsi302
|
- name: hd6437021.lsi302
|
||||||
description: SH-1 CPU internal mask ROM
|
description: SH-1 CPU internal mask ROM
|
||||||
|
|||||||
@@ -680,7 +680,7 @@ files:
|
|||||||
required: true
|
required: true
|
||||||
category: bios_zip
|
category: bios_zip
|
||||||
system: casio-loopy
|
system: casio-loopy
|
||||||
source_ref: "src/mame/casio/casloopy.cpp:2105-2462"
|
source_ref: "src/mame/casio/casloopy.cpp:2097-2105"
|
||||||
contents:
|
contents:
|
||||||
- name: hd6437021.lsi302
|
- name: hd6437021.lsi302
|
||||||
description: SH-1 CPU internal mask ROM
|
description: SH-1 CPU internal mask ROM
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ files:
|
|||||||
- name: "scph5502.bin"
|
- name: "scph5502.bin"
|
||||||
system: sony-playstation
|
system: sony-playstation
|
||||||
description: "SCPH-5502 / v3.0E BIOS"
|
description: "SCPH-5502 / v3.0E BIOS"
|
||||||
region: [europe]
|
region: [europe, oceania]
|
||||||
required: true
|
required: true
|
||||||
size: 524288
|
size: 524288
|
||||||
sha256: "1faaa18fa820a0225e488d9f086296b8e6c46df739666093987ff7d8fd352c09"
|
sha256: "1faaa18fa820a0225e488d9f086296b8e6c46df739666093987ff7d8fd352c09"
|
||||||
@@ -376,7 +376,7 @@ files:
|
|||||||
- name: "epr-17952a.ic8"
|
- name: "epr-17952a.ic8"
|
||||||
system: sega-stv
|
system: sega-stv
|
||||||
description: "ST-V BIOS (North America)"
|
description: "ST-V BIOS (North America)"
|
||||||
region: [north-america]
|
region: [north-america, south-korea, asia-pal, brazil, latin-america]
|
||||||
required: true
|
required: true
|
||||||
size: 524288
|
size: 524288
|
||||||
validation: [size]
|
validation: [size]
|
||||||
|
|||||||
@@ -23,28 +23,24 @@ notes: >
|
|||||||
files:
|
files:
|
||||||
- name: "syscard3.pce"
|
- name: "syscard3.pce"
|
||||||
description: "CD-ROM System Card 3.0 (Japan)"
|
description: "CD-ROM System Card 3.0 (Japan)"
|
||||||
region: [japan]
|
|
||||||
required: true
|
required: true
|
||||||
source_ref: "libretro.c:65, libretro.c:1896-1901"
|
source_ref: "libretro.c:65, libretro.c:1896-1901"
|
||||||
note: "Default value for pce_fast_cdbios option."
|
note: "Default value for pce_fast_cdbios option."
|
||||||
|
|
||||||
- name: "syscard2.pce"
|
- name: "syscard2.pce"
|
||||||
description: "CD-ROM System Card 2.0 (Japan)"
|
description: "CD-ROM System Card 2.0 (Japan)"
|
||||||
region: [japan]
|
|
||||||
required: false
|
required: false
|
||||||
source_ref: "libretro.c:1903"
|
source_ref: "libretro.c:1903"
|
||||||
note: "pce_fast_cdbios = 'System Card 2'."
|
note: "pce_fast_cdbios = 'System Card 2'."
|
||||||
|
|
||||||
- name: "syscard1.pce"
|
- name: "syscard1.pce"
|
||||||
description: "CD-ROM System Card 1.0 (Japan)"
|
description: "CD-ROM System Card 1.0 (Japan)"
|
||||||
region: [japan]
|
|
||||||
required: false
|
required: false
|
||||||
source_ref: "libretro.c:1905"
|
source_ref: "libretro.c:1905"
|
||||||
note: "pce_fast_cdbios = 'System Card 1'."
|
note: "pce_fast_cdbios = 'System Card 1'."
|
||||||
|
|
||||||
- name: "gexpress.pce"
|
- name: "gexpress.pce"
|
||||||
description: "Games Express CD Card"
|
description: "Games Express CD Card"
|
||||||
region: [japan]
|
|
||||||
required: false
|
required: false
|
||||||
aliases: ["gecard.pce"]
|
aliases: ["gecard.pce"]
|
||||||
source_ref: "libretro.c:1906-1907"
|
source_ref: "libretro.c:1906-1907"
|
||||||
@@ -52,14 +48,12 @@ files:
|
|||||||
|
|
||||||
- name: "syscard3u.pce"
|
- name: "syscard3u.pce"
|
||||||
description: "CD-ROM System Card 3.0 (US / TurboGrafx-CD)"
|
description: "CD-ROM System Card 3.0 (US / TurboGrafx-CD)"
|
||||||
region: [north-america]
|
|
||||||
required: false
|
required: false
|
||||||
source_ref: "libretro.c:1908-1909"
|
source_ref: "libretro.c:1908-1909"
|
||||||
note: "pce_fast_cdbios = 'System Card 3 US'. Not in .info."
|
note: "pce_fast_cdbios = 'System Card 3 US'. Not in .info."
|
||||||
|
|
||||||
- name: "syscard2u.pce"
|
- name: "syscard2u.pce"
|
||||||
description: "CD-ROM System Card 2.0 (US / TurboGrafx-CD)"
|
description: "CD-ROM System Card 2.0 (US / TurboGrafx-CD)"
|
||||||
region: [north-america]
|
|
||||||
required: false
|
required: false
|
||||||
source_ref: "libretro.c:1910-1911"
|
source_ref: "libretro.c:1910-1911"
|
||||||
note: "pce_fast_cdbios = 'System Card 2 US'. Not in .info."
|
note: "pce_fast_cdbios = 'System Card 2 US'. Not in .info."
|
||||||
+9
-9
@@ -30,7 +30,7 @@ notes: |
|
|||||||
mgba_skip_bios (default OFF) - skip the boot animation even if loaded
|
mgba_skip_bios (default OFF) - skip the boot animation even if loaded
|
||||||
|
|
||||||
The libretro port resolves BIOS filenames in retro_load_game()
|
The libretro port resolves BIOS filenames in retro_load_game()
|
||||||
(src/platform/libretro/libretro.c:2091-2148). It picks the filename
|
(src/platform/libretro/libretro.c:2060-2118). It picks the filename
|
||||||
based on detected model:
|
based on detected model:
|
||||||
GBA -> gba_bios.bin
|
GBA -> gba_bios.bin
|
||||||
GB (DMG/MGB) -> gb_bios.bin
|
GB (DMG/MGB) -> gb_bios.bin
|
||||||
@@ -38,7 +38,7 @@ notes: |
|
|||||||
SGB -> sgb_bios.bin
|
SGB -> sgb_bios.bin
|
||||||
SGB2 -> gb_bios.bin (falls to default, missing case)
|
SGB2 -> gb_bios.bin (falls to default, missing case)
|
||||||
|
|
||||||
The standalone core (src/gb/core.c:637-684) correctly maps SGB2 to
|
The standalone core (src/gb/core.c:649-696) correctly maps SGB2 to
|
||||||
sgb_bios.bin (marked TODO for a dedicated file). The libretro port
|
sgb_bios.bin (marked TODO for a dedicated file). The libretro port
|
||||||
omits GB_MODEL_SGB2 from its switch, so SGB2 falls to default.
|
omits GB_MODEL_SGB2 from its switch, so SGB2 falls to default.
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ files:
|
|||||||
size: 16384 # 16 KB (0x4000, GBA_SIZE_BIOS)
|
size: 16384 # 16 KB (0x4000, GBA_SIZE_BIOS)
|
||||||
validation: [size]
|
validation: [size]
|
||||||
note: "GBA BIOS. HLE replacement built in. Real BIOS adds boot logo and full SWI accuracy."
|
note: "GBA BIOS. HLE replacement built in. Real BIOS adds boot logo and full SWI accuracy."
|
||||||
source_ref: "src/platform/libretro/libretro.c:2099"
|
source_ref: "src/platform/libretro/libretro.c:2068"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Game Boy - boot ROM (optional, skip fallback)
|
# Game Boy - boot ROM (optional, skip fallback)
|
||||||
@@ -68,7 +68,7 @@ files:
|
|||||||
size: 256 # 256 B (0x100)
|
size: 256 # 256 B (0x100)
|
||||||
validation: [crc32]
|
validation: [crc32]
|
||||||
note: "DMG/MGB boot ROM. Scrolling Nintendo logo. Skipped if absent."
|
note: "DMG/MGB boot ROM. Scrolling Nintendo logo. Skipped if absent."
|
||||||
source_ref: "src/platform/libretro/libretro.c:971"
|
source_ref: "src/platform/libretro/libretro.c:2104"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Game Boy Color - boot ROM (optional, skip fallback)
|
# Game Boy Color - boot ROM (optional, skip fallback)
|
||||||
@@ -79,7 +79,7 @@ files:
|
|||||||
size: 2304 # 2304 B (0x900)
|
size: 2304 # 2304 B (0x900)
|
||||||
validation: [crc32]
|
validation: [crc32]
|
||||||
note: "CGB boot ROM. Used for CGB, AGB (GBA in GB mode), and SCGB models."
|
note: "CGB boot ROM. Used for CGB, AGB (GBA in GB mode), and SCGB models."
|
||||||
source_ref: "src/platform/libretro/libretro.c:2127"
|
source_ref: "src/platform/libretro/libretro.c:2097"
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Super Game Boy - boot ROM (optional, skip fallback)
|
# Super Game Boy - boot ROM (optional, skip fallback)
|
||||||
@@ -89,14 +89,14 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
size: 256 # 256 B (0x100)
|
size: 256 # 256 B (0x100)
|
||||||
validation: [crc32]
|
validation: [crc32]
|
||||||
note: "SGB boot ROM. SGB2 maps here in standalone (src/gb/core.c:643) but falls to gb_bios.bin in libretro."
|
note: "SGB boot ROM. SGB2 maps here in standalone (src/gb/core.c:655) but falls to gb_bios.bin in libretro."
|
||||||
source_ref: "src/platform/libretro/libretro.c:2130"
|
source_ref: "src/platform/libretro/libretro.c:2100"
|
||||||
|
|
||||||
platform_details:
|
platform_details:
|
||||||
gba:
|
gba:
|
||||||
bios_size: 16384 # 16 KB
|
bios_size: 16384 # 16 KB
|
||||||
hle_bios: true
|
hle_bios: true
|
||||||
source_ref: "src/gba/hle-bios.c, src/gba/gba.c:520-551"
|
source_ref: "src/gba/hle-bios.c, src/gba/gba.c:513-552"
|
||||||
note: |
|
note: |
|
||||||
GBALoadBIOS() validates size (must be exactly 0x4000) then computes
|
GBALoadBIOS() validates size (must be exactly 0x4000) then computes
|
||||||
an internal checksum. Two official checksums are recognized:
|
an internal checksum. Two official checksums are recognized:
|
||||||
@@ -125,7 +125,7 @@ platform_details:
|
|||||||
sgb:
|
sgb:
|
||||||
bios_size: 256 # 256 B
|
bios_size: 256 # 256 B
|
||||||
hle_bios: false
|
hle_bios: false
|
||||||
source_ref: "src/gb/gb.c:570-605, src/gb/core.c:642-644"
|
source_ref: "src/gb/gb.c:570-605, src/gb/core.c:654-656"
|
||||||
note: |
|
note: |
|
||||||
Accepted SGB CRC32 checksums:
|
Accepted SGB CRC32 checksums:
|
||||||
SGB = 0xEC8A83B9, SGB2 = 0x53D0DD63
|
SGB = 0xEC8A83B9, SGB2 = 0x53D0DD63
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ notes: |
|
|||||||
MSX.emu/src/blueMSX/Memory/romMapperDRAM.c:88-99), and the Philips FDC, Panasonic
|
MSX.emu/src/blueMSX/Memory/romMapperDRAM.c:88-99), and the Philips FDC, Panasonic
|
||||||
FDC and MoonSound mappers test nothing at all
|
FDC and MoonSound mappers test nothing at all
|
||||||
(MSX.emu/src/blueMSX/Memory/romMapperPhilipsFdc.c:139-146,
|
(MSX.emu/src/blueMSX/Memory/romMapperPhilipsFdc.c:139-146,
|
||||||
MSX.emu/src/blueMSX/Memory/romMapperTC8566AF.c:236-262,
|
MSX.emu/src/blueMSX/Memory/romMapperTC8566AF.c:228-255,
|
||||||
MSX.emu/src/blueMSX/Memory/romMapperMoonsound.c:129-158).
|
MSX.emu/src/blueMSX/Memory/romMapperMoonsound.c:129-158).
|
||||||
|
|
||||||
The ROM database is compiled in. mediaDbGuessRom matches a cartridge's SHA-1 against
|
The ROM database is compiled in. mediaDbGuessRom matches a cartridge's SHA-1 against
|
||||||
@@ -396,7 +396,7 @@ files:
|
|||||||
required: true
|
required: true
|
||||||
size: 16384
|
size: 16384
|
||||||
description: "Panasonic floppy disk ROM of the MSX2+ machine"
|
description: "Panasonic floppy disk ROM of the MSX2+ machine"
|
||||||
source_ref: "MSX.emu/src/blueMSX/Board/Machine.c:300-339,450-468,1168-1181, MSX.emu/src/main/RomLoader.cc:57-92,94-133, MSX.emu/src/blueMSX/Memory/romMapperTC8566AF.c:236-262"
|
source_ref: "MSX.emu/src/blueMSX/Board/Machine.c:300-339,450-468,1168-1181, MSX.emu/src/main/RomLoader.cc:57-92,94-133, MSX.emu/src/blueMSX/Memory/romMapperTC8566AF.c:228-255"
|
||||||
|
|
||||||
- name: "MSXTR.rom"
|
- name: "MSXTR.rom"
|
||||||
path: "Machines/Shared Roms/MSXTR.rom"
|
path: "Machines/Shared Roms/MSXTR.rom"
|
||||||
@@ -436,7 +436,7 @@ files:
|
|||||||
required: true
|
required: true
|
||||||
size: 65536
|
size: 65536
|
||||||
description: "MSX-DOS 2.3 disk ROM of the turbo R machine"
|
description: "MSX-DOS 2.3 disk ROM of the turbo R machine"
|
||||||
source_ref: "MSX.emu/src/blueMSX/Board/Machine.c:300-339,450-468,1168-1181, MSX.emu/src/main/RomLoader.cc:57-92,94-133, MSX.emu/src/blueMSX/Memory/romMapperTC8566AF.c:236-262"
|
source_ref: "MSX.emu/src/blueMSX/Board/Machine.c:300-339,450-468,1168-1181, MSX.emu/src/main/RomLoader.cc:57-92,94-133, MSX.emu/src/blueMSX/Memory/romMapperTC8566AF.c:228-255"
|
||||||
|
|
||||||
- name: "coleco.rom"
|
- name: "coleco.rom"
|
||||||
path: "Machines/COL - ColecoVision/coleco.rom"
|
path: "Machines/COL - ColecoVision/coleco.rom"
|
||||||
|
|||||||
@@ -151,16 +151,18 @@ files:
|
|||||||
source_ref: "cbus/ideio.c:1928-1930, cbus/ideio.c:1944-1981"
|
source_ref: "cbus/ideio.c:1928-1930, cbus/ideio.c:1944-1981"
|
||||||
|
|
||||||
# -- SCSI BIOS ROM --
|
# -- SCSI BIOS ROM --
|
||||||
# Loaded by scsiio.c:219. 16 KB (0x4000). Falls back to scsibios[] stub.
|
# Read into a 0x4000 buffer; any non-empty read is kept. Falls back to the
|
||||||
|
# scsibios[] stub.
|
||||||
- name: "scsi.rom"
|
- name: "scsi.rom"
|
||||||
path: "np2kai/scsi.rom"
|
path: "np2kai/scsi.rom"
|
||||||
size: 16384
|
max_size: 16384
|
||||||
required: false
|
required: false
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: >
|
note: >
|
||||||
SCSI controller BIOS ROM (16 KB). The core includes a built-in
|
SCSI controller BIOS ROM, up to 16 KB: the full-size test is commented
|
||||||
|
out and a shorter file is used as read. The core includes a built-in
|
||||||
SCSI BIOS stub as fallback.
|
SCSI BIOS stub as fallback.
|
||||||
source_ref: "cbus/scsiio.c:214-231"
|
source_ref: "cbus/scsiio.c:720-732"
|
||||||
|
|
||||||
# -- SASI BIOS ROM --
|
# -- SASI BIOS ROM --
|
||||||
# Loaded by sasiio.c:455. 4 KB (0x1000). Falls back to sasibios[] stub.
|
# Loaded by sasiio.c:455. 4 KB (0x1000). Falls back to sasibios[] stub.
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ platform_details:
|
|||||||
size: 4096
|
size: 4096
|
||||||
|
|
||||||
load_priority:
|
load_priority:
|
||||||
source_ref: "source/PokeMini.c:515-523"
|
source_ref: "source/PokeMini.c:500-509"
|
||||||
note: |
|
note: |
|
||||||
During PokeMini_Reset(), the core first reloads FreeBIOS as a
|
During PokeMini_Reset(), the core first reloads FreeBIOS as a
|
||||||
safe baseline, then attempts to load bios.min from disk. If
|
safe baseline, then attempts to load bios.min from disk. If
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ files:
|
|||||||
region: [north-america]
|
region: [north-america]
|
||||||
size: 2097152
|
size: 2097152
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "GameCube NTSC-U boot ROM. HLE available but real IPL needed for GC menu boot and accurate fonts"
|
note: "GameCube NTSC-U boot ROM. HLE available but real IPL needed for GC menu boot and accurate fonts"
|
||||||
source_ref: "Source/Core/Common/CommonPaths.h:138, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:109-110"
|
source_ref: "Source/Core/Common/CommonPaths.h:138, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:109-110"
|
||||||
@@ -47,7 +46,6 @@ files:
|
|||||||
region: [europe]
|
region: [europe]
|
||||||
size: 2097152
|
size: 2097152
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "GameCube PAL boot ROM"
|
note: "GameCube PAL boot ROM"
|
||||||
source_ref: "Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:184"
|
source_ref: "Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:184"
|
||||||
@@ -57,7 +55,6 @@ files:
|
|||||||
region: [japan]
|
region: [japan]
|
||||||
size: 2097152
|
size: 2097152
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "GameCube NTSC-J boot ROM. JAP is the legacy directory name"
|
note: "GameCube NTSC-J boot ROM. JAP is the legacy directory name"
|
||||||
source_ref: "Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:186"
|
source_ref: "Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:186"
|
||||||
@@ -70,7 +67,6 @@ files:
|
|||||||
path: "GC/dsp_rom.bin"
|
path: "GC/dsp_rom.bin"
|
||||||
size: 8192
|
size: 8192
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
validation: [size]
|
validation: [size]
|
||||||
known_hash_adler32: "0x66f334fe"
|
known_hash_adler32: "0x66f334fe"
|
||||||
@@ -82,7 +78,6 @@ files:
|
|||||||
path: "GC/dsp_coef.bin"
|
path: "GC/dsp_coef.bin"
|
||||||
size: 4096
|
size: 4096
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
validation: [size]
|
validation: [size]
|
||||||
known_hash_adler32: "0xf3b93527"
|
known_hash_adler32: "0xf3b93527"
|
||||||
@@ -98,7 +93,6 @@ files:
|
|||||||
path: "GC/font_western.bin"
|
path: "GC/font_western.bin"
|
||||||
size: 9589
|
size: 9589
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "Windows-1252 font for GC/Wii text rendering. Free alternative bundled, real one from IPL dump preferred"
|
note: "Windows-1252 font for GC/Wii text rendering. Free alternative bundled, real one from IPL dump preferred"
|
||||||
source_ref: "Source/Core/Common/CommonPaths.h:132, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:131,203-218"
|
source_ref: "Source/Core/Common/CommonPaths.h:132, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:131,203-218"
|
||||||
@@ -107,7 +101,6 @@ files:
|
|||||||
path: "GC/font_japanese.bin"
|
path: "GC/font_japanese.bin"
|
||||||
size: 303693
|
size: 303693
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "Shift-JIS font for Japanese text. Free alternative bundled, real one from IPL dump preferred"
|
note: "Shift-JIS font for Japanese text. Free alternative bundled, real one from IPL dump preferred"
|
||||||
source_ref: "Source/Core/Common/CommonPaths.h:135, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:130"
|
source_ref: "Source/Core/Common/CommonPaths.h:135, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:130"
|
||||||
@@ -118,7 +111,6 @@ files:
|
|||||||
- name: "gba_bios.bin"
|
- name: "gba_bios.bin"
|
||||||
path: "GBA/gba_bios.bin"
|
path: "GBA/gba_bios.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "GBA BIOS for GC-GBA link feature (uses integrated mGBA). Path configurable in settings"
|
note: "GBA BIOS for GC-GBA link feature (uses integrated mGBA). Path configurable in settings"
|
||||||
source_ref: "Source/Core/Common/CommonPaths.h:144, Source/Core/Core/HW/GBACore.cpp:344-361"
|
source_ref: "Source/Core/Common/CommonPaths.h:144, Source/Core/Core/HW/GBACore.cpp:344-361"
|
||||||
@@ -127,7 +119,6 @@ files:
|
|||||||
- name: "SYSCONF"
|
- name: "SYSCONF"
|
||||||
path: "Wii/shared2/sys/SYSCONF"
|
path: "Wii/shared2/sys/SYSCONF"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "Wii system configuration. Auto-generated by PrimeHack, can be imported from NAND backup"
|
note: "Wii system configuration. Auto-generated by PrimeHack, can be imported from NAND backup"
|
||||||
source_ref: "Source/Core/Common/CommonPaths.h:118, Source/Core/Core/WiiRoot.cpp:266"
|
source_ref: "Source/Core/Common/CommonPaths.h:118, Source/Core/Core/WiiRoot.cpp:266"
|
||||||
@@ -136,7 +127,6 @@ files:
|
|||||||
path: "Wii/title/00000001/00000002/data/setting.txt"
|
path: "Wii/title/00000001/00000002/data/setting.txt"
|
||||||
size: 256
|
size: 256
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "Wii region/language settings. Auto-generated during Wii boot emulation"
|
note: "Wii region/language settings. Auto-generated during Wii boot emulation"
|
||||||
source_ref: "Source/Core/Common/CommonPaths.h:152, Source/Core/Core/Boot/Boot_BS2Emu.cpp:377-454"
|
source_ref: "Source/Core/Common/CommonPaths.h:152, Source/Core/Core/Boot/Boot_BS2Emu.cpp:377-454"
|
||||||
@@ -145,7 +135,6 @@ files:
|
|||||||
- name: "nand.bin"
|
- name: "nand.bin"
|
||||||
path: null
|
path: null
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "BootMii NAND backup. Can be imported to populate Wii NAND with channels, saves, system menu"
|
note: "BootMii NAND backup. Can be imported to populate Wii NAND with channels, saves, system menu"
|
||||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:26-89"
|
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:26-89"
|
||||||
@@ -154,7 +143,6 @@ files:
|
|||||||
path: null
|
path: null
|
||||||
size: 1024
|
size: 1024
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "OTP/SEEPROM dump (Wii encryption keys). Needed if not appended to nand.bin for NAND import"
|
note: "OTP/SEEPROM dump (Wii encryption keys). Needed if not appended to nand.bin for NAND import"
|
||||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:19,76-88"
|
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:19,76-88"
|
||||||
@@ -163,7 +151,6 @@ files:
|
|||||||
- name: "WiiSD.raw"
|
- name: "WiiSD.raw"
|
||||||
path: "Load/WiiSD.raw"
|
path: "Load/WiiSD.raw"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "Virtual SD card image for Wii homebrew. Auto-created, supports SD/SDHC up to 4GB"
|
note: "Virtual SD card image for Wii homebrew. Auto-created, supports SD/SDHC up to 4GB"
|
||||||
source_ref: "Source/Core/Common/CommonPaths.h:149"
|
source_ref: "Source/Core/Common/CommonPaths.h:149"
|
||||||
@@ -172,7 +159,6 @@ files:
|
|||||||
- name: "codehandler.bin"
|
- name: "codehandler.bin"
|
||||||
path: null
|
path: null
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "Gecko/Ocarina cheat code handler. Shipped with PrimeHack in Sys/"
|
note: "Gecko/Ocarina cheat code handler. Shipped with PrimeHack in Sys/"
|
||||||
source_ref: "Source/Core/Common/CommonPaths.h:154, Source/Core/Core/GeckoCode.cpp:121"
|
source_ref: "Source/Core/Common/CommonPaths.h:154, Source/Core/Core/GeckoCode.cpp:121"
|
||||||
@@ -181,7 +167,6 @@ files:
|
|||||||
- name: "Wii System Menu"
|
- name: "Wii System Menu"
|
||||||
path: null
|
path: null
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "Wii System Menu WAD. Installed to NAND via Tools > Install WAD, needed for Wii Menu boot"
|
note: "Wii System Menu WAD. Installed to NAND via Tools > Install WAD, needed for Wii Menu boot"
|
||||||
source_ref: "Source/Core/DolphinQt/MenuBar.cpp:335, Source/Core/DolphinQt/MenuBar.cpp:1582-1590"
|
source_ref: "Source/Core/DolphinQt/MenuBar.cpp:335, Source/Core/DolphinQt/MenuBar.cpp:1582-1590"
|
||||||
@@ -190,7 +175,6 @@ files:
|
|||||||
- name: "clientca.pem"
|
- name: "clientca.pem"
|
||||||
path: "Wii/clientca.pem"
|
path: "Wii/clientca.pem"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "SSL client certificate. Auto-extracted from IOS13 content during NAND import"
|
note: "SSL client certificate. Auto-extracted from IOS13 content during NAND import"
|
||||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:201-285"
|
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:201-285"
|
||||||
@@ -198,7 +182,6 @@ files:
|
|||||||
- name: "clientcakey.pem"
|
- name: "clientcakey.pem"
|
||||||
path: "Wii/clientcakey.pem"
|
path: "Wii/clientcakey.pem"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "SSL client private key. Auto-extracted from IOS13 content during NAND import"
|
note: "SSL client private key. Auto-extracted from IOS13 content during NAND import"
|
||||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:237"
|
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:237"
|
||||||
@@ -206,7 +189,6 @@ files:
|
|||||||
- name: "rootca.pem"
|
- name: "rootca.pem"
|
||||||
path: "Wii/rootca.pem"
|
path: "Wii/rootca.pem"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
hle_fallback: true
|
hle_fallback: true
|
||||||
note: "SSL root CA certificate. Auto-extracted from IOS13 content during NAND import"
|
note: "SSL root CA certificate. Auto-extracted from IOS13 content during NAND import"
|
||||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:238"
|
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:238"
|
||||||
@@ -220,105 +202,90 @@ files:
|
|||||||
- name: "rtl8723a_fw.bin"
|
- name: "rtl8723a_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8723a_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8723a_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8723A BT firmware for Wiimote passthrough"
|
note: "Realtek 8723A BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:379"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:379"
|
||||||
|
|
||||||
- name: "rtl8723b_fw.bin"
|
- name: "rtl8723b_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8723b_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8723b_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8723B BT firmware for Wiimote passthrough"
|
note: "Realtek 8723B BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:516-519"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:516-519"
|
||||||
|
|
||||||
- name: "rtl8723d_fw.bin"
|
- name: "rtl8723d_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8723d_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8723d_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8723D BT firmware for Wiimote passthrough"
|
note: "Realtek 8723D BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:383"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:383"
|
||||||
|
|
||||||
- name: "rtl8761a_fw.bin"
|
- name: "rtl8761a_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8761a_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8761a_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8761A BT firmware for Wiimote passthrough"
|
note: "Realtek 8761A BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:385"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:385"
|
||||||
|
|
||||||
- name: "rtl8761bu_fw.bin"
|
- name: "rtl8761bu_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8761bu_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8761bu_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8761BU BT firmware for Wiimote passthrough"
|
note: "Realtek 8761BU BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:387"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:387"
|
||||||
|
|
||||||
- name: "rtl8821a_fw.bin"
|
- name: "rtl8821a_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8821a_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8821a_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8821A BT firmware for Wiimote passthrough"
|
note: "Realtek 8821A BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:389"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:389"
|
||||||
|
|
||||||
- name: "rtl8821c_fw.bin"
|
- name: "rtl8821c_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8821c_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8821c_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8821C BT firmware for Wiimote passthrough"
|
note: "Realtek 8821C BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:391"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:391"
|
||||||
|
|
||||||
- name: "rtl8822b_fw.bin"
|
- name: "rtl8822b_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8822b_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8822b_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8822B BT firmware for Wiimote passthrough"
|
note: "Realtek 8822B BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:395"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:395"
|
||||||
|
|
||||||
- name: "rtl8822cu_fw.bin"
|
- name: "rtl8822cu_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8822cu_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8822cu_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8822CU BT firmware for Wiimote passthrough"
|
note: "Realtek 8822CU BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:393"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:393"
|
||||||
|
|
||||||
- name: "rtl8851bu_fw.bin"
|
- name: "rtl8851bu_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8851bu_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8851bu_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8851BU BT firmware for Wiimote passthrough"
|
note: "Realtek 8851BU BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:397"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:397"
|
||||||
|
|
||||||
- name: "rtl8852au_fw.bin"
|
- name: "rtl8852au_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8852au_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8852au_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8852AU BT firmware for Wiimote passthrough"
|
note: "Realtek 8852AU BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:399"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:399"
|
||||||
|
|
||||||
- name: "rtl8852bu_fw.bin"
|
- name: "rtl8852bu_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8852bu_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8852bu_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8852BU BT firmware for Wiimote passthrough"
|
note: "Realtek 8852BU BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:401"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:401"
|
||||||
|
|
||||||
- name: "rtl8852cu_fw.bin"
|
- name: "rtl8852cu_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8852cu_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8852cu_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8852CU BT firmware for Wiimote passthrough"
|
note: "Realtek 8852CU BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:403"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:403"
|
||||||
|
|
||||||
- name: "rtl8852btu_fw.bin"
|
- name: "rtl8852btu_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8852btu_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8852btu_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8852BT/8852BE-VT BT firmware for Wiimote passthrough"
|
note: "Realtek 8852BT/8852BE-VT BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:405"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:405"
|
||||||
|
|
||||||
- name: "rtl8922au_fw.bin"
|
- name: "rtl8922au_fw.bin"
|
||||||
path: "Load/Firmware/rtl_bt/rtl8922au_fw.bin"
|
path: "Load/Firmware/rtl_bt/rtl8922au_fw.bin"
|
||||||
required: false
|
required: false
|
||||||
mode: standalone
|
|
||||||
note: "Realtek 8922AU BT firmware for Wiimote passthrough"
|
note: "Realtek 8922AU BT firmware for Wiimote passthrough"
|
||||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:407"
|
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:407"
|
||||||
|
|
||||||
|
|||||||
@@ -21,21 +21,18 @@ files:
|
|||||||
- name: "prod.keys"
|
- name: "prod.keys"
|
||||||
required: true
|
required: true
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Switch production keys for NCA decryption"
|
note: "Switch production keys for NCA decryption"
|
||||||
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:239"
|
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:239"
|
||||||
|
|
||||||
- name: "title.keys"
|
- name: "title.keys"
|
||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Per-title decryption keys, needed for specific game content"
|
note: "Per-title decryption keys, needed for specific game content"
|
||||||
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:240"
|
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:240"
|
||||||
|
|
||||||
- name: "console.keys"
|
- name: "console.keys"
|
||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Console-specific keys for encrypted content"
|
note: "Console-specific keys for encrypted content"
|
||||||
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:241"
|
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:241"
|
||||||
|
|
||||||
|
|||||||
@@ -26,21 +26,18 @@ files:
|
|||||||
- name: "prod.keys"
|
- name: "prod.keys"
|
||||||
required: true
|
required: true
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:654"
|
source_ref: "src/core/crypto/key_manager.cpp:654"
|
||||||
|
|
||||||
- name: "title.keys"
|
- name: "title.keys"
|
||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:657"
|
source_ref: "src/core/crypto/key_manager.cpp:657"
|
||||||
|
|
||||||
- name: "console.keys"
|
- name: "console.keys"
|
||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Console-specific keys (BIS, SD seed)"
|
note: "Console-specific keys (BIS, SD seed)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:658"
|
source_ref: "src/core/crypto/key_manager.cpp:658"
|
||||||
|
|
||||||
@@ -48,7 +45,6 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
size: 160
|
size: 160
|
||||||
mode: standalone
|
|
||||||
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
||||||
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294"
|
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294"
|
||||||
|
|
||||||
|
|||||||
@@ -27,21 +27,18 @@ files:
|
|||||||
- name: "prod.keys"
|
- name: "prod.keys"
|
||||||
required: true
|
required: true
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:655-656"
|
source_ref: "src/core/crypto/key_manager.cpp:655-656"
|
||||||
|
|
||||||
- name: "title.keys"
|
- name: "title.keys"
|
||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:659-660"
|
source_ref: "src/core/crypto/key_manager.cpp:659-660"
|
||||||
|
|
||||||
- name: "console.keys"
|
- name: "console.keys"
|
||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
mode: standalone
|
|
||||||
note: "Console-specific keys (BIS, SD seed)"
|
note: "Console-specific keys (BIS, SD seed)"
|
||||||
source_ref: "src/core/crypto/key_manager.cpp:661-662"
|
source_ref: "src/core/crypto/key_manager.cpp:661-662"
|
||||||
|
|
||||||
@@ -49,7 +46,6 @@ files:
|
|||||||
required: false
|
required: false
|
||||||
path: "switch/"
|
path: "switch/"
|
||||||
size: 160
|
size: 160
|
||||||
mode: standalone
|
|
||||||
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
||||||
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294"
|
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ param(
|
|||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||||
$defaultInstallSha256 = "42b1747ae867d41da39b11daf783c0106daa6ed18861a2214c196f3a5c9a3c60"
|
$defaultInstallSha256 = "60e18da18b5a9a43b18d37020b676ad47babd6d12c5314e8fcc1370e4864c4e5"
|
||||||
$maximumInstallerBytes = 2MB
|
$maximumInstallerBytes = 2MB
|
||||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||||
$temporary = $null
|
$temporary = $null
|
||||||
|
|||||||
+31
-2
@@ -103,8 +103,35 @@ DEFAULT_DESTS = {
|
|||||||
"rocknix": Path("/storage/roms/bios"),
|
"rocknix": Path("/storage/roms/bios"),
|
||||||
"misterfpga": Path("/media/fat/games"),
|
"misterfpga": Path("/media/fat/games"),
|
||||||
"retropie": Path.home() / "RetroPie" / "BIOS",
|
"retropie": Path.home() / "RetroPie" / "BIOS",
|
||||||
|
"romm": Path("/romm/library/bios"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _os_default_dests(os_type: str) -> dict[str, Path]:
|
||||||
|
"""Where a platform keeps its BIOS on this OS, when it is not detected.
|
||||||
|
|
||||||
|
The same folders the platform's detection looks for: a platform named on
|
||||||
|
the command line but not found must not land in ~/bios, which none of
|
||||||
|
them reads.
|
||||||
|
"""
|
||||||
|
home = Path.home()
|
||||||
|
if os_type == "windows":
|
||||||
|
profile = Path(os.environ.get("USERPROFILE", str(home)))
|
||||||
|
appdata = Path(os.environ.get("APPDATA", str(home / "AppData" / "Roaming")))
|
||||||
|
return {
|
||||||
|
"retroarch": appdata / "RetroArch" / "system",
|
||||||
|
"retrobat": profile / "RetroBat" / "bios",
|
||||||
|
"bizhawk": profile / "BizHawk" / "Firmware",
|
||||||
|
}
|
||||||
|
if os_type == "darwin":
|
||||||
|
return {
|
||||||
|
"retroarch": home / "Library" / "Application Support" / "RetroArch" / "system",
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"retroarch": home / ".config" / "retroarch" / "system",
|
||||||
|
"bizhawk": home / ".config" / "BizHawk" / "Firmware",
|
||||||
|
}
|
||||||
|
|
||||||
# Set when the run is interrupted, so queued downloads end instead of draining
|
# Set when the run is interrupted, so queued downloads end instead of draining
|
||||||
# the pool: every future is submitted before the first one completes.
|
# the pool: every future is submitted before the first one completes.
|
||||||
_stop = threading.Event()
|
_stop = threading.Event()
|
||||||
@@ -1385,7 +1412,7 @@ def _prompt_manual_platform(os_type: str) -> list[tuple[str, Path]]:
|
|||||||
plat = AVAILABLE_PLATFORMS[int(choice) - 1]
|
plat = AVAILABLE_PLATFORMS[int(choice) - 1]
|
||||||
elif choice in AVAILABLE_PLATFORMS:
|
elif choice in AVAILABLE_PLATFORMS:
|
||||||
plat = choice
|
plat = choice
|
||||||
default_dest = DEFAULT_DESTS.get(plat, Path.home() / "bios")
|
default_dest = _default_dest(os_type, plat)
|
||||||
try:
|
try:
|
||||||
raw = input(f"BIOS directory [{default_dest}]: ").strip()
|
raw = input(f"BIOS directory [{default_dest}]: ").strip()
|
||||||
except (EOFError, KeyboardInterrupt):
|
except (EOFError, KeyboardInterrupt):
|
||||||
@@ -1405,7 +1432,9 @@ def _default_dest(os_type: str, platform_name: str) -> Path:
|
|||||||
"""
|
"""
|
||||||
if os_type == "android" and platform_name == "retroarch":
|
if os_type == "android" and platform_name == "retroarch":
|
||||||
return _android_storage_root() / "RetroArch" / "system"
|
return _android_storage_root() / "RetroArch" / "system"
|
||||||
return DEFAULT_DESTS.get(platform_name, Path.home() / "bios")
|
return _os_default_dests(os_type).get(
|
||||||
|
platform_name, DEFAULT_DESTS.get(platform_name, Path.home() / "bios")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _manual_usage_hint(os_type: str) -> list[str]:
|
def _manual_usage_hint(os_type: str) -> list[str]:
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
|||||||
TEMP_INSTALLER=""
|
TEMP_INSTALLER=""
|
||||||
TEMP_DIRECTORY=""
|
TEMP_DIRECTORY=""
|
||||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||||
DEFAULT_INSTALL_SHA256="42b1747ae867d41da39b11daf783c0106daa6ed18861a2214c196f3a5c9a3c60"
|
DEFAULT_INSTALL_SHA256="60e18da18b5a9a43b18d37020b676ad47babd6d12c5314e8fcc1370e4864c4e5"
|
||||||
MAX_INSTALLER_BYTES=2097152
|
MAX_INSTALLER_BYTES=2097152
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
|
|||||||
+15
-15
@@ -5,7 +5,7 @@
|
|||||||
"platform": "batocera",
|
"platform": "batocera",
|
||||||
"display_name": "Batocera",
|
"display_name": "Batocera",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"generated": "2026-10-06T03:26:07Z",
|
"generated": "2026-10-06T06:50:25Z",
|
||||||
"base_destination": "bios",
|
"base_destination": "bios",
|
||||||
"detect": [
|
"detect": [
|
||||||
{
|
{
|
||||||
@@ -238,9 +238,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"total_files": 12826,
|
"total_files": 12826,
|
||||||
"total_size": 8065364335,
|
"total_size": 8065338769,
|
||||||
"pack_files": 12828,
|
"pack_files": 12828,
|
||||||
"pack_size": 8065364335,
|
"pack_size": 8065338769,
|
||||||
"total_omitted": 30,
|
"total_omitted": 30,
|
||||||
"omitted_files": [
|
"omitted_files": [
|
||||||
{
|
{
|
||||||
@@ -40969,20 +40969,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "switch/prod.keys",
|
"dest": "switch/prod.keys",
|
||||||
"sha1": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
"sha1": "c146b651696d25287241f4bab18226e488bc45b3",
|
||||||
"sha256": "76df55f85c6628cd924f227a72a301bbb731833c0a0f00c440e01e6b34480990",
|
"sha256": "039895c6e93a0c6208c3af27da8c3fc408083873a96f2320dc334a6fa252c112",
|
||||||
"size": 15039,
|
"size": 16025,
|
||||||
"repo_path": "bios/Arcade/Arcade/switch/prod.keys",
|
"repo_path": "bios/Nintendo/Switch/prod.keys",
|
||||||
"cores": [
|
"cores": [
|
||||||
"citron"
|
"citron"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "switch/title.keys",
|
"dest": "switch/title.keys",
|
||||||
"sha1": "8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
"sha1": "bfee8e708dc4c8f27bfe51a12d57caaa0b7f74de",
|
||||||
"sha256": "dc6e31aae8daa9067dafcf2bca6d9d3aa691a63474eb698ad8c54be3e360753b",
|
"sha256": "3ed66f7e82df79922739c6c2d34de90bc36a48d13acdff24f9d51013bd8c0677",
|
||||||
"size": 19584,
|
"size": 1224,
|
||||||
"repo_path": "bios/Arcade/Arcade/switch/title.keys",
|
"repo_path": "bios/Nintendo/Switch/title.keys",
|
||||||
"cores": [
|
"cores": [
|
||||||
"citron"
|
"citron"
|
||||||
]
|
]
|
||||||
@@ -85625,10 +85625,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "np2kai/scsi.rom",
|
"dest": "np2kai/scsi.rom",
|
||||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||||
"size": 16384,
|
"size": 8192,
|
||||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||||
"cores": [
|
"cores": [
|
||||||
"np2kai"
|
"np2kai"
|
||||||
]
|
]
|
||||||
|
|||||||
+1555
-185
File diff suppressed because it is too large.
Load diff
+531
-31
@@ -5,7 +5,7 @@
|
|||||||
"platform": "emudeck",
|
"platform": "emudeck",
|
||||||
"display_name": "EmuDeck",
|
"display_name": "EmuDeck",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"generated": "2026-10-06T03:26:07Z",
|
"generated": "2026-10-06T10:11:17Z",
|
||||||
"base_destination": "bios",
|
"base_destination": "bios",
|
||||||
"detect": [
|
"detect": [
|
||||||
{
|
{
|
||||||
@@ -250,10 +250,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"total_files": 569,
|
"total_files": 619,
|
||||||
"total_size": 3452229095,
|
"total_size": 3473031414,
|
||||||
"pack_files": 571,
|
"pack_files": 621,
|
||||||
"pack_size": 3452229095,
|
"pack_size": 3473031414,
|
||||||
"total_omitted": 0,
|
"total_omitted": 0,
|
||||||
"omitted_files": [],
|
"omitted_files": [],
|
||||||
"files": [
|
"files": [
|
||||||
@@ -331,18 +331,18 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "disksys.rom",
|
"dest": "disksys.rom",
|
||||||
"sha1": "57fe1bdee955bb48d357e463ccbf129496930b62",
|
"sha1": "af5af53f66982e749643fdf8b2acbb7d4d3ed229",
|
||||||
"sha256": "99c18490ed9002d9c6d999b9d8d15be5c051bdfa7cc7e73318053c9a994b0178",
|
"sha256": "fdc1a76e654feea993fcb38366e05ee5f4eb641f86fe6bebaeefd412e112dd72",
|
||||||
"size": 8192,
|
"size": 8192,
|
||||||
"repo_path": "bios/Arcade/FBNeo/fdsbios.nes",
|
"repo_path": "bios/Nintendo/Famicom Disk System/.variants/disksys.rom.af5af53f",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "prod.keys",
|
"dest": "prod.keys",
|
||||||
"sha1": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
"sha1": "c146b651696d25287241f4bab18226e488bc45b3",
|
||||||
"sha256": "76df55f85c6628cd924f227a72a301bbb731833c0a0f00c440e01e6b34480990",
|
"sha256": "039895c6e93a0c6208c3af27da8c3fc408083873a96f2320dc334a6fa252c112",
|
||||||
"size": 15039,
|
"size": 16025,
|
||||||
"repo_path": "bios/Arcade/Arcade/switch/prod.keys",
|
"repo_path": "bios/Nintendo/Switch/prod.keys",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -387,26 +387,26 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "bios_CD_E.bin",
|
"dest": "bios_CD_E.bin",
|
||||||
"sha1": "f891e0ea651e2232af0c5c4cb46a0cae2ee8f356",
|
"sha1": "7063192ae9f6b696c5b81bc8f0a9fe6f0c400e58",
|
||||||
"sha256": "16735df92fcc1d0fc6a1eced244867e95281c5006340926082b74e363e073dc4",
|
"sha256": "fe608a2a07676a23ab5fd5eee2f53c9e2526d69a28aa16ccd85c0ec42e6933cb",
|
||||||
"size": 131072,
|
"size": 131072,
|
||||||
"repo_path": "bios/Sega/Mega CD/MCD_eu_100.bin",
|
"repo_path": "bios/Sega/Mega CD/eu_mcd2_9303.bin",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "bios_CD_U.bin",
|
"dest": "bios_CD_U.bin",
|
||||||
"sha1": "f4f315adcef9b8feb0364c21ab7f0eaf5457f3ed",
|
"sha1": "5a8c4b91d3034c1448aac4b5dc9a6484fce51636",
|
||||||
"sha256": "807384678ef259494d943eddb6954dfc7069693d0dde0c424e2028488d88e81a",
|
"sha256": "fb477cdbf94c84424c2feca4fe40656d85393fe7b7b401911b45ad2eb991258c",
|
||||||
"size": 131072,
|
"size": 131072,
|
||||||
"repo_path": "bios/Sega/Mega CD/bios_CD_U.bin",
|
"repo_path": "bios/Sega/Mega CD/SegaCDBIOS9303.bin",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "bios_CD_J.bin",
|
"dest": "bios_CD_J.bin",
|
||||||
"sha1": "4846f448160059a7da0215a5df12ca160f26dd69",
|
"sha1": "d203cfe22c03ae479dd8ca33840cf8d9776eb3ff",
|
||||||
"sha256": "923eb9da740f98ab1572085e3d351e2c0982df0906b0b99efb68a9997c94cf8f",
|
"sha256": "7133fc2dd2fe5b7d0acd53a5f10f3d00b5d31270239ad20d74ef32393e24af88",
|
||||||
"size": 131072,
|
"size": 131072,
|
||||||
"repo_path": "bios/Sega/Mega CD/MCD_jp_100p.bin",
|
"repo_path": "bios/Sega/Mega CD/jp_mcd2_921222.bin",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -529,6 +529,316 @@
|
|||||||
"repo_path": "bios/Microsoft/Xbox/Complex_4627.bin",
|
"repo_path": "bios/Microsoft/Xbox/Complex_4627.bin",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "gba_bios.bin",
|
||||||
|
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
||||||
|
"sha256": "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570",
|
||||||
|
"size": 16384,
|
||||||
|
"repo_path": "bios/Nintendo/Game Boy Advance/GBA_bios.rom",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "bios.gg",
|
||||||
|
"sha1": "914aa165e3d879f060be77870d345b60cfeb4ede",
|
||||||
|
"sha256": "8c8a21335038285cfa03dc076100c1f0bfadf3e4ff70796f11f3dfaaab60eee2",
|
||||||
|
"size": 1024,
|
||||||
|
"repo_path": "bios/Sega/Game Gear/GG_majesco.gg",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "bios_U.sms",
|
||||||
|
"sha1": "c315672807d8ddb8d91443729405c766dd95cae7",
|
||||||
|
"sha256": "477617917a12a30f9f43844909dc2de6e6a617430f5c9a36306c86414a670d50",
|
||||||
|
"size": 8192,
|
||||||
|
"repo_path": "bios/Other/zesarux/sms.rom",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "bios_J.sms",
|
||||||
|
"sha1": "a8c1b39a2e41137835eda6a5de6d46dd9fadbaf2",
|
||||||
|
"sha256": "67846e26764bd862f19179294347f7353a4166b62ac4198a5ec32933b7da486e",
|
||||||
|
"size": 8192,
|
||||||
|
"repo_path": "bios/Sega/Master System/MasterSystem/japanese-bios.sms",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "bios_E.sms",
|
||||||
|
"sha1": "c315672807d8ddb8d91443729405c766dd95cae7",
|
||||||
|
"sha256": "477617917a12a30f9f43844909dc2de6e6a617430f5c9a36306c86414a670d50",
|
||||||
|
"size": 8192,
|
||||||
|
"repo_path": "bios/Other/zesarux/sms.rom",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "mega_ld_bios_U.bin",
|
||||||
|
"sha1": "aa811861f8874775075bd3f53008c8aaf59b07db",
|
||||||
|
"sha256": "e89b5a319f66406611ec82fe5c4aa6827c175a05135bd7bd177366cba0465021",
|
||||||
|
"size": 131072,
|
||||||
|
"repo_path": "bios/Pioneer/LaserActive/Pioneer LaserActive Sega PAC Boot ROM v1.04 (1993)(Pioneer - Sega)(US).bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "mega_ld_bios_J.bin",
|
||||||
|
"sha1": "26237b333db4a4c6770297fa5e655ea95840d5d9",
|
||||||
|
"sha256": "dca942d977217f703d8d1c6eb1aeb6b32c78ecc421486bbb46c459d385161c94",
|
||||||
|
"size": 131072,
|
||||||
|
"repo_path": "bios/Pioneer/LaserActive/Pioneer LaserActive Sega PAC Boot ROM v1.02 (1993)(Pioneer - Sega)(JP)(en-ja).bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "MSX.ROM",
|
||||||
|
"sha1": "302afb5d8be26c758309ca3df611ae69cced2821",
|
||||||
|
"sha256": "413a2b601a94b3792e054be2439cc77a1819cceadbfa9542f88d51c7480f2ef0",
|
||||||
|
"size": 32768,
|
||||||
|
"repo_path": "bios/Microsoft/MSX/CLK/msx-japanese.rom",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "MSX2.ROM",
|
||||||
|
"sha1": "0081ea0d25bc5cd8d70b60ad8cfdc7307812c0fd",
|
||||||
|
"sha256": "0c672d86ead61a97f49a583b88b7c1905da120645cd44f0c9f2baf4f4631e0b1",
|
||||||
|
"size": 32768,
|
||||||
|
"repo_path": "bios/Other/msx-emu/Machines/Shared Roms/MSX2J.rom",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "MSX2EXT.ROM",
|
||||||
|
"sha1": "5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02",
|
||||||
|
"sha256": "6c6f421a10c428d960b7ecc990f99af1c638147f747bddca7b0bf0e2ab738300",
|
||||||
|
"size": 16384,
|
||||||
|
"repo_path": "bios/Microsoft/MSX/MSX2EXT.ROM",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "neo-epo.bin",
|
||||||
|
"sha1": "1b3b22092f30c4d1b2c15f04d1670eb1e9fbea07",
|
||||||
|
"sha256": "70f7906d68acbc3630b6e7792ebb485288b05ca72c6d4c243e07e230aae8d1e4",
|
||||||
|
"size": 131072,
|
||||||
|
"repo_path": "bios/SNK/Neo Geo/neo-epo.bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "sp-45.sp1",
|
||||||
|
"sha1": "cdf1f49e3ff2bac528c21ed28449cf35b7957dc1",
|
||||||
|
"sha256": "0fbeee82b463187d6360e92811f1fad58649feb8ab2584c524222d0b5bf53a17",
|
||||||
|
"size": 524288,
|
||||||
|
"repo_path": "bios/SNK/Neo Geo/sp-45.sp1",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "ngp_bios.rom",
|
||||||
|
"sha1": "2f6429b68446536d8b03f35d02f1e98beb6460a0",
|
||||||
|
"sha256": "0293555b21c4fac516d25199df7809b26beeae150e1d4504a050db32264a6ad7",
|
||||||
|
"size": 65536,
|
||||||
|
"repo_path": "bios/SNK/Neo Geo Pocket/SNK Neo-Geo Pocket BIOS (1998)(SNK)(en-ja).bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "ngpc_bios.rom",
|
||||||
|
"sha1": "edc13192054a59be49c6d55f83b70e2510968e86",
|
||||||
|
"sha256": "8fb845a2f71514cec20728e2f0fecfade69444f8d50898b92c2259f1ba63e10d",
|
||||||
|
"size": 65536,
|
||||||
|
"repo_path": "bios/SNK/Neo Geo Pocket/SNK Neo-Geo Pocket Color BIOS (1999)(SNK)(en-ja).bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "64dd_ipl_J.bin",
|
||||||
|
"sha1": "bf861922dcb78c316360e3e742f4f70ff63c9bc3",
|
||||||
|
"sha256": "806400ec0df94b0755de6c5b8249d6b6a9866124c5ddbdac198bde22499bfb8b",
|
||||||
|
"size": 4194304,
|
||||||
|
"repo_path": "bios/Nintendo/64DD/64DD_IPL_v12_JPN.bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "64dd_ipl_U.bin",
|
||||||
|
"sha1": "3c5b93ca231550c68693a14f03cea8d5dbd1be9e",
|
||||||
|
"sha256": "e9fec87a45fba02399e88064b9e2f8cf0f2106e351c58279a87f05da5bc984ad",
|
||||||
|
"size": 4194304,
|
||||||
|
"repo_path": "bios/Nintendo/64DD/64DD_IPL_USA.bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "64dd_ipl_DEV.bin",
|
||||||
|
"sha1": "10c4173c2a7eb09c6579818f72ef18fa0b6d32de",
|
||||||
|
"sha256": "9c2962a8b994a29e4cd04b3a6e4ed730a751414655ab6a9799ebf5fc08b79d44",
|
||||||
|
"size": 4194304,
|
||||||
|
"repo_path": "bios/Nintendo/64DD/64DD_IPL_DEV.bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "syscard1.pce",
|
||||||
|
"sha1": "a39a66da7de6ba94ab84d04eef7afeec7d4ee66a",
|
||||||
|
"sha256": "afe9f27f91ac918348555b86298b4f984643eafa2773196f2c5441ea84f0c3bb",
|
||||||
|
"size": 262144,
|
||||||
|
"repo_path": "bios/NEC/PC Engine/syscard1.pce",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "syscard3.pce",
|
||||||
|
"sha1": "79f5ff55dd10187c7fd7b8daab0b3ffbd1f56a2c",
|
||||||
|
"sha256": "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce",
|
||||||
|
"size": 262144,
|
||||||
|
"repo_path": "bios/NEC/PC Engine/syscard3.pce",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "syscard3u.pce",
|
||||||
|
"sha1": "d02611d99921986147c753df14c7349b31d71950",
|
||||||
|
"sha256": "cadac2725711b3c442bcf237b02f5a5210c96f17625c35fa58f009e0ed39e4db",
|
||||||
|
"size": 262144,
|
||||||
|
"repo_path": "bios/NEC/PC Engine/syscard3u.pce",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "games_express.pce",
|
||||||
|
"sha1": "014881a959e045e00f4db8f52955200865d40280",
|
||||||
|
"sha256": "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d",
|
||||||
|
"size": 32768,
|
||||||
|
"repo_path": "bios/NEC/PC Engine/gecard.pce",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "pac-n10.pce",
|
||||||
|
"sha1": "f7412aa822d70a55b2ff3d7095137263dc54f6b6",
|
||||||
|
"sha256": "0e87a3385a27b3a4cac51934819b7eefa5b3d690768d2495633838488cd0e2e4",
|
||||||
|
"size": 524288,
|
||||||
|
"repo_path": "bios/Pioneer/LaserActive/[BIOS] LaserActive PAC-N10 (US) (v1.02).bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "pac-n1.pce",
|
||||||
|
"sha1": "6973e2593e66fd21627fedccec98d4a364afaaff",
|
||||||
|
"sha256": "459325690a458baebd77495c91e37c4dddfdd542ba13a821ce954e5bb245627f",
|
||||||
|
"size": 524288,
|
||||||
|
"repo_path": "bios/Pioneer/LaserActive/[BIOS] LaserActive PAC-N1 (Japan) (v1.02).bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "pce-lp1.pce",
|
||||||
|
"sha1": "c1b9202cbe072db12114b223a9ba5374b30718fb",
|
||||||
|
"sha256": "3f43b3b577117d84002e99cb0baeb97b0d65b1d70b4adadc68817185c6a687f0",
|
||||||
|
"size": 524288,
|
||||||
|
"repo_path": "bios/Pioneer/LaserActive/[BIOS] LaserActive PCE-LP1 (Japan) (v1.02).bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "saturn_bios_U.bin",
|
||||||
|
"sha1": "faa8ea183a6d7bbe5d4e03bb1332519800d3fbc3",
|
||||||
|
"sha256": "96e106f740ab448cf89f0dd49dfbac7fe5391cb6bd6e14ad5e3061c13330266f",
|
||||||
|
"size": 524288,
|
||||||
|
"repo_path": "bios/Sega/Saturn/SAT_1.00-(U+E).bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "saturn_bios_J.bin",
|
||||||
|
"sha1": "df94c5b4d47eb3cc404d88b33a8fda237eaf4720",
|
||||||
|
"sha256": "dcfef4b99605f872b6c3b6d05c045385cdea3d1b702906a0ed930df7bcb7deac",
|
||||||
|
"size": 524288,
|
||||||
|
"repo_path": "bios/Sega/Saturn/SAT_1.01-(J).bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "saturn_bios_E.bin",
|
||||||
|
"sha1": "faa8ea183a6d7bbe5d4e03bb1332519800d3fbc3",
|
||||||
|
"sha256": "96e106f740ab448cf89f0dd49dfbac7fe5391cb6bd6e14ad5e3061c13330266f",
|
||||||
|
"size": 524288,
|
||||||
|
"repo_path": "bios/Sega/Saturn/SAT_1.00-(U+E).bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "SGB1.sfc",
|
||||||
|
"sha1": "973e10840db683cf3faf61bd443090786b3a9f04",
|
||||||
|
"sha256": "a75160f7b89b1f0e20fd2f6441bb86285c7378db5035ef6885485eaff6059376",
|
||||||
|
"size": 262144,
|
||||||
|
"repo_path": "bios/Nintendo/Super Game Boy/SGB1.sfc/program.rom",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "SGB2.sfc",
|
||||||
|
"sha1": "e5b2922ca137051059e4269b236d07a22c07bc84",
|
||||||
|
"sha256": "c172498a23d1176672931bab33b629c7d28f914a43dca9e540b8af1b37ccf2c6",
|
||||||
|
"size": 524288,
|
||||||
|
"repo_path": "bios/Nintendo/Super Game Boy/SGB2.sfc/program.rom",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "STBIOS.bin",
|
||||||
|
"sha1": "ef86ea192eed03d5c413fdbbfd46043be1d7a127",
|
||||||
|
"sha256": "edacb453da14f825f05d1134d6035f4bf034e55f7cfb97c70c4ee107eabc7342",
|
||||||
|
"size": 262144,
|
||||||
|
"repo_path": "bios/Nintendo/SuFami Turbo/STBIOS.bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "BS-X.bin",
|
||||||
|
"sha1": "4891d739a8a8b67923681bad4fb67edab2e90e50",
|
||||||
|
"sha256": "a49827b45ff9ac9cf5b4658190e1428e59251bc82d8a63d8e9e0f71e439f008f",
|
||||||
|
"size": 1048576,
|
||||||
|
"repo_path": "bios/Nintendo/SNES/BS-X.bin",
|
||||||
|
"cores": [
|
||||||
|
"ares"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "sysdata/shared_font.bin",
|
"dest": "sysdata/shared_font.bin",
|
||||||
"sha1": "60022d6d026c8df950722478aab25702a0dfa067",
|
"sha1": "60022d6d026c8df950722478aab25702a0dfa067",
|
||||||
@@ -699,6 +1009,36 @@
|
|||||||
"cemu"
|
"cemu"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "switch/title.keys",
|
||||||
|
"sha1": "bfee8e708dc4c8f27bfe51a12d57caaa0b7f74de",
|
||||||
|
"sha256": "3ed66f7e82df79922739c6c2d34de90bc36a48d13acdff24f9d51013bd8c0677",
|
||||||
|
"size": 1224,
|
||||||
|
"repo_path": "bios/Nintendo/Switch/title.keys",
|
||||||
|
"cores": [
|
||||||
|
"citron"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "switch/console.keys",
|
||||||
|
"sha1": "721ca59a261d04af13fb84497ad79b6d8c6b7609",
|
||||||
|
"sha256": "b98c31cc764c49b8259142de3ada5bb7440ce0fd11315ae64294ddb0d56a523d",
|
||||||
|
"size": 451,
|
||||||
|
"repo_path": "bios/Other/citron/switch/console.keys",
|
||||||
|
"cores": [
|
||||||
|
"citron"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "switch/key_retail.bin",
|
||||||
|
"sha1": "bbdbb49a917d14f7a997d327ba40d40c39e606ce",
|
||||||
|
"sha256": "7f9283654ec1097fbdff31de946651ae60c2854afb544abe8963d389639c710e",
|
||||||
|
"size": 160,
|
||||||
|
"repo_path": "bios/Other/citron/switch/key_retail.bin",
|
||||||
|
"cores": [
|
||||||
|
"citron"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "GC/dsp_rom.bin",
|
"dest": "GC/dsp_rom.bin",
|
||||||
"sha1": "f4f683a49d7eb4155566f793f2c1c27e90159992",
|
"sha1": "f4f683a49d7eb4155566f793f2c1c27e90159992",
|
||||||
@@ -1841,6 +2181,16 @@
|
|||||||
"duckstation"
|
"duckstation"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "switch/dev.keys",
|
||||||
|
"sha1": "fc916ff462b842983d4061d09230163ac4b38f16",
|
||||||
|
"sha256": "72685b5ce6c7ea3e18e3e0d98be1a99de8197299ee15429687b27df614ec92b9",
|
||||||
|
"size": 781,
|
||||||
|
"repo_path": "bios/Other/eden/switch/dev.keys",
|
||||||
|
"cores": [
|
||||||
|
"eden"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "dc/dc_nvmem.bin",
|
"dest": "dc/dc_nvmem.bin",
|
||||||
"sha1": "72c65f2a11ee7b72b77b9945187b7a332632e14a",
|
"sha1": "72c65f2a11ee7b72b77b9945187b7a332632e14a",
|
||||||
@@ -3551,16 +3901,6 @@
|
|||||||
"melonds"
|
"melonds"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"dest": "gba_bios.bin",
|
|
||||||
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
|
||||||
"sha256": "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570",
|
|
||||||
"size": 16384,
|
|
||||||
"repo_path": "bios/Nintendo/Game Boy Advance/GBA_bios.rom",
|
|
||||||
"cores": [
|
|
||||||
"mgba"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"dest": "gb_bios.bin",
|
"dest": "gb_bios.bin",
|
||||||
"sha1": "4ed31ec6b0b175bb109c0eb5fd3d193da823339f",
|
"sha1": "4ed31ec6b0b175bb109c0eb5fd3d193da823339f",
|
||||||
@@ -3631,6 +3971,156 @@
|
|||||||
"ppsspp"
|
"ppsspp"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8723a_fw.bin",
|
||||||
|
"sha1": "2937529b25c46e29429811829988f56f28142a41",
|
||||||
|
"sha256": "756951ee36e417c0714b00547a6175d5d5decbe1e4cf7585f211019150c7cd10",
|
||||||
|
"size": 24548,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8723a_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8723b_fw.bin",
|
||||||
|
"sha1": "5c43fb17ef04a2ee13f9e697f685f68f0d0d1096",
|
||||||
|
"sha256": "c9c8a9f6ebd90b7449a7e4d38ee39952dc7cc47d6c1d054e42cfbb5e4d818853",
|
||||||
|
"size": 45048,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8723b_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8723d_fw.bin",
|
||||||
|
"sha1": "a9182178ae400dd68b3e82f5f4c650001e9d1b85",
|
||||||
|
"sha256": "6b70723b2db9b658cd9535fe36f1494b4494699e1ad07920c9356f8bd90d9263",
|
||||||
|
"size": 52080,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8723d_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8761a_fw.bin",
|
||||||
|
"sha1": "7eca505aad586772d66c3d6336d450c591e10670",
|
||||||
|
"sha256": "581e5e81f5b51a29f475f8a276be95ad3210b3e3065987541c508e7f7a83a462",
|
||||||
|
"size": 74488,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8761a_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8761bu_fw.bin",
|
||||||
|
"sha1": "f5bf3a7fc9605ee31b3904d64b89ce0fa3d9eaf5",
|
||||||
|
"sha256": "1d7a9597349ad89344fa16c1913d3e39e9a12e966e417ca16871bc79bbe59edb",
|
||||||
|
"size": 44484,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8761bu_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8821a_fw.bin",
|
||||||
|
"sha1": "0183b48cda05bd4aed70b9f0b762eea5d2fc3b1d",
|
||||||
|
"sha256": "3ed5077f8f02ff2b06be257691dc72061efac976cd2dc174057833624c820b9d",
|
||||||
|
"size": 37420,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8821a_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8821c_fw.bin",
|
||||||
|
"sha1": "9a192c6a003390a5eab017fe79d42514ec6712e7",
|
||||||
|
"sha256": "dfe7ef772d078ec09490c02190a32fcd40b3eaf3b040049d8930eb80e6d5743c",
|
||||||
|
"size": 54636,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8821c_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8822b_fw.bin",
|
||||||
|
"sha1": "d5ec968f1609b1c436ad25db7d34f5e393373396",
|
||||||
|
"sha256": "eecef2c3b2ff03c3a41292a93a25eca6f4ec38eb4a7491a3651b530be89a04fc",
|
||||||
|
"size": 51176,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8822b_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8822cu_fw.bin",
|
||||||
|
"sha1": "08751f476d0d0aa165af020ed19d2ed1fbaeeef6",
|
||||||
|
"sha256": "09d81457457a7760b867c715517d12dd3919f9ecec7b080f2985e75f1563209f",
|
||||||
|
"size": 38207,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8822cu_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8851bu_fw.bin",
|
||||||
|
"sha1": "0e877d65077a42db4971c7f930dddc7b63047617",
|
||||||
|
"sha256": "7fcf88c00b2548a5ff157102a35722db5e4be655dab1ce8d9bddb6df85f88f24",
|
||||||
|
"size": 49760,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8851bu_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8852au_fw.bin",
|
||||||
|
"sha1": "24118f0e3b1ac5be44abacd4fa0bc07e2364e223",
|
||||||
|
"sha256": "56be2eb99d36109bb5e01eee171573b6f3dd041238c01cd97ce4f01df8dbf09e",
|
||||||
|
"size": 67188,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8852au_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8852bu_fw.bin",
|
||||||
|
"sha1": "22cacf5d1b9da25801cd5d6e8b7f06dff0965439",
|
||||||
|
"sha256": "c2b0d34a4ff8a8ea136993f0dc4ba657522d84231b4094c2a53cda39afc91c57",
|
||||||
|
"size": 129733,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8852bu_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8852cu_fw.bin",
|
||||||
|
"sha1": "acd067bdc7933d1d9b8f2d72a750de5d125a5529",
|
||||||
|
"sha256": "9b8b27741a914c1cecc1ab9837194236890522101a57ba1898eb55f8d538dae0",
|
||||||
|
"size": 113336,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8852cu_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8852btu_fw.bin",
|
||||||
|
"sha1": "eb1139c1b27429eef639b6002b16212d702ad378",
|
||||||
|
"sha256": "bd162fd3b70e805cf7a8098f22f4ddf5d89da58fafff97d87da1a1e2e0cbeb80",
|
||||||
|
"size": 128140,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8852btu_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Load/Firmware/rtl_bt/rtl8922au_fw.bin",
|
||||||
|
"sha1": "088a3cf44d2372cdb557241bbbf519bcbbac156f",
|
||||||
|
"sha256": "112290ca9dc3985034f572594812f0deb8530dd031686461b55cac43a1c35a79",
|
||||||
|
"size": 71360,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/Load/Firmware/rtl_bt/rtl8922au_fw.bin",
|
||||||
|
"cores": [
|
||||||
|
"primehack"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "PS3UPDAT.PUP",
|
"dest": "PS3UPDAT.PUP",
|
||||||
"sha1": "093f8698b54b78dcb701de2043f82639de51d63b",
|
"sha1": "093f8698b54b78dcb701de2043f82639de51d63b",
|
||||||
@@ -4567,6 +5057,16 @@
|
|||||||
"cemu"
|
"cemu"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "switch/prod.keys",
|
||||||
|
"sha1": "c146b651696d25287241f4bab18226e488bc45b3",
|
||||||
|
"sha256": "039895c6e93a0c6208c3af27da8c3fc408083873a96f2320dc334a6fa252c112",
|
||||||
|
"size": 16025,
|
||||||
|
"repo_path": "bios/Nintendo/Switch/prod.keys",
|
||||||
|
"cores": [
|
||||||
|
"citron"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "GC/USA/IPL.bin",
|
"dest": "GC/USA/IPL.bin",
|
||||||
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
||||||
|
|||||||
+14
-14
@@ -5,7 +5,7 @@
|
|||||||
"platform": "lakka",
|
"platform": "lakka",
|
||||||
"display_name": "Lakka",
|
"display_name": "Lakka",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"generated": "2026-10-06T03:26:07Z",
|
"generated": "2026-10-06T06:50:25Z",
|
||||||
"base_destination": "system",
|
"base_destination": "system",
|
||||||
"detect": [
|
"detect": [
|
||||||
{
|
{
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
],
|
],
|
||||||
"standalone_copies": [],
|
"standalone_copies": [],
|
||||||
"total_files": 5867,
|
"total_files": 5867,
|
||||||
"total_size": 6399851574,
|
"total_size": 6399839286,
|
||||||
"pack_files": 8510,
|
"pack_files": 8510,
|
||||||
"pack_size": 6440384738,
|
"pack_size": 6440372450,
|
||||||
"total_omitted": 0,
|
"total_omitted": 0,
|
||||||
"omitted_files": [],
|
"omitted_files": [],
|
||||||
"files": [
|
"files": [
|
||||||
@@ -961,10 +961,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "galaksija/ROM2.BIN",
|
"dest": "galaksija/ROM2.BIN",
|
||||||
"sha1": "1bae895fbdd658cfb56c53cc2139282cc1e778de",
|
"sha1": "5d5ab4313a2d0effe7572bb129193b64cab002c1",
|
||||||
"sha256": "9fc01e6beab84e61e0267fa39e23c649cdf2ccd25ad1cbfc2de0facfd160e723",
|
"sha256": "7d93fe474fcc98c06a6d96e533f8935e4e707edf0792d3e9d679ea68404375de",
|
||||||
"size": 8192,
|
"size": 4096,
|
||||||
"repo_path": "bios/Sony/PlayStation 2/ROM2.BIN",
|
"repo_path": "bios/Galaksija/ROM2.BIN",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1771,10 +1771,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
||||||
"sha1": "f4f683a49d7eb4155566f793f2c1c27e90159992",
|
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||||
"sha256": "7f33e6600722791ccee5fd15d4330af8d8f74165f7d1ded6738684c0294aaffe",
|
"sha256": "49d987ee1eab29a157425b82d54516957a81e1bac247c8834e494642605c3e8c",
|
||||||
"size": 8192,
|
"size": 8192,
|
||||||
"repo_path": "bios/Nintendo/GameCube/Sys/GC/dsp_rom.bin",
|
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_rom.bin.3c6cc6e0",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -47491,10 +47491,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "np2kai/scsi.rom",
|
"dest": "np2kai/scsi.rom",
|
||||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||||
"size": 16384,
|
"size": 8192,
|
||||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||||
"cores": [
|
"cores": [
|
||||||
"np2kai"
|
"np2kai"
|
||||||
]
|
]
|
||||||
|
|||||||
+11
-11
@@ -5,7 +5,7 @@
|
|||||||
"platform": "recalbox",
|
"platform": "recalbox",
|
||||||
"display_name": "Recalbox",
|
"display_name": "Recalbox",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"generated": "2026-10-06T03:26:07Z",
|
"generated": "2026-10-06T06:50:25Z",
|
||||||
"base_destination": "bios",
|
"base_destination": "bios",
|
||||||
"detect": [
|
"detect": [
|
||||||
{
|
{
|
||||||
@@ -238,9 +238,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"total_files": 3229,
|
"total_files": 3229,
|
||||||
"total_size": 4193670403,
|
"total_size": 4193661699,
|
||||||
"pack_files": 3231,
|
"pack_files": 3231,
|
||||||
"pack_size": 4193670403,
|
"pack_size": 4193661699,
|
||||||
"total_omitted": 1,
|
"total_omitted": 1,
|
||||||
"omitted_files": [
|
"omitted_files": [
|
||||||
{
|
{
|
||||||
@@ -1815,10 +1815,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "pcenginecd/syscard3.pce",
|
"dest": "pcenginecd/syscard3.pce",
|
||||||
"sha1": "1b4c260326d905bc718812dad0f68089977f427b",
|
"sha1": "79f5ff55dd10187c7fd7b8daab0b3ffbd1f56a2c",
|
||||||
"sha256": "df4f75feebb95e53dfef72dea0787df743e3474ba046ff5a4cb88e34dda93ff1",
|
"sha256": "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce",
|
||||||
"size": 262656,
|
"size": 262144,
|
||||||
"repo_path": "bios/NEC/PC Engine/.variants/syscard3.pce.1b4c2603",
|
"repo_path": "bios/NEC/PC Engine/syscard3.pce",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -25025,10 +25025,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "np2kai/scsi.rom",
|
"dest": "np2kai/scsi.rom",
|
||||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||||
"size": 16384,
|
"size": 8192,
|
||||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||||
"cores": [
|
"cores": [
|
||||||
"np2kai"
|
"np2kai"
|
||||||
]
|
]
|
||||||
|
|||||||
+14
-14
@@ -5,7 +5,7 @@
|
|||||||
"platform": "retroarch",
|
"platform": "retroarch",
|
||||||
"display_name": "RetroArch",
|
"display_name": "RetroArch",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"generated": "2026-10-06T03:26:07Z",
|
"generated": "2026-10-06T06:50:25Z",
|
||||||
"base_destination": "system",
|
"base_destination": "system",
|
||||||
"detect": [
|
"detect": [
|
||||||
{
|
{
|
||||||
@@ -256,9 +256,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"total_files": 5867,
|
"total_files": 5867,
|
||||||
"total_size": 6399851574,
|
"total_size": 6399839286,
|
||||||
"pack_files": 8510,
|
"pack_files": 8510,
|
||||||
"pack_size": 6440384738,
|
"pack_size": 6440372450,
|
||||||
"total_omitted": 0,
|
"total_omitted": 0,
|
||||||
"omitted_files": [],
|
"omitted_files": [],
|
||||||
"files": [
|
"files": [
|
||||||
@@ -1200,10 +1200,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "galaksija/ROM2.BIN",
|
"dest": "galaksija/ROM2.BIN",
|
||||||
"sha1": "1bae895fbdd658cfb56c53cc2139282cc1e778de",
|
"sha1": "5d5ab4313a2d0effe7572bb129193b64cab002c1",
|
||||||
"sha256": "9fc01e6beab84e61e0267fa39e23c649cdf2ccd25ad1cbfc2de0facfd160e723",
|
"sha256": "7d93fe474fcc98c06a6d96e533f8935e4e707edf0792d3e9d679ea68404375de",
|
||||||
"size": 8192,
|
"size": 4096,
|
||||||
"repo_path": "bios/Sony/PlayStation 2/ROM2.BIN",
|
"repo_path": "bios/Galaksija/ROM2.BIN",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2010,10 +2010,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
||||||
"sha1": "f4f683a49d7eb4155566f793f2c1c27e90159992",
|
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||||
"sha256": "7f33e6600722791ccee5fd15d4330af8d8f74165f7d1ded6738684c0294aaffe",
|
"sha256": "49d987ee1eab29a157425b82d54516957a81e1bac247c8834e494642605c3e8c",
|
||||||
"size": 8192,
|
"size": 8192,
|
||||||
"repo_path": "bios/Nintendo/GameCube/Sys/GC/dsp_rom.bin",
|
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_rom.bin.3c6cc6e0",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -47730,10 +47730,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "np2kai/scsi.rom",
|
"dest": "np2kai/scsi.rom",
|
||||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||||
"size": 16384,
|
"size": 8192,
|
||||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||||
"cores": [
|
"cores": [
|
||||||
"np2kai"
|
"np2kai"
|
||||||
]
|
]
|
||||||
|
|||||||
+1479
-99
File diff suppressed because it is too large.
Load diff
+1192
-12
File diff suppressed because it is too large.
Load diff
+14
-14
@@ -5,7 +5,7 @@
|
|||||||
"platform": "retropie",
|
"platform": "retropie",
|
||||||
"display_name": "RetroPie",
|
"display_name": "RetroPie",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"generated": "2026-10-06T03:26:07Z",
|
"generated": "2026-10-06T06:50:25Z",
|
||||||
"base_destination": "BIOS",
|
"base_destination": "BIOS",
|
||||||
"detect": [
|
"detect": [
|
||||||
{
|
{
|
||||||
@@ -16,9 +16,9 @@
|
|||||||
],
|
],
|
||||||
"standalone_copies": [],
|
"standalone_copies": [],
|
||||||
"total_files": 9899,
|
"total_files": 9899,
|
||||||
"total_size": 8731282254,
|
"total_size": 8731269966,
|
||||||
"pack_files": 12542,
|
"pack_files": 12542,
|
||||||
"pack_size": 8771815418,
|
"pack_size": 8771803130,
|
||||||
"total_omitted": 8,
|
"total_omitted": 8,
|
||||||
"omitted_files": [
|
"omitted_files": [
|
||||||
{
|
{
|
||||||
@@ -1041,10 +1041,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "galaksija/ROM2.BIN",
|
"dest": "galaksija/ROM2.BIN",
|
||||||
"sha1": "1bae895fbdd658cfb56c53cc2139282cc1e778de",
|
"sha1": "5d5ab4313a2d0effe7572bb129193b64cab002c1",
|
||||||
"sha256": "9fc01e6beab84e61e0267fa39e23c649cdf2ccd25ad1cbfc2de0facfd160e723",
|
"sha256": "7d93fe474fcc98c06a6d96e533f8935e4e707edf0792d3e9d679ea68404375de",
|
||||||
"size": 8192,
|
"size": 4096,
|
||||||
"repo_path": "bios/Sony/PlayStation 2/ROM2.BIN",
|
"repo_path": "bios/Galaksija/ROM2.BIN",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1851,10 +1851,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
||||||
"sha1": "f4f683a49d7eb4155566f793f2c1c27e90159992",
|
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||||
"sha256": "7f33e6600722791ccee5fd15d4330af8d8f74165f7d1ded6738684c0294aaffe",
|
"sha256": "49d987ee1eab29a157425b82d54516957a81e1bac247c8834e494642605c3e8c",
|
||||||
"size": 8192,
|
"size": 8192,
|
||||||
"repo_path": "bios/Nintendo/GameCube/Sys/GC/dsp_rom.bin",
|
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_rom.bin.3c6cc6e0",
|
||||||
"cores": null
|
"cores": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -84803,10 +84803,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "np2kai/scsi.rom",
|
"dest": "np2kai/scsi.rom",
|
||||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||||
"size": 16384,
|
"size": 8192,
|
||||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||||
"cores": [
|
"cores": [
|
||||||
"np2kai"
|
"np2kai"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"platform": "rocknix",
|
"platform": "rocknix",
|
||||||
"display_name": "ROCKNIX",
|
"display_name": "ROCKNIX",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"generated": "2026-10-06T03:26:07Z",
|
"generated": "2026-10-06T06:50:25Z",
|
||||||
"base_destination": "bios",
|
"base_destination": "bios",
|
||||||
"detect": [
|
"detect": [
|
||||||
{
|
{
|
||||||
@@ -18,9 +18,9 @@
|
|||||||
],
|
],
|
||||||
"standalone_copies": [],
|
"standalone_copies": [],
|
||||||
"total_files": 5804,
|
"total_files": 5804,
|
||||||
"total_size": 6570732492,
|
"total_size": 6570724300,
|
||||||
"pack_files": 5806,
|
"pack_files": 5806,
|
||||||
"pack_size": 6570732492,
|
"pack_size": 6570724300,
|
||||||
"total_omitted": 0,
|
"total_omitted": 0,
|
||||||
"omitted_files": [],
|
"omitted_files": [],
|
||||||
"files": [
|
"files": [
|
||||||
@@ -45704,10 +45704,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "np2kai/scsi.rom",
|
"dest": "np2kai/scsi.rom",
|
||||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||||
"size": 16384,
|
"size": 8192,
|
||||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||||
"cores": [
|
"cores": [
|
||||||
"np2kai"
|
"np2kai"
|
||||||
]
|
]
|
||||||
|
|||||||
+1319
-9
File diff suppressed because it is too large.
Load diff
@@ -148,6 +148,7 @@ platforms:
|
|||||||
retroarch:
|
retroarch:
|
||||||
config: retroarch.yml
|
config: retroarch.yml
|
||||||
status: active
|
status: active
|
||||||
|
extract_to: system/
|
||||||
logo: https://raw.githubusercontent.com/libretro/RetroArch/master/media/retroarch-vector_invader-only.svg
|
logo: https://raw.githubusercontent.com/libretro/RetroArch/master/media/retroarch-vector_invader-only.svg
|
||||||
scraper: libretro
|
scraper: libretro
|
||||||
source_url: https://raw.githubusercontent.com/libretro/libretro-database/master/dat/System.dat
|
source_url: https://raw.githubusercontent.com/libretro/libretro-database/master/dat/System.dat
|
||||||
@@ -182,6 +183,7 @@ platforms:
|
|||||||
batocera:
|
batocera:
|
||||||
config: batocera.yml
|
config: batocera.yml
|
||||||
status: active
|
status: active
|
||||||
|
extract_to: /userdata/bios/
|
||||||
logo: https://raw.githubusercontent.com/batocera-linux/batocera-emulationstation/master/resources/splash_batocera.svg
|
logo: https://raw.githubusercontent.com/batocera-linux/batocera-emulationstation/master/resources/splash_batocera.svg
|
||||||
scraper: batocera
|
scraper: batocera
|
||||||
source_url: https://raw.githubusercontent.com/batocera-linux/batocera.linux/master/package/batocera/core/batocera-scripts/scripts/batocera-systems
|
source_url: https://raw.githubusercontent.com/batocera-linux/batocera.linux/master/package/batocera/core/batocera-scripts/scripts/batocera-systems
|
||||||
@@ -407,6 +409,7 @@ platforms:
|
|||||||
recalbox:
|
recalbox:
|
||||||
config: recalbox.yml
|
config: recalbox.yml
|
||||||
status: active
|
status: active
|
||||||
|
extract_to: /recalbox/share/bios/
|
||||||
logo: https://raw.githubusercontent.com/homarr-labs/dashboard-icons/main/svg/recalbox.svg
|
logo: https://raw.githubusercontent.com/homarr-labs/dashboard-icons/main/svg/recalbox.svg
|
||||||
scraper: recalbox
|
scraper: recalbox
|
||||||
source_url: https://gitlab.com/recalbox/recalbox/-/raw/master/board/recalbox/fsoverlay/recalbox/share_init/system/.emulationstation/es_bios.xml
|
source_url: https://gitlab.com/recalbox/recalbox/-/raw/master/board/recalbox/fsoverlay/recalbox/share_init/system/.emulationstation/es_bios.xml
|
||||||
@@ -620,6 +623,7 @@ platforms:
|
|||||||
retrobat:
|
retrobat:
|
||||||
config: retrobat.yml
|
config: retrobat.yml
|
||||||
status: active
|
status: active
|
||||||
|
extract_to: bios/
|
||||||
logo: https://raw.githubusercontent.com/RetroBat-Official/retrobat/main/system/resources/retrobat_logo_notext.png
|
logo: https://raw.githubusercontent.com/RetroBat-Official/retrobat/main/system/resources/retrobat_logo_notext.png
|
||||||
scraper: retrobat
|
scraper: retrobat
|
||||||
source_url: https://raw.githubusercontent.com/RetroBat-Official/emulatorlauncher/master/batocera-systems/Resources/batocera-systems.json
|
source_url: https://raw.githubusercontent.com/RetroBat-Official/emulatorlauncher/master/batocera-systems/Resources/batocera-systems.json
|
||||||
@@ -808,6 +812,7 @@ platforms:
|
|||||||
emudeck:
|
emudeck:
|
||||||
config: emudeck.yml
|
config: emudeck.yml
|
||||||
status: active
|
status: active
|
||||||
|
extract_to: ~/Emulation/bios/
|
||||||
logo: https://raw.githubusercontent.com/dragoonDorise/EmuDeck/main/icons/EmuDeck.png
|
logo: https://raw.githubusercontent.com/dragoonDorise/EmuDeck/main/icons/EmuDeck.png
|
||||||
scraper: emudeck
|
scraper: emudeck
|
||||||
source_url: https://raw.githubusercontent.com/dragoonDorise/EmuDeck/main/functions/checkBIOS.sh
|
source_url: https://raw.githubusercontent.com/dragoonDorise/EmuDeck/main/functions/checkBIOS.sh
|
||||||
@@ -838,6 +843,7 @@ platforms:
|
|||||||
lakka:
|
lakka:
|
||||||
config: lakka.yml
|
config: lakka.yml
|
||||||
status: active
|
status: active
|
||||||
|
extract_to: /storage/system/
|
||||||
logo: https://raw.githubusercontent.com/libretro/retroarch-assets/master/src/xmb/flatui/lakka.svg
|
logo: https://raw.githubusercontent.com/libretro/retroarch-assets/master/src/xmb/flatui/lakka.svg
|
||||||
scraper: libretro
|
scraper: libretro
|
||||||
inherits_from: retroarch
|
inherits_from: retroarch
|
||||||
@@ -854,6 +860,7 @@ platforms:
|
|||||||
retrodeck:
|
retrodeck:
|
||||||
config: retrodeck.yml
|
config: retrodeck.yml
|
||||||
status: active
|
status: active
|
||||||
|
extract_to: ~/retrodeck/
|
||||||
logo: https://raw.githubusercontent.com/RetroDECK/RetroDECK/main/res/icon.svg
|
logo: https://raw.githubusercontent.com/RetroDECK/RetroDECK/main/res/icon.svg
|
||||||
scraper: retrodeck
|
scraper: retrodeck
|
||||||
source_url: https://github.com/RetroDECK/components
|
source_url: https://github.com/RetroDECK/components
|
||||||
@@ -898,6 +905,7 @@ platforms:
|
|||||||
romm:
|
romm:
|
||||||
config: romm.yml
|
config: romm.yml
|
||||||
status: active
|
status: active
|
||||||
|
extract_to: library/bios/
|
||||||
logo: https://avatars.githubusercontent.com/u/168586850
|
logo: https://avatars.githubusercontent.com/u/168586850
|
||||||
scraper: romm
|
scraper: romm
|
||||||
source_url: https://raw.githubusercontent.com/rommapp/romm/master/backend/models/fixtures/known_bios_files.json
|
source_url: https://raw.githubusercontent.com/rommapp/romm/master/backend/models/fixtures/known_bios_files.json
|
||||||
@@ -945,6 +953,7 @@ platforms:
|
|||||||
rocknix:
|
rocknix:
|
||||||
config: rocknix.yml
|
config: rocknix.yml
|
||||||
status: active
|
status: active
|
||||||
|
extract_to: /storage/roms/bios/
|
||||||
logo: https://avatars.githubusercontent.com/u/152618023
|
logo: https://avatars.githubusercontent.com/u/152618023
|
||||||
scraper: rocknix
|
scraper: rocknix
|
||||||
cores: all_libretro
|
cores: all_libretro
|
||||||
@@ -959,6 +968,7 @@ platforms:
|
|||||||
retropie:
|
retropie:
|
||||||
config: retropie.yml
|
config: retropie.yml
|
||||||
status: archived
|
status: archived
|
||||||
|
extract_to: ~/RetroPie/BIOS/
|
||||||
logo: https://avatars.githubusercontent.com/u/11378204
|
logo: https://avatars.githubusercontent.com/u/11378204
|
||||||
scraper: retropie
|
scraper: retropie
|
||||||
source: https://github.com/RetroPie/RetroPie-Setup
|
source: https://github.com/RetroPie/RetroPie-Setup
|
||||||
@@ -975,6 +985,7 @@ platforms:
|
|||||||
bizhawk:
|
bizhawk:
|
||||||
config: bizhawk.yml
|
config: bizhawk.yml
|
||||||
status: active
|
status: active
|
||||||
|
extract_to: Firmware/
|
||||||
logo: https://avatars.githubusercontent.com/u/11743303
|
logo: https://avatars.githubusercontent.com/u/11743303
|
||||||
scraper: bizhawk
|
scraper: bizhawk
|
||||||
source_url: https://raw.githubusercontent.com/TASEmulators/BizHawk/master/src/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs
|
source_url: https://raw.githubusercontent.com/TASEmulators/BizHawk/master/src/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs
|
||||||
@@ -1039,6 +1050,7 @@ platforms:
|
|||||||
misterfpga:
|
misterfpga:
|
||||||
config: misterfpga.yml
|
config: misterfpga.yml
|
||||||
status: active
|
status: active
|
||||||
|
extract_to: /media/fat/games/
|
||||||
logo: https://avatars.githubusercontent.com/u/47264183
|
logo: https://avatars.githubusercontent.com/u/47264183
|
||||||
scraper: misterfpga
|
scraper: misterfpga
|
||||||
source_url: https://raw.githubusercontent.com/ajgowans/BiosDB_MiSTer/db/bios_db.json.zip
|
source_url: https://raw.githubusercontent.com/ajgowans/BiosDB_MiSTer/db/bios_db.json.zip
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ dependencies = [
|
|||||||
# and the CI contract check are the only callers, and no user path imports it.
|
# and the CI contract check are the only callers, and no user path imports it.
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"jsonschema>=4.23",
|
"jsonschema[format-nongpl]>=4.23",
|
||||||
]
|
]
|
||||||
# The site build the repo generates and documents. Both were hard
|
# The site build the repo generates and documents. Both were hard
|
||||||
# requirements of deploy-site.yml and of wiki/release-process.md while being
|
# requirements of deploy-site.yml and of wiki/release-process.md while being
|
||||||
|
|||||||
@@ -31,6 +31,37 @@
|
|||||||
"files"
|
"files"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Per-file mode separates the two builds of a dual profile. A profile with only a standalone build has no libretro side to separate it from, and a file marked standalone there drops out of the gap analysis and of the packs of every platform that lists the emulator without standalone_cores.",
|
||||||
|
"if": {
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "standalone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"properties": {
|
||||||
|
"files": {
|
||||||
|
"items": {
|
||||||
|
"not": {
|
||||||
|
"properties": {
|
||||||
|
"mode": {
|
||||||
|
"const": "standalone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"mode"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
@@ -72,6 +72,11 @@ def _strip_timestamps(text: str) -> str:
|
|||||||
class ArtifactLockBusy(RuntimeError):
|
class ArtifactLockBusy(RuntimeError):
|
||||||
"""Raised when another process already holds the artifact directory."""
|
"""Raised when another process already holds the artifact directory."""
|
||||||
|
|
||||||
|
# A run that holds an artifact directory for its whole duration names it
|
||||||
|
# here for the steps it spawns: they would otherwise be refused by it.
|
||||||
|
HELD_LOCK_ENV = "RETROBIOS_HELD_ARTIFACT_DIR"
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def artifact_lock(directory: str, exclusive: bool = True):
|
def artifact_lock(directory: str, exclusive: bool = True):
|
||||||
"""Serialize access to a shared artifact directory across processes.
|
"""Serialize access to a shared artifact directory across processes.
|
||||||
@@ -88,6 +93,9 @@ def artifact_lock(directory: str, exclusive: bool = True):
|
|||||||
return
|
return
|
||||||
|
|
||||||
os.makedirs(directory, exist_ok=True)
|
os.makedirs(directory, exist_ok=True)
|
||||||
|
if os.environ.get(HELD_LOCK_ENV) == os.path.realpath(directory):
|
||||||
|
yield
|
||||||
|
return
|
||||||
lock_path = os.path.join(directory, ".lock")
|
lock_path = os.path.join(directory, ".lock")
|
||||||
mode = fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH
|
mode = fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH
|
||||||
with open(lock_path, "w") as handle:
|
with open(lock_path, "w") as handle:
|
||||||
@@ -103,6 +111,26 @@ def artifact_lock(directory: str, exclusive: bool = True):
|
|||||||
finally:
|
finally:
|
||||||
fcntl.flock(handle, fcntl.LOCK_UN)
|
fcntl.flock(handle, fcntl.LOCK_UN)
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def hold_artifact_lock(directory: str):
|
||||||
|
"""Hold a directory exclusively across several child steps.
|
||||||
|
|
||||||
|
A lock taken step by step frees the directory between them: another
|
||||||
|
run can purge the packs one step built before the next step checks
|
||||||
|
them. The children this process spawns inherit the hold.
|
||||||
|
"""
|
||||||
|
with artifact_lock(directory):
|
||||||
|
previous = os.environ.get(HELD_LOCK_ENV)
|
||||||
|
os.environ[HELD_LOCK_ENV] = os.path.realpath(directory)
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
if previous is None:
|
||||||
|
os.environ.pop(HELD_LOCK_ENV, None)
|
||||||
|
else:
|
||||||
|
os.environ[HELD_LOCK_ENV] = previous
|
||||||
|
|
||||||
def _build_timestamp(db: dict | None = None) -> str:
|
def _build_timestamp(db: dict | None = None) -> str:
|
||||||
"""Timestamp for generated artifacts.
|
"""Timestamp for generated artifacts.
|
||||||
|
|
||||||
|
|||||||
@@ -157,6 +157,22 @@ def _core_names(config: dict) -> set[str]:
|
|||||||
return names
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def _keyed_files(files: list[dict]) -> dict[str, dict]:
|
||||||
|
"""Each declaration under its own key.
|
||||||
|
|
||||||
|
A platform declares one destination several times (an archive once per
|
||||||
|
inner ROM, MD5 alternatives): keyed by destination alone, the dict kept
|
||||||
|
the last and a change to the others read as identical.
|
||||||
|
"""
|
||||||
|
keyed: dict[str, dict] = {}
|
||||||
|
seen: dict[str, int] = {}
|
||||||
|
for f in files:
|
||||||
|
key = _file_key(f)
|
||||||
|
seen[key] = seen.get(key, 0) + 1
|
||||||
|
keyed[key if seen[key] == 1 else f"{key}#{seen[key]}"] = f
|
||||||
|
return keyed
|
||||||
|
|
||||||
|
|
||||||
def diff_platform(old: dict, new: dict) -> PlatformDiff:
|
def diff_platform(old: dict, new: dict) -> PlatformDiff:
|
||||||
"""Compare two platform files the way a reviewer reads the diff.
|
"""Compare two platform files the way a reviewer reads the diff.
|
||||||
|
|
||||||
@@ -172,12 +188,8 @@ def diff_platform(old: dict, new: dict) -> PlatformDiff:
|
|||||||
diff.systems_added = sorted(set(new_systems) - set(old_systems))
|
diff.systems_added = sorted(set(new_systems) - set(old_systems))
|
||||||
diff.systems_removed = sorted(set(old_systems) - set(new_systems))
|
diff.systems_removed = sorted(set(old_systems) - set(new_systems))
|
||||||
for system in sorted(set(old_systems) & set(new_systems)):
|
for system in sorted(set(old_systems) & set(new_systems)):
|
||||||
old_files = {
|
old_files = _keyed_files((old_systems[system] or {}).get("files") or [])
|
||||||
_file_key(f): f for f in (old_systems[system] or {}).get("files") or []
|
new_files = _keyed_files((new_systems[system] or {}).get("files") or [])
|
||||||
}
|
|
||||||
new_files = {
|
|
||||||
_file_key(f): f for f in (new_systems[system] or {}).get("files") or []
|
|
||||||
}
|
|
||||||
diff.files_added.extend(
|
diff.files_added.extend(
|
||||||
f"{system}/{k}" for k in sorted(set(new_files) - set(old_files))
|
f"{system}/{k}" for k in sorted(set(new_files) - set(old_files))
|
||||||
)
|
)
|
||||||
@@ -792,7 +804,9 @@ def _check_fbneo(cache_dir: str, offline: bool) -> Finding:
|
|||||||
|
|
||||||
# --- CI toolchain -----------------------------------------------------------
|
# --- CI toolchain -----------------------------------------------------------
|
||||||
|
|
||||||
_PIN_RE = re.compile(r'"?([A-Za-z0-9_.\-]+)((?:[<>=!~]=?[^,"\s]+)(?:,[<>=!~]=?[^,"\s]+)*)?"?')
|
_PIN_RE = re.compile(
|
||||||
|
r'"?([A-Za-z0-9_.\-]+)(?:\[[^\]]*\])?((?:[<>=!~]=?[^,"\s]+)(?:,[<>=!~]=?[^,"\s]+)*)?"?'
|
||||||
|
)
|
||||||
_USES_RE = re.compile(r"uses:\s*([\w.\-]+/[\w.\-]+)@([0-9a-f]{40})\s*(?:#\s*(\S+))?")
|
_USES_RE = re.compile(r"uses:\s*([\w.\-]+/[\w.\-]+)@([0-9a-f]{40})\s*(?:#\s*(\S+))?")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+25
-9
@@ -480,6 +480,15 @@ def _affinity_tokens(text: str) -> set[str]:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _owner_tokens(file_entry: dict) -> set[str]:
|
||||||
|
"""Tokens naming the profile that asks for the file."""
|
||||||
|
tokens: set[str] = set()
|
||||||
|
for owner in (file_entry.get("source_profile"), file_entry.get("source_emulator")):
|
||||||
|
if owner:
|
||||||
|
tokens |= _affinity_tokens(str(owner).replace(" ", ""))
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
|
||||||
def _by_affinity(paths: list[str], file_entry: dict, dest_hint: str) -> list[str]:
|
def _by_affinity(paths: list[str], file_entry: dict, dest_hint: str) -> list[str]:
|
||||||
"""Same-named candidates, the likeliest first.
|
"""Same-named candidates, the likeliest first.
|
||||||
|
|
||||||
@@ -495,16 +504,17 @@ def _by_affinity(paths: list[str], file_entry: dict, dest_hint: str) -> list[str
|
|||||||
return paths
|
return paths
|
||||||
hint = dest_hint or file_entry.get("path") or file_entry.get("destination") or ""
|
hint = dest_hint or file_entry.get("path") or file_entry.get("destination") or ""
|
||||||
wanted = _affinity_tokens(hint.rsplit("/", 1)[0] if "/" in hint else "")
|
wanted = _affinity_tokens(hint.rsplit("/", 1)[0] if "/" in hint else "")
|
||||||
for owner in (file_entry.get("source_profile"), file_entry.get("source_emulator")):
|
owners = _owner_tokens(file_entry)
|
||||||
if owner:
|
|
||||||
wanted |= _affinity_tokens(str(owner).replace(" ", ""))
|
|
||||||
sized = any(file_entry.get(k) for k in ("size", "min_size", "max_size"))
|
sized = any(file_entry.get(k) for k in ("size", "min_size", "max_size"))
|
||||||
|
|
||||||
def score(path: str) -> tuple[int, int]:
|
def score(path: str) -> tuple[int, bool, int]:
|
||||||
fits = 0
|
fits = 0
|
||||||
if sized and os.path.exists(path):
|
if sized and os.path.exists(path):
|
||||||
fits = int(size_fits(file_entry, os.path.getsize(path)))
|
fits = int(size_fits(file_entry, os.path.getsize(path)))
|
||||||
return fits, len(wanted & _affinity_tokens(path.rsplit("/", 1)[0]))
|
tokens = _affinity_tokens(path.rsplit("/", 1)[0])
|
||||||
|
# The owner's own copy ranks above any shared tree segment: NetherSX2's
|
||||||
|
# shaders/common/ beat armsx2's own fxaa.fx on two directory tokens.
|
||||||
|
return fits, bool(owners & tokens), len(wanted & tokens)
|
||||||
|
|
||||||
return sorted(paths, key=score, reverse=True)
|
return sorted(paths, key=score, reverse=True)
|
||||||
|
|
||||||
@@ -721,12 +731,17 @@ def resolve_local_file(
|
|||||||
own_file_absent = True
|
own_file_absent = True
|
||||||
if len(candidates) > 1:
|
if len(candidates) > 1:
|
||||||
depth = len(tail.split("/"))
|
depth = len(tail.split("/"))
|
||||||
|
# At equal depth the owner's own copy: super3's Games.xml was
|
||||||
|
# served to Supermodel-Dojo by index order alone.
|
||||||
|
owners = _owner_tokens(file_entry)
|
||||||
candidates = sorted(
|
candidates = sorted(
|
||||||
candidates,
|
candidates,
|
||||||
key=lambda h: len(
|
key=lambda h: (
|
||||||
files_db.get(h, {}).get("path", "").split("/")
|
len(files_db.get(h, {}).get("path", "").split("/")) - depth,
|
||||||
)
|
not owners & _affinity_tokens(
|
||||||
- depth,
|
files_db.get(h, {}).get("path", "").rsplit("/", 1)[0]
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
for match_sha1 in candidates:
|
for match_sha1 in candidates:
|
||||||
if match_sha1 not in files_db:
|
if match_sha1 not in files_db:
|
||||||
@@ -1718,6 +1733,7 @@ from artifacts import ( # noqa: E402,F401
|
|||||||
write_if_changed,
|
write_if_changed,
|
||||||
ArtifactLockBusy,
|
ArtifactLockBusy,
|
||||||
artifact_lock,
|
artifact_lock,
|
||||||
|
hold_artifact_lock,
|
||||||
_TIMESTAMP_PATTERNS,
|
_TIMESTAMP_PATTERNS,
|
||||||
_strip_timestamps,
|
_strip_timestamps,
|
||||||
)
|
)
|
||||||
|
|||||||
+155
-92
@@ -20,6 +20,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(__file__))
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
from common import (
|
from common import (
|
||||||
|
_norm_system_id,
|
||||||
get_mame_clone_map,
|
get_mame_clone_map,
|
||||||
list_registered_platforms,
|
list_registered_platforms,
|
||||||
load_database,
|
load_database,
|
||||||
@@ -28,7 +29,10 @@ from common import (
|
|||||||
name_match_size_ok,
|
name_match_size_ok,
|
||||||
parse_md5_list,
|
parse_md5_list,
|
||||||
require_yaml,
|
require_yaml,
|
||||||
|
resolve_local_file,
|
||||||
|
runs_standalone,
|
||||||
)
|
)
|
||||||
|
from validation import outside_gap_scope
|
||||||
|
|
||||||
yaml = require_yaml()
|
yaml = require_yaml()
|
||||||
|
|
||||||
@@ -44,6 +48,9 @@ def load_platform_files(
|
|||||||
"""Collect declared filenames + data_directories per system.
|
"""Collect declared filenames + data_directories per system.
|
||||||
|
|
||||||
Restricted to *platforms* when given, otherwise every registered platform.
|
Restricted to *platforms* when given, otherwise every registered platform.
|
||||||
|
Keyed by the normalized system ID: a profile writes `sega-megacd` where
|
||||||
|
RetroArch writes `sega-mega-cd`, and an exact match called 924 declared
|
||||||
|
files undeclared.
|
||||||
"""
|
"""
|
||||||
declared = {}
|
declared = {}
|
||||||
platform_data_dirs = {}
|
platform_data_dirs = {}
|
||||||
@@ -55,58 +62,68 @@ def load_platform_files(
|
|||||||
for fe in system.get("files", []):
|
for fe in system.get("files", []):
|
||||||
name = fe.get("name", "")
|
name = fe.get("name", "")
|
||||||
if name:
|
if name:
|
||||||
declared.setdefault(sys_id, set()).add(name)
|
declared.setdefault(_norm_system_id(sys_id), set()).add(name)
|
||||||
for dd in system.get("data_directories", []):
|
for dd in system.get("data_directories", []):
|
||||||
ref = dd.get("ref", "")
|
ref = dd.get("ref", "")
|
||||||
if ref:
|
if ref:
|
||||||
platform_data_dirs.setdefault(sys_id, set()).add(ref)
|
platform_data_dirs.setdefault(_norm_system_id(sys_id), set()).add(ref)
|
||||||
return declared, platform_data_dirs
|
return declared, platform_data_dirs
|
||||||
|
|
||||||
|
|
||||||
def _build_supplemental_index(
|
def _build_supplemental_index(
|
||||||
data_root: str = "data", bios_root: str = "bios"
|
data_root: str = "data", bios_root: str = "bios"
|
||||||
) -> set[str]:
|
) -> set[str]:
|
||||||
"""Build a set of filenames and directory names in data/ and inside bios/ ZIPs."""
|
"""Build a set of filenames and directory names in data/ and inside bios/ ZIPs.
|
||||||
|
|
||||||
|
A directory is indexed only with its trailing slash: a bare name matched a
|
||||||
|
file entry of the same name, and BasiliskII's required `ROM` read as held
|
||||||
|
because cpcemu keeps a directory called ROM.
|
||||||
|
"""
|
||||||
names: set[str] = set()
|
names: set[str] = set()
|
||||||
root_path = Path(data_root)
|
root_path = Path(data_root)
|
||||||
if root_path.is_dir():
|
if root_path.is_dir():
|
||||||
for fpath in root_path.rglob("*"):
|
for fpath in root_path.rglob("*"):
|
||||||
if fpath.name.startswith("."):
|
if fpath.name.startswith("."):
|
||||||
continue
|
continue
|
||||||
names.add(fpath.name)
|
if fpath.is_file():
|
||||||
names.add(fpath.name.lower())
|
names.add(fpath.name)
|
||||||
|
names.add(fpath.name.lower())
|
||||||
|
else:
|
||||||
|
names.add(fpath.name + "/")
|
||||||
|
names.add(fpath.name.lower() + "/")
|
||||||
if fpath.is_dir():
|
if fpath.is_dir():
|
||||||
# Also index relative path from data/subdir/ for directory entries
|
# Also index relative path from data/subdir/ for directory entries
|
||||||
parts = fpath.relative_to(root_path).parts
|
parts = fpath.relative_to(root_path).parts
|
||||||
if len(parts) > 1:
|
if len(parts) > 1:
|
||||||
rel = "/".join(parts[1:])
|
rel = "/".join(parts[1:])
|
||||||
names.add(rel)
|
|
||||||
names.add(rel + "/")
|
names.add(rel + "/")
|
||||||
names.add(rel.lower())
|
|
||||||
names.add(rel.lower() + "/")
|
names.add(rel.lower() + "/")
|
||||||
bios_path = Path(bios_root)
|
bios_path = Path(bios_root)
|
||||||
if bios_path.is_dir():
|
if bios_path.is_dir():
|
||||||
# Index directory names for directory-type entries (e.g., "nestopia/samples/moepro/")
|
# Index directory names for directory-type entries (e.g., "nestopia/samples/moepro/")
|
||||||
for dpath in bios_path.rglob("*"):
|
for dpath in bios_path.rglob("*"):
|
||||||
if dpath.is_dir() and not dpath.name.startswith("."):
|
if dpath.is_dir() and not dpath.name.startswith("."):
|
||||||
names.add(dpath.name)
|
|
||||||
names.add(dpath.name.lower())
|
|
||||||
names.add(dpath.name + "/")
|
names.add(dpath.name + "/")
|
||||||
names.add(dpath.name.lower() + "/")
|
names.add(dpath.name.lower() + "/")
|
||||||
import zipfile
|
names |= _zip_member_names(bios_path)
|
||||||
|
return names
|
||||||
|
|
||||||
for zpath in bios_path.rglob("*.zip"):
|
|
||||||
try:
|
def _zip_member_names(bios_path: Path) -> set[str]:
|
||||||
with zipfile.ZipFile(zpath) as zf:
|
"""Basenames of the files inside every ZIP under bios/, both cases."""
|
||||||
for member in zf.namelist():
|
import zipfile
|
||||||
if not member.endswith("/"):
|
|
||||||
basename = (
|
names: set[str] = set()
|
||||||
member.rsplit("/", 1)[-1] if "/" in member else member
|
for zpath in bios_path.rglob("*.zip"):
|
||||||
)
|
try:
|
||||||
names.add(basename)
|
with zipfile.ZipFile(zpath) as zf:
|
||||||
names.add(basename.lower())
|
members = [m for m in zf.namelist() if not m.endswith("/")]
|
||||||
except (zipfile.BadZipFile, OSError):
|
except (zipfile.BadZipFile, OSError) as exc:
|
||||||
pass
|
print(f" WARNING: unreadable archive {zpath}: {exc}", file=sys.stderr)
|
||||||
|
continue
|
||||||
|
for member in members:
|
||||||
|
basename = member.rsplit("/", 1)[-1]
|
||||||
|
names.update((basename, basename.lower()))
|
||||||
return names
|
return names
|
||||||
|
|
||||||
|
|
||||||
@@ -168,15 +185,108 @@ def _resolve_source(
|
|||||||
by_name_lower[canonical.lower()]
|
by_name_lower[canonical.lower()]
|
||||||
):
|
):
|
||||||
return "bios"
|
return "bios"
|
||||||
# data/ supplemental index
|
if data_names and _data_hit(fname, basename, file_entry, data_names):
|
||||||
if data_names:
|
return "data"
|
||||||
if fname in data_names or key in data_names:
|
|
||||||
return "data"
|
|
||||||
if basename and (basename in data_names or basename.lower() in data_names):
|
|
||||||
return "data"
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _data_hit(
|
||||||
|
fname: str, basename: str | None, file_entry: dict | None, data_names: set[str]
|
||||||
|
) -> bool:
|
||||||
|
"""Whether data/ or a ZIP holds the name: a directory entry looks for a
|
||||||
|
directory, a file entry for a file."""
|
||||||
|
looked_up = [fname, fname.lower()] + ([basename, basename.lower()] if basename else [])
|
||||||
|
if fname.endswith("/") or (file_entry or {}).get("type") == "directory":
|
||||||
|
looked_up = [name.rstrip("/") + "/" for name in looked_up]
|
||||||
|
return any(name in data_names for name in looked_up)
|
||||||
|
|
||||||
|
|
||||||
|
class FileConsumers:
|
||||||
|
"""Who reads each collected file, joined through the resolver.
|
||||||
|
|
||||||
|
The site joined database files to emulators and platforms by bare name:
|
||||||
|
each of the four pak0.pk3 of four games was attributed to every engine
|
||||||
|
reading a pak0.pk3. A consumer here is an entry that resolves to the
|
||||||
|
file by the evidence the pack uses, so a homonym is nobody's file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, db: dict, zip_contents: dict | None = None) -> None:
|
||||||
|
self._db = db
|
||||||
|
self._zip_contents = zip_contents
|
||||||
|
self._sha1_by_path = {
|
||||||
|
path: sha1
|
||||||
|
for sha1, entry in db.get("files", {}).items()
|
||||||
|
for path in {entry.get("path", ""), *entry.get("paths", [])}
|
||||||
|
if path
|
||||||
|
}
|
||||||
|
self.platforms: dict[str, set[str]] = {}
|
||||||
|
self.emulators: dict[str, set[str]] = {}
|
||||||
|
|
||||||
|
def sha1_of(self, entry: dict, dest: str = "") -> str | None:
|
||||||
|
"""The collected file an entry resolves to, or None."""
|
||||||
|
if not entry.get("name"):
|
||||||
|
return None
|
||||||
|
path, status = resolve_local_file(
|
||||||
|
entry, self._db, self._zip_contents, dest_hint=dest
|
||||||
|
)
|
||||||
|
if not path or status in ("not_found", "hash_mismatch"):
|
||||||
|
return None
|
||||||
|
return self._sha1_by_path.get(path)
|
||||||
|
|
||||||
|
def add_platforms(self, configs: dict[str, dict]) -> None:
|
||||||
|
for name, config in configs.items():
|
||||||
|
for system in config.get("systems", {}).values():
|
||||||
|
for entry in system.get("files", []):
|
||||||
|
sha1 = self.sha1_of(entry, entry.get("destination", ""))
|
||||||
|
if sha1:
|
||||||
|
self.platforms.setdefault(sha1, set()).add(name)
|
||||||
|
|
||||||
|
def add_emulators(self, profiles: dict[str, dict]) -> None:
|
||||||
|
for name, profile in profiles.items():
|
||||||
|
if profile.get("type") == "alias":
|
||||||
|
continue
|
||||||
|
for entry in profile.get("files", []):
|
||||||
|
sha1 = self.sha1_of(entry, entry.get("path") or "")
|
||||||
|
if sha1:
|
||||||
|
self.emulators.setdefault(sha1, set()).add(name)
|
||||||
|
|
||||||
|
|
||||||
|
def entry_source(f: dict, index: dict) -> str | None:
|
||||||
|
"""Where the collection holds a profile entry, or None.
|
||||||
|
|
||||||
|
By name, path and alias, then by any hash the entry declares. The gap
|
||||||
|
report and the site pages read this one function, so a file is held or
|
||||||
|
missing in the same way on both.
|
||||||
|
"""
|
||||||
|
by_name, by_name_lower = index["by_name"], index["by_name_lower"]
|
||||||
|
data_names, by_path_suffix = index.get("data_names"), index["by_path_suffix"]
|
||||||
|
db_files = index["db_files"]
|
||||||
|
fname = f.get("name", "")
|
||||||
|
path_field = f.get("path") or ""
|
||||||
|
for candidate in [fname, *([path_field] if path_field != fname else []),
|
||||||
|
*(f.get("aliases") or [])]:
|
||||||
|
if not candidate:
|
||||||
|
continue
|
||||||
|
source = _resolve_source(
|
||||||
|
candidate, by_name, by_name_lower, data_names, by_path_suffix, f, db_files,
|
||||||
|
)
|
||||||
|
if source is not None:
|
||||||
|
return source
|
||||||
|
|
||||||
|
def values(field: str) -> list[str]:
|
||||||
|
raw = f.get(field) or []
|
||||||
|
return [str(v).lower() for v in (raw if isinstance(raw, list) else [raw]) if v]
|
||||||
|
|
||||||
|
held = (
|
||||||
|
any(index["by_md5"].get(v) for v in parse_md5_list(f.get("md5")))
|
||||||
|
or any(v in db_files for v in values("sha1"))
|
||||||
|
# rust_dos names its SC-55 ROMs by sha256 alone
|
||||||
|
or any(v in index["by_sha256"] for v in values("sha256"))
|
||||||
|
or any(index["by_crc32"].get(v) for v in values("crc32"))
|
||||||
|
)
|
||||||
|
return "bios" if held else None
|
||||||
|
|
||||||
|
|
||||||
def _resolve_archive_source(
|
def _resolve_archive_source(
|
||||||
archive_name: str,
|
archive_name: str,
|
||||||
by_name: dict[str, list],
|
by_name: dict[str, list],
|
||||||
@@ -197,6 +307,7 @@ def _cross_reference_profile(
|
|||||||
declared: dict[str, set[str]],
|
declared: dict[str, set[str]],
|
||||||
report: dict,
|
report: dict,
|
||||||
index: dict,
|
index: dict,
|
||||||
|
standalone_cores: set[str] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Compare one emulator profile against what the platforms declare.
|
"""Compare one emulator profile against what the platforms declare.
|
||||||
|
|
||||||
@@ -207,10 +318,7 @@ def _cross_reference_profile(
|
|||||||
"""
|
"""
|
||||||
by_name = index["by_name"]
|
by_name = index["by_name"]
|
||||||
by_name_lower = index["by_name_lower"]
|
by_name_lower = index["by_name_lower"]
|
||||||
by_md5 = index["by_md5"]
|
|
||||||
by_crc32 = index["by_crc32"]
|
|
||||||
by_path_suffix = index["by_path_suffix"]
|
by_path_suffix = index["by_path_suffix"]
|
||||||
db_files = index["db_files"]
|
|
||||||
data_names = index["data_names"]
|
data_names = index["data_names"]
|
||||||
all_declared = index["all_declared"]
|
all_declared = index["all_declared"]
|
||||||
emu_files = profile.get("files", [])
|
emu_files = profile.get("files", [])
|
||||||
@@ -225,8 +333,11 @@ def _cross_reference_profile(
|
|||||||
else:
|
else:
|
||||||
platform_names = set()
|
platform_names = set()
|
||||||
for sys_id in systems:
|
for sys_id in systems:
|
||||||
platform_names.update(declared.get(sys_id, set()))
|
platform_names.update(declared.get(_norm_system_id(sys_id), set()))
|
||||||
|
|
||||||
|
# The build a platform runs decides which entries it reads, as in
|
||||||
|
# find_undeclared_files: without a platform, the libretro build.
|
||||||
|
is_standalone = runs_standalone(emu_name, profile, standalone_cores or set())
|
||||||
gaps = []
|
gaps = []
|
||||||
covered = []
|
covered = []
|
||||||
unsourceable_list: list[dict] = []
|
unsourceable_list: list[dict] = []
|
||||||
@@ -258,26 +369,7 @@ def _cross_reference_profile(
|
|||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip pattern placeholders (e.g., <bios>.bin, <user-selected>.bin)
|
if outside_gap_scope(f, is_standalone):
|
||||||
if "<" in fname or ">" in fname or "*" in fname:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Skip UI-imported files with explicit path: null (not resolvable by pack)
|
|
||||||
if "path" in f and f["path"] is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Skip standalone-only files
|
|
||||||
file_mode = f.get("mode", "both")
|
|
||||||
if file_mode == "standalone":
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Skip files loaded from non-system directories (save_dir, content_dir)
|
|
||||||
load_from = f.get("load_from", "")
|
|
||||||
if load_from and load_from != "system_dir":
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Skip filename-agnostic files (handled by agnostic scan)
|
|
||||||
if f.get("agnostic"):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
archive = f.get("archive")
|
archive = f.get("archive")
|
||||||
@@ -339,44 +431,7 @@ def _cross_reference_profile(
|
|||||||
if storage in ("release", "large_file"):
|
if storage in ("release", "large_file"):
|
||||||
source = "large_file"
|
source = "large_file"
|
||||||
else:
|
else:
|
||||||
source = _resolve_source(
|
source = entry_source(f, index)
|
||||||
fname, by_name, by_name_lower, data_names, by_path_suffix,
|
|
||||||
f, db_files,
|
|
||||||
)
|
|
||||||
if source is None:
|
|
||||||
path_field = f.get("path", "")
|
|
||||||
if path_field and path_field != fname:
|
|
||||||
source = _resolve_source(
|
|
||||||
path_field, by_name, by_name_lower,
|
|
||||||
data_names, by_path_suffix, f, db_files,
|
|
||||||
)
|
|
||||||
# Try the alternate names the emulator accepts, like
|
|
||||||
# resolve_local_file does
|
|
||||||
if source is None:
|
|
||||||
for alias in f.get("aliases") or []:
|
|
||||||
source = _resolve_source(
|
|
||||||
alias, by_name, by_name_lower,
|
|
||||||
data_names, by_path_suffix, f, db_files,
|
|
||||||
)
|
|
||||||
if source is not None:
|
|
||||||
break
|
|
||||||
# Try MD5 hash match
|
|
||||||
if source is None:
|
|
||||||
for md5_val in parse_md5_list(f.get("md5")):
|
|
||||||
if by_md5.get(md5_val):
|
|
||||||
source = "bios"
|
|
||||||
break
|
|
||||||
# Try SHA1 hash match
|
|
||||||
if source is None:
|
|
||||||
raw_sha1 = f.get("sha1", "")
|
|
||||||
sha1_values = raw_sha1 if isinstance(raw_sha1, list) else [raw_sha1]
|
|
||||||
if any(value and value in db_files for value in sha1_values):
|
|
||||||
source = "bios"
|
|
||||||
# Try CRC32 hash match
|
|
||||||
if source is None:
|
|
||||||
crc32 = str(f.get("crc32", "")).lower()
|
|
||||||
if crc32 and by_crc32.get(crc32):
|
|
||||||
source = "bios"
|
|
||||||
if source is None:
|
if source is None:
|
||||||
source = "missing"
|
source = "missing"
|
||||||
|
|
||||||
@@ -421,6 +476,7 @@ def cross_reference(
|
|||||||
platform_data_dirs: dict[str, set[str]] | None = None,
|
platform_data_dirs: dict[str, set[str]] | None = None,
|
||||||
data_names: set[str] | None = None,
|
data_names: set[str] | None = None,
|
||||||
all_declared: set[str] | None = None,
|
all_declared: set[str] | None = None,
|
||||||
|
standalone_cores: set[str] | None = None,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Compare emulator profiles against platform declarations.
|
"""Compare emulator profiles against platform declarations.
|
||||||
|
|
||||||
@@ -442,6 +498,7 @@ def cross_reference(
|
|||||||
by_name_lower = {k.lower(): k for k in by_name}
|
by_name_lower = {k.lower(): k for k in by_name}
|
||||||
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
||||||
by_crc32 = db.get("indexes", {}).get("by_crc32", {})
|
by_crc32 = db.get("indexes", {}).get("by_crc32", {})
|
||||||
|
by_sha256 = db.get("indexes", {}).get("by_sha256", {})
|
||||||
by_path_suffix = db.get("indexes", {}).get("by_path_suffix", {})
|
by_path_suffix = db.get("indexes", {}).get("by_path_suffix", {})
|
||||||
db_files = db.get("files", {})
|
db_files = db.get("files", {})
|
||||||
report = {}
|
report = {}
|
||||||
@@ -451,6 +508,7 @@ def cross_reference(
|
|||||||
"by_name_lower": by_name_lower,
|
"by_name_lower": by_name_lower,
|
||||||
"by_md5": by_md5,
|
"by_md5": by_md5,
|
||||||
"by_crc32": by_crc32,
|
"by_crc32": by_crc32,
|
||||||
|
"by_sha256": by_sha256,
|
||||||
"by_path_suffix": by_path_suffix,
|
"by_path_suffix": by_path_suffix,
|
||||||
"db_files": db_files,
|
"db_files": db_files,
|
||||||
"data_names": data_names,
|
"data_names": data_names,
|
||||||
@@ -458,7 +516,7 @@ def cross_reference(
|
|||||||
}
|
}
|
||||||
for emu_name, profile in profiles.items():
|
for emu_name, profile in profiles.items():
|
||||||
_cross_reference_profile(
|
_cross_reference_profile(
|
||||||
emu_name, profile, declared, report, index
|
emu_name, profile, declared, report, index, standalone_cores
|
||||||
)
|
)
|
||||||
|
|
||||||
return report
|
return report
|
||||||
@@ -535,6 +593,7 @@ def main():
|
|||||||
if args.target and not args.platform:
|
if args.target and not args.platform:
|
||||||
parser.error("--target requires --platform")
|
parser.error("--target requires --platform")
|
||||||
|
|
||||||
|
standalone_cores: set[str] = set()
|
||||||
if args.platform:
|
if args.platform:
|
||||||
from common import load_target_config, resolve_platform_cores
|
from common import load_target_config, resolve_platform_cores
|
||||||
|
|
||||||
@@ -546,6 +605,7 @@ def main():
|
|||||||
config = load_platform_config(args.platform, args.platforms_dir)
|
config = load_platform_config(args.platform, args.platforms_dir)
|
||||||
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
||||||
profiles = {k: v for k, v in profiles.items() if k in relevant}
|
profiles = {k: v for k, v in profiles.items() if k in relevant}
|
||||||
|
standalone_cores = set(map(str, config.get("standalone_cores", [])))
|
||||||
|
|
||||||
if not profiles:
|
if not profiles:
|
||||||
print("No emulator profiles found.", file=sys.stderr)
|
print("No emulator profiles found.", file=sys.stderr)
|
||||||
@@ -556,7 +616,10 @@ def main():
|
|||||||
)
|
)
|
||||||
db = load_database(args.db)
|
db = load_database(args.db)
|
||||||
data_names = _build_supplemental_index()
|
data_names = _build_supplemental_index()
|
||||||
report = cross_reference(profiles, declared, db, plat_data_dirs, data_names)
|
report = cross_reference(
|
||||||
|
profiles, declared, db, plat_data_dirs, data_names,
|
||||||
|
standalone_cores=standalone_cores,
|
||||||
|
)
|
||||||
|
|
||||||
if args.json:
|
if args.json:
|
||||||
print(json.dumps(report, indent=2))
|
print(json.dumps(report, indent=2))
|
||||||
|
|||||||
@@ -38,6 +38,19 @@ def _is_placeholder(name: str) -> bool:
|
|||||||
return "<" in name or ">" in name or "*" in name
|
return "<" in name or ">" in name or "*" in name
|
||||||
|
|
||||||
|
|
||||||
|
def _field_sets(entry: dict | None) -> dict[str, set[str]]:
|
||||||
|
return {f: set(_hash_values(entry or {}, f)) for f in HASH_FIELDS}
|
||||||
|
|
||||||
|
|
||||||
|
def _single_size(value: object) -> int | None:
|
||||||
|
"""One size, or None: a list names several accepted revisions, which a
|
||||||
|
single size field cannot state (int() on it stopped the whole export)."""
|
||||||
|
if isinstance(value, list):
|
||||||
|
distinct = {int(v) for v in value}
|
||||||
|
return distinct.pop() if len(distinct) == 1 else None
|
||||||
|
return int(value)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NativeFile:
|
class NativeFile:
|
||||||
"""One file as the platform will read it, after correction."""
|
"""One file as the platform will read it, after correction."""
|
||||||
@@ -61,19 +74,26 @@ class NativeFile:
|
|||||||
"""Which side's content fields describe the file: one side, or both.
|
"""Which side's content fields describe the file: one side, or both.
|
||||||
|
|
||||||
Mixing them field by field wrote the truth's size and crc beside the
|
Mixing them field by field wrote the truth's size and crc beside the
|
||||||
platform's sha1, a record no file satisfies. Both sides are merged
|
platform's sha1, a record no file satisfies. The truth decides alone
|
||||||
only where they agree on some hash; where they contradict, the truth
|
only on a contradiction: a hash both sides declare without a value in
|
||||||
decides alone; where the truth declares none, the platform does.
|
common, or two declared sizes. Different fields prove nothing: the
|
||||||
|
truth's crc32 beside Batocera's md5 for bios7.bin is one dump, and
|
||||||
|
reading it as a contradiction emptied the md5 the frontend checks.
|
||||||
"""
|
"""
|
||||||
truth = {f: set(_hash_values(self.truth or {}, f)) for f in HASH_FIELDS}
|
truth, plat = _field_sets(self.truth), _field_sets(self.platform)
|
||||||
plat = {f: set(_hash_values(self.platform or {}, f)) for f in HASH_FIELDS}
|
|
||||||
if not any(truth.values()):
|
if not any(truth.values()):
|
||||||
return "platform"
|
return "platform"
|
||||||
if not any(plat.values()):
|
if not any(plat.values()):
|
||||||
return "truth"
|
return "truth"
|
||||||
if any(truth[f] & plat[f] for f in HASH_FIELDS):
|
shared = [f for f in HASH_FIELDS if truth[f] and plat[f]]
|
||||||
|
if any(truth[f] & plat[f] for f in shared):
|
||||||
return "both"
|
return "both"
|
||||||
return "truth"
|
return "truth" if shared or self._sizes_differ() else "both"
|
||||||
|
|
||||||
|
def _sizes_differ(self) -> bool:
|
||||||
|
t_size = (self.truth or {}).get("size")
|
||||||
|
p_size = (self.platform or {}).get("size")
|
||||||
|
return isinstance(t_size, int) and isinstance(p_size, int) and t_size != p_size
|
||||||
|
|
||||||
def hashes(self, field_name: str) -> list[str]:
|
def hashes(self, field_name: str) -> list[str]:
|
||||||
"""Accepted values for a hash, truth first when it has an opinion.
|
"""Accepted values for a hash, truth first when it has an opinion.
|
||||||
@@ -136,7 +156,7 @@ class NativeFile:
|
|||||||
order = (self.platform, self.truth) if source == "platform" else (self.truth, self.platform)
|
order = (self.platform, self.truth) if source == "platform" else (self.truth, self.platform)
|
||||||
for entry in order:
|
for entry in order:
|
||||||
if entry and entry.get("size"):
|
if entry and entry.get("size"):
|
||||||
return int(entry["size"])
|
return _single_size(entry["size"])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def native(self, key: str, default: object = "") -> object:
|
def native(self, key: str, default: object = "") -> object:
|
||||||
@@ -302,14 +322,27 @@ def build_native_model(
|
|||||||
if candidate.truth is None
|
if candidate.truth is None
|
||||||
]
|
]
|
||||||
|
|
||||||
def by_destination(candidate: NativeFile) -> bool:
|
def by_destination(candidate: NativeFile, t_dest: str = t_dest) -> bool:
|
||||||
|
# Dolphin writes dolphin-emu/Sys/GC/USA/IPL.bin for the truth's
|
||||||
|
# GC/USA/IPL.bin: a destination ending in the path is the file.
|
||||||
theirs = _match_key(candidate.platform or {})[0]
|
theirs = _match_key(candidate.platform or {})[0]
|
||||||
return bool(t_dest) and theirs == t_dest
|
return bool(t_dest) and (theirs == t_dest or theirs.endswith("/" + t_dest))
|
||||||
|
|
||||||
def by_name(candidate: NativeFile) -> bool:
|
def by_name(
|
||||||
theirs = _match_key(candidate.platform or {})[1]
|
candidate: NativeFile,
|
||||||
|
t_dest: str = t_dest,
|
||||||
|
t_name: str = t_name,
|
||||||
|
truth_entry: dict = truth_entry,
|
||||||
|
) -> bool:
|
||||||
|
theirs_dest, theirs = _match_key(candidate.platform or {})
|
||||||
if not t_name or theirs != t_name:
|
if not t_name or theirs != t_name:
|
||||||
return False
|
return False
|
||||||
|
# Two declared directories that differ name two files: the
|
||||||
|
# truth's BeebFile/BIOS.rom is not Recalbox's np2kai/bios.rom.
|
||||||
|
if "/" in t_dest and "/" in theirs_dest and not theirs_dest.endswith(
|
||||||
|
"/" + t_dest.rsplit("/", 1)[0].rsplit("/", 1)[-1] + "/" + t_name
|
||||||
|
):
|
||||||
|
return False
|
||||||
# A shared name with two declared sizes is two files: fbneo's
|
# A shared name with two declared sizes is two files: fbneo's
|
||||||
# 480-byte boot.bin is not RomM's 2 MB Dreamcast boot.bin.
|
# 480-byte boot.bin is not RomM's 2 MB Dreamcast boot.bin.
|
||||||
t_size = truth_entry.get("size")
|
t_size = truth_entry.get("size")
|
||||||
@@ -318,7 +351,7 @@ def build_native_model(
|
|||||||
isinstance(t_size, int) and isinstance(p_size, int) and t_size != p_size
|
isinstance(t_size, int) and isinstance(p_size, int) and t_size != p_size
|
||||||
)
|
)
|
||||||
|
|
||||||
def by_hash(candidate: NativeFile) -> bool:
|
def by_hash(candidate: NativeFile, t_hashes: set = t_hashes) -> bool:
|
||||||
if not t_hashes:
|
if not t_hashes:
|
||||||
return False
|
return False
|
||||||
theirs = {
|
theirs = {
|
||||||
|
|||||||
@@ -41,6 +41,18 @@ class Exporter(BaseExporter):
|
|||||||
def carries() -> frozenset[str]:
|
def carries() -> frozenset[str]:
|
||||||
return frozenset({"md5"})
|
return frozenset({"md5"})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def writable(cls, fe: NativeFile, require: str = "") -> bool:
|
||||||
|
"""An addition the format can state whole.
|
||||||
|
|
||||||
|
checkBios compares one md5 for a plain file (altmd5 is read only for
|
||||||
|
a zippedFile): written with the first of four, retrodream's boot.bin
|
||||||
|
refused the three other dumps the core accepts.
|
||||||
|
"""
|
||||||
|
if fe.platform is None and len(set(fe.hashes("md5"))) > 1:
|
||||||
|
return False
|
||||||
|
return super().writable(fe, require)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def native_sources() -> dict[str, str]:
|
def native_sources() -> dict[str, str]:
|
||||||
return {"batocera-systems": SOURCE_URL}
|
return {"batocera-systems": SOURCE_URL}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from __future__ import annotations
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .base_exporter import BaseExporter
|
from .base_exporter import BaseExporter
|
||||||
from .baseline import NativeFile, NativeSystem, Report
|
from .baseline import NativeFile, NativeSystem, Report, _hash_values
|
||||||
|
|
||||||
SOURCE_URL = (
|
SOURCE_URL = (
|
||||||
"https://raw.githubusercontent.com/TASEmulators/BizHawk/master"
|
"https://raw.githubusercontent.com/TASEmulators/BizHawk/master"
|
||||||
@@ -60,6 +60,14 @@ class Exporter(BaseExporter):
|
|||||||
# hang off exist nowhere else.
|
# hang off exist nowhere else.
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def states(self, fe: NativeFile, field_name: str) -> bool:
|
||||||
|
"""A name that resolves to several SHA1s is left alone, so its
|
||||||
|
correction is not a change the database receives."""
|
||||||
|
touchable = getattr(self, "_touchable", None)
|
||||||
|
if field_name == "sha1" and touchable is not None and fe.name.casefold() not in touchable:
|
||||||
|
return False
|
||||||
|
return super().states(fe, field_name)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _unambiguous(systems: dict[str, NativeSystem]) -> dict[str, NativeFile]:
|
def _unambiguous(systems: dict[str, NativeSystem]) -> dict[str, NativeFile]:
|
||||||
"""Files whose name identifies exactly one entry with a SHA1.
|
"""Files whose name identifies exactly one entry with a SHA1.
|
||||||
@@ -72,7 +80,10 @@ class Exporter(BaseExporter):
|
|||||||
seen: dict[str, list[NativeFile]] = {}
|
seen: dict[str, list[NativeFile]] = {}
|
||||||
for system in systems.values():
|
for system in systems.values():
|
||||||
for fe in system.files:
|
for fe in system.files:
|
||||||
if fe.hash("sha1"):
|
# A SHA1 BizHawk declares is BizHawk's own code, the authority
|
||||||
|
# for its cores: the truth's MSX.rom came from FBNeo and would
|
||||||
|
# have made MSXHawk refuse the dump it accepts.
|
||||||
|
if fe.hash("sha1") and not _hash_values(fe.platform or {}, "sha1"):
|
||||||
seen.setdefault(fe.name.casefold(), []).append(fe)
|
seen.setdefault(fe.name.casefold(), []).append(fe)
|
||||||
resolved: dict[str, NativeFile] = {}
|
resolved: dict[str, NativeFile] = {}
|
||||||
for name, entries in seen.items():
|
for name, entries in seen.items():
|
||||||
@@ -96,6 +107,7 @@ class Exporter(BaseExporter):
|
|||||||
)
|
)
|
||||||
|
|
||||||
index = self._unambiguous(systems)
|
index = self._unambiguous(systems)
|
||||||
|
self._touchable = set(index)
|
||||||
|
|
||||||
def commented_out(text: str, position: int) -> bool:
|
def commented_out(text: str, position: int) -> bool:
|
||||||
"""Whether the call sits on a line the compiler never sees.
|
"""Whether the call sits on a line the compiler never sees.
|
||||||
|
|||||||
@@ -90,8 +90,35 @@ class Exporter(BaseExporter):
|
|||||||
def states(self, fe: NativeFile, field_name: str) -> bool:
|
def states(self, fe: NativeFile, field_name: str) -> bool:
|
||||||
if field_name in fe.filled:
|
if field_name in fe.filled:
|
||||||
return False
|
return False
|
||||||
|
if field_name == "md5" and fe.native_system in self._refused_systems():
|
||||||
|
# The array was left as EmuDeck wrote it: nothing was corrected.
|
||||||
|
return False
|
||||||
return super().states(fe, field_name)
|
return super().states(fe, field_name)
|
||||||
|
|
||||||
|
def _expected_md5s(
|
||||||
|
self, systems: dict[str, NativeSystem], name: str, system_id: str
|
||||||
|
) -> list[str]:
|
||||||
|
"""What a check must now hold; a refused rewrite keeps EmuDeck's own array."""
|
||||||
|
if name in getattr(self, "_withdrawn", {}):
|
||||||
|
return []
|
||||||
|
return self._md5s(systems, system_id)
|
||||||
|
|
||||||
|
def _refused_systems(self) -> set[str]:
|
||||||
|
withdrawn = getattr(self, "_withdrawn", {})
|
||||||
|
return {FUNCTION_HASH_MAP[name] for name in withdrawn if name in FUNCTION_HASH_MAP}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _corrected_away(systems: dict[str, NativeSystem], system_id: str) -> set[str]:
|
||||||
|
"""The platform md5 values a correction of ours replaces."""
|
||||||
|
return {
|
||||||
|
value
|
||||||
|
for system in systems.values()
|
||||||
|
if system.native_id == system_id
|
||||||
|
for fe in system.files
|
||||||
|
if "md5" in fe.corrections
|
||||||
|
for value in _hash_values(fe.platform or {}, "md5")
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _md5s(cls, systems: dict[str, NativeSystem], system_id: str) -> list[str]:
|
def _md5s(cls, systems: dict[str, NativeSystem], system_id: str) -> list[str]:
|
||||||
"""Every MD5 the system accepts, in a stable order, deduplicated."""
|
"""Every MD5 the system accepts, in a stable order, deduplicated."""
|
||||||
@@ -151,7 +178,9 @@ class Exporter(BaseExporter):
|
|||||||
# and our model does not was deleted: a user whose dump matched
|
# and our model does not was deleted: a user whose dump matched
|
||||||
# it would stop passing the check. A rewrite that withdraws one
|
# it would stop passing the check. A rewrite that withdraws one
|
||||||
# is refused and reported instead.
|
# is refused and reported instead.
|
||||||
withdrawn = theirs - set(md5s)
|
# A correction replaces the value it corrects, which is not a
|
||||||
|
# withdrawal; any other value missing from ours would be.
|
||||||
|
withdrawn = theirs - set(md5s) - self._corrected_away(systems, system_id)
|
||||||
if withdrawn:
|
if withdrawn:
|
||||||
self._withdrawn.setdefault(name, set()).update(withdrawn)
|
self._withdrawn.setdefault(name, set()).update(withdrawn)
|
||||||
elif set(md5s) != theirs:
|
elif set(md5s) != theirs:
|
||||||
@@ -179,7 +208,7 @@ class Exporter(BaseExporter):
|
|||||||
if name not in defined:
|
if name not in defined:
|
||||||
issues.append(f"check absent from the output: {name}")
|
issues.append(f"check absent from the output: {name}")
|
||||||
continue
|
continue
|
||||||
md5s = self._md5s(systems, system_id)
|
md5s = self._expected_md5s(systems, name, system_id)
|
||||||
if not md5s:
|
if not md5s:
|
||||||
continue
|
continue
|
||||||
body = next(
|
body = next(
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import zipfile
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from .base_exporter import BaseExporter
|
from .base_exporter import BaseExporter
|
||||||
from .baseline import NativeSystem, Report
|
from .baseline import NativeFile, NativeSystem, Report
|
||||||
|
|
||||||
SOURCE_URL = (
|
SOURCE_URL = (
|
||||||
"https://raw.githubusercontent.com/ajgowans/BiosDB_MiSTer/db/bios_db.json.zip"
|
"https://raw.githubusercontent.com/ajgowans/BiosDB_MiSTer/db/bios_db.json.zip"
|
||||||
@@ -57,6 +57,13 @@ class Exporter(BaseExporter):
|
|||||||
with zipfile.ZipFile(io.BytesIO(raw)) as archive:
|
with zipfile.ZipFile(io.BytesIO(raw)) as archive:
|
||||||
return {_DB_NAME: archive.read(_DB_NAME).decode("utf-8")}
|
return {_DB_NAME: archive.read(_DB_NAME).decode("utf-8")}
|
||||||
|
|
||||||
|
def states(self, fe: NativeFile, field_name: str) -> bool:
|
||||||
|
"""A correction whose path the database does not hold is not written."""
|
||||||
|
written = getattr(self, "_written", None)
|
||||||
|
if written is not None and id(fe) not in written:
|
||||||
|
return False
|
||||||
|
return super().states(fe, field_name)
|
||||||
|
|
||||||
def _by_path(self, systems: dict[str, NativeSystem]) -> dict[str, object]:
|
def _by_path(self, systems: dict[str, NativeSystem]) -> dict[str, object]:
|
||||||
indexed: dict[str, object] = {}
|
indexed: dict[str, object] = {}
|
||||||
for system in systems.values():
|
for system in systems.values():
|
||||||
@@ -80,11 +87,13 @@ class Exporter(BaseExporter):
|
|||||||
)
|
)
|
||||||
database = json.loads(original, object_pairs_hook=OrderedDict)
|
database = json.loads(original, object_pairs_hook=OrderedDict)
|
||||||
indexed = self._by_path(systems)
|
indexed = self._by_path(systems)
|
||||||
|
self._written = set()
|
||||||
|
|
||||||
for path, entry in database.get("files", {}).items():
|
for path, entry in database.get("files", {}).items():
|
||||||
fe = indexed.get(path)
|
fe = indexed.get(path)
|
||||||
if fe is None:
|
if fe is None:
|
||||||
continue
|
continue
|
||||||
|
self._written.add(id(fe))
|
||||||
md5 = fe.hash("md5")
|
md5 = fe.hash("md5")
|
||||||
if md5:
|
if md5:
|
||||||
entry["hash"] = md5
|
entry["hash"] = md5
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ SOURCE_URL = (
|
|||||||
class Exporter(BaseExporter):
|
class Exporter(BaseExporter):
|
||||||
"""Write RomM's known_bios_files.json, corrected."""
|
"""Write RomM's known_bios_files.json, corrected."""
|
||||||
|
|
||||||
|
# (system, filename) keys the platform itself declares, set by render.
|
||||||
|
_platform_keys: frozenset[tuple[str, str]] = frozenset()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def platform_name() -> str:
|
def platform_name() -> str:
|
||||||
return "romm"
|
return "romm"
|
||||||
@@ -61,16 +64,20 @@ class Exporter(BaseExporter):
|
|||||||
"""
|
"""
|
||||||
return native_id in SLUG_MAP
|
return native_id in SLUG_MAP
|
||||||
|
|
||||||
@classmethod
|
def writable(self, fe: NativeFile, require: str = "") -> bool: # noqa: ARG002
|
||||||
def writable(cls, fe: NativeFile, require: str = "") -> bool:
|
|
||||||
"""What RomM already ships stays; the conditions gate additions.
|
"""What RomM already ships stays; the conditions gate additions.
|
||||||
|
|
||||||
An entry of theirs that could never verify is still theirs, and the
|
An entry of theirs that could never verify is still theirs, and the
|
||||||
round trip is not the place to decide otherwise.
|
round trip is not the place to decide otherwise. An addition whose
|
||||||
|
slug:filename key the platform already holds cannot be written: the
|
||||||
|
fixture is a dict, and the second write replaced RomM's Dreamcast
|
||||||
|
boot.bin with fbneo's 480-byte file of the same name.
|
||||||
"""
|
"""
|
||||||
if fe.platform is not None:
|
if fe.platform is not None:
|
||||||
return True
|
return True
|
||||||
return cls._verifiable(fe) and cls._known_platform(fe.native_system)
|
if (fe.native_system, fe.name) in self._platform_keys:
|
||||||
|
return False
|
||||||
|
return self._verifiable(fe) and self._known_platform(fe.native_system)
|
||||||
|
|
||||||
def render(
|
def render(
|
||||||
self,
|
self,
|
||||||
@@ -80,6 +87,12 @@ class Exporter(BaseExporter):
|
|||||||
scraped: dict | None = None,
|
scraped: dict | None = None,
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
output: OrderedDict[str, dict] = OrderedDict()
|
output: OrderedDict[str, dict] = OrderedDict()
|
||||||
|
self._platform_keys = {
|
||||||
|
(system.native_id, fe.name)
|
||||||
|
for system in systems.values()
|
||||||
|
for fe in system.files
|
||||||
|
if fe.platform is not None
|
||||||
|
}
|
||||||
|
|
||||||
for system in sorted(systems.values(), key=lambda s: s.native_id):
|
for system in sorted(systems.values(), key=lambda s: s.native_id):
|
||||||
for fe in sorted(system.files, key=lambda f: f.name):
|
for fe in sorted(system.files, key=lambda f: f.name):
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ libretro gives them, matching libretro-database/dat/System.dat.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -21,9 +23,17 @@ SOURCE_URL = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _quote(name: str) -> str:
|
_QUOTED_NAME = re.compile(r'rom \( name "([^"]+)"')
|
||||||
"""Quote a ROM name the way the original does: only when it must be."""
|
|
||||||
return f'"{name}"' if any(c in name for c in ' ()') else name
|
|
||||||
|
def _quote(name: str, quoted: frozenset[str] = frozenset()) -> str:
|
||||||
|
"""Quote a ROM name the way the original does.
|
||||||
|
|
||||||
|
Where it must be, and where the maintainers chose to: the file quotes
|
||||||
|
246 names it did not need to, and writing them bare put a quoting change
|
||||||
|
on every one of those lines of the diff.
|
||||||
|
"""
|
||||||
|
return f'"{name}"' if name in quoted or any(c in name for c in ' ()') else name
|
||||||
|
|
||||||
|
|
||||||
class Exporter(BaseExporter):
|
class Exporter(BaseExporter):
|
||||||
@@ -108,6 +118,7 @@ class Exporter(BaseExporter):
|
|||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
lines = self._header(originals, scraped)
|
lines = self._header(originals, scraped)
|
||||||
lines.extend(["game (", '\tname "System"', '\tcomment "System"'])
|
lines.extend(["game (", '\tname "System"', '\tcomment "System"'])
|
||||||
|
quoted = frozenset(_QUOTED_NAME.findall(originals.get(self.native_filename(), "")))
|
||||||
|
|
||||||
for system, files in sorted(
|
for system, files in sorted(
|
||||||
self.exportable(systems), key=lambda pair: pair[0].native_id
|
self.exportable(systems), key=lambda pair: pair[0].native_id
|
||||||
@@ -116,7 +127,7 @@ class Exporter(BaseExporter):
|
|||||||
for fe in files:
|
for fe in files:
|
||||||
if not any(fe.hash(h) for h in ("crc32", "md5", "sha1")):
|
if not any(fe.hash(h) for h in ("crc32", "md5", "sha1")):
|
||||||
continue
|
continue
|
||||||
parts = [f"name {_quote(self._rom_name(fe))}"]
|
parts = [f"name {_quote(self._rom_name(fe), quoted)}"]
|
||||||
size = fe.size()
|
size = fe.size()
|
||||||
if size:
|
if size:
|
||||||
parts.append(f"size {size}")
|
parts.append(f"size {size}")
|
||||||
|
|||||||
+20
-2
@@ -15,6 +15,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -270,6 +271,24 @@ def _load_gitignored_large_files() -> set[str]:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _restore(cached: str, path: str) -> None:
|
||||||
|
"""Copy a fetched asset to its registered path, whole or not at all.
|
||||||
|
|
||||||
|
An interrupted copy at the final name is a truncated file the next scan
|
||||||
|
hashes and publishes; a scratch beside it is swapped in only once full.
|
||||||
|
"""
|
||||||
|
directory = os.path.dirname(path)
|
||||||
|
os.makedirs(directory, exist_ok=True)
|
||||||
|
fd, scratch = tempfile.mkstemp(dir=directory, prefix=".restore-")
|
||||||
|
os.close(fd)
|
||||||
|
try:
|
||||||
|
shutil.copy2(cached, scratch)
|
||||||
|
os.replace(scratch, path)
|
||||||
|
finally:
|
||||||
|
if os.path.exists(scratch):
|
||||||
|
os.remove(scratch)
|
||||||
|
|
||||||
|
|
||||||
def _preserve_large_file_entries(files: dict, db_path: str) -> int:
|
def _preserve_large_file_entries(files: dict, db_path: str) -> int:
|
||||||
"""Keep the entries of release assets the checkout does not hold.
|
"""Keep the entries of release assets the checkout does not hold.
|
||||||
|
|
||||||
@@ -309,8 +328,7 @@ def _preserve_large_file_entries(files: dict, db_path: str) -> int:
|
|||||||
expected_md5=entry.get("md5", ""),
|
expected_md5=entry.get("md5", ""),
|
||||||
)
|
)
|
||||||
if cached and not os.path.exists(path):
|
if cached and not os.path.exists(path):
|
||||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
_restore(cached, path)
|
||||||
shutil.copy2(cached, path)
|
|
||||||
files[sha1] = entry
|
files[sha1] = entry
|
||||||
count += 1
|
count += 1
|
||||||
return count
|
return count
|
||||||
|
|||||||
+201
-115
@@ -73,6 +73,9 @@ from nativemode import (
|
|||||||
reads_file_contents,
|
reads_file_contents,
|
||||||
)
|
)
|
||||||
from validation import (
|
from validation import (
|
||||||
|
existence_discrepancy,
|
||||||
|
destination_owners,
|
||||||
|
validated_choice,
|
||||||
inner_rom_check,
|
inner_rom_check,
|
||||||
settle_mismatch,
|
settle_mismatch,
|
||||||
agnostic_substitute,
|
agnostic_substitute,
|
||||||
@@ -201,6 +204,18 @@ def _system_tag(system_filter: list[str] | None) -> str:
|
|||||||
return "_" + "_".join(display_parts)
|
return "_" + "_".join(display_parts)
|
||||||
|
|
||||||
|
|
||||||
|
_UNSAFE_IN_NAME = re.compile(r'[/\\:*?"<>|]')
|
||||||
|
|
||||||
|
|
||||||
|
def _name_part(text: str, space: str = "") -> str:
|
||||||
|
"""A display name made safe to stand in a file name.
|
||||||
|
|
||||||
|
'FinalBurn Neo (CPS-1/CPS-2)' named a directory that did not exist; a
|
||||||
|
Windows user cannot write ':' or '?' either.
|
||||||
|
"""
|
||||||
|
return _UNSAFE_IN_NAME.sub("-", text.replace(" ", space))
|
||||||
|
|
||||||
|
|
||||||
def _platform_pack_stem(
|
def _platform_pack_stem(
|
||||||
group_platforms: list[str], representative: str, platforms_dir: str
|
group_platforms: list[str], representative: str, platforms_dir: str
|
||||||
) -> str:
|
) -> str:
|
||||||
@@ -211,14 +226,14 @@ def _platform_pack_stem(
|
|||||||
"""
|
"""
|
||||||
rep_cfg = load_platform_config(representative, platforms_dir)
|
rep_cfg = load_platform_config(representative, platforms_dir)
|
||||||
version = rep_cfg.get("version", rep_cfg.get("dat_version", ""))
|
version = rep_cfg.get("version", rep_cfg.get("dat_version", ""))
|
||||||
version_tag = f"_{version.replace(' ', '')}" if version else ""
|
version_tag = f"_{_name_part(str(version))}" if version else ""
|
||||||
if len(group_platforms) <= 1:
|
if len(group_platforms) <= 1:
|
||||||
return rep_cfg.get("platform", representative).replace(" ", "_") + version_tag
|
return _name_part(rep_cfg.get("platform", representative), "_") + version_tag
|
||||||
names = [
|
names = [
|
||||||
load_platform_config(p, platforms_dir).get("platform", p)
|
load_platform_config(p, platforms_dir).get("platform", p)
|
||||||
for p in group_platforms
|
for p in group_platforms
|
||||||
]
|
]
|
||||||
return "_".join(n.replace(" ", "") for n in names) + version_tag
|
return "_".join(_name_part(n) for n in names) + version_tag
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -562,13 +577,30 @@ def _select_variants(
|
|||||||
return region_drops, region_fallbacks, slot_undecidable
|
return region_drops, region_fallbacks, slot_undecidable
|
||||||
|
|
||||||
|
|
||||||
|
def _platform_pack_name(
|
||||||
|
platform_name: str,
|
||||||
|
platforms_dir: str,
|
||||||
|
narrowings: list[tuple[str, str]],
|
||||||
|
system_filter: list[str] | None,
|
||||||
|
pack_name: str | None = None,
|
||||||
|
) -> str:
|
||||||
|
"""File name of a platform pack: stem, narrowing tags, system tag.
|
||||||
|
|
||||||
|
A caller naming the pack itself (a --split part) is taken at its word.
|
||||||
|
"""
|
||||||
|
if pack_name:
|
||||||
|
return pack_name
|
||||||
|
stem = _platform_pack_stem([platform_name], platform_name, platforms_dir)
|
||||||
|
tags = "".join(tag for tag, _label in narrowings)
|
||||||
|
return f"{stem}{tags}_BIOS_Pack{_system_tag(system_filter)}.zip"
|
||||||
|
|
||||||
|
|
||||||
def generate_pack(
|
def generate_pack(
|
||||||
platform_name: str,
|
platform_name: str,
|
||||||
platforms_dir: str,
|
platforms_dir: str,
|
||||||
db: dict,
|
db: dict,
|
||||||
bios_dir: str,
|
bios_dir: str,
|
||||||
output_dir: str,
|
output_dir: str,
|
||||||
include_extras: bool = False,
|
|
||||||
emulators_dir: str = "emulators",
|
emulators_dir: str = "emulators",
|
||||||
zip_contents: dict | None = None,
|
zip_contents: dict | None = None,
|
||||||
data_registry: dict | None = None,
|
data_registry: dict | None = None,
|
||||||
@@ -577,15 +609,21 @@ def generate_pack(
|
|||||||
required_only: bool = False,
|
required_only: bool = False,
|
||||||
system_filter: list[str] | None = None,
|
system_filter: list[str] | None = None,
|
||||||
precomputed_extras: list[dict] | None = None,
|
precomputed_extras: list[dict] | None = None,
|
||||||
|
extras_only: bool = False,
|
||||||
source: str = "full",
|
source: str = "full",
|
||||||
flatten: bool = True,
|
flatten: bool = True,
|
||||||
regions: list[str] | None = None,
|
regions: list[str] | None = None,
|
||||||
target_name: str | None = None,
|
target_name: str | None = None,
|
||||||
one_per_slot: bool = False,
|
one_per_slot: bool = False,
|
||||||
offline: bool | None = None,
|
offline: bool | None = None,
|
||||||
|
pack_name: str | None = None,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Generate a ZIP pack for a platform.
|
"""Generate a ZIP pack for a platform.
|
||||||
|
|
||||||
|
``pack_name`` replaces the derived file name: a split part is named after
|
||||||
|
its group, and a name built from every system of a hundred-system group
|
||||||
|
is longer than a file name can be.
|
||||||
|
|
||||||
Returns the path to the generated ZIP, or None on failure.
|
Returns the path to the generated ZIP, or None on failure.
|
||||||
"""
|
"""
|
||||||
config = load_platform_config(platform_name, platforms_dir)
|
config = load_platform_config(platform_name, platforms_dir)
|
||||||
@@ -603,10 +641,12 @@ def generate_pack(
|
|||||||
narrowings = _narrowings(
|
narrowings = _narrowings(
|
||||||
source, regions, target_name, one_per_slot, required_only
|
source, regions, target_name, one_per_slot, required_only
|
||||||
)
|
)
|
||||||
narrow_tags = "".join(tag for tag, _label in narrowings)
|
zip_path = os.path.join(
|
||||||
stem = _platform_pack_stem([platform_name], platform_name, platforms_dir)
|
output_dir,
|
||||||
zip_name = f"{stem}{narrow_tags}_BIOS_Pack{_system_tag(system_filter)}.zip"
|
_platform_pack_name(
|
||||||
zip_path = os.path.join(output_dir, zip_name)
|
platform_name, platforms_dir, narrowings, system_filter, pack_name
|
||||||
|
),
|
||||||
|
)
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
|
||||||
# Case-insensitive dedup only for platforms targeting Windows/macOS.
|
# Case-insensitive dedup only for platforms targeting Windows/macOS.
|
||||||
@@ -643,6 +683,7 @@ def generate_pack(
|
|||||||
for name in resolve_platform_cores(config, emu_profiles)
|
for name in resolve_platform_cores(config, emu_profiles)
|
||||||
}
|
}
|
||||||
validation_index = _build_validation_index(platform_profiles)
|
validation_index = _build_validation_index(platform_profiles)
|
||||||
|
validation_owners = destination_owners(platform_profiles)
|
||||||
slot_overrides = slots.pack_overrides(
|
slot_overrides = slots.pack_overrides(
|
||||||
config, emu_profiles or {}, db, zip_contents, data_registry
|
config, emu_profiles or {}, db, zip_contents, data_registry
|
||||||
)
|
)
|
||||||
@@ -675,6 +716,10 @@ def generate_pack(
|
|||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
pack_systems = filtered
|
pack_systems = filtered
|
||||||
|
if extras_only:
|
||||||
|
# The part of a split that carries the core extras no platform system
|
||||||
|
# owns: the platform's own files and trees went to the other parts.
|
||||||
|
pack_systems = {}
|
||||||
|
|
||||||
preferred_entries: dict[str, int] = {}
|
preferred_entries: dict[str, int] = {}
|
||||||
if source != "truth":
|
if source != "truth":
|
||||||
@@ -851,8 +896,12 @@ def generate_pack(
|
|||||||
file_reasons[dedup_key] = "not found"
|
file_reasons[dedup_key] = "not found"
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if status == "hash_mismatch" and hash_mismatch_excludes_file(
|
# Batocera hashes the member, never the ZIP: a zipped_file entry
|
||||||
verification_mode
|
# is checked inside the archive whatever the outer resolution
|
||||||
|
# said, as verify does. An archive without the member passed.
|
||||||
|
inner_checked = bool(file_entry.get("zipped_file") and local_path)
|
||||||
|
if hash_mismatch_excludes_file(verification_mode) and (
|
||||||
|
status == "hash_mismatch" or inner_checked
|
||||||
):
|
):
|
||||||
zf_name = file_entry.get("zipped_file")
|
zf_name = file_entry.get("zipped_file")
|
||||||
if zf_name and local_path:
|
if zf_name and local_path:
|
||||||
@@ -888,15 +937,9 @@ def generate_pack(
|
|||||||
# their code never reads the bytes. An upstream hash that
|
# their code never reads the bytes. An upstream hash that
|
||||||
# contradicts a local dump is reported, not acted on.
|
# contradicts a local dump is reported, not acted on.
|
||||||
if status == "hash_mismatch" and local_path:
|
if status == "hash_mismatch" and local_path:
|
||||||
declared = file_entry.get("md5", "") or file_entry.get(
|
|
||||||
"sha1", ""
|
|
||||||
)
|
|
||||||
actual = compute_hashes(local_path)
|
|
||||||
file_reasons.setdefault(
|
file_reasons.setdefault(
|
||||||
dedup_key,
|
dedup_key,
|
||||||
f"packed per {platform_display} existence check; "
|
existence_discrepancy(file_entry, local_path, platform_display),
|
||||||
f"declared hash {declared}, file md5 "
|
|
||||||
f"{actual['md5']} sha1 {actual['sha1']}",
|
|
||||||
)
|
)
|
||||||
file_status.setdefault(dedup_key, "ok")
|
file_status.setdefault(dedup_key, "ok")
|
||||||
|
|
||||||
@@ -904,33 +947,15 @@ def generate_pack(
|
|||||||
# Platform verification (existence/md5) is the authority for pack status.
|
# Platform verification (existence/md5) is the authority for pack status.
|
||||||
# Emulator checks are supplementary -logged but don't downgrade.
|
# Emulator checks are supplementary -logged but don't downgrade.
|
||||||
# When a discrepancy is found, try to find a file satisfying both.
|
# When a discrepancy is found, try to find a file satisfying both.
|
||||||
if (
|
if file_status.get(dedup_key) == "ok":
|
||||||
file_status.get(dedup_key) == "ok"
|
local_path, disagreement = validated_choice(
|
||||||
and local_path
|
file_entry, local_path, db, validation_index, bios_dir,
|
||||||
and validation_index
|
digest_algorithm(verification_mode), dest, validation_owners,
|
||||||
):
|
|
||||||
fname = file_entry.get("name", "")
|
|
||||||
check = check_file_validation(
|
|
||||||
local_path, fname, validation_index, bios_dir
|
|
||||||
)
|
)
|
||||||
if check:
|
if disagreement:
|
||||||
reason, emus_list = check
|
file_reasons.setdefault(
|
||||||
better = find_validated_variant(
|
dedup_key, f"{platform_display} says OK but {disagreement}"
|
||||||
file_entry,
|
|
||||||
db,
|
|
||||||
local_path,
|
|
||||||
validation_index,
|
|
||||||
bios_dir,
|
|
||||||
platform_digest=digest_algorithm(verification_mode),
|
|
||||||
)
|
)
|
||||||
if better:
|
|
||||||
local_path = better
|
|
||||||
else:
|
|
||||||
emus = ", ".join(emus_list)
|
|
||||||
file_reasons.setdefault(
|
|
||||||
dedup_key,
|
|
||||||
f"{platform_display} says OK but {emus} says {reason}",
|
|
||||||
)
|
|
||||||
|
|
||||||
if already_packed:
|
if already_packed:
|
||||||
continue
|
continue
|
||||||
@@ -1222,6 +1247,38 @@ def _archive_is_optional(archive_entry: dict, archive_name: str, files: list[dic
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _pack_data_tree(
|
||||||
|
zf: zipfile.ZipFile,
|
||||||
|
local_cache: str,
|
||||||
|
dd_dest: str,
|
||||||
|
seen_destinations: set[str],
|
||||||
|
seen_parents: set[str],
|
||||||
|
seen_lower: set[str],
|
||||||
|
) -> int:
|
||||||
|
"""Write one data directory into an emulator pack, under its shared lock.
|
||||||
|
|
||||||
|
A refresh swaps the tree by rename; without the lock a walk half done
|
||||||
|
read the old tree and wrote files of the new one.
|
||||||
|
"""
|
||||||
|
added = 0
|
||||||
|
with cache_lock(local_cache, shared=True):
|
||||||
|
for root, _dirs, filenames in os.walk(local_cache):
|
||||||
|
for fname in filenames:
|
||||||
|
src = os.path.join(root, fname)
|
||||||
|
rel = os.path.relpath(src, local_cache)
|
||||||
|
full = f"{dd_dest}/{rel}" if dd_dest else rel
|
||||||
|
if full.lower() in seen_lower or _has_path_conflict(
|
||||||
|
full, seen_destinations, seen_parents
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
seen_destinations.add(full)
|
||||||
|
_register_path(full, seen_destinations, seen_parents)
|
||||||
|
seen_lower.add(full.lower())
|
||||||
|
_add_pack_member(zf, src, full)
|
||||||
|
added += 1
|
||||||
|
return added
|
||||||
|
|
||||||
|
|
||||||
def generate_emulator_pack(
|
def generate_emulator_pack(
|
||||||
profile_names: list[str],
|
profile_names: list[str],
|
||||||
emulators_dir: str,
|
emulators_dir: str,
|
||||||
@@ -1233,6 +1290,7 @@ def generate_emulator_pack(
|
|||||||
required_only: bool = False,
|
required_only: bool = False,
|
||||||
regions: list[str] | None = None,
|
regions: list[str] | None = None,
|
||||||
offline: bool | None = None,
|
offline: bool | None = None,
|
||||||
|
platforms_dir: str | None = None,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Generate a ZIP pack for specific emulator profiles."""
|
"""Generate a ZIP pack for specific emulator profiles."""
|
||||||
all_profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
|
all_profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
|
||||||
@@ -1246,7 +1304,7 @@ def generate_emulator_pack(
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# ZIP naming
|
# ZIP naming
|
||||||
display_names = [p.get("emulator", n).replace(" ", "") for n, p in selected]
|
display_names = [_name_part(p.get("emulator", n)) for n, p in selected]
|
||||||
validation_index = _build_validation_index(dict(selected))
|
validation_index = _build_validation_index(dict(selected))
|
||||||
narrow_tags = "".join(
|
narrow_tags = "".join(
|
||||||
tag
|
tag
|
||||||
@@ -1259,7 +1317,9 @@ def generate_emulator_pack(
|
|||||||
os.makedirs(output_dir, exist_ok=True)
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
|
||||||
region_drops: set[str] = (
|
region_drops: set[str] = (
|
||||||
emulator_region_drops(selected, standalone, regions) if regions else set()
|
emulator_region_drops(selected, standalone, regions, required_only)
|
||||||
|
if regions
|
||||||
|
else set()
|
||||||
)
|
)
|
||||||
|
|
||||||
total_files = 0
|
total_files = 0
|
||||||
@@ -1272,7 +1332,7 @@ def generate_emulator_pack(
|
|||||||
seen_hashes: set[str] = set() # SHA1 dedup for same file, different path
|
seen_hashes: set[str] = set() # SHA1 dedup for same file, different path
|
||||||
data_dir_notices: list[str] = []
|
data_dir_notices: list[str] = []
|
||||||
data_registry = load_data_dir_registry(
|
data_registry = load_data_dir_registry(
|
||||||
os.path.join(os.path.dirname(__file__), "..", "platforms")
|
platforms_dir or os.path.join(os.path.dirname(__file__), "..", "platforms")
|
||||||
)
|
)
|
||||||
|
|
||||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||||
@@ -1303,20 +1363,9 @@ def generate_emulator_pack(
|
|||||||
prefix = pack_structure.get(mode_key, "")
|
prefix = pack_structure.get(mode_key, "")
|
||||||
if prefix:
|
if prefix:
|
||||||
dd_dest = f"{prefix}/{dd_dest}" if dd_dest else prefix
|
dd_dest = f"{prefix}/{dd_dest}" if dd_dest else prefix
|
||||||
for root, _dirs, filenames in os.walk(local_cache):
|
total_files += _pack_data_tree(
|
||||||
for fname in filenames:
|
zf, local_cache, dd_dest, seen_destinations, seen_parents, seen_lower
|
||||||
src = os.path.join(root, fname)
|
)
|
||||||
rel = os.path.relpath(src, local_cache)
|
|
||||||
full = f"{dd_dest}/{rel}" if dd_dest else rel
|
|
||||||
if full.lower() in seen_lower:
|
|
||||||
continue
|
|
||||||
if _has_path_conflict(full, seen_destinations, seen_parents):
|
|
||||||
continue
|
|
||||||
seen_destinations.add(full)
|
|
||||||
_register_path(full, seen_destinations, seen_parents)
|
|
||||||
seen_lower.add(full.lower())
|
|
||||||
_add_pack_member(zf, src, full)
|
|
||||||
total_files += 1
|
|
||||||
|
|
||||||
if not files:
|
if not files:
|
||||||
print(f" No files needed for {profile.get('emulator', emu_name)}")
|
print(f" No files needed for {profile.get('emulator', emu_name)}")
|
||||||
@@ -1503,6 +1552,7 @@ def generate_system_pack(
|
|||||||
required_only: bool = False,
|
required_only: bool = False,
|
||||||
regions: list[str] | None = None,
|
regions: list[str] | None = None,
|
||||||
offline: bool | None = None,
|
offline: bool | None = None,
|
||||||
|
platforms_dir: str | None = None,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Generate a ZIP pack for all emulators supporting given system IDs."""
|
"""Generate a ZIP pack for all emulators supporting given system IDs."""
|
||||||
profiles = load_emulator_profiles(emulators_dir)
|
profiles = load_emulator_profiles(emulators_dir)
|
||||||
@@ -1539,36 +1589,41 @@ def generate_system_pack(
|
|||||||
|
|
||||||
# Use system-based ZIP name
|
# Use system-based ZIP name
|
||||||
sys_display = "_".join(
|
sys_display = "_".join(
|
||||||
"_".join(w.title() for w in sid.split("-")) for sid in system_ids
|
_name_part("_".join(w.title() for w in sid.split("-"))) for sid in system_ids
|
||||||
)
|
)
|
||||||
result = generate_emulator_pack(
|
# Built in a scratch directory: under its emulator name in output_dir it
|
||||||
matching,
|
# overwrote, then carried away, an --emulator pack already there.
|
||||||
emulators_dir,
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
db,
|
scratch = tempfile.mkdtemp(prefix=".system-", dir=output_dir)
|
||||||
bios_dir,
|
try:
|
||||||
output_dir,
|
result = generate_emulator_pack(
|
||||||
standalone,
|
matching,
|
||||||
zip_contents,
|
emulators_dir,
|
||||||
required_only=required_only,
|
db,
|
||||||
regions=regions,
|
bios_dir,
|
||||||
offline=offline,
|
scratch,
|
||||||
)
|
standalone,
|
||||||
if result:
|
zip_contents,
|
||||||
# Rename to system-based name. Every dimension goes through the one
|
required_only=required_only,
|
||||||
# list: keeping only the region tag made --system X --required-only
|
regions=regions,
|
||||||
# overwrite the pack built without it.
|
offline=offline,
|
||||||
|
platforms_dir=platforms_dir,
|
||||||
|
)
|
||||||
|
if not result:
|
||||||
|
return None
|
||||||
|
# Every dimension goes through the one list: keeping only the region
|
||||||
|
# tag made --system X --required-only overwrite the pack built without it.
|
||||||
tags = "".join(
|
tags = "".join(
|
||||||
tag
|
tag
|
||||||
for tag, _label in _narrowings(
|
for tag, _label in _narrowings(
|
||||||
"full", regions, None, False, required_only, standalone=standalone
|
"full", regions, None, False, required_only, standalone=standalone
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
new_name = f"{sys_display}{tags}_BIOS_Pack.zip"
|
new_path = os.path.join(output_dir, f"{sys_display}{tags}_BIOS_Pack.zip")
|
||||||
new_path = os.path.join(output_dir, new_name)
|
os.replace(result, new_path)
|
||||||
if new_path != result:
|
return new_path
|
||||||
os.rename(result, new_path)
|
finally:
|
||||||
result = new_path
|
shutil.rmtree(scratch, ignore_errors=True)
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def list_platforms(platforms_dir: str) -> list[str]:
|
def list_platforms(platforms_dir: str) -> list[str]:
|
||||||
@@ -1653,10 +1708,13 @@ def generate_split_packs(
|
|||||||
# Two groupings write different files; without the tag they accumulate in
|
# Two groupings write different files; without the tag they accumulate in
|
||||||
# one directory under one SHA256SUMS.txt.
|
# one directory under one SHA256SUMS.txt.
|
||||||
group_tag = "" if group_by == "system" else f"_By{group_by.title()}"
|
group_tag = "" if group_by == "system" else f"_By{group_by.title()}"
|
||||||
split_dir = os.path.join(
|
# The parts carry the version, so the directory does too: a rescrape that
|
||||||
output_dir,
|
# moves the version would otherwise leave the old parts beside the new
|
||||||
f"{platform_display.replace(' ', '_')}{split_tags}{group_tag}_Split",
|
# under one SHA256SUMS.txt.
|
||||||
)
|
version = config.get("version", config.get("dat_version", ""))
|
||||||
|
ver_tag = f"_{version.replace(' ', '')}" if version else ""
|
||||||
|
part_prefix = f"{platform_display.replace(' ', '_')}{ver_tag}{split_tags}"
|
||||||
|
split_dir = os.path.join(output_dir, f"{part_prefix}{group_tag}_Split")
|
||||||
os.makedirs(split_dir, exist_ok=True)
|
os.makedirs(split_dir, exist_ok=True)
|
||||||
|
|
||||||
systems = config.get("systems", {})
|
systems = config.get("systems", {})
|
||||||
@@ -1702,25 +1760,14 @@ def generate_split_packs(
|
|||||||
target_name=target_name,
|
target_name=target_name,
|
||||||
one_per_slot=one_per_slot,
|
one_per_slot=one_per_slot,
|
||||||
offline=offline,
|
offline=offline,
|
||||||
|
pack_name=f"{part_prefix}_{_name_part(group_name, '_')}_BIOS_Pack.zip",
|
||||||
)
|
)
|
||||||
if zip_path:
|
if zip_path:
|
||||||
version = config.get("version", config.get("dat_version", ""))
|
|
||||||
ver_tag = f"_{version.replace(' ', '')}" if version else ""
|
|
||||||
narrow_tags = "".join(
|
|
||||||
tag
|
|
||||||
for tag, _label in _narrowings(
|
|
||||||
source, regions, target_name, one_per_slot, required_only
|
|
||||||
)
|
|
||||||
)
|
|
||||||
safe_group = group_name.replace(" ", "_")
|
|
||||||
new_name = f"{platform_display.replace(' ', '_')}{ver_tag}{narrow_tags}_{safe_group}_BIOS_Pack.zip"
|
|
||||||
new_path = os.path.join(split_dir, new_name)
|
|
||||||
if new_path != zip_path:
|
|
||||||
os.rename(zip_path, new_path)
|
|
||||||
zip_path = new_path
|
|
||||||
results.append(zip_path)
|
results.append(zip_path)
|
||||||
|
|
||||||
# Warn about extras that couldn't be distributed (emulators without systems: field)
|
# Extras whose system the platform does not declare, or that name none,
|
||||||
|
# belong to no group: they go in a part of their own, or the parts would
|
||||||
|
# not add up to the full pack (4507 of RetroArch's 5430 were left out).
|
||||||
distributed = {
|
distributed = {
|
||||||
id(fe)
|
id(fe)
|
||||||
for fe in _extras_for_systems(
|
for fe in _extras_for_systems(
|
||||||
@@ -1729,12 +1776,17 @@ def generate_split_packs(
|
|||||||
}
|
}
|
||||||
undistributed = [fe for fe in all_extras if id(fe) not in distributed]
|
undistributed = [fe for fe in all_extras if id(fe) not in distributed]
|
||||||
if undistributed:
|
if undistributed:
|
||||||
emus = sorted({fe.get("source_emulator", "?") for fe in undistributed})
|
zip_path = generate_pack(
|
||||||
print(
|
platform_name, platforms_dir, db, bios_dir, split_dir,
|
||||||
f" NOTE: {len(undistributed)} core extras from {len(emus)} emulators "
|
emulators_dir=emulators_dir, zip_contents=zip_contents,
|
||||||
f"not in split packs (missing systems: field in profiles: "
|
data_registry=data_registry, emu_profiles=emu_profiles,
|
||||||
f"{', '.join(emus[:5])}{'...' if len(emus) > 5 else ''})"
|
target_cores=target_cores, required_only=required_only,
|
||||||
|
precomputed_extras=undistributed, extras_only=True, source=source,
|
||||||
|
regions=regions, target_name=target_name, one_per_slot=one_per_slot,
|
||||||
|
offline=offline, pack_name=f"{part_prefix}_Other_Cores_BIOS_Pack.zip",
|
||||||
)
|
)
|
||||||
|
if zip_path:
|
||||||
|
results.append(zip_path)
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@@ -1792,7 +1844,7 @@ def generate_md5_pack(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
zip_name = (
|
zip_name = (
|
||||||
f"{context_name.replace(' ', '_')}_Custom{custom_tags}_BIOS_Pack.zip"
|
f"{_name_part(context_name, '_')}_Custom{custom_tags}_BIOS_Pack.zip"
|
||||||
)
|
)
|
||||||
zip_path = os.path.join(output_dir, zip_name)
|
zip_path = os.path.join(output_dir, zip_name)
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
@@ -2309,7 +2361,6 @@ def _run_platform_packs(
|
|||||||
db,
|
db,
|
||||||
args.bios_dir,
|
args.bios_dir,
|
||||||
args.output_dir,
|
args.output_dir,
|
||||||
include_extras=args.include_extras,
|
|
||||||
emulators_dir=args.emulators_dir,
|
emulators_dir=args.emulators_dir,
|
||||||
zip_contents=zip_contents,
|
zip_contents=zip_contents,
|
||||||
data_registry=data_registry,
|
data_registry=data_registry,
|
||||||
@@ -2411,6 +2462,31 @@ def _refuse_for_all_variants(args, parser) -> None:
|
|||||||
parser.error(f"{flag} is incompatible with --verify-packs")
|
parser.error(f"{flag} is incompatible with --verify-packs")
|
||||||
|
|
||||||
|
|
||||||
|
def _refuse_refresh_data(args, parser) -> None:
|
||||||
|
"""Only the platform pack path refreshes the data directories.
|
||||||
|
|
||||||
|
Elsewhere the cache on disk was packed as it stood, and --offline wins
|
||||||
|
over the refresh without a word.
|
||||||
|
"""
|
||||||
|
if not args.refresh_data:
|
||||||
|
return
|
||||||
|
for flag, on in (
|
||||||
|
("--offline", args.offline),
|
||||||
|
("--emulator", args.emulator),
|
||||||
|
("--system", args.system and not (args.platform or args.all)),
|
||||||
|
("--from-md5", args.from_md5 or args.from_md5_file),
|
||||||
|
):
|
||||||
|
if on:
|
||||||
|
parser.error(f"--refresh-data is incompatible with {flag}")
|
||||||
|
|
||||||
|
|
||||||
|
def _refuse_include_archived(args, parser) -> None:
|
||||||
|
"""Only --all chooses among registered platforms; every other mode names
|
||||||
|
its own platform, emulator, system or hash."""
|
||||||
|
if args.include_archived and not args.all:
|
||||||
|
parser.error("--include-archived requires --all")
|
||||||
|
|
||||||
|
|
||||||
def _refuse_unapplied_flags(args, parser) -> None:
|
def _refuse_unapplied_flags(args, parser) -> None:
|
||||||
"""Refuse every flag the requested mode would not apply.
|
"""Refuse every flag the requested mode would not apply.
|
||||||
|
|
||||||
@@ -2418,6 +2494,7 @@ def _refuse_unapplied_flags(args, parser) -> None:
|
|||||||
about an artifact the caller did not name. Run before any quick-exit
|
about an artifact the caller did not name. Run before any quick-exit
|
||||||
mode, since --verify-packs and --manifest-targets return early.
|
mode, since --verify-packs and --manifest-targets return early.
|
||||||
"""
|
"""
|
||||||
|
_refuse_include_archived(args, parser)
|
||||||
# Parsed before the quick-exit modes: --verify-packs returns early and
|
# Parsed before the quick-exit modes: --verify-packs returns early and
|
||||||
# still needs the region priority list to narrow its expectation.
|
# still needs the region priority list to narrow its expectation.
|
||||||
args.regions = []
|
args.regions = []
|
||||||
@@ -2430,6 +2507,7 @@ def _refuse_unapplied_flags(args, parser) -> None:
|
|||||||
parser.error("--region is incompatible with --manifest-targets")
|
parser.error("--region is incompatible with --manifest-targets")
|
||||||
if args.one_per_slot and args.manifest_targets:
|
if args.one_per_slot and args.manifest_targets:
|
||||||
parser.error("--one-per-slot is incompatible with --manifest-targets")
|
parser.error("--one-per-slot is incompatible with --manifest-targets")
|
||||||
|
_refuse_refresh_data(args, parser)
|
||||||
|
|
||||||
_refuse_for_all_variants(args, parser)
|
_refuse_for_all_variants(args, parser)
|
||||||
mode = next(
|
mode = next(
|
||||||
@@ -2523,11 +2601,6 @@ def main():
|
|||||||
parser.add_argument("--db", default=DEFAULT_DB_FILE, help="Path to database.json")
|
parser.add_argument("--db", default=DEFAULT_DB_FILE, help="Path to database.json")
|
||||||
parser.add_argument("--bios-dir", default=DEFAULT_BIOS_DIR)
|
parser.add_argument("--bios-dir", default=DEFAULT_BIOS_DIR)
|
||||||
parser.add_argument("--output-dir", "-o", default=DEFAULT_OUTPUT_DIR)
|
parser.add_argument("--output-dir", "-o", default=DEFAULT_OUTPUT_DIR)
|
||||||
parser.add_argument(
|
|
||||||
"--include-extras",
|
|
||||||
action="store_true",
|
|
||||||
help="(no-op) Core requirements are always included",
|
|
||||||
)
|
|
||||||
parser.add_argument("--emulators-dir", default="emulators")
|
parser.add_argument("--emulators-dir", default="emulators")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--offline",
|
"--offline",
|
||||||
@@ -2700,6 +2773,7 @@ def main():
|
|||||||
required_only=args.required_only,
|
required_only=args.required_only,
|
||||||
regions=getattr(args, "regions", None),
|
regions=getattr(args, "regions", None),
|
||||||
offline=args.offline,
|
offline=args.offline,
|
||||||
|
platforms_dir=args.platforms_dir,
|
||||||
)
|
)
|
||||||
if not built:
|
if not built:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -2720,6 +2794,7 @@ def main():
|
|||||||
required_only=args.required_only,
|
required_only=args.required_only,
|
||||||
regions=getattr(args, "regions", None),
|
regions=getattr(args, "regions", None),
|
||||||
offline=args.offline,
|
offline=args.offline,
|
||||||
|
platforms_dir=args.platforms_dir,
|
||||||
)
|
)
|
||||||
if not built:
|
if not built:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -3039,6 +3114,8 @@ def generate_manifest(
|
|||||||
name: emu_profiles[name]
|
name: emu_profiles[name]
|
||||||
for name in _platform_cores(config, emu_profiles)
|
for name in _platform_cores(config, emu_profiles)
|
||||||
}
|
}
|
||||||
|
manifest_validation = _build_validation_index(manifest_profiles)
|
||||||
|
manifest_owners = destination_owners(manifest_profiles)
|
||||||
|
|
||||||
# Load registry for install metadata
|
# Load registry for install metadata
|
||||||
registry: dict = {}
|
registry: dict = {}
|
||||||
@@ -3179,6 +3256,12 @@ def generate_manifest(
|
|||||||
):
|
):
|
||||||
record_omission(full_dest, file_entry, sys_id, status, None)
|
record_omission(full_dest, file_entry, sys_id, status, None)
|
||||||
continue
|
continue
|
||||||
|
# The variant the pack ships when an emulator rejects the
|
||||||
|
# resolved file: the installer must place the same bytes.
|
||||||
|
local_path, _disagreement = validated_choice(
|
||||||
|
file_entry, local_path, db, manifest_validation, bios_dir,
|
||||||
|
digest_algorithm(verification_mode), dest, manifest_owners,
|
||||||
|
)
|
||||||
|
|
||||||
# Get SHA1 and size. The installer fetches by hash, so record
|
# Get SHA1 and size. The installer fetches by hash, so record
|
||||||
# the copy this repo holds: an upstream hash carried by no
|
# the copy this repo holds: an upstream hash carried by no
|
||||||
@@ -3354,12 +3437,15 @@ def _target_cores_for(
|
|||||||
) -> set[str] | None:
|
) -> set[str] | None:
|
||||||
"""Cores a target leaves available, or None when no target was asked for.
|
"""Cores a target leaves available, or None when no target was asked for.
|
||||||
|
|
||||||
None means no narrowing, which is what an unfiltered pack expects.
|
None means no narrowing, which is what an unfiltered pack expects. Read
|
||||||
|
the way --all builds: a platform without a target file was packed
|
||||||
|
unnarrowed under the target's name, and checking that pack must not
|
||||||
|
raise where building it did not.
|
||||||
"""
|
"""
|
||||||
if not target_name:
|
if not target_name:
|
||||||
return None
|
return None
|
||||||
cache, _kept = build_target_cores_cache(
|
cache, _kept = build_target_cores_cache(
|
||||||
[platform_name], target_name, platforms_dir
|
[platform_name], target_name, platforms_dir, is_all=True
|
||||||
)
|
)
|
||||||
return cache.get(platform_name)
|
return cache.get(platform_name)
|
||||||
|
|
||||||
|
|||||||
+69
-24
@@ -15,6 +15,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(__file__))
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
from common import (
|
from common import (
|
||||||
@@ -33,17 +34,24 @@ import release_record
|
|||||||
from verify import verify_platform
|
from verify import verify_platform
|
||||||
|
|
||||||
|
|
||||||
|
# The repository's profiles wherever the script is run from: a bare
|
||||||
|
# "emulators" read the working directory, and a run elsewhere counted 0 cores.
|
||||||
|
_EMULATORS_DIR = str(Path(__file__).resolve().parent.parent / "emulators")
|
||||||
|
|
||||||
|
|
||||||
def compute_coverage(
|
def compute_coverage(
|
||||||
platform_name: str,
|
platform_name: str,
|
||||||
platforms_dir: str,
|
platforms_dir: str,
|
||||||
db: dict,
|
db: dict,
|
||||||
data_registry: dict | None = None,
|
data_registry: dict | None = None,
|
||||||
supplemental_names: set[str] | None = None,
|
supplemental_names: set[str] | None = None,
|
||||||
|
emulators_dir: str = _EMULATORS_DIR,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
config = load_platform_config(platform_name, platforms_dir)
|
config = load_platform_config(platform_name, platforms_dir)
|
||||||
result = verify_platform(
|
result = verify_platform(
|
||||||
config,
|
config,
|
||||||
db,
|
db,
|
||||||
|
emulators_dir,
|
||||||
data_dir_registry=data_registry,
|
data_dir_registry=data_registry,
|
||||||
supplemental_names=supplemental_names,
|
supplemental_names=supplemental_names,
|
||||||
)
|
)
|
||||||
@@ -59,7 +67,7 @@ def compute_coverage(
|
|||||||
core_missing = len(undeclared) - core_present
|
core_missing = len(undeclared) - core_present
|
||||||
# Files a profile marks unsourceable never reach the undeclared list, so
|
# Files a profile marks unsourceable never reach the undeclared list, so
|
||||||
# the gap they represent has to be counted back in.
|
# the gap they represent has to be counted back in.
|
||||||
profiles = load_emulator_profiles("emulators")
|
profiles = load_emulator_profiles(emulators_dir)
|
||||||
unsourceable_names = {
|
unsourceable_names = {
|
||||||
f.get("name", "")
|
f.get("name", "")
|
||||||
for emu in resolve_platform_cores(config, profiles)
|
for emu in resolve_platform_cores(config, profiles)
|
||||||
@@ -110,6 +118,44 @@ def release_totals(platform_name: str, record: dict) -> tuple[int | None, int |
|
|||||||
return pack["files"], pack["extracted_size"]
|
return pack["files"], pack["extracted_size"]
|
||||||
|
|
||||||
|
|
||||||
|
def extract_targets(platforms_dir: str) -> list[tuple[str, str]]:
|
||||||
|
"""(display name, folder) per registered platform, where its pack extracts.
|
||||||
|
|
||||||
|
Read from the registry so the README, the site and the pack guide answer
|
||||||
|
from one place. Sorted by display name.
|
||||||
|
"""
|
||||||
|
rows = [
|
||||||
|
(load_platform_config(key, platforms_dir).get("platform", key), entry["extract_to"])
|
||||||
|
for key, entry in load_platform_registry(platforms_dir).items()
|
||||||
|
if entry.get("extract_to")
|
||||||
|
]
|
||||||
|
return sorted(rows, key=lambda row: row[0].casefold())
|
||||||
|
|
||||||
|
|
||||||
|
def extract_notes(platforms_dir: str) -> list[str]:
|
||||||
|
"""A pack whose entries carry their own root extracts above the BIOS folder."""
|
||||||
|
folders = dict(extract_targets(platforms_dir))
|
||||||
|
notes = []
|
||||||
|
for key in sorted(load_platform_registry(platforms_dir)):
|
||||||
|
config = load_platform_config(key, platforms_dir)
|
||||||
|
display = config.get("platform", key)
|
||||||
|
if config.get("base_destination") == "" and display in folders:
|
||||||
|
notes.append(
|
||||||
|
f"The {display} pack already carries its own `bios/` folder, so it"
|
||||||
|
f" extracts into `{folders[display]}` rather than into the BIOS folder."
|
||||||
|
)
|
||||||
|
return notes
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_cells(platforms_dir: str) -> dict[str, str]:
|
||||||
|
"""The download table's "Extract to" cell, by display name."""
|
||||||
|
return {display: f"`{folder}`" for display, folder in extract_targets(platforms_dir)}
|
||||||
|
|
||||||
|
|
||||||
|
def _paragraphs(texts: list[str]) -> list[str]:
|
||||||
|
return [line for text in texts for line in ("", text)]
|
||||||
|
|
||||||
|
|
||||||
def download_table(
|
def download_table(
|
||||||
coverages: dict,
|
coverages: dict,
|
||||||
archived: set[str],
|
archived: set[str],
|
||||||
@@ -356,20 +402,7 @@ def generate_readme(db: dict, platforms_dir: str) -> str:
|
|||||||
# Where the pack itself is extracted, which is not always the BIOS folder:
|
# Where the pack itself is extracted, which is not always the BIOS folder:
|
||||||
# a pack whose entries already carry their own root (RetroDECK) extracts
|
# a pack whose entries already carry their own root (RetroDECK) extracts
|
||||||
# one level above it.
|
# one level above it.
|
||||||
extract_paths = {
|
extract_paths = _extract_cells(platforms_dir)
|
||||||
"RetroArch": "`system/`",
|
|
||||||
"Lakka": "`/storage/system/`",
|
|
||||||
"Batocera": "`/userdata/bios/`",
|
|
||||||
"BizHawk": "`Firmware/`",
|
|
||||||
"Recalbox": "`/recalbox/share/bios/`",
|
|
||||||
"RetroBat": "`bios/`",
|
|
||||||
"RetroPie": "`~/RetroPie/BIOS/`",
|
|
||||||
"RetroDECK": "`~/retrodeck/`",
|
|
||||||
"EmuDeck": "`~/Emulation/bios/`",
|
|
||||||
"RomM": "`bios/{platform_slug}/`",
|
|
||||||
"ROCKNIX": "`/storage/roms/bios/`",
|
|
||||||
"MiSTer FPGA": "`/media/fat/games/`",
|
|
||||||
}
|
|
||||||
archived = {
|
archived = {
|
||||||
name
|
name
|
||||||
for name, entry in load_platform_registry(platforms_dir).items()
|
for name, entry in load_platform_registry(platforms_dir).items()
|
||||||
@@ -382,12 +415,10 @@ def generate_readme(db: dict, platforms_dir: str) -> str:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
lines.extend(_paragraphs(extract_notes(platforms_dir)))
|
||||||
if archived:
|
if archived:
|
||||||
lines.extend(
|
lines.extend(
|
||||||
[
|
[
|
||||||
"",
|
|
||||||
"The RetroDECK pack already contains its own `bios/` folder, so it"
|
|
||||||
" extracts into `~/retrodeck/` rather than into the BIOS folder.",
|
|
||||||
"",
|
"",
|
||||||
"\\* Archived: the configuration is kept and packs are still built,"
|
"\\* Archived: the configuration is kept and packs are still built,"
|
||||||
" but upstream is no longer scraped on a schedule.",
|
" but upstream is no longer scraped on a schedule.",
|
||||||
@@ -641,8 +672,14 @@ def generate_readme(db: dict, platforms_dir: str) -> str:
|
|||||||
return "\n".join(lines) + "\n"
|
return "\n".join(lines) + "\n"
|
||||||
|
|
||||||
|
|
||||||
def generate_contributing() -> str:
|
def contributing_text(link, title: str, site_words: str) -> str:
|
||||||
return f"""# Contributing to RetroBIOS
|
"""The contribution guide, one text for the repository and the site.
|
||||||
|
|
||||||
|
Two copies drifted apart: four steps to add a platform here, five on the
|
||||||
|
site. *link* renders a site page (absolute URL for the README, relative
|
||||||
|
.md for MkDocs); *site_words* names the site from where the text is read.
|
||||||
|
"""
|
||||||
|
return f"""{title}
|
||||||
|
|
||||||
## Add a BIOS file
|
## Add a BIOS file
|
||||||
|
|
||||||
@@ -651,7 +688,7 @@ def generate_contributing() -> str:
|
|||||||
3. Variants (alternate hashes for the same file): `bios/Manufacturer/Console/.variants/`
|
3. Variants (alternate hashes for the same file): `bios/Manufacturer/Console/.variants/`
|
||||||
4. Open a Pull Request - hashes are verified automatically and reported as a comment
|
4. Open a Pull Request - hashes are verified automatically and reported as a comment
|
||||||
|
|
||||||
The [dump provenance]({SITE_URL}provenance/) page lists catalogued dumps still
|
The [dump provenance]({link("provenance")}) page lists catalogued dumps still
|
||||||
missing from the collection, with their hashes. A file matching one of those is
|
missing from the collection, with their hashes. A file matching one of those is
|
||||||
the most useful contribution.
|
the most useful contribution.
|
||||||
|
|
||||||
@@ -662,7 +699,7 @@ the most useful contribution.
|
|||||||
3. Register it in `platforms/_registry.yml`
|
3. Register it in `platforms/_registry.yml`
|
||||||
4. Generate the platform YAML and test: `python scripts/verify.py --platform <name>`
|
4. Generate the platform YAML and test: `python scripts/verify.py --platform <name>`
|
||||||
|
|
||||||
Full walkthrough: [adding a platform]({SITE_URL}wiki/adding-a-platform/).
|
Full walkthrough: [adding a platform]({link("wiki/adding-a-platform")}).
|
||||||
|
|
||||||
## Add an emulator profile
|
## Add an emulator profile
|
||||||
|
|
||||||
@@ -672,7 +709,7 @@ Full walkthrough: [adding a platform]({SITE_URL}wiki/adding-a-platform/).
|
|||||||
4. Write the YAML to `emulators/<name>.yml`
|
4. Write the YAML to `emulators/<name>.yml`
|
||||||
5. Test: `python scripts/cross_reference.py --emulator <name>`
|
5. Test: `python scripts/cross_reference.py --emulator <name>`
|
||||||
|
|
||||||
Full walkthrough: [profiling guide]({SITE_URL}wiki/profiling/).
|
Full walkthrough: [profiling guide]({link("wiki/profiling")}).
|
||||||
|
|
||||||
## File conventions
|
## File conventions
|
||||||
|
|
||||||
@@ -697,10 +734,18 @@ configs, validates the YAML against the schemas, runs the test suite, and posts
|
|||||||
a report on the PR.
|
a report on the PR.
|
||||||
|
|
||||||
Contributors who add platform support are credited in the README,
|
Contributors who add platform support are credited in the README,
|
||||||
on the documentation site, and in the BIOS packs.
|
{site_words}, and in the BIOS packs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def generate_contributing() -> str:
|
||||||
|
return contributing_text(
|
||||||
|
lambda page: f"{SITE_URL}{page}/",
|
||||||
|
"# Contributing to RetroBIOS",
|
||||||
|
"on the documentation site",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="Generate slim README.md")
|
parser = argparse.ArgumentParser(description="Generate slim README.md")
|
||||||
parser.add_argument("--db", default="database.json")
|
parser.add_argument("--db", default="database.json")
|
||||||
|
|||||||
+138
-233
@@ -31,20 +31,20 @@ from pathlib import Path
|
|||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(__file__))
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
from common import (
|
from common import (
|
||||||
|
build_zip_contents_index,
|
||||||
compute_composition,
|
compute_composition,
|
||||||
count_catalog_matched,
|
count_catalog_matched,
|
||||||
list_registered_platforms,
|
list_registered_platforms,
|
||||||
load_database,
|
load_database,
|
||||||
load_emulator_profiles,
|
load_emulator_profiles,
|
||||||
load_provenance_snapshots,
|
load_provenance_snapshots,
|
||||||
parse_md5_list,
|
|
||||||
preferred_profile,
|
|
||||||
require_yaml,
|
require_yaml,
|
||||||
|
resolve_platform_cores,
|
||||||
unique_emulator_profiles,
|
unique_emulator_profiles,
|
||||||
upstream_profile_index,
|
|
||||||
write_if_changed as _write_artifact,
|
write_if_changed as _write_artifact,
|
||||||
yaml_load,
|
yaml_load,
|
||||||
)
|
)
|
||||||
|
from cross_reference import FileConsumers
|
||||||
from nativemode import reads_file_contents
|
from nativemode import reads_file_contents
|
||||||
|
|
||||||
|
|
||||||
@@ -53,7 +53,13 @@ from nativemode import reads_file_contents
|
|||||||
|
|
||||||
|
|
||||||
yaml = require_yaml()
|
yaml = require_yaml()
|
||||||
from generate_readme import compute_coverage, release_totals
|
from generate_readme import (
|
||||||
|
compute_coverage,
|
||||||
|
contributing_text,
|
||||||
|
extract_notes,
|
||||||
|
extract_targets,
|
||||||
|
release_totals,
|
||||||
|
)
|
||||||
from profile_sync import source_ref_values, split_source_ref
|
from profile_sync import source_ref_values, split_source_ref
|
||||||
from provenance_report import build_report
|
from provenance_report import build_report
|
||||||
import release_record
|
import release_record
|
||||||
@@ -201,8 +207,22 @@ def _timestamp() -> str:
|
|||||||
# Home page
|
# Home page
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_table(platforms_dir: str) -> list[str]:
|
||||||
|
"""The home page's extraction table, indented for its admonition."""
|
||||||
|
rows = [
|
||||||
|
f" | {display} | `{folder}` |"
|
||||||
|
for display, folder in extract_targets(platforms_dir)
|
||||||
|
]
|
||||||
|
notes = [f" {note}" for note in extract_notes(platforms_dir)]
|
||||||
|
return [" | Platform | Extract to |", " |----------|-----------|", *rows, "", *notes]
|
||||||
|
|
||||||
|
|
||||||
def generate_home(
|
def generate_home(
|
||||||
db: dict, coverages: dict, profiles: dict, registry: dict | None = None
|
db: dict,
|
||||||
|
coverages: dict,
|
||||||
|
profiles: dict,
|
||||||
|
registry: dict | None = None,
|
||||||
|
platforms_dir: str = "platforms",
|
||||||
) -> str:
|
) -> str:
|
||||||
total_files = db.get("total_files", 0)
|
total_files = db.get("total_files", 0)
|
||||||
total_size = db.get("total_size", 0)
|
total_size = db.get("total_size", 0)
|
||||||
@@ -318,24 +338,9 @@ def generate_home(
|
|||||||
"",
|
"",
|
||||||
'??? info "Where to extract"',
|
'??? info "Where to extract"',
|
||||||
"",
|
"",
|
||||||
" | Platform | Extract to |",
|
*_extract_table(platforms_dir),
|
||||||
" |----------|-----------|",
|
" Every other pack extracts straight into the BIOS folder."
|
||||||
" | RetroArch | `system/` |",
|
" [Full instructions per setup](which-pack.md).",
|
||||||
" | Batocera | `/userdata/bios/` |",
|
|
||||||
" | BizHawk | `Firmware/` |",
|
|
||||||
" | EmuDeck | `~/Emulation/bios/` |",
|
|
||||||
" | Lakka | `/storage/system/` |",
|
|
||||||
" | MiSTer FPGA | `/media/fat/games/` |",
|
|
||||||
" | ROCKNIX | `/storage/roms/bios/` |",
|
|
||||||
" | Recalbox | `/recalbox/share/bios/` |",
|
|
||||||
" | RetroBat | `bios/` |",
|
|
||||||
" | RetroDECK | `~/retrodeck/` |",
|
|
||||||
" | RetroPie | `~/RetroPie/BIOS/` |",
|
|
||||||
" | RomM | `bios/{platform_slug}/` |",
|
|
||||||
"",
|
|
||||||
" The RetroDECK pack already carries its own `bios/` folder, so it "
|
|
||||||
"extracts one level above it. Every other pack extracts straight into "
|
|
||||||
"the BIOS folder. [Full instructions per setup](which-pack.md).",
|
|
||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -525,7 +530,7 @@ def build_emulator_gap_report(
|
|||||||
The gap analysis page and the published gaps export must not compute this
|
The gap analysis page and the published gaps export must not compute this
|
||||||
twice and drift; both read this one report.
|
twice and drift; both read this one report.
|
||||||
"""
|
"""
|
||||||
from common import expand_platform_declared_names
|
from common import _norm_system_id, expand_platform_declared_names
|
||||||
from cross_reference import cross_reference as run_cross_reference
|
from cross_reference import cross_reference as run_cross_reference
|
||||||
|
|
||||||
all_declared: set[str] = set()
|
all_declared: set[str] = set()
|
||||||
@@ -538,7 +543,7 @@ def build_emulator_gap_report(
|
|||||||
for fe in system.get("files", []):
|
for fe in system.get("files", []):
|
||||||
fname = fe.get("name", "")
|
fname = fe.get("name", "")
|
||||||
if fname:
|
if fname:
|
||||||
declared.setdefault(sys_id, set()).add(fname)
|
declared.setdefault(_norm_system_id(sys_id), set()).add(fname)
|
||||||
|
|
||||||
unique_profiles = {
|
unique_profiles = {
|
||||||
k: v
|
k: v
|
||||||
@@ -1647,8 +1652,7 @@ def generate_provenance_page(db: dict, report: dict) -> str:
|
|||||||
def generate_system_page(
|
def generate_system_page(
|
||||||
manufacturer: str,
|
manufacturer: str,
|
||||||
consoles: dict[str, list],
|
consoles: dict[str, list],
|
||||||
platform_files: dict[str, set],
|
consumers: FileConsumers,
|
||||||
emulator_files: dict[str, dict],
|
|
||||||
) -> str:
|
) -> str:
|
||||||
manufacturer.lower().replace(" ", "-")
|
manufacturer.lower().replace(" ", "-")
|
||||||
lines = [
|
lines = [
|
||||||
@@ -1671,13 +1675,9 @@ def generate_system_page(
|
|||||||
md5_full = f.get("md5", "unknown")
|
md5_full = f.get("md5", "unknown")
|
||||||
size = _fmt_size(f.get("size", 0))
|
size = _fmt_size(f.get("size", 0))
|
||||||
|
|
||||||
# Cross-reference
|
# Who reads this file, not who reads a file of this name
|
||||||
plats = sorted(p for p, names in platform_files.items() if name in names)
|
plats = sorted(consumers.platforms.get(sha1_full, ()))
|
||||||
emus = sorted(
|
emus = sorted(consumers.emulators.get(sha1_full, ()))
|
||||||
e
|
|
||||||
for e, data in emulator_files.items()
|
|
||||||
if name in data.get("files", set())
|
|
||||||
)
|
|
||||||
|
|
||||||
# Truncated hashes for readability
|
# Truncated hashes for readability
|
||||||
sha1_short = sha1_full[:12] if sha1_full != "unknown" else "-"
|
sha1_short = sha1_full[:12] if sha1_full != "unknown" else "-"
|
||||||
@@ -1873,14 +1873,18 @@ def _file_badges(f: dict, in_repo: bool) -> list[str]:
|
|||||||
badges.append(
|
badges.append(
|
||||||
'<span class="rb-badge rb-badge-muted">optional</span>'
|
'<span class="rb-badge rb-badge-muted">optional</span>'
|
||||||
)
|
)
|
||||||
if not in_repo:
|
if in_repo:
|
||||||
badges.append(
|
|
||||||
'<span class="rb-badge rb-badge-warning">missing</span>'
|
|
||||||
)
|
|
||||||
elif in_repo:
|
|
||||||
badges.append(
|
badges.append(
|
||||||
'<span class="rb-badge rb-badge-success">in repo</span>'
|
'<span class="rb-badge rb-badge-success">in repo</span>'
|
||||||
)
|
)
|
||||||
|
elif f.get("unsourceable"):
|
||||||
|
badges.append(
|
||||||
|
'<span class="rb-badge rb-badge-muted">unsourceable</span>'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
badges.append(
|
||||||
|
'<span class="rb-badge rb-badge-warning">missing</span>'
|
||||||
|
)
|
||||||
if hle:
|
if hle:
|
||||||
badges.append(
|
badges.append(
|
||||||
'<span class="rb-badge rb-badge-info">HLE fallback</span>'
|
'<span class="rb-badge rb-badge-info">HLE fallback</span>'
|
||||||
@@ -1931,7 +1935,7 @@ def _file_badges(f: dict, in_repo: bool) -> list[str]:
|
|||||||
def _render_emulator_file(
|
def _render_emulator_file(
|
||||||
f: dict,
|
f: dict,
|
||||||
profile: dict,
|
profile: dict,
|
||||||
platform_files: dict | None,
|
consumers: FileConsumers | None,
|
||||||
files: list,
|
files: list,
|
||||||
_file_available,
|
_file_available,
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
@@ -2045,10 +2049,9 @@ def _render_emulator_file(
|
|||||||
details.append(
|
details.append(
|
||||||
f"Source: {_source_ref_markdown(profile, source_ref)}"
|
f"Source: {_source_ref_markdown(profile, source_ref)}"
|
||||||
)
|
)
|
||||||
if platform_files:
|
if consumers:
|
||||||
plats = sorted(
|
sha1 = consumers.sha1_of(f, f.get("path") or "")
|
||||||
p for p, names in platform_files.items() if fname in names
|
plats = sorted(consumers.platforms.get(sha1, ())) if sha1 else []
|
||||||
)
|
|
||||||
if plats:
|
if plats:
|
||||||
plat_links = [_platform_link(p, p, "../") for p in plats]
|
plat_links = [_platform_link(p, p, "../") for p in plats]
|
||||||
details.append(f"Platforms: {', '.join(plat_links)}")
|
details.append(f"Platforms: {', '.join(plat_links)}")
|
||||||
@@ -2217,55 +2220,75 @@ def _availability_check(db: dict, data_names):
|
|||||||
missing while the gap report called it held would describe a
|
missing while the gap report called it held would describe a
|
||||||
different collection on two pages of the same site.
|
different collection on two pages of the same site.
|
||||||
"""
|
"""
|
||||||
from cross_reference import _resolve_source
|
from cross_reference import entry_source
|
||||||
|
|
||||||
by_name = db.get("indexes", {}).get("by_name", {})
|
by_name = db.get("indexes", {}).get("by_name", {})
|
||||||
by_name_lower = {k.lower(): k for k in by_name}
|
by_name_lower = {k.lower(): k for k in by_name}
|
||||||
by_path_suffix = db.get("indexes", {}).get("by_path_suffix", {})
|
by_path_suffix = db.get("indexes", {}).get("by_path_suffix", {})
|
||||||
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
|
||||||
db_files = db.get("files", {})
|
index = {
|
||||||
|
"by_name": by_name,
|
||||||
|
"by_name_lower": by_name_lower,
|
||||||
|
"by_path_suffix": by_path_suffix,
|
||||||
|
"by_md5": db.get("indexes", {}).get("by_md5", {}),
|
||||||
|
"by_sha256": db.get("indexes", {}).get("by_sha256", {}),
|
||||||
|
"by_crc32": db.get("indexes", {}).get("by_crc32", {}),
|
||||||
|
"db_files": db.get("files", {}),
|
||||||
|
"data_names": data_names,
|
||||||
|
}
|
||||||
|
|
||||||
def _file_available(f: dict) -> bool:
|
def _file_available(f: dict) -> bool:
|
||||||
"""Check if a file is available using the same resolution as cross_reference."""
|
"""Check if a file is available using the same resolution as cross_reference."""
|
||||||
fname = f.get("name", "")
|
# cross_reference sets these aside before resolving: the file found
|
||||||
if not fname:
|
# under the name belongs to another game or release.
|
||||||
|
if not f.get("name") or f.get("unsourceable"):
|
||||||
return False
|
return False
|
||||||
storage = f.get("storage", "")
|
if f.get("storage", "") in ("release", "large_file"):
|
||||||
if storage in ("release", "large_file"):
|
|
||||||
return True
|
return True
|
||||||
src = _resolve_source(
|
return entry_source(f, index) is not None
|
||||||
fname, by_name, by_name_lower, data_names, by_path_suffix,
|
|
||||||
f, db_files,
|
|
||||||
)
|
|
||||||
if src is not None:
|
|
||||||
return True
|
|
||||||
path_field = f.get("path", "")
|
|
||||||
if path_field and path_field != fname:
|
|
||||||
src = _resolve_source(
|
|
||||||
path_field, by_name, by_name_lower, data_names,
|
|
||||||
by_path_suffix, f, db_files,
|
|
||||||
)
|
|
||||||
if src is not None:
|
|
||||||
return True
|
|
||||||
md5_raw = f.get("md5", "")
|
|
||||||
if md5_raw:
|
|
||||||
for md5_val in parse_md5_list(md5_raw):
|
|
||||||
if by_md5.get(md5_val):
|
|
||||||
return True
|
|
||||||
# A profile lists several sha1 when the code accepts several dumps.
|
|
||||||
sha1 = f.get("sha1") or []
|
|
||||||
if any(value in db_files for value in ([sha1] if isinstance(sha1, str) else sha1)):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
return _file_available
|
return _file_available
|
||||||
|
|
||||||
|
|
||||||
|
def _emulator_file_summary(files: list[dict], available) -> list[str]:
|
||||||
|
"""The counts above an emulator's file table, and its categories."""
|
||||||
|
in_repo_count = sum(1 for f in files if available(f))
|
||||||
|
unsourceable_count = sum(1 for f in files if f.get("unsourceable"))
|
||||||
|
missing_count = len(files) - in_repo_count - unsourceable_count
|
||||||
|
req_count = sum(1 for f in files if f.get("required"))
|
||||||
|
hle_count = sum(1 for f in files if f.get("hle_fallback"))
|
||||||
|
|
||||||
|
held = f"{in_repo_count} in repo, {missing_count} missing"
|
||||||
|
if unsourceable_count:
|
||||||
|
held += f", {unsourceable_count} unsourceable"
|
||||||
|
parts = [
|
||||||
|
f"**{len(files)} files**",
|
||||||
|
f"{req_count} required, {len(files) - req_count} optional",
|
||||||
|
held,
|
||||||
|
]
|
||||||
|
if hle_count:
|
||||||
|
parts.append(f"{hle_count} with HLE fallback")
|
||||||
|
lines = [" | ".join(parts)]
|
||||||
|
|
||||||
|
categories = [f.get("category", "bios") for f in files]
|
||||||
|
if "game_data" in categories or "bios_zip" in categories:
|
||||||
|
cats = [
|
||||||
|
f"{categories.count(key)} {label}"
|
||||||
|
for key, label in (
|
||||||
|
("bios", "BIOS"), ("game_data", "game data"), ("bios_zip", "BIOS ZIPs")
|
||||||
|
)
|
||||||
|
if categories.count(key)
|
||||||
|
]
|
||||||
|
lines.append(f"Categories: {', '.join(cats)}")
|
||||||
|
lines.append("")
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
def generate_emulator_page(
|
def generate_emulator_page(
|
||||||
name: str,
|
name: str,
|
||||||
profile: dict,
|
profile: dict,
|
||||||
db: dict,
|
db: dict,
|
||||||
platform_files: dict | None = None,
|
consumers: FileConsumers | None = None,
|
||||||
data_names: set[str] | None = None,
|
data_names: set[str] | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
if profile.get("type") == "alias":
|
if profile.get("type") == "alias":
|
||||||
@@ -2377,40 +2400,13 @@ def generate_emulator_page(
|
|||||||
else:
|
else:
|
||||||
_file_available = _availability_check(db, data_names)
|
_file_available = _availability_check(db, data_names)
|
||||||
|
|
||||||
# Stats by category
|
lines.extend(_emulator_file_summary(files, _file_available))
|
||||||
bios_files = [f for f in files if f.get("category", "bios") == "bios"]
|
|
||||||
game_data = [f for f in files if f.get("category") == "game_data"]
|
|
||||||
bios_zips = [f for f in files if f.get("category") == "bios_zip"]
|
|
||||||
|
|
||||||
in_repo_count = sum(1 for f in files if _file_available(f))
|
|
||||||
missing_count = len(files) - in_repo_count
|
|
||||||
req_count = sum(1 for f in files if f.get("required"))
|
|
||||||
opt_count = len(files) - req_count
|
|
||||||
hle_count = sum(1 for f in files if f.get("hle_fallback"))
|
|
||||||
|
|
||||||
parts = [f"**{len(files)} files**"]
|
|
||||||
parts.append(f"{req_count} required, {opt_count} optional")
|
|
||||||
parts.append(f"{in_repo_count} in repo, {missing_count} missing")
|
|
||||||
if hle_count:
|
|
||||||
parts.append(f"{hle_count} with HLE fallback")
|
|
||||||
lines.append(" | ".join(parts))
|
|
||||||
|
|
||||||
if game_data or bios_zips:
|
|
||||||
cats = []
|
|
||||||
if bios_files:
|
|
||||||
cats.append(f"{len(bios_files)} BIOS")
|
|
||||||
if game_data:
|
|
||||||
cats.append(f"{len(game_data)} game data")
|
|
||||||
if bios_zips:
|
|
||||||
cats.append(f"{len(bios_zips)} BIOS ZIPs")
|
|
||||||
lines.append(f"Categories: {', '.join(cats)}")
|
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
# File table
|
# File table
|
||||||
for f in files:
|
for f in files:
|
||||||
lines.extend(
|
lines.extend(
|
||||||
_render_emulator_file(
|
_render_emulator_file(
|
||||||
f, profile, platform_files, files, _file_available
|
f, profile, consumers, files, _file_available
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2941,7 +2937,7 @@ def generate_gap_analysis(
|
|||||||
|
|
||||||
|
|
||||||
def _render_xref_by_platform(
|
def _render_xref_by_platform(
|
||||||
coverages: dict, unique: dict, core_to_profile: dict
|
coverages: dict, unique: dict
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
"""Which cores each platform pulls in, and what they need."""
|
"""Which cores each platform pulls in, and what they need."""
|
||||||
lines: list[str] = []
|
lines: list[str] = []
|
||||||
@@ -2960,26 +2956,14 @@ def _render_xref_by_platform(
|
|||||||
)
|
)
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
# Resolve which profiles this platform uses
|
# The cores the builder and verify resolve: a hand copy here dropped
|
||||||
if platform_cores == "all_libretro":
|
# the libretro set a list naming retroarch pulls in, and the system
|
||||||
matched = {
|
# fallback EmuDeck relies on.
|
||||||
k: v for k, v in unique.items() if "libretro" in v.get("type", "")
|
matched = {
|
||||||
}
|
key: unique[key]
|
||||||
elif isinstance(platform_cores, list):
|
for key in resolve_platform_cores(config, unique)
|
||||||
matched = {}
|
if key in unique
|
||||||
for cname in platform_cores:
|
}
|
||||||
cname_str = str(cname)
|
|
||||||
if cname_str in unique:
|
|
||||||
matched[cname_str] = unique[cname_str]
|
|
||||||
elif cname_str in core_to_profile:
|
|
||||||
pkey = core_to_profile[cname_str]
|
|
||||||
matched[pkey] = unique[pkey]
|
|
||||||
else:
|
|
||||||
# Fallback: system intersection
|
|
||||||
psystems = set(config.get("systems", {}).keys())
|
|
||||||
matched = {
|
|
||||||
k: v for k, v in unique.items() if set(v.get("systems", [])) & psystems
|
|
||||||
}
|
|
||||||
|
|
||||||
if platform_cores == "all_libretro":
|
if platform_cores == "all_libretro":
|
||||||
lines.append(f" **{len(matched)} cores** (all libretro)")
|
lines.append(f" **{len(matched)} cores** (all libretro)")
|
||||||
@@ -3043,7 +3027,7 @@ def _render_xref_by_platform(
|
|||||||
|
|
||||||
|
|
||||||
def _render_xref_by_upstream(
|
def _render_xref_by_upstream(
|
||||||
coverages: dict, unique: dict, core_to_profile: dict
|
coverages: dict, unique: dict
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
"""The same relation read the other way: one row per upstream."""
|
"""The same relation read the other way: one row per upstream."""
|
||||||
lines: list[str] = []
|
lines: list[str] = []
|
||||||
@@ -3068,20 +3052,8 @@ def _render_xref_by_upstream(
|
|||||||
# Build platform membership per core
|
# Build platform membership per core
|
||||||
platform_membership: dict[str, set[str]] = {}
|
platform_membership: dict[str, set[str]] = {}
|
||||||
for pname, cov in coverages.items():
|
for pname, cov in coverages.items():
|
||||||
config = cov["config"]
|
for key in resolve_platform_cores(cov["config"], unique):
|
||||||
pcores = config.get("cores", [])
|
platform_membership.setdefault(key, set()).add(pname)
|
||||||
if pcores == "all_libretro":
|
|
||||||
for k, v in unique.items():
|
|
||||||
if "libretro" in v.get("type", ""):
|
|
||||||
platform_membership.setdefault(k, set()).add(pname)
|
|
||||||
elif isinstance(pcores, list):
|
|
||||||
for cname in pcores:
|
|
||||||
cname_str = str(cname)
|
|
||||||
if cname_str in unique:
|
|
||||||
platform_membership.setdefault(cname_str, set()).add(pname)
|
|
||||||
elif cname_str in core_to_profile:
|
|
||||||
pkey = core_to_profile[cname_str]
|
|
||||||
platform_membership.setdefault(pkey, set()).add(pname)
|
|
||||||
|
|
||||||
for upstream_url in sorted(by_upstream.keys()):
|
for upstream_url in sorted(by_upstream.keys()):
|
||||||
cores = by_upstream[upstream_url]
|
cores = by_upstream[upstream_url]
|
||||||
@@ -3118,12 +3090,6 @@ def generate_cross_reference(
|
|||||||
k: v for k, v in profiles.items() if v.get("type") not in ("alias", "test")
|
k: v for k, v in profiles.items() if v.get("type") not in ("alias", "test")
|
||||||
}
|
}
|
||||||
|
|
||||||
# Core name -> the profile it designates, the rule target filtering uses.
|
|
||||||
index = upstream_profile_index(unique)
|
|
||||||
core_to_profile: dict[str, str] = {
|
|
||||||
core: preferred_profile(index, core) for core in index
|
|
||||||
}
|
|
||||||
|
|
||||||
total_cores = len(unique)
|
total_cores = len(unique)
|
||||||
total_upstreams = len({
|
total_upstreams = len({
|
||||||
p.get("upstream", p.get("source", ""))
|
p.get("upstream", p.get("source", ""))
|
||||||
@@ -3147,72 +3113,17 @@ def generate_cross_reference(
|
|||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
|
|
||||||
lines.extend(_render_xref_by_platform(coverages, unique, core_to_profile))
|
lines.extend(_render_xref_by_platform(coverages, unique))
|
||||||
lines.extend(_render_xref_by_upstream(coverages, unique, core_to_profile))
|
lines.extend(_render_xref_by_upstream(coverages, unique))
|
||||||
|
|
||||||
lines.extend(["", f"*Generated on {_timestamp()}*"])
|
lines.extend(["", f"*Generated on {_timestamp()}*"])
|
||||||
return "\n".join(lines) + "\n"
|
return "\n".join(lines) + "\n"
|
||||||
|
|
||||||
|
|
||||||
def generate_contributing() -> str:
|
def generate_contributing() -> str:
|
||||||
return """# Contributing - RetroBIOS
|
return contributing_text(
|
||||||
|
lambda page: f"{page}.md", "# Contributing - RetroBIOS", "on this site"
|
||||||
## Add a BIOS file
|
)
|
||||||
|
|
||||||
1. Fork this repository
|
|
||||||
2. Place the file in `bios/Manufacturer/Console/filename`
|
|
||||||
3. Variants (alternate hashes for the same file): place in `bios/Manufacturer/Console/.variants/`
|
|
||||||
4. Open a Pull Request - hashes are verified automatically and reported as a comment
|
|
||||||
|
|
||||||
The [dump provenance](provenance.md) page lists catalogued dumps still missing
|
|
||||||
from the collection, with their hashes. A file matching one of those is the
|
|
||||||
most useful contribution.
|
|
||||||
|
|
||||||
## Add a platform
|
|
||||||
|
|
||||||
1. Create a scraper in `scripts/scraper/` (inherit `BaseScraper`)
|
|
||||||
2. Read the platform's upstream source to determine how it checks BIOS files
|
|
||||||
3. Add an entry to `platforms/_registry.yml`
|
|
||||||
4. Generate the platform YAML config
|
|
||||||
5. Test: `python scripts/verify.py --platform <name>`
|
|
||||||
|
|
||||||
Full walkthrough: [adding a platform](wiki/adding-a-platform.md).
|
|
||||||
|
|
||||||
## Add an emulator profile
|
|
||||||
|
|
||||||
1. Clone the emulator's source code, upstream and libretro port
|
|
||||||
2. Trace the file loading from the entry point, not from a keyword grep
|
|
||||||
3. Document every file the code loads, with a `source_ref` line reference
|
|
||||||
4. Write the YAML to `emulators/<name>.yml`
|
|
||||||
5. Test: `python scripts/cross_reference.py --emulator <name>`
|
|
||||||
|
|
||||||
Full walkthrough: [profiling guide](wiki/profiling.md).
|
|
||||||
|
|
||||||
## File conventions
|
|
||||||
|
|
||||||
- `bios/Manufacturer/Console/filename` for canonical files
|
|
||||||
- `bios/Manufacturer/Console/.variants/filename.sha1prefix` for alternate versions
|
|
||||||
- Files >50 MB go in GitHub release assets (`large-files` release)
|
|
||||||
- RPG Maker and ScummVM directories are excluded from deduplication
|
|
||||||
- Two paths differing only by case break clones on Windows and macOS;
|
|
||||||
`tests/test_no_case_collisions.py` enforces this
|
|
||||||
|
|
||||||
## Before opening a PR
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -m unittest discover tests
|
|
||||||
python scripts/pipeline.py --offline
|
|
||||||
```
|
|
||||||
|
|
||||||
## PR validation
|
|
||||||
|
|
||||||
CI computes SHA1/MD5/CRC32 for every new file, checks them against the platform
|
|
||||||
configs, validates the YAML against the schemas, runs the test suite, and posts
|
|
||||||
a report on the PR.
|
|
||||||
|
|
||||||
Contributors who add platform support are credited in the README, on this site,
|
|
||||||
and in the BIOS packs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
# Wiki pages
|
# Wiki pages
|
||||||
@@ -3338,30 +3249,21 @@ def generate_wiki_data_model(db: dict, profiles: dict) -> str:
|
|||||||
# Build cross-reference indexes
|
# Build cross-reference indexes
|
||||||
|
|
||||||
|
|
||||||
def _build_platform_file_index(coverages: dict) -> dict[str, set]:
|
def _build_file_consumers(db: dict, coverages: dict, profiles: dict) -> FileConsumers:
|
||||||
"""Map platform_name -> set of declared file names."""
|
"""Which platforms and emulators read each collected file."""
|
||||||
index = {}
|
consumers = FileConsumers(db, build_zip_contents_index(db))
|
||||||
for name, cov in coverages.items():
|
consumers.add_platforms({name: cov["config"] for name, cov in coverages.items()})
|
||||||
names = set()
|
consumers.add_emulators(profiles)
|
||||||
config = cov["config"]
|
return consumers
|
||||||
for system in config.get("systems", {}).values():
|
|
||||||
for fe in system.get("files", []):
|
|
||||||
names.add(fe.get("name", ""))
|
|
||||||
index[name] = names
|
|
||||||
return index
|
|
||||||
|
|
||||||
|
|
||||||
def _build_emulator_file_index(profiles: dict) -> dict[str, dict]:
|
def _build_emulator_file_index(profiles: dict) -> dict[str, dict]:
|
||||||
"""Map emulator_name -> {files: set, systems: set} for cross-reference."""
|
"""Map emulator_name -> {systems: set}, for the platform pages."""
|
||||||
index = {}
|
return {
|
||||||
for name, profile in profiles.items():
|
name: {"systems": set(profile.get("systems", []))}
|
||||||
if profile.get("type") == "alias":
|
for name, profile in profiles.items()
|
||||||
continue
|
if profile.get("type") != "alias"
|
||||||
index[name] = {
|
}
|
||||||
"files": {f.get("name", "") for f in profile.get("files", [])},
|
|
||||||
"systems": set(profile.get("systems", [])),
|
|
||||||
}
|
|
||||||
return index
|
|
||||||
|
|
||||||
|
|
||||||
# mkdocs.yml nav generator
|
# mkdocs.yml nav generator
|
||||||
@@ -3687,7 +3589,8 @@ def main():
|
|||||||
for name in sorted(platform_names):
|
for name in sorted(platform_names):
|
||||||
try:
|
try:
|
||||||
cov = compute_coverage(
|
cov = compute_coverage(
|
||||||
name, args.platforms_dir, db, data_registry, suppl_names
|
name, args.platforms_dir, db, data_registry, suppl_names,
|
||||||
|
emulators_dir=args.emulators_dir,
|
||||||
)
|
)
|
||||||
coverages[name] = cov
|
coverages[name] = cov
|
||||||
print(
|
print(
|
||||||
@@ -3704,13 +3607,15 @@ def main():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Build cross-reference indexes
|
# Build cross-reference indexes
|
||||||
platform_files = _build_platform_file_index(coverages)
|
consumers = _build_file_consumers(db, coverages, profiles)
|
||||||
emulator_files = _build_emulator_file_index(profiles)
|
emulator_files = _build_emulator_file_index(profiles)
|
||||||
|
|
||||||
# Generate home
|
# Generate home
|
||||||
print("Generating home page...")
|
print("Generating home page...")
|
||||||
write_if_changed(
|
write_if_changed(
|
||||||
str(docs / "index.md"), generate_home(db, coverages, profiles, registry)
|
str(docs / "index.md"), generate_home(
|
||||||
|
db, coverages, profiles, registry, args.platforms_dir
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
stats = compute_stats(db, coverages, profiles)
|
stats = compute_stats(db, coverages, profiles)
|
||||||
@@ -3759,7 +3664,7 @@ def main():
|
|||||||
)
|
)
|
||||||
for mfr, consoles in manufacturers.items():
|
for mfr, consoles in manufacturers.items():
|
||||||
slug = mfr.lower().replace(" ", "-")
|
slug = mfr.lower().replace(" ", "-")
|
||||||
page = generate_system_page(mfr, consoles, platform_files, emulator_files)
|
page = generate_system_page(mfr, consoles, consumers)
|
||||||
write_if_changed(str(docs / "systems" / f"{slug}.md"), page)
|
write_if_changed(str(docs / "systems" / f"{slug}.md"), page)
|
||||||
|
|
||||||
# Generate emulator pages
|
# Generate emulator pages
|
||||||
@@ -3773,7 +3678,7 @@ def main():
|
|||||||
if profile.get("type") not in ("alias", "test")
|
if profile.get("type") not in ("alias", "test")
|
||||||
}
|
}
|
||||||
for name, profile in public_profiles.items():
|
for name, profile in public_profiles.items():
|
||||||
page = generate_emulator_page(name, profile, db, platform_files, suppl_names)
|
page = generate_emulator_page(name, profile, db, consumers, suppl_names)
|
||||||
write_if_changed(str(docs / "emulators" / f"{name}.md"), page)
|
write_if_changed(str(docs / "emulators" / f"{name}.md"), page)
|
||||||
|
|
||||||
# Generate cross-reference page
|
# Generate cross-reference page
|
||||||
|
|||||||
+59
-16
@@ -163,11 +163,15 @@ def _fetch_asset(
|
|||||||
hashes, expected_sha1, expected_md5
|
hashes, expected_sha1, expected_md5
|
||||||
):
|
):
|
||||||
return cached
|
return cached
|
||||||
else:
|
elif not _replaced_on_release(name, cached, offline):
|
||||||
# A verified copy of this asset that answers another hash: the
|
# A copy of this asset that answers another hash: the caller
|
||||||
# caller wants a different revision under the same name. Keeping
|
# wants a different revision under the same name, and the
|
||||||
# it is what lets the next caller for the primary find it.
|
# release still serves this one. Keeping it is what lets the
|
||||||
|
# next caller for the primary find it.
|
||||||
return None
|
return None
|
||||||
|
# Otherwise the release was re-uploaded since this copy was cached
|
||||||
|
# (gh release upload --clobber): only a download can answer, and
|
||||||
|
# it replaces the copy only if it verifies.
|
||||||
|
|
||||||
if offline:
|
if offline:
|
||||||
return None
|
return None
|
||||||
@@ -179,20 +183,9 @@ def _fetch_asset(
|
|||||||
dir=dest_dir, prefix=os.path.basename(cached) + ".", suffix=".tmp"
|
dir=dest_dir, prefix=os.path.basename(cached) + ".", suffix=".tmp"
|
||||||
)
|
)
|
||||||
os.close(tmp_fd)
|
os.close(tmp_fd)
|
||||||
# GitHub rewrites spaces to dots in release asset names, so a file whose
|
|
||||||
# name contains spaces is published under a dotted name.
|
|
||||||
candidates = [name]
|
|
||||||
if " " in name:
|
|
||||||
candidates.append(name.replace(" ", "."))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
downloaded = False
|
downloaded = False
|
||||||
for candidate in candidates:
|
for candidate, url in _asset_urls(name):
|
||||||
encoded_name = urllib.parse.quote(candidate)
|
|
||||||
url = (
|
|
||||||
f"https://github.com/{LARGE_FILES_REPO}/releases/download/"
|
|
||||||
f"{LARGE_FILES_RELEASE}/{encoded_name}"
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
req = urllib.request.Request(url, headers={"User-Agent": "retrobios/1.0"})
|
req = urllib.request.Request(url, headers={"User-Agent": "retrobios/1.0"})
|
||||||
with urllib.request.urlopen(req, timeout=300) as resp:
|
with urllib.request.urlopen(req, timeout=300) as resp:
|
||||||
@@ -220,6 +213,56 @@ def _fetch_asset(
|
|||||||
_drop(tmp_path)
|
_drop(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
def _asset_urls(name: str) -> list[tuple[str, str]]:
|
||||||
|
"""Release URLs an asset may be published under, with their names.
|
||||||
|
|
||||||
|
GitHub rewrites spaces to dots in release asset names, so a file whose
|
||||||
|
name contains spaces is published under a dotted name.
|
||||||
|
"""
|
||||||
|
candidates = [name]
|
||||||
|
if " " in name:
|
||||||
|
candidates.append(name.replace(" ", "."))
|
||||||
|
return [
|
||||||
|
(
|
||||||
|
candidate,
|
||||||
|
(
|
||||||
|
f"https://github.com/{LARGE_FILES_REPO}/releases/download/"
|
||||||
|
f"{LARGE_FILES_RELEASE}/{urllib.parse.quote(candidate)}"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
for candidate in candidates
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _replaced_on_release(name: str, cached: str, offline: bool) -> bool:
|
||||||
|
"""Whether the release now serves other bytes than the cached copy.
|
||||||
|
|
||||||
|
Sizes are compared, as check_release_assets does: a request per asset,
|
||||||
|
no download. Offline or unreadable, the copy is assumed current.
|
||||||
|
"""
|
||||||
|
if offline:
|
||||||
|
return False
|
||||||
|
served = _served_size(name)
|
||||||
|
return served is not None and served != os.path.getsize(cached)
|
||||||
|
|
||||||
|
|
||||||
|
def _served_size(name: str) -> int | None:
|
||||||
|
"""Size of the asset the release serves now, or None when unreadable."""
|
||||||
|
for candidate, url in _asset_urls(name):
|
||||||
|
try:
|
||||||
|
req = urllib.request.Request(
|
||||||
|
url, method="HEAD", headers={"User-Agent": "retrobios/1.0"}
|
||||||
|
)
|
||||||
|
with urllib.request.urlopen(req, timeout=60) as resp:
|
||||||
|
length = resp.headers.get("Content-Length")
|
||||||
|
except (urllib.error.URLError, OSError, http.client.HTTPException) as exc:
|
||||||
|
print(f" large file {candidate}: {exc}", file=sys.stderr)
|
||||||
|
continue
|
||||||
|
if length and length.isdigit():
|
||||||
|
return int(length)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _matches(hashes: dict, expected_sha1: str, expected_md5: str) -> bool:
|
def _matches(hashes: dict, expected_sha1: str, expected_md5: str) -> bool:
|
||||||
"""Whether computed hashes answer the caller's declaration."""
|
"""Whether computed hashes answer the caller's declaration."""
|
||||||
if expected_sha1 and hashes["sha1"].lower() != expected_sha1.lower():
|
if expected_sha1 and hashes["sha1"].lower() != expected_sha1.lower():
|
||||||
|
|||||||
+10
-5
@@ -14,6 +14,7 @@ from common import resolve_local_file
|
|||||||
from common import runs_standalone
|
from common import runs_standalone
|
||||||
from common import sanitize_pack_path
|
from common import sanitize_pack_path
|
||||||
from common import size_fits
|
from common import size_fits
|
||||||
|
from validation import read_from_system_dir
|
||||||
def _emulator_systems_index(emu_profiles: dict | None) -> dict[str, list[str]]:
|
def _emulator_systems_index(emu_profiles: dict | None) -> dict[str, list[str]]:
|
||||||
"""Map both the profile key and its display name to the profile's systems.
|
"""Map both the profile key and its display name to the profile's systems.
|
||||||
|
|
||||||
@@ -564,9 +565,7 @@ def _collect_emulator_extras(
|
|||||||
continue
|
continue
|
||||||
if file_mode == "libretro" and is_standalone:
|
if file_mode == "libretro" and is_standalone:
|
||||||
continue
|
continue
|
||||||
# Skip files loaded from non-system directories (save_dir, content_dir)
|
if not read_from_system_dir(f):
|
||||||
load_from = f.get("load_from", "")
|
|
||||||
if load_from and load_from != "system_dir":
|
|
||||||
continue
|
continue
|
||||||
if is_standalone:
|
if is_standalone:
|
||||||
raw = f.get("standalone_path") or f.get("path") or fname
|
raw = f.get("standalone_path") or f.get("path") or fname
|
||||||
@@ -709,7 +708,10 @@ def platform_region_groups(
|
|||||||
return groups, extra_dests
|
return groups, extra_dests
|
||||||
|
|
||||||
def emulator_region_drops(
|
def emulator_region_drops(
|
||||||
selected: list[tuple[str, dict]], standalone: bool, regions: list[str]
|
selected: list[tuple[str, dict]],
|
||||||
|
standalone: bool,
|
||||||
|
regions: list[str],
|
||||||
|
required_only: bool = False,
|
||||||
) -> set[str]:
|
) -> set[str]:
|
||||||
"""Destinations an emulator-mode `--region` withdraws.
|
"""Destinations an emulator-mode `--region` withdraws.
|
||||||
|
|
||||||
@@ -726,7 +728,10 @@ def emulator_region_drops(
|
|||||||
region_groups: dict[str, list[tuple[str, str]]] = {}
|
region_groups: dict[str, list[tuple[str, str]]] = {}
|
||||||
for emu_name, profile in sorted(selected):
|
for emu_name, profile in sorted(selected):
|
||||||
structure = profile.get("pack_structure")
|
structure = profile.get("pack_structure")
|
||||||
for fe in filter_files_by_mode(profile.get("files", []), standalone):
|
# Decided over what the pack keeps, as on the platform side: an
|
||||||
|
# optional winner removed afterwards left its slot empty.
|
||||||
|
entries = filter_files_by_mode(profile.get("files", []), standalone)
|
||||||
|
for fe in _kept(entries, required_only):
|
||||||
dest = _resolve_destination(fe, structure, standalone)
|
dest = _resolve_destination(fe, structure, standalone)
|
||||||
if dest:
|
if dest:
|
||||||
group_id = _emulator_region_group(emu_name, profile, fe)
|
group_id = _emulator_region_group(emu_name, profile, fe)
|
||||||
|
|||||||
+14
-17
@@ -26,6 +26,7 @@ Usage:
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import contextlib
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -33,7 +34,7 @@ import time
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
from common import ArtifactLockBusy, artifact_lock
|
from common import ArtifactLockBusy, artifact_lock, hold_artifact_lock
|
||||||
|
|
||||||
|
|
||||||
def run(cmd: list[str], label: str) -> tuple[bool, str]:
|
def run(cmd: list[str], label: str) -> tuple[bool, str]:
|
||||||
@@ -119,11 +120,11 @@ def parse_pack_exclusions(output: str) -> dict[str, int]:
|
|||||||
for line in output.splitlines():
|
for line in output.splitlines():
|
||||||
label_match = re.match(r"Generating (?:shared )?pack for (.+)\.\.\.", line)
|
label_match = re.match(r"Generating (?:shared )?pack for (.+)\.\.\.", line)
|
||||||
if label_match:
|
if label_match:
|
||||||
current_label = re.sub(
|
# The same reading as parse_pack_counts: a reduced variant must
|
||||||
r"\s+\[source=[^\]]+\]$", "", label_match.group(1).strip()
|
# not overwrite the full pack's count under the shared label.
|
||||||
)
|
current_label = _full_pack_label(label_match.group(1).strip())
|
||||||
continue
|
continue
|
||||||
if "files packed" not in line:
|
if not current_label or "files packed" not in line:
|
||||||
continue
|
continue
|
||||||
excluded_match = re.search(r"(\d+) unsafe excluded", line)
|
excluded_match = re.search(r"(\d+) unsafe excluded", line)
|
||||||
exclusions[current_label] = (
|
exclusions[current_label] = (
|
||||||
@@ -317,12 +318,6 @@ def main():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--output-dir", default="dist", help="Pack output directory (default: dist/)"
|
"--output-dir", default="dist", help="Pack output directory (default: dist/)"
|
||||||
)
|
)
|
||||||
# --include-extras is now a no-op: core requirements are always included
|
|
||||||
parser.add_argument(
|
|
||||||
"--include-extras",
|
|
||||||
action="store_true",
|
|
||||||
help="(no-op) Core requirements are always included",
|
|
||||||
)
|
|
||||||
parser.add_argument("--target", "-t", help="Hardware target (e.g., switch, rpi4)")
|
parser.add_argument("--target", "-t", help="Hardware target (e.g., switch, rpi4)")
|
||||||
parser.add_argument("--source", choices=["platform", "truth", "full"], default="full")
|
parser.add_argument("--source", choices=["platform", "truth", "full"], default="full")
|
||||||
parser.add_argument("--all-variants", action="store_true")
|
parser.add_argument("--all-variants", action="store_true")
|
||||||
@@ -346,11 +341,14 @@ def main():
|
|||||||
|
|
||||||
# A second run on the same output directory is refused before any work:
|
# A second run on the same output directory is refused before any work:
|
||||||
# the database rebuild alone takes minutes, and the reader holding the
|
# the database rebuild alone takes minutes, and the reader holding the
|
||||||
# directory would otherwise see the answer only after all of it.
|
# directory would otherwise see the answer only after all of it. The
|
||||||
if not args.skip_packs and Path(args.output_dir).is_dir():
|
# hold lasts until this process exits: taken step by step, it let another
|
||||||
|
# run purge the packs between the build and the integrity check, which
|
||||||
|
# then reported every platform SKIP and passed.
|
||||||
|
held = contextlib.ExitStack()
|
||||||
|
if not args.skip_packs:
|
||||||
try:
|
try:
|
||||||
with artifact_lock(args.output_dir):
|
held.enter_context(hold_artifact_lock(args.output_dir))
|
||||||
pass
|
|
||||||
except ArtifactLockBusy as exc:
|
except ArtifactLockBusy as exc:
|
||||||
print(f"ERROR: {exc}")
|
print(f"ERROR: {exc}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -525,8 +523,6 @@ def main():
|
|||||||
pack_cmd.append("--include-archived")
|
pack_cmd.append("--include-archived")
|
||||||
if args.offline:
|
if args.offline:
|
||||||
pack_cmd.append("--offline")
|
pack_cmd.append("--offline")
|
||||||
if args.include_extras:
|
|
||||||
pack_cmd.append("--include-extras")
|
|
||||||
if args.target:
|
if args.target:
|
||||||
pack_cmd.extend(["--target", args.target])
|
pack_cmd.extend(["--target", args.target])
|
||||||
if args.source != "full":
|
if args.source != "full":
|
||||||
@@ -667,6 +663,7 @@ def main():
|
|||||||
print(f"{'=' * 60}")
|
print(f"{'=' * 60}")
|
||||||
print(f" Pipeline {'COMPLETE' if all_ok else 'FINISHED WITH ERRORS'}")
|
print(f" Pipeline {'COMPLETE' if all_ok else 'FINISHED WITH ERRORS'}")
|
||||||
print(f"{'=' * 60}")
|
print(f"{'=' * 60}")
|
||||||
|
held.close()
|
||||||
sys.exit(0 if all_ok else 1)
|
sys.exit(0 if all_ok else 1)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+187
-67
@@ -71,6 +71,11 @@ class RefPart:
|
|||||||
end: int | None
|
end: int | None
|
||||||
raw: str = ""
|
raw: str = ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last(self) -> int | None:
|
||||||
|
"""Last cited line: the end of a range, or the single line."""
|
||||||
|
return self.end or self.start
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class AnchorResult:
|
class AnchorResult:
|
||||||
@@ -227,6 +232,24 @@ def collect_tokens(entry: dict) -> list[str]:
|
|||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
|
|
||||||
|
_VERSION_NOTE_RE = re.compile(r"\(\s*v?(\d+(?:\.\d+)+)\s*\)")
|
||||||
|
|
||||||
|
|
||||||
|
def cites_another_version(part: RefPart, core_version: object) -> str | None:
|
||||||
|
"""The version a part's annotation names, when it is not the documented one.
|
||||||
|
|
||||||
|
A profile may cite two programs side by side, `main.cpp:407-411 (v0.0.11)`
|
||||||
|
beside its v0.16.0 pin. The annotation was dropped as prose and the part
|
||||||
|
compared with the pin, where it fell on unrelated code yet read ANCHORED.
|
||||||
|
"""
|
||||||
|
match = _VERSION_NOTE_RE.search(part.raw or "")
|
||||||
|
if not match:
|
||||||
|
return None
|
||||||
|
declared = core_version.values() if isinstance(core_version, dict) else [core_version]
|
||||||
|
documented = {str(v or "").strip().lstrip("vV") for v in declared}
|
||||||
|
return None if match.group(1) in documented else match.group(1)
|
||||||
|
|
||||||
|
|
||||||
def is_external_citation(path: str) -> bool:
|
def is_external_citation(path: str) -> bool:
|
||||||
"""True when a ref names a project rather than a path in a known repo.
|
"""True when a ref names a project rather than a path in a known repo.
|
||||||
|
|
||||||
@@ -657,8 +680,8 @@ def anchor_block(
|
|||||||
pin = _normalize(pin_lines)
|
pin = _normalize(pin_lines)
|
||||||
head = _normalize(head_lines)
|
head = _normalize(head_lines)
|
||||||
lo, hi = start - 1, end
|
lo, hi = start - 1, end
|
||||||
if lo < 0 or lo >= len(pin):
|
if lo < 0 or hi > len(pin):
|
||||||
return AnchorResult("GONE", None, None, [])
|
return AnchorResult("GONE", None, None, [], "cited range ends past the end of the file")
|
||||||
if not any(pin[lo:hi]):
|
if not any(pin[lo:hi]):
|
||||||
return AnchorResult("CHANGED", None, None, [], "cited range is blank")
|
return AnchorResult("CHANGED", None, None, [], "cited range is blank")
|
||||||
|
|
||||||
@@ -892,17 +915,22 @@ def anchor_part(
|
|||||||
reason = "written against HEAD, pin names an older revision"
|
reason = "written against HEAD, pin names an older revision"
|
||||||
return PartResult(part, "GONE", None, None, None, [], reason, slug, url)
|
return PartResult(part, "GONE", None, None, None, [], reason, slug, url)
|
||||||
|
|
||||||
if part.start > len(pin_lines) and _written_against_head(part, head_lines):
|
end = part.last
|
||||||
|
if end > len(pin_lines):
|
||||||
# The file is there and the line is not yet: the pinned revision is
|
# The file is there and the line is not yet: the pinned revision is
|
||||||
# shorter than the one the ref was written against. nestopia cited
|
# shorter than the one the ref was written against. nestopia cited
|
||||||
# the palette and database loads at 2041 and 2063, which is where
|
# the palette and database loads at 2041 and 2063, which is where
|
||||||
# HEAD carries them, over a pin four hundred lines shorter.
|
# HEAD carries them, over a pin four hundred lines shorter. A range
|
||||||
return PartResult(
|
# that only ends past the file is no better: dolphin cited 239-323 in
|
||||||
part, "GONE", None, None, None, [],
|
# a 317-line NANDImporter.cpp, and the block found above the end
|
||||||
"written against HEAD, pin names an older revision", slug, url,
|
# anchored it.
|
||||||
|
reason = (
|
||||||
|
"written against HEAD, pin names an older revision"
|
||||||
|
if _written_against_head(part, head_lines)
|
||||||
|
else "cited range ends past the end of the file"
|
||||||
)
|
)
|
||||||
|
return PartResult(part, "GONE", None, None, None, [], reason, slug, url)
|
||||||
|
|
||||||
end = part.end or part.start
|
|
||||||
anchored = anchor_block(pin_lines, head_lines, part.start, end)
|
anchored = anchor_block(pin_lines, head_lines, part.start, end)
|
||||||
note = anchored.reason
|
note = anchored.reason
|
||||||
if anchored.status in REVIEW_STATUSES:
|
if anchored.status in REVIEW_STATUSES:
|
||||||
@@ -1150,7 +1178,7 @@ def verify_at_pin(part: RefPart, pin_lines, tokens, hash_tokens=()) -> PartResul
|
|||||||
)
|
)
|
||||||
if part.start is None:
|
if part.start is None:
|
||||||
return PartResult(part, "ANCHORED", None, None, None, [])
|
return PartResult(part, "ANCHORED", None, None, None, [])
|
||||||
if part.start > len(pin_lines):
|
if part.last > len(pin_lines):
|
||||||
return PartResult(
|
return PartResult(
|
||||||
part, "GONE", None, None, None, [], "beyond the end of the file"
|
part, "GONE", None, None, None, [], "beyond the end of the file"
|
||||||
)
|
)
|
||||||
@@ -1167,7 +1195,7 @@ def verify_at_pin(part: RefPart, pin_lines, tokens, hash_tokens=()) -> PartResul
|
|||||||
# per line, so the window reaches forward as far as the entry has members.
|
# per line, so the window reaches forward as far as the entry has members.
|
||||||
reach = SELF_CHECK_CONTEXT + len(tokens)
|
reach = SELF_CHECK_CONTEXT + len(tokens)
|
||||||
lo = max(0, part.start - 1 - SELF_CHECK_CONTEXT)
|
lo = max(0, part.start - 1 - SELF_CHECK_CONTEXT)
|
||||||
hi = min(len(pin_lines), (part.end or part.start) + reach)
|
hi = min(len(pin_lines), part.last + reach)
|
||||||
window = "\n".join(pin_lines[lo:hi]).lower()
|
window = "\n".join(pin_lines[lo:hi]).lower()
|
||||||
if any(token in window for token in tokens):
|
if any(token in window for token in tokens):
|
||||||
return PartResult(part, "ANCHORED", None, None, None, [])
|
return PartResult(part, "ANCHORED", None, None, None, [])
|
||||||
@@ -1183,8 +1211,8 @@ def verify_at_pin(part: RefPart, pin_lines, tokens, hash_tokens=()) -> PartResul
|
|||||||
{
|
{
|
||||||
index
|
index
|
||||||
for index, line in enumerate(pin_lines, 1)
|
for index, line in enumerate(pin_lines, 1)
|
||||||
for token in tokens
|
for token in hash_tokens
|
||||||
if token in line.lower()
|
if token.lower() in line.lower()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if not elsewhere:
|
if not elsewhere:
|
||||||
@@ -1670,12 +1698,26 @@ def build_report(
|
|||||||
view, _ = resolve_path(part.path, part.start, tuple(tokens))
|
view, _ = resolve_path(part.path, part.start, tuple(tokens))
|
||||||
return view.pin == view.head
|
return view.pin == view.head
|
||||||
|
|
||||||
|
def judge(part, flag, tokens, hashes) -> PartResult:
|
||||||
|
other = cites_another_version(part, profile.get("core_version"))
|
||||||
|
if other:
|
||||||
|
# ti99sim cites v0.0.11 beside its v0.16.0 pin: no revision of
|
||||||
|
# the pinned repository holds that program, so it cannot anchor.
|
||||||
|
return PartResult(
|
||||||
|
part, "EXTERNAL", None, None, None, [],
|
||||||
|
f"annotated for version {other}, not the documented one",
|
||||||
|
)
|
||||||
|
if flag:
|
||||||
|
return verify_at_pin(
|
||||||
|
part, fetch(PIN, part.path, part.start, tokens), tokens, hashes
|
||||||
|
)
|
||||||
|
return anchor_across_views(part, tokens)
|
||||||
|
|
||||||
staged = []
|
staged = []
|
||||||
for entry_name, ref, tokens, hashes, ref_parts, citation in refs:
|
for entry_name, ref, tokens, hashes, ref_parts, citation in refs:
|
||||||
flags = [self_checked(part, tokens) for part in ref_parts]
|
flags = [self_checked(part, tokens) for part in ref_parts]
|
||||||
judged = [
|
judged = [
|
||||||
verify_at_pin(part, fetch(PIN, part.path, part.start, tokens), tokens, hashes)
|
judge(part, flag, tokens, hashes)
|
||||||
if flag else anchor_across_views(part, tokens)
|
|
||||||
for part, flag in zip(ref_parts, flags)
|
for part, flag in zip(ref_parts, flags)
|
||||||
]
|
]
|
||||||
staged.append((entry_name, ref, citation, reconcile_self_check(judged, flags)))
|
staged.append((entry_name, ref, citation, reconcile_self_check(judged, flags)))
|
||||||
@@ -2459,6 +2501,15 @@ def bump_commit(
|
|||||||
if pending_recale(report, accept_changed, text):
|
if pending_recale(report, accept_changed, text):
|
||||||
return False
|
return False
|
||||||
document = yaml.safe_load(text)
|
document = yaml.safe_load(text)
|
||||||
|
if any(isinstance(document.get(f), dict) for f in ("source_commit", "upstream_commit")):
|
||||||
|
# A pin per build mode names one revision per repository, and the
|
||||||
|
# report heads only the primary one: rewriting str(dict) was a
|
||||||
|
# refused write on mesence, supermodel and xemu.
|
||||||
|
print(
|
||||||
|
f"{path.stem}: pins keyed by build mode are advanced by hand",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return False
|
||||||
upstream_moves = bool(
|
upstream_moves = bool(
|
||||||
report.upstream_head and report.upstream_pin != report.upstream_head
|
report.upstream_head and report.upstream_pin != report.upstream_head
|
||||||
)
|
)
|
||||||
@@ -2466,9 +2517,6 @@ def bump_commit(
|
|||||||
# Rewriting the pin to the value it already holds is not a bump, and
|
# Rewriting the pin to the value it already holds is not a bump, and
|
||||||
# announcing it buries the profiles that did move.
|
# announcing it buries the profiles that did move.
|
||||||
return False
|
return False
|
||||||
if isinstance(document.get("upstream_commit"), dict):
|
|
||||||
# A pin per build mode names no single upstream revision to advance.
|
|
||||||
return False
|
|
||||||
expected = dict(document)
|
expected = dict(document)
|
||||||
expected["source_commit"] = report.head
|
expected["source_commit"] = report.head
|
||||||
new_text = _set_pin(text, document, "source_commit", report.head, "profiled_date")
|
new_text = _set_pin(text, document, "source_commit", report.head, "profiled_date")
|
||||||
@@ -2622,10 +2670,14 @@ def _realign_part(
|
|||||||
|
|
||||||
def _writing_pairs(
|
def _writing_pairs(
|
||||||
document: dict, repos: list, revisions: list[tuple[str, dict]], intro_sha: str
|
document: dict, repos: list, revisions: list[tuple[str, dict]], intro_sha: str
|
||||||
) -> tuple[list, list[str]]:
|
) -> tuple[list[list], list[str]]:
|
||||||
"""(repo, writing pin, current pin) per declared repository, and the
|
"""Every reading of where the text was written, and the ambiguities.
|
||||||
repositories whose writing pin the history cannot single out."""
|
|
||||||
pairs = []
|
Each reading lists (repo, writing pin, current pin) per declared
|
||||||
|
repository. A repository whose text lived under several pins gives one
|
||||||
|
reading per pin; the caller settles them when they all agree.
|
||||||
|
"""
|
||||||
|
options: list[list] = []
|
||||||
ambiguous: list[str] = []
|
ambiguous: list[str] = []
|
||||||
for pin_field, repo in repos:
|
for pin_field, repo in repos:
|
||||||
current = document.get(f"{pin_field}_commit")
|
current = document.get(f"{pin_field}_commit")
|
||||||
@@ -2634,9 +2686,58 @@ def _writing_pairs(
|
|||||||
written = _writing_pins(revisions, intro_sha, f"{pin_field}_commit")
|
written = _writing_pins(revisions, intro_sha, f"{pin_field}_commit")
|
||||||
if len(written) > 1:
|
if len(written) > 1:
|
||||||
ambiguous.append(f"{pin_field}_commit {' -> '.join(written)}")
|
ambiguous.append(f"{pin_field}_commit {' -> '.join(written)}")
|
||||||
elif written and written[0] != current:
|
# The current pin among the candidates reads as "no move" (None) for
|
||||||
pairs.append((repo, written[0], current))
|
# that repository, which the other candidates must then agree with.
|
||||||
return pairs, ambiguous
|
options.append([
|
||||||
|
(repo, pin, current) if pin != current else None for pin in written
|
||||||
|
])
|
||||||
|
readings = [
|
||||||
|
[pair for pair in combo if pair is not None]
|
||||||
|
for combo in itertools.product(*options)
|
||||||
|
]
|
||||||
|
return readings, ambiguous
|
||||||
|
|
||||||
|
|
||||||
|
def _realign_citation(
|
||||||
|
citation: Citation,
|
||||||
|
readings: list[list],
|
||||||
|
ambiguous: list[str],
|
||||||
|
cache_dir: str,
|
||||||
|
offline: bool,
|
||||||
|
) -> tuple[dict[int, tuple[int, int, str | None]], list[str]]:
|
||||||
|
"""The moves one prose run needs, and what blocks them.
|
||||||
|
|
||||||
|
Several pins under one text: the history cannot say which the text
|
||||||
|
describes, but when the cited range lands in the same place from every
|
||||||
|
one of them the question does not matter. When the readings disagree,
|
||||||
|
moving from the wrong one rewrites a correct citation onto someone
|
||||||
|
else's code, with nothing to show it happened.
|
||||||
|
"""
|
||||||
|
moves: dict[int, tuple[int, int, str | None]] = {}
|
||||||
|
blocked: list[str] = []
|
||||||
|
for index, part in enumerate(citation.parts):
|
||||||
|
outcomes = {
|
||||||
|
repr(result): result
|
||||||
|
for result in (
|
||||||
|
_realign_part(part, pairs, cache_dir, offline) if pairs else None
|
||||||
|
for pairs in readings
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if len(outcomes) > 1:
|
||||||
|
blocked.extend(
|
||||||
|
f"pin moved under this text: {item}; the readings disagree"
|
||||||
|
for item in ambiguous
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
outcome = next(iter(outcomes.values()))
|
||||||
|
if outcome is None:
|
||||||
|
continue
|
||||||
|
state, payload = outcome
|
||||||
|
if state == "ok":
|
||||||
|
moves[index] = (payload[0], payload[1], None)
|
||||||
|
else:
|
||||||
|
blocked.append(payload)
|
||||||
|
return moves, blocked
|
||||||
|
|
||||||
|
|
||||||
def realign_prose(
|
def realign_prose(
|
||||||
@@ -2697,30 +2798,10 @@ def realign_prose(
|
|||||||
if intro_sha is None:
|
if intro_sha is None:
|
||||||
# The scalar is not committed yet: written now, under this pin.
|
# The scalar is not committed yet: written now, under this pin.
|
||||||
continue
|
continue
|
||||||
pairs, ambiguous = _writing_pairs(document, repos, revisions, intro_sha)
|
readings, ambiguous = _writing_pairs(document, repos, revisions, intro_sha)
|
||||||
if ambiguous:
|
if not any(readings):
|
||||||
# Moving from the wrong one rewrites a correct citation onto
|
|
||||||
# someone else's code, with nothing to show it happened.
|
|
||||||
messages.extend(
|
|
||||||
f"read again: {citation.field}: {citation.ref} (pin moved under "
|
|
||||||
f"this text: {item}; the history does not say which it describes)"
|
|
||||||
for item in ambiguous
|
|
||||||
)
|
|
||||||
continue
|
continue
|
||||||
if not pairs:
|
moves, blocked = _realign_citation(citation, readings, ambiguous, cache_dir, offline)
|
||||||
continue
|
|
||||||
moves: dict[int, tuple[int, int, str | None]] = {}
|
|
||||||
blocked: list[str] = []
|
|
||||||
for index, part in enumerate(citation.parts):
|
|
||||||
outcome = _realign_part(part, pairs, cache_dir, offline)
|
|
||||||
if outcome is None:
|
|
||||||
continue
|
|
||||||
state, payload = outcome
|
|
||||||
if state == "ok":
|
|
||||||
start, end = payload
|
|
||||||
moves[index] = (start, end, None)
|
|
||||||
else:
|
|
||||||
blocked.append(payload)
|
|
||||||
if blocked:
|
if blocked:
|
||||||
# Half a run must not move: the untouched ranges would read as
|
# Half a run must not move: the untouched ranges would read as
|
||||||
# already realigned when they were never even located.
|
# already realigned when they were never even located.
|
||||||
@@ -2747,14 +2828,39 @@ def realign_prose(
|
|||||||
return messages
|
return messages
|
||||||
|
|
||||||
|
|
||||||
|
def _refuse_dirty_tree(emulators_dir: str) -> None:
|
||||||
|
"""Writes need a clean profile tree to roll back to."""
|
||||||
|
try:
|
||||||
|
dirty = emulators_dir_is_dirty(emulators_dir)
|
||||||
|
except RuntimeError as e:
|
||||||
|
print(f"{e}. Pass --force to write anyway.", file=sys.stderr)
|
||||||
|
raise SystemExit(1) from e
|
||||||
|
if dirty:
|
||||||
|
print(
|
||||||
|
f"{emulators_dir} carries uncommitted changes. "
|
||||||
|
"Commit them first or pass --force.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
|
||||||
def emulators_dir_is_dirty(emulators_dir: str) -> bool:
|
def emulators_dir_is_dirty(emulators_dir: str) -> bool:
|
||||||
"""True when the profile directory carries uncommitted changes."""
|
"""True when the profile directory carries uncommitted changes.
|
||||||
|
|
||||||
|
Git is asked from inside the directory, so a profile tree that lives in
|
||||||
|
another repository answers for itself. A directory git cannot read is
|
||||||
|
never reported clean: the writes have nothing to roll back to.
|
||||||
|
"""
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["git", "status", "--porcelain", "--", emulators_dir],
|
["git", "-C", emulators_dir, "status", "--porcelain", "--", "."],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
check=False,
|
check=False,
|
||||||
)
|
)
|
||||||
|
if result.returncode:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"cannot read the git status of {emulators_dir}: {result.stderr.strip()}"
|
||||||
|
)
|
||||||
return bool(result.stdout.strip())
|
return bool(result.stdout.strip())
|
||||||
|
|
||||||
|
|
||||||
@@ -2856,16 +2962,40 @@ def _check_quota(count: int, offline: bool) -> None:
|
|||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def _stale_prose_blocks(name: str, path: Path, args) -> bool:
|
||||||
|
"""Whether prose written under an older pin has to move first.
|
||||||
|
|
||||||
|
Anchored from the current pin, a recale or a bump follows someone else's
|
||||||
|
code (boom3 FileSystem.cpp:2125, written at b810234e, an unrelated
|
||||||
|
OpenFileRead at 132dfddb). It moves first, from the pin it was written at.
|
||||||
|
"""
|
||||||
|
stale = realign_prose(path, args.cache_dir, args.offline, dry_run=True)
|
||||||
|
if stale:
|
||||||
|
print(
|
||||||
|
f"{name}: {len(stale)} prose citation(s) written under an older pin; "
|
||||||
|
"run --realign-prose or read them before the pin moves",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return bool(stale)
|
||||||
|
|
||||||
|
|
||||||
|
def _backfill(args, name: str, profile: dict, report: ProfileReport, path: Path) -> None:
|
||||||
|
"""Write the pin a profile without source_commit was written at."""
|
||||||
|
if not report.pin or profile.get("source_commit"):
|
||||||
|
return
|
||||||
|
if args.dry_run:
|
||||||
|
print(f"{name}: would write source_commit {report.pin[:7]}")
|
||||||
|
elif backfill_commit(path, report.pin):
|
||||||
|
print(f"{name}: source_commit {report.pin[:7]}")
|
||||||
|
|
||||||
|
|
||||||
def _apply_writes(args, name: str, profile: dict, report: ProfileReport) -> None:
|
def _apply_writes(args, name: str, profile: dict, report: ProfileReport) -> None:
|
||||||
path = Path(args.emulators_dir) / f"{name}.yml"
|
path = Path(args.emulators_dir) / f"{name}.yml"
|
||||||
if not path.is_file():
|
if not path.is_file():
|
||||||
return
|
return
|
||||||
if args.backfill_commits and report.pin and not profile.get("source_commit"):
|
if args.backfill_commits:
|
||||||
if args.dry_run:
|
_backfill(args, name, profile, report, path)
|
||||||
print(f"{name}: would write source_commit {report.pin[:7]}")
|
if not (args.rebase_refs or args.bump_commit) or _stale_prose_blocks(name, path, args):
|
||||||
elif backfill_commit(path, report.pin):
|
|
||||||
print(f"{name}: source_commit {report.pin[:7]}")
|
|
||||||
if not (args.rebase_refs or args.bump_commit):
|
|
||||||
return
|
return
|
||||||
with tempfile.TemporaryDirectory() as scratch:
|
with tempfile.TemporaryDirectory() as scratch:
|
||||||
# Always work on a copy. A dry run reports what it left there; a real
|
# Always work on a copy. A dry run reports what it left there; a real
|
||||||
@@ -3123,18 +3253,8 @@ def main() -> None:
|
|||||||
args.backfill_commits or args.rebase_refs or args.bump_commit
|
args.backfill_commits or args.rebase_refs or args.bump_commit
|
||||||
or args.realign_prose
|
or args.realign_prose
|
||||||
)
|
)
|
||||||
if (
|
if writes and not args.dry_run and not args.force:
|
||||||
writes
|
_refuse_dirty_tree(args.emulators_dir)
|
||||||
and not args.dry_run
|
|
||||||
and not args.force
|
|
||||||
and emulators_dir_is_dirty(args.emulators_dir)
|
|
||||||
):
|
|
||||||
print(
|
|
||||||
f"{args.emulators_dir} carries uncommitted changes. "
|
|
||||||
"Commit them first or pass --force.",
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
raise SystemExit(1)
|
|
||||||
|
|
||||||
if args.realign_prose:
|
if args.realign_prose:
|
||||||
for name in selected:
|
for name in selected:
|
||||||
|
|||||||
+14
-5
@@ -255,16 +255,25 @@ def fallback_groups(
|
|||||||
index: dict[str, dict],
|
index: dict[str, dict],
|
||||||
requested: list[str],
|
requested: list[str],
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
"""Group IDs where no candidate matched, so the whole group was kept."""
|
"""Group IDs where no candidate matched, so the whole group was kept.
|
||||||
|
|
||||||
|
A world file, or one that declares no region at all, answers every
|
||||||
|
region: the C64 kernal VICE loads by default is untagged, and a group
|
||||||
|
holding it beside a Japanese and a Swedish kernal does have a BIOS for
|
||||||
|
North America.
|
||||||
|
"""
|
||||||
if not requested:
|
if not requested:
|
||||||
return []
|
return []
|
||||||
out: list[str] = []
|
out: list[str] = []
|
||||||
for group_id, members in groups.items():
|
for group_id, members in groups.items():
|
||||||
ranked = _competing_ranks(members, index, requested)
|
ranked = _competing_ranks(members, index, requested)
|
||||||
has_world = any(
|
served = any(
|
||||||
WORLD in lookup_regions(index, destination, name)
|
not regions or WORLD in regions
|
||||||
for destination, name in members
|
for regions in (
|
||||||
|
lookup_regions(index, destination, name)
|
||||||
|
for destination, name in members
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if ranked and not has_world and min(r for r, _ in ranked) == len(requested):
|
if ranked and not served and min(r for r, _ in ranked) == len(requested):
|
||||||
out.append(group_id)
|
out.append(group_id)
|
||||||
return sorted(out)
|
return sorted(out)
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
@@ -225,22 +226,38 @@ class BaseScraper(ABC):
|
|||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
def fetch_github_latest_version(repo: str) -> str | None:
|
def github_headers() -> dict[str, str]:
|
||||||
"""Fetch the latest release version tag from a GitHub repo."""
|
"""Headers for api.github.com, authenticated when a token is set.
|
||||||
|
|
||||||
|
Anonymous calls share a quota of 60 per hour, so a scrape run after a
|
||||||
|
few others gets 403 for every request.
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
"User-Agent": "retrobios-scraper/1.0",
|
||||||
|
"Accept": "application/vnd.github.v3+json",
|
||||||
|
}
|
||||||
|
token = os.environ.get("GITHUB_TOKEN")
|
||||||
|
if token:
|
||||||
|
headers["Authorization"] = f"Bearer {token}"
|
||||||
|
return headers
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_github_latest_version(repo: str) -> str:
|
||||||
|
"""Return the tag of the latest release of a GitHub repo.
|
||||||
|
|
||||||
|
Raises instead of returning a fallback: a platform YAML written without
|
||||||
|
the release it was read from cannot be patched back by the exporter.
|
||||||
|
"""
|
||||||
url = f"https://api.github.com/repos/{repo}/releases/latest"
|
url = f"https://api.github.com/repos/{repo}/releases/latest"
|
||||||
try:
|
try:
|
||||||
req = urllib.request.Request(
|
req = urllib.request.Request(url, headers=github_headers())
|
||||||
url,
|
|
||||||
headers={
|
|
||||||
"User-Agent": "retrobios-scraper/1.0",
|
|
||||||
"Accept": "application/vnd.github.v3+json",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||||
data = json.loads(resp.read())
|
tag = json.loads(resp.read()).get("tag_name", "")
|
||||||
return data.get("tag_name", "")
|
except (urllib.error.URLError, json.JSONDecodeError) as e:
|
||||||
except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError):
|
raise RuntimeError(f"cannot read the latest release of {repo}: {e}") from e
|
||||||
return None
|
if not tag:
|
||||||
|
raise RuntimeError(f"the latest release of {repo} names no tag")
|
||||||
|
return tag
|
||||||
|
|
||||||
|
|
||||||
class _PlatformDumper(yaml.SafeDumper):
|
class _PlatformDumper(yaml.SafeDumper):
|
||||||
|
|||||||
@@ -19,7 +19,12 @@ from pathlib import Path
|
|||||||
|
|
||||||
from common import yaml_load
|
from common import yaml_load
|
||||||
|
|
||||||
from .base_scraper import BaseScraper, BiosRequirement, requirement_entry
|
from .base_scraper import (
|
||||||
|
BaseScraper,
|
||||||
|
BiosRequirement,
|
||||||
|
github_headers,
|
||||||
|
requirement_entry,
|
||||||
|
)
|
||||||
|
|
||||||
PLATFORM_NAME = "batocera"
|
PLATFORM_NAME = "batocera"
|
||||||
|
|
||||||
@@ -38,39 +43,32 @@ def pick_stable_tag(names: list[str]) -> str | None:
|
|||||||
return max(stable)[1] if stable else None
|
return max(stable)[1] if stable else None
|
||||||
|
|
||||||
|
|
||||||
def fetch_stable_tag() -> str | None:
|
def fetch_stable_tag() -> str:
|
||||||
"""Return the newest stable batocera-N(.M) tag name."""
|
"""Return the newest stable batocera-N(.M) tag name.
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import urllib.error
|
|
||||||
import urllib.request
|
|
||||||
|
|
||||||
|
Raises rather than falling back to master: the YAML records the tag
|
||||||
|
it was read from, and the exporter patches that revision.
|
||||||
|
"""
|
||||||
url = "https://api.github.com/repos/batocera-linux/batocera.linux/tags?per_page=100"
|
url = "https://api.github.com/repos/batocera-linux/batocera.linux/tags?per_page=100"
|
||||||
headers = {
|
|
||||||
"User-Agent": "retrobios-scraper/1.0",
|
|
||||||
"Accept": "application/vnd.github.v3+json",
|
|
||||||
}
|
|
||||||
token = os.environ.get("GITHUB_TOKEN")
|
|
||||||
if token:
|
|
||||||
headers["Authorization"] = f"Bearer {token}"
|
|
||||||
try:
|
try:
|
||||||
req = urllib.request.Request(url, headers=headers)
|
req = urllib.request.Request(url, headers=github_headers())
|
||||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||||
tags = json.loads(resp.read())
|
tags = json.loads(resp.read())
|
||||||
except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError):
|
except (urllib.error.URLError, json.JSONDecodeError) as e:
|
||||||
return None
|
raise RuntimeError(f"cannot list batocera tags: {e}") from e
|
||||||
return pick_stable_tag([tag["name"] for tag in tags])
|
tag = pick_stable_tag([tag["name"] for tag in tags])
|
||||||
|
if not tag:
|
||||||
|
raise RuntimeError("no stable batocera tag among the latest 100")
|
||||||
|
return tag
|
||||||
|
|
||||||
|
|
||||||
_STABLE_TAG = fetch_stable_tag() or "master"
|
|
||||||
|
|
||||||
SOURCE_URL = (
|
SOURCE_URL = (
|
||||||
f"{_RAW_BASE}/{_STABLE_TAG}"
|
_RAW_BASE + "/{tag}"
|
||||||
"/package/batocera/core/batocera-scripts/scripts/batocera-systems"
|
"/package/batocera/core/batocera-scripts/scripts/batocera-systems"
|
||||||
)
|
)
|
||||||
|
|
||||||
CONFIGGEN_DEFAULTS_URL = (
|
CONFIGGEN_DEFAULTS_URL = (
|
||||||
f"{_RAW_BASE}/{_STABLE_TAG}"
|
_RAW_BASE + "/{tag}"
|
||||||
"/package/batocera/core/batocera-configgen/configs/"
|
"/package/batocera/core/batocera-configgen/configs/"
|
||||||
"configgen-defaults.yml"
|
"configgen-defaults.yml"
|
||||||
)
|
)
|
||||||
@@ -186,8 +184,9 @@ def _resolve_truncated_md5(md5: str, md5_index: dict[str, str]) -> str:
|
|||||||
class Scraper(BaseScraper):
|
class Scraper(BaseScraper):
|
||||||
"""Scraper for batocera-systems Python dict."""
|
"""Scraper for batocera-systems Python dict."""
|
||||||
|
|
||||||
def __init__(self, url: str = SOURCE_URL):
|
def __init__(self):
|
||||||
super().__init__(url=url)
|
self.tag = fetch_stable_tag()
|
||||||
|
super().__init__(url=SOURCE_URL.format(tag=self.tag))
|
||||||
|
|
||||||
def _fetch_cores(self) -> tuple[list[str], list[str]]:
|
def _fetch_cores(self) -> tuple[list[str], list[str]]:
|
||||||
"""Extract core names and standalone cores from configgen-defaults.yml.
|
"""Extract core names and standalone cores from configgen-defaults.yml.
|
||||||
@@ -195,16 +194,17 @@ class Scraper(BaseScraper):
|
|||||||
Returns (all_cores, standalone_cores) where standalone_cores are
|
Returns (all_cores, standalone_cores) where standalone_cores are
|
||||||
those with emulator != "libretro".
|
those with emulator != "libretro".
|
||||||
"""
|
"""
|
||||||
|
url = CONFIGGEN_DEFAULTS_URL.format(tag=self.tag)
|
||||||
try:
|
try:
|
||||||
req = urllib.request.Request(
|
req = urllib.request.Request(
|
||||||
CONFIGGEN_DEFAULTS_URL,
|
url,
|
||||||
headers={"User-Agent": "retrobios-scraper/1.0"},
|
headers={"User-Agent": "retrobios-scraper/1.0"},
|
||||||
)
|
)
|
||||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
raw = resp.read().decode("utf-8")
|
raw = resp.read().decode("utf-8")
|
||||||
except urllib.error.URLError as e:
|
except urllib.error.URLError as e:
|
||||||
raise ConnectionError(
|
raise ConnectionError(
|
||||||
f"Failed to fetch {CONFIGGEN_DEFAULTS_URL}: {e}"
|
f"Failed to fetch {url}: {e}"
|
||||||
) from e
|
) from e
|
||||||
data = yaml_load(raw)
|
data = yaml_load(raw)
|
||||||
cores: set[str] = set()
|
cores: set[str] = set()
|
||||||
@@ -372,25 +372,12 @@ class Scraper(BaseScraper):
|
|||||||
|
|
||||||
systems[req.system]["files"].append(requirement_entry(req))
|
systems[req.system]["files"].append(requirement_entry(req))
|
||||||
|
|
||||||
batocera_version = ""
|
|
||||||
if _STABLE_TAG != "master":
|
|
||||||
batocera_version = _STABLE_TAG.removeprefix("batocera-")
|
|
||||||
if not batocera_version:
|
|
||||||
# Preserve existing version when fetch fails (offline mode)
|
|
||||||
existing = (
|
|
||||||
Path(__file__).resolve().parents[2] / "platforms" / "batocera.yml"
|
|
||||||
)
|
|
||||||
if existing.exists():
|
|
||||||
with open(existing) as f:
|
|
||||||
old = yaml_load(f) or {}
|
|
||||||
batocera_version = str(old.get("version", ""))
|
|
||||||
|
|
||||||
cores, standalone = self._fetch_cores()
|
cores, standalone = self._fetch_cores()
|
||||||
result = {
|
result = {
|
||||||
"platform": "Batocera",
|
"platform": "Batocera",
|
||||||
"version": batocera_version or "",
|
"version": self.tag.removeprefix("batocera-"),
|
||||||
"homepage": "https://batocera.org",
|
"homepage": "https://batocera.org",
|
||||||
"source": SOURCE_URL,
|
"source": self.url,
|
||||||
"base_destination": "bios",
|
"base_destination": "bios",
|
||||||
"hash_type": "md5",
|
"hash_type": "md5",
|
||||||
"verification_mode": "md5",
|
"verification_mode": "md5",
|
||||||
|
|||||||
@@ -41,10 +41,8 @@ PLATFORM_NAME = "bizhawk"
|
|||||||
|
|
||||||
GITHUB_REPO = "TASEmulators/BizHawk"
|
GITHUB_REPO = "TASEmulators/BizHawk"
|
||||||
|
|
||||||
_STABLE_TAG = fetch_github_latest_version(GITHUB_REPO) or "master"
|
|
||||||
|
|
||||||
SOURCE_URL = (
|
SOURCE_URL = (
|
||||||
f"https://raw.githubusercontent.com/TASEmulators/BizHawk/{_STABLE_TAG}"
|
"https://raw.githubusercontent.com/TASEmulators/BizHawk/{tag}"
|
||||||
"/src/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs"
|
"/src/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -332,7 +330,8 @@ class Scraper(BaseScraper):
|
|||||||
"""BizHawk firmware database scraper."""
|
"""BizHawk firmware database scraper."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(url=SOURCE_URL)
|
self.tag = fetch_github_latest_version(GITHUB_REPO)
|
||||||
|
super().__init__(url=SOURCE_URL.format(tag=self.tag))
|
||||||
|
|
||||||
def validate_format(self, raw_data: str) -> bool:
|
def validate_format(self, raw_data: str) -> bool:
|
||||||
return "FirmwareDatabase" in raw_data and "FirmwareAndOption" in raw_data
|
return "FirmwareDatabase" in raw_data and "FirmwareAndOption" in raw_data
|
||||||
@@ -372,13 +371,11 @@ class Scraper(BaseScraper):
|
|||||||
|
|
||||||
systems[req.system]["files"].append(requirement_entry(req))
|
systems[req.system]["files"].append(requirement_entry(req))
|
||||||
|
|
||||||
version = _STABLE_TAG if _STABLE_TAG != "master" else ""
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"platform": "BizHawk",
|
"platform": "BizHawk",
|
||||||
"version": version,
|
"version": self.tag,
|
||||||
"homepage": "https://tasvideos.org/BizHawk",
|
"homepage": "https://tasvideos.org/BizHawk",
|
||||||
"source": SOURCE_URL,
|
"source": self.url,
|
||||||
"base_destination": "Firmware",
|
"base_destination": "Firmware",
|
||||||
"hash_type": "sha1",
|
"hash_type": "sha1",
|
||||||
"verification_mode": "sha1",
|
"verification_mode": "sha1",
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ class Scraper(BaseScraper):
|
|||||||
def fetch_metadata(self) -> dict:
|
def fetch_metadata(self) -> dict:
|
||||||
"""Fetch version info from GitHub."""
|
"""Fetch version info from GitHub."""
|
||||||
version = fetch_github_latest_version("libretro/libretro-core-info")
|
version = fetch_github_latest_version("libretro/libretro-core-info")
|
||||||
return {"version": version or ""}
|
return {"version": version}
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ try:
|
|||||||
BaseScraper,
|
BaseScraper,
|
||||||
BiosRequirement,
|
BiosRequirement,
|
||||||
fetch_github_latest_version,
|
fetch_github_latest_version,
|
||||||
|
github_headers,
|
||||||
requirement_entry,
|
requirement_entry,
|
||||||
)
|
)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -29,6 +30,7 @@ except ImportError:
|
|||||||
BaseScraper,
|
BaseScraper,
|
||||||
BiosRequirement,
|
BiosRequirement,
|
||||||
fetch_github_latest_version,
|
fetch_github_latest_version,
|
||||||
|
github_headers,
|
||||||
requirement_entry,
|
requirement_entry,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -426,13 +428,7 @@ class Scraper(BaseScraper):
|
|||||||
|
|
||||||
systems[req.system]["files"].append(requirement_entry(req))
|
systems[req.system]["files"].append(requirement_entry(req))
|
||||||
|
|
||||||
version = ""
|
version = fetch_github_latest_version("dragoonDorise/EmuDeck")
|
||||||
try:
|
|
||||||
v = fetch_github_latest_version("dragoonDorise/EmuDeck")
|
|
||||||
if v:
|
|
||||||
version = v
|
|
||||||
except (ConnectionError, ValueError, OSError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
cores = self._fetch_installed_emulators()
|
cores = self._fetch_installed_emulators()
|
||||||
|
|
||||||
@@ -468,17 +464,15 @@ class Scraper(BaseScraper):
|
|||||||
skip = {"retroarch_maincfg", "retroarch"}
|
skip = {"retroarch_maincfg", "retroarch"}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req = urllib.request.Request(
|
req = urllib.request.Request(api_url, headers=github_headers())
|
||||||
api_url,
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
headers={"User-Agent": "retrobios-scraper/1.0"},
|
data = json.loads(resp.read())
|
||||||
)
|
except (urllib.error.URLError, json.JSONDecodeError) as e:
|
||||||
data = json.loads(urllib.request.urlopen(req, timeout=30).read())
|
raise RuntimeError(f"cannot list EmuDeck EmuScripts: {e}") from e
|
||||||
except (urllib.error.URLError, OSError):
|
|
||||||
return []
|
|
||||||
|
|
||||||
cores: list[str] = []
|
cores: list[str] = []
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
for entry in data:
|
for entry in data if isinstance(data, list) else []:
|
||||||
name = entry.get("name", "")
|
name = entry.get("name", "")
|
||||||
if not name.endswith(".sh"):
|
if not name.endswith(".sh"):
|
||||||
continue
|
continue
|
||||||
@@ -493,6 +487,8 @@ class Scraper(BaseScraper):
|
|||||||
if core not in seen:
|
if core not in seen:
|
||||||
seen.add(core)
|
seen.add(core)
|
||||||
cores.append(core)
|
cores.append(core)
|
||||||
|
if not cores:
|
||||||
|
raise RuntimeError(f"no emulator script in {api_url}")
|
||||||
return sorted(cores)
|
return sorted(cores)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -101,24 +102,7 @@ def _extract_version() -> tuple[str, str]:
|
|||||||
version = _version_from_resource_h()
|
version = _version_from_resource_h()
|
||||||
# Last resort: use GitHub API for latest real release tag
|
# Last resort: use GitHub API for latest real release tag
|
||||||
if version == "unknown":
|
if version == "unknown":
|
||||||
try:
|
version = _version_from_api()
|
||||||
import urllib.error
|
|
||||||
import urllib.request
|
|
||||||
|
|
||||||
req = urllib.request.Request(
|
|
||||||
"https://api.github.com/repos/finalburnneo/FBNeo/tags?per_page=10",
|
|
||||||
headers={"User-Agent": "retrobios-scraper/1.0"},
|
|
||||||
)
|
|
||||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
||||||
import json as json_mod
|
|
||||||
|
|
||||||
tags = json_mod.loads(resp.read())
|
|
||||||
for t in tags:
|
|
||||||
if t["name"] != "latest" and t["name"].startswith("v"):
|
|
||||||
version = t["name"]
|
|
||||||
break
|
|
||||||
except (urllib.error.URLError, OSError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
sha_result = subprocess.run(
|
sha_result = subprocess.run(
|
||||||
["git", "rev-parse", "HEAD"],
|
["git", "rev-parse", "HEAD"],
|
||||||
@@ -132,6 +116,31 @@ def _extract_version() -> tuple[str, str]:
|
|||||||
return version, commit
|
return version, commit
|
||||||
|
|
||||||
|
|
||||||
|
def _version_from_api() -> str:
|
||||||
|
"""The latest real release tag, or an error: "unknown" was written as
|
||||||
|
core_version and cached for a day."""
|
||||||
|
import json as json_mod
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
headers = {"User-Agent": "retrobios-scraper/1.0"}
|
||||||
|
token = os.environ.get("GITHUB_TOKEN")
|
||||||
|
if token:
|
||||||
|
headers["Authorization"] = f"Bearer {token}"
|
||||||
|
req = urllib.request.Request(
|
||||||
|
"https://api.github.com/repos/finalburnneo/FBNeo/tags?per_page=10", headers=headers
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||||
|
tags = json_mod.loads(resp.read())
|
||||||
|
except (urllib.error.URLError, OSError, ValueError) as exc:
|
||||||
|
raise RuntimeError(f"cannot determine the FBNeo version: {exc}") from exc
|
||||||
|
for tag in tags:
|
||||||
|
if tag["name"] != "latest" and tag["name"].startswith("v"):
|
||||||
|
return tag["name"]
|
||||||
|
raise RuntimeError("cannot determine the FBNeo version: no tag, resource.h or release")
|
||||||
|
|
||||||
|
|
||||||
def _version_from_resource_h() -> str:
|
def _version_from_resource_h() -> str:
|
||||||
"""Fallback: parse VER_FULL_VERSION_STR from resource.h."""
|
"""Fallback: parse VER_FULL_VERSION_STR from resource.h."""
|
||||||
resource_h = CLONE_DIR / "src" / "burner" / "resource.h"
|
resource_h = CLONE_DIR / "src" / "burner" / "resource.h"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from .base_scraper import (
|
|||||||
BaseScraper,
|
BaseScraper,
|
||||||
BiosRequirement,
|
BiosRequirement,
|
||||||
fetch_github_latest_version,
|
fetch_github_latest_version,
|
||||||
|
github_headers,
|
||||||
requirement_entry,
|
requirement_entry,
|
||||||
)
|
)
|
||||||
from .dat_parser import parse_dat, parse_dat_metadata, validate_dat_format
|
from .dat_parser import parse_dat, parse_dat_metadata, validate_dat_format
|
||||||
@@ -100,6 +101,27 @@ SYSTEM_SLUG_MAP = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _core_info_files() -> list[str]:
|
||||||
|
"""Paths of every .info file in libretro-core-info, or RuntimeError."""
|
||||||
|
import json
|
||||||
|
|
||||||
|
url = "https://api.github.com/repos/libretro/libretro-core-info/git/trees/master?recursive=1"
|
||||||
|
try:
|
||||||
|
req = urllib.request.Request(url, headers=github_headers())
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
|
tree = json.loads(resp.read())
|
||||||
|
except (urllib.error.URLError, json.JSONDecodeError) as e:
|
||||||
|
raise RuntimeError(f"cannot list libretro-core-info: {e}") from e
|
||||||
|
info_files = [
|
||||||
|
item["path"]
|
||||||
|
for item in tree.get("tree", [])
|
||||||
|
if item["path"].endswith("_libretro.info")
|
||||||
|
]
|
||||||
|
if not info_files:
|
||||||
|
raise RuntimeError(f"no .info file in {url}")
|
||||||
|
return info_files
|
||||||
|
|
||||||
|
|
||||||
class Scraper(BaseScraper):
|
class Scraper(BaseScraper):
|
||||||
"""Scraper for libretro System.dat."""
|
"""Scraper for libretro System.dat."""
|
||||||
|
|
||||||
@@ -166,71 +188,39 @@ class Scraper(BaseScraper):
|
|||||||
|
|
||||||
def _fetch_core_metadata(self) -> dict[str, dict]:
|
def _fetch_core_metadata(self) -> dict[str, dict]:
|
||||||
"""Fetch per-core metadata from libretro-core-info .info files."""
|
"""Fetch per-core metadata from libretro-core-info .info files."""
|
||||||
metadata = {}
|
from .coreinfo_scraper import CORE_SYSTEM_MAP
|
||||||
try:
|
|
||||||
url = "https://api.github.com/repos/libretro/libretro-core-info/git/trees/master?recursive=1"
|
|
||||||
req = urllib.request.Request(
|
|
||||||
url,
|
|
||||||
headers={
|
|
||||||
"User-Agent": "retrobios-scraper/1.0",
|
|
||||||
"Accept": "application/vnd.github.v3+json",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
|
||||||
import json
|
|
||||||
|
|
||||||
tree = json.loads(resp.read())
|
metadata: dict[str, dict] = {}
|
||||||
|
for filename in _core_info_files():
|
||||||
|
core_name = filename.replace("_libretro.info", "")
|
||||||
|
system_slug = CORE_SYSTEM_MAP.get(core_name)
|
||||||
|
if not system_slug or system_slug in metadata:
|
||||||
|
continue
|
||||||
|
info_url = f"https://raw.githubusercontent.com/libretro/libretro-core-info/master/{filename}"
|
||||||
|
try:
|
||||||
|
req = urllib.request.Request(
|
||||||
|
info_url, headers={"User-Agent": "retrobios-scraper/1.0"}
|
||||||
|
)
|
||||||
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||||
|
content = resp.read().decode("utf-8")
|
||||||
|
except urllib.error.URLError as e:
|
||||||
|
raise RuntimeError(f"cannot fetch {info_url}: {e}") from e
|
||||||
|
|
||||||
info_files = [
|
info = {}
|
||||||
item["path"]
|
for line in content.split("\n"):
|
||||||
for item in tree.get("tree", [])
|
key, sep, value = line.strip().partition(" = ")
|
||||||
if item["path"].endswith("_libretro.info")
|
if sep:
|
||||||
]
|
info[key.strip()] = value.strip().strip('"')
|
||||||
|
|
||||||
for filename in info_files:
|
if int(info.get("firmware_count", "0")) == 0:
|
||||||
core_name = filename.replace("_libretro.info", "")
|
continue
|
||||||
try:
|
metadata[system_slug] = {
|
||||||
info_url = f"https://raw.githubusercontent.com/libretro/libretro-core-info/master/{filename}"
|
"core": core_name,
|
||||||
req = urllib.request.Request(
|
"manufacturer": info.get("manufacturer", ""),
|
||||||
info_url, headers={"User-Agent": "retrobios-scraper/1.0"}
|
"display_name": info.get("display_name", "")
|
||||||
)
|
or info.get("systemname", ""),
|
||||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
"docs": f"https://docs.libretro.com/library/{core_name}/",
|
||||||
content = resp.read().decode("utf-8")
|
}
|
||||||
|
|
||||||
info = {}
|
|
||||||
for line in content.split("\n"):
|
|
||||||
line = line.strip()
|
|
||||||
if " = " in line:
|
|
||||||
key, _, value = line.partition(" = ")
|
|
||||||
info[key.strip()] = value.strip().strip('"')
|
|
||||||
|
|
||||||
fw_count = int(info.get("firmware_count", "0"))
|
|
||||||
if fw_count == 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
system_name = info.get("systemname", "")
|
|
||||||
manufacturer = info.get("manufacturer", "")
|
|
||||||
display_name = info.get("display_name", "")
|
|
||||||
info.get("categories", "")
|
|
||||||
|
|
||||||
# Map core to our system slug via firmware paths
|
|
||||||
from .coreinfo_scraper import CORE_SYSTEM_MAP
|
|
||||||
|
|
||||||
system_slug = CORE_SYSTEM_MAP.get(core_name)
|
|
||||||
if not system_slug:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if system_slug not in metadata:
|
|
||||||
metadata[system_slug] = {
|
|
||||||
"core": core_name,
|
|
||||||
"manufacturer": manufacturer,
|
|
||||||
"display_name": display_name or system_name,
|
|
||||||
"docs": f"https://docs.libretro.com/library/{core_name}/",
|
|
||||||
}
|
|
||||||
except (urllib.error.URLError, urllib.error.HTTPError):
|
|
||||||
continue
|
|
||||||
except (ConnectionError, ValueError, OSError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import logging
|
import logging
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -109,24 +110,28 @@ def _sparse_clone() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def _get_version() -> str:
|
def _get_version() -> str:
|
||||||
# version.cpp is generated at build time, not in the repo.
|
"""The latest MAME release, from the GitHub API.
|
||||||
# Use GitHub API to get the latest release tag.
|
|
||||||
|
version.cpp is generated at build time, not in the repo. A failed lookup
|
||||||
|
raises: "unknown" was cached for a day and written as core_version.
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
"User-Agent": "retrobios-scraper/1.0",
|
||||||
|
"Accept": "application/vnd.github.v3+json",
|
||||||
|
}
|
||||||
|
token = os.environ.get("GITHUB_TOKEN")
|
||||||
|
if token:
|
||||||
|
headers["Authorization"] = f"Bearer {token}"
|
||||||
|
url = "https://api.github.com/repos/mamedev/mame/releases/latest"
|
||||||
try:
|
try:
|
||||||
req = urllib.request.Request(
|
req = urllib.request.Request(url, headers=headers)
|
||||||
"https://api.github.com/repos/mamedev/mame/releases/latest",
|
|
||||||
headers={
|
|
||||||
"User-Agent": "retrobios-scraper/1.0",
|
|
||||||
"Accept": "application/vnd.github.v3+json",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||||
data = json.loads(resp.read())
|
tag = json.loads(resp.read()).get("tag_name", "")
|
||||||
tag = data.get("tag_name", "")
|
except (urllib.error.URLError, json.JSONDecodeError, OSError) as exc:
|
||||||
if tag:
|
raise RuntimeError(f"cannot read the MAME release from {url}: {exc}") from exc
|
||||||
return _parse_version_tag(tag)
|
if not tag:
|
||||||
except (urllib.error.URLError, json.JSONDecodeError, OSError):
|
raise RuntimeError(f"no tag_name in {url}")
|
||||||
pass
|
return _parse_version_tag(tag)
|
||||||
return "unknown"
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_version_tag(tag: str) -> str:
|
def _parse_version_tag(tag: str) -> str:
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from .base_scraper import BaseScraper, BiosRequirement, requirement_entry
|
|||||||
|
|
||||||
PLATFORM_NAME = "recalbox"
|
PLATFORM_NAME = "recalbox"
|
||||||
|
|
||||||
def _fetch_gitlab_stable_tag() -> str | None:
|
def _fetch_gitlab_stable_tag() -> str:
|
||||||
"""Fetch the latest stable x.y.z tag from the Recalbox GitLab."""
|
"""Fetch the latest stable x.y.z tag from the Recalbox GitLab."""
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
@@ -33,16 +33,16 @@ def _fetch_gitlab_stable_tag() -> str | None:
|
|||||||
req = urllib.request.Request(url, headers={"User-Agent": "retrobios-scraper/1.0"})
|
req = urllib.request.Request(url, headers={"User-Agent": "retrobios-scraper/1.0"})
|
||||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||||
tags = json.loads(resp.read())
|
tags = json.loads(resp.read())
|
||||||
except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError):
|
except (urllib.error.URLError, json.JSONDecodeError) as e:
|
||||||
return None
|
raise RuntimeError(f"cannot list Recalbox tags: {e}") from e
|
||||||
stable = [t["name"] for t in tags if re.fullmatch(r"[0-9]+\.[0-9]+(\.[0-9]+)?", t["name"])]
|
stable = [t["name"] for t in tags if re.fullmatch(r"[0-9]+\.[0-9]+(\.[0-9]+)?", t["name"])]
|
||||||
return stable[0] if stable else None
|
if not stable:
|
||||||
|
raise RuntimeError("no stable Recalbox tag among the latest 50")
|
||||||
|
return stable[0]
|
||||||
|
|
||||||
|
|
||||||
_STABLE_TAG = _fetch_gitlab_stable_tag() or "master"
|
|
||||||
|
|
||||||
SOURCE_URL = (
|
SOURCE_URL = (
|
||||||
f"https://gitlab.com/recalbox/recalbox/-/raw/{_STABLE_TAG}/"
|
"https://gitlab.com/recalbox/recalbox/-/raw/{tag}/"
|
||||||
"board/recalbox/fsoverlay/recalbox/share_init/system/"
|
"board/recalbox/fsoverlay/recalbox/share_init/system/"
|
||||||
".emulationstation/es_bios.xml"
|
".emulationstation/es_bios.xml"
|
||||||
)
|
)
|
||||||
@@ -109,8 +109,9 @@ def split_cores(names) -> tuple[list[str], list[str]]:
|
|||||||
class Scraper(BaseScraper):
|
class Scraper(BaseScraper):
|
||||||
"""Scraper for Recalbox es_bios.xml."""
|
"""Scraper for Recalbox es_bios.xml."""
|
||||||
|
|
||||||
def __init__(self, url: str = SOURCE_URL):
|
def __init__(self):
|
||||||
super().__init__(url=url)
|
self.tag = _fetch_gitlab_stable_tag()
|
||||||
|
super().__init__(url=SOURCE_URL.format(tag=self.tag))
|
||||||
|
|
||||||
def _fetch_cores(self) -> tuple[list[str], list[str]]:
|
def _fetch_cores(self) -> tuple[list[str], list[str]]:
|
||||||
"""Core names from es_bios.xml, and those Recalbox runs standalone.
|
"""Core names from es_bios.xml, and those Recalbox runs standalone.
|
||||||
@@ -217,16 +218,12 @@ class Scraper(BaseScraper):
|
|||||||
|
|
||||||
systems[req.system]["files"].append(requirement_entry(req))
|
systems[req.system]["files"].append(requirement_entry(req))
|
||||||
|
|
||||||
version = _STABLE_TAG if _STABLE_TAG != "master" else ""
|
|
||||||
if not version:
|
|
||||||
version = "10.0"
|
|
||||||
|
|
||||||
cores, standalone = self._fetch_cores()
|
cores, standalone = self._fetch_cores()
|
||||||
return {
|
return {
|
||||||
"platform": "Recalbox",
|
"platform": "Recalbox",
|
||||||
"version": version,
|
"version": self.tag,
|
||||||
"homepage": "https://www.recalbox.com",
|
"homepage": "https://www.recalbox.com",
|
||||||
"source": SOURCE_URL,
|
"source": self.url,
|
||||||
"base_destination": "bios",
|
"base_destination": "bios",
|
||||||
"hash_type": "md5",
|
"hash_type": "md5",
|
||||||
"verification_mode": "md5",
|
"verification_mode": "md5",
|
||||||
|
|||||||
@@ -160,10 +160,7 @@ class Scraper(BaseScraper):
|
|||||||
|
|
||||||
systems[req.system]["files"].append(requirement_entry(req))
|
systems[req.system]["files"].append(requirement_entry(req))
|
||||||
|
|
||||||
version = ""
|
version = fetch_github_latest_version(GITHUB_REPO)
|
||||||
tag = fetch_github_latest_version(GITHUB_REPO)
|
|
||||||
if tag:
|
|
||||||
version = tag
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"platform": "RetroBat",
|
"platform": "RetroBat",
|
||||||
|
|||||||
@@ -431,7 +431,7 @@ class Scraper(BaseScraper):
|
|||||||
from .base_scraper import fetch_github_latest_version
|
from .base_scraper import fetch_github_latest_version
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from scraper.base_scraper import fetch_github_latest_version
|
from scraper.base_scraper import fetch_github_latest_version
|
||||||
version = fetch_github_latest_version("RetroDECK/RetroDECK") or ""
|
version = fetch_github_latest_version("RetroDECK/RetroDECK")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"platform": "RetroDECK",
|
"platform": "RetroDECK",
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ class Scraper(BaseScraper):
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"platform": "ROCKNIX",
|
"platform": "ROCKNIX",
|
||||||
"version": fetch_github_latest_version(GITHUB_REPO) or "",
|
"version": fetch_github_latest_version(GITHUB_REPO),
|
||||||
"homepage": "https://rocknix.org",
|
"homepage": "https://rocknix.org",
|
||||||
"source": SOURCE_URL,
|
"source": SOURCE_URL,
|
||||||
"base_destination": "bios",
|
"base_destination": "bios",
|
||||||
|
|||||||
@@ -44,10 +44,8 @@ PLATFORM_NAME = "romm"
|
|||||||
|
|
||||||
GITHUB_REPO = "rommapp/romm"
|
GITHUB_REPO = "rommapp/romm"
|
||||||
|
|
||||||
_STABLE_TAG = fetch_github_latest_version(GITHUB_REPO) or "master"
|
|
||||||
|
|
||||||
SOURCE_URL = (
|
SOURCE_URL = (
|
||||||
f"https://raw.githubusercontent.com/rommapp/romm/{_STABLE_TAG}"
|
"https://raw.githubusercontent.com/rommapp/romm/{tag}"
|
||||||
"/backend/models/fixtures/known_bios_files.json"
|
"/backend/models/fixtures/known_bios_files.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -124,8 +122,9 @@ FIRMWARE_MIRRORS: dict[str, tuple[str, ...]] = {
|
|||||||
class Scraper(BaseScraper):
|
class Scraper(BaseScraper):
|
||||||
"""Scraper for RomM known_bios_files.json."""
|
"""Scraper for RomM known_bios_files.json."""
|
||||||
|
|
||||||
def __init__(self, url: str = SOURCE_URL):
|
def __init__(self):
|
||||||
super().__init__(url=url)
|
self.tag = fetch_github_latest_version(GITHUB_REPO)
|
||||||
|
super().__init__(url=SOURCE_URL.format(tag=self.tag))
|
||||||
self._parsed: dict | None = None
|
self._parsed: dict | None = None
|
||||||
|
|
||||||
def _parse_json(self) -> dict:
|
def _parse_json(self) -> dict:
|
||||||
@@ -212,14 +211,12 @@ class Scraper(BaseScraper):
|
|||||||
|
|
||||||
systems[req.system]["files"].append(requirement_entry(req))
|
systems[req.system]["files"].append(requirement_entry(req))
|
||||||
|
|
||||||
version = _STABLE_TAG if _STABLE_TAG != "master" else ""
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"inherits": "emulatorjs",
|
"inherits": "emulatorjs",
|
||||||
"platform": "RomM",
|
"platform": "RomM",
|
||||||
"version": version,
|
"version": self.tag,
|
||||||
"homepage": "https://romm.app",
|
"homepage": "https://romm.app",
|
||||||
"source": SOURCE_URL,
|
"source": self.url,
|
||||||
"base_destination": "bios",
|
"base_destination": "bios",
|
||||||
"hash_type": "sha1",
|
"hash_type": "sha1",
|
||||||
"verification_mode": "md5",
|
"verification_mode": "md5",
|
||||||
|
|||||||
@@ -27,25 +27,16 @@ PLATFORM_NAME = "batocera"
|
|||||||
|
|
||||||
GITHUB_API = "https://api.github.com/repos/batocera-linux/batocera.linux/contents"
|
GITHUB_API = "https://api.github.com/repos/batocera-linux/batocera.linux/contents"
|
||||||
try:
|
try:
|
||||||
|
from ..base_scraper import github_headers
|
||||||
from ..batocera_scraper import fetch_stable_tag
|
from ..batocera_scraper import fetch_stable_tag
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
from scraper.base_scraper import github_headers
|
||||||
from scraper.batocera_scraper import fetch_stable_tag
|
from scraper.batocera_scraper import fetch_stable_tag
|
||||||
|
|
||||||
_STABLE_TAG = fetch_stable_tag() or "master"
|
RAW_BASE = "https://raw.githubusercontent.com/batocera-linux/batocera.linux/{tag}"
|
||||||
RAW_BASE = f"https://raw.githubusercontent.com/batocera-linux/batocera.linux/{_STABLE_TAG}"
|
|
||||||
|
|
||||||
CONFIG_IN_URL = f"{RAW_BASE}/package/batocera/core/batocera-system/Config.in"
|
CONFIG_IN_PATH = "package/batocera/core/batocera-system/Config.in"
|
||||||
ES_SYSTEMS_URL = (
|
ES_SYSTEMS_PATH = "package/batocera/emulationstation/batocera-es-system/es_systems.yml"
|
||||||
f"{RAW_BASE}/package/batocera/emulationstation/batocera-es-system/es_systems.yml"
|
|
||||||
)
|
|
||||||
|
|
||||||
_HEADERS = {
|
|
||||||
"User-Agent": "retrobios-scraper/1.0",
|
|
||||||
"Accept": "application/vnd.github.v3+json",
|
|
||||||
}
|
|
||||||
import os as _os
|
|
||||||
if _os.environ.get("GITHUB_TOKEN"):
|
|
||||||
_HEADERS["Authorization"] = f"Bearer {_os.environ['GITHUB_TOKEN']}"
|
|
||||||
|
|
||||||
_TARGET_FLAG_RE = re.compile(r"^(BR2_PACKAGE_BATOCERA_TARGET_\w+)=y", re.MULTILINE)
|
_TARGET_FLAG_RE = re.compile(r"^(BR2_PACKAGE_BATOCERA_TARGET_\w+)=y", re.MULTILINE)
|
||||||
|
|
||||||
@@ -68,26 +59,22 @@ _META_BLOCK_RE = re.compile(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _fetch(url: str, headers: dict | None = None) -> str | None:
|
def _fetch(url: str, headers: dict | None = None) -> str:
|
||||||
h = headers or {"User-Agent": "retrobios-scraper/1.0"}
|
h = headers or {"User-Agent": "retrobios-scraper/1.0"}
|
||||||
try:
|
try:
|
||||||
req = urllib.request.Request(url, headers=h)
|
req = urllib.request.Request(url, headers=h)
|
||||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
return resp.read().decode("utf-8")
|
return resp.read().decode("utf-8")
|
||||||
except urllib.error.URLError as e:
|
except urllib.error.URLError as e:
|
||||||
print(f" skip {url}: {e}", file=sys.stderr)
|
raise RuntimeError(f"cannot fetch {url}: {e}") from e
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _fetch_json(url: str) -> list | dict | None:
|
def _fetch_json(url: str) -> list | dict:
|
||||||
text = _fetch(url, headers=_HEADERS)
|
text = _fetch(url, headers=github_headers())
|
||||||
if text is None:
|
|
||||||
return None
|
|
||||||
try:
|
try:
|
||||||
return json.loads(text)
|
return json.loads(text)
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
print(f" json parse error {url}: {e}", file=sys.stderr)
|
raise RuntimeError(f"cannot parse {url}: {e}") from e
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _normalise_condition(raw: str) -> str:
|
def _normalise_condition(raw: str) -> str:
|
||||||
@@ -272,40 +259,41 @@ class Scraper(BaseTargetScraper):
|
|||||||
|
|
||||||
def __init__(self, url: str = "https://github.com/batocera-linux/batocera.linux"):
|
def __init__(self, url: str = "https://github.com/batocera-linux/batocera.linux"):
|
||||||
super().__init__(url=url)
|
super().__init__(url=url)
|
||||||
|
self.tag = ""
|
||||||
|
|
||||||
|
def _raw(self, path: str) -> str:
|
||||||
|
return f"{RAW_BASE.format(tag=self.tag)}/{path}"
|
||||||
|
|
||||||
def _list_boards(self) -> list[str]:
|
def _list_boards(self) -> list[str]:
|
||||||
"""List batocera-*.board files from configs/ via GitHub API."""
|
"""List batocera-*.board files from configs/ via GitHub API."""
|
||||||
data = _fetch_json(f"{GITHUB_API}/configs")
|
data = _fetch_json(f"{GITHUB_API}/configs?ref={self.tag}")
|
||||||
if not data or not isinstance(data, list):
|
boards = [
|
||||||
return []
|
|
||||||
return [
|
|
||||||
item["name"]
|
item["name"]
|
||||||
for item in data
|
for item in data
|
||||||
if isinstance(item, dict)
|
if isinstance(item, dict)
|
||||||
and item.get("name", "").startswith("batocera-")
|
and item.get("name", "").startswith("batocera-")
|
||||||
and item.get("name", "").endswith(".board")
|
and item.get("name", "").endswith(".board")
|
||||||
]
|
] if isinstance(data, list) else []
|
||||||
|
if not boards:
|
||||||
|
raise RuntimeError(f"no batocera-*.board file in configs/ at {self.tag}")
|
||||||
|
return boards
|
||||||
|
|
||||||
def _fetch_board_flag(self, board_name: str) -> str | None:
|
def _fetch_board_flag(self, board_name: str) -> str | None:
|
||||||
"""Fetch a board file and extract its BR2_PACKAGE_BATOCERA_TARGET_* flag."""
|
"""Fetch a board file and extract its BR2_PACKAGE_BATOCERA_TARGET_* flag."""
|
||||||
url = f"{RAW_BASE}/configs/{board_name}"
|
m = _TARGET_FLAG_RE.search(_fetch(self._raw(f"configs/{board_name}")))
|
||||||
text = _fetch(url)
|
|
||||||
if text is None:
|
|
||||||
return None
|
|
||||||
m = _TARGET_FLAG_RE.search(text)
|
|
||||||
return m.group(1) if m else None
|
return m.group(1) if m else None
|
||||||
|
|
||||||
def fetch_targets(self) -> dict:
|
def fetch_targets(self) -> dict:
|
||||||
"""Build per-board emulator availability map."""
|
"""Build per-board emulator availability map."""
|
||||||
|
self.tag = fetch_stable_tag()
|
||||||
print(" fetching board list...", file=sys.stderr)
|
print(" fetching board list...", file=sys.stderr)
|
||||||
boards = self._list_boards()
|
boards = self._list_boards()
|
||||||
if not boards:
|
|
||||||
print(" warning: no boards found", file=sys.stderr)
|
|
||||||
|
|
||||||
print(" fetching Config.in...", file=sys.stderr)
|
print(" fetching Config.in...", file=sys.stderr)
|
||||||
config_in_text = _fetch(CONFIG_IN_URL)
|
config_in_url = self._raw(CONFIG_IN_PATH)
|
||||||
|
config_in_text = _fetch(config_in_url)
|
||||||
if not config_in_text:
|
if not config_in_text:
|
||||||
raise RuntimeError(f"empty Config.in from {CONFIG_IN_URL}")
|
raise RuntimeError(f"empty Config.in from {config_in_url}")
|
||||||
|
|
||||||
meta_rules = _parse_meta_flags(config_in_text)
|
meta_rules = _parse_meta_flags(config_in_text)
|
||||||
selects = _parse_selects(config_in_text)
|
selects = _parse_selects(config_in_text)
|
||||||
@@ -314,19 +302,20 @@ class Scraper(BaseTargetScraper):
|
|||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
if not selects:
|
if not selects:
|
||||||
raise RuntimeError(f"no 'select BR2_PACKAGE_*' lines parsed from {CONFIG_IN_URL}")
|
raise RuntimeError(f"no 'select BR2_PACKAGE_*' lines parsed from {config_in_url}")
|
||||||
|
|
||||||
print(" fetching es_systems.yml...", file=sys.stderr)
|
print(" fetching es_systems.yml...", file=sys.stderr)
|
||||||
es_text = _fetch(ES_SYSTEMS_URL)
|
es_systems_url = self._raw(ES_SYSTEMS_PATH)
|
||||||
|
es_text = _fetch(es_systems_url)
|
||||||
if not es_text:
|
if not es_text:
|
||||||
raise RuntimeError(f"empty es_systems.yml from {ES_SYSTEMS_URL}")
|
raise RuntimeError(f"empty es_systems.yml from {es_systems_url}")
|
||||||
package_to_emulators = _parse_es_systems(es_text)
|
package_to_emulators = _parse_es_systems(es_text)
|
||||||
print(
|
print(
|
||||||
f" parsed {len(package_to_emulators)} package->emulator mappings",
|
f" parsed {len(package_to_emulators)} package->emulator mappings",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
if not package_to_emulators:
|
if not package_to_emulators:
|
||||||
raise RuntimeError(f"no package->emulator mappings parsed from {ES_SYSTEMS_URL}")
|
raise RuntimeError(f"no package->emulator mappings parsed from {es_systems_url}")
|
||||||
|
|
||||||
targets: dict[str, dict] = {}
|
targets: dict[str, dict] = {}
|
||||||
for board_name in sorted(boards):
|
for board_name in sorted(boards):
|
||||||
|
|||||||
+12
-5
@@ -21,6 +21,8 @@ import sys
|
|||||||
import zipfile
|
import zipfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from artifacts import ArtifactLockBusy, artifact_lock
|
||||||
|
|
||||||
# "Each file included in a release must be under 2 GiB."
|
# "Each file included in a release must be under 2 GiB."
|
||||||
ASSET_LIMIT = 2 * 1024**3
|
ASSET_LIMIT = 2 * 1024**3
|
||||||
|
|
||||||
@@ -222,12 +224,17 @@ def main() -> int:
|
|||||||
help="a part stays under this many bytes (K, M, G suffixes; default 2G)",
|
help="a part stays under this many bytes (K, M, G suffixes; default 2G)",
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
# The packs are rewritten in place by generate_pack under this lock: a
|
||||||
|
# build running beside the split mixes its bytes into the pack being
|
||||||
|
# read, or writes a pack that the split then deletes.
|
||||||
|
directory = args.target if args.target.is_dir() else args.target.parent
|
||||||
try:
|
try:
|
||||||
if args.target.is_dir():
|
with artifact_lock(str(directory)):
|
||||||
published = split_directory(args.target, args.max_size)
|
if args.target.is_dir():
|
||||||
else:
|
published = split_directory(args.target, args.max_size)
|
||||||
published = split_pack(args.target, args.max_size)
|
else:
|
||||||
except (OSError, ValueError, zipfile.BadZipFile) as exc:
|
published = split_pack(args.target, args.max_size)
|
||||||
|
except (ArtifactLockBusy, OSError, ValueError, zipfile.BadZipFile) as exc:
|
||||||
print(f"Error: {exc}", file=sys.stderr)
|
print(f"Error: {exc}", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
for path in published:
|
for path in published:
|
||||||
|
|||||||
+38
-5
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from common import _norm_system_id, resolve_platform_cores, runs_standalone
|
from common import _norm_system_id, resolve_platform_cores, runs_standalone
|
||||||
from validation import filter_files_by_mode
|
from validation import filter_files_by_mode, read_from_system_dir
|
||||||
|
|
||||||
|
|
||||||
def _serialize_source_ref(sr: object) -> str:
|
def _serialize_source_ref(sr: object) -> str:
|
||||||
@@ -165,12 +165,36 @@ def _same_file(files: list[dict], file_entry: dict, emu_name: str) -> dict | Non
|
|||||||
same_name = [f for f in files if f["name"].lower() == name_lower]
|
same_name = [f for f in files if f["name"].lower() == name_lower]
|
||||||
return (
|
return (
|
||||||
next((f for f in same_name if path and other_path(f) == path), None)
|
next((f for f in same_name if path and other_path(f) == path), None)
|
||||||
or next((f for f in same_name if emu_name not in f.get("_cores", ())), None)
|
# Another core's file of the same name is the same file unless their
|
||||||
|
# contents disagree: beebem's 16 KB BBC BIOS.rom and np2kai's PC-98
|
||||||
|
# bios.rom merged, and the export wrote the BBC md5 over Recalbox's.
|
||||||
|
or next(
|
||||||
|
(f for f in same_name
|
||||||
|
if emu_name not in f.get("_cores", ()) and not _contents_disagree(f, file_entry)),
|
||||||
|
None,
|
||||||
|
)
|
||||||
# Revisions accepted under one name and no path fill one slot.
|
# Revisions accepted under one name and no path fill one slot.
|
||||||
or next((f for f in same_name if not path or not other_path(f)), None)
|
or next((f for f in same_name if not path or not other_path(f)), None)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _declared(entry: dict, field: str) -> set[str]:
|
||||||
|
"""The values an entry declares for a hash field, one or a list."""
|
||||||
|
value = entry.get(field)
|
||||||
|
values = value if isinstance(value, list) else [value]
|
||||||
|
return {str(v).lower() for v in values if v}
|
||||||
|
|
||||||
|
|
||||||
|
def _contents_disagree(a: dict, b: dict) -> bool:
|
||||||
|
"""A hash both declare without a value in common, or two declared sizes."""
|
||||||
|
for field in ("sha1", "md5", "sha256", "crc32"):
|
||||||
|
ours, theirs = _declared(a, field), _declared(b, field)
|
||||||
|
if ours and theirs and not ours & theirs:
|
||||||
|
return True
|
||||||
|
size_a, size_b = a.get("size"), b.get("size")
|
||||||
|
return isinstance(size_a, int) and isinstance(size_b, int) and size_a != size_b
|
||||||
|
|
||||||
|
|
||||||
def _fold_into(existing: dict, file_entry: dict, emu_name: str) -> None:
|
def _fold_into(existing: dict, file_entry: dict, emu_name: str) -> None:
|
||||||
"""Add one core's declaration to the entry that already stands for it."""
|
"""Add one core's declaration to the entry that already stands for it."""
|
||||||
existing["_cores"] = existing.get("_cores", set()) | {emu_name}
|
existing["_cores"] = existing.get("_cores", set()) | {emu_name}
|
||||||
@@ -276,6 +300,16 @@ def _has_exploitable_data(entry: dict) -> bool:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _system_dir_files(profile: dict, standalone: bool) -> list[dict]:
|
||||||
|
"""Entries of the build the platform runs, read from the system directory."""
|
||||||
|
return list(
|
||||||
|
filter(
|
||||||
|
read_from_system_dir,
|
||||||
|
filter_files_by_mode(profile.get("files", []), standalone=standalone),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def generate_platform_truth(
|
def generate_platform_truth(
|
||||||
platform_name: str,
|
platform_name: str,
|
||||||
config: dict,
|
config: dict,
|
||||||
@@ -354,9 +388,8 @@ def generate_platform_truth(
|
|||||||
continue
|
continue
|
||||||
cores_profiled.add(emu_name)
|
cores_profiled.add(emu_name)
|
||||||
|
|
||||||
filtered = filter_files_by_mode(
|
filtered = _system_dir_files(
|
||||||
profile.get("files", []),
|
profile, runs_standalone(emu_name, profile, standalone_set)
|
||||||
standalone=runs_standalone(emu_name, profile, standalone_set),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for fe in filtered:
|
for fe in filtered:
|
||||||
|
|||||||
@@ -254,6 +254,28 @@ def _semantic_install_checks(path: Path, manifest: dict) -> list[str]:
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def unchecked_formats() -> list[str]:
|
||||||
|
"""Formats the schemas declare that this jsonschema install cannot check.
|
||||||
|
|
||||||
|
date-time and uri need the format extras; without them the ten format
|
||||||
|
constraints of the published schemas were accepted unread.
|
||||||
|
"""
|
||||||
|
used = {
|
||||||
|
match
|
||||||
|
for path in SCHEMAS.glob("*.json")
|
||||||
|
for match in re.findall(r'"format"\s*:\s*"([^"]+)"', path.read_text(encoding="utf-8"))
|
||||||
|
}
|
||||||
|
return sorted(used - set(FormatChecker().checkers))
|
||||||
|
|
||||||
|
|
||||||
|
def _format_errors() -> list[str]:
|
||||||
|
"""jsonschema skips a format it has no checker for: say so, and fail."""
|
||||||
|
return [
|
||||||
|
f"no checker for format {name}: install \"jsonschema[format-nongpl]\""
|
||||||
|
for name in unchecked_formats()
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -263,7 +285,7 @@ def main() -> int:
|
|||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
errors: list[str] = []
|
errors: list[str] = _format_errors()
|
||||||
errors.extend(
|
errors.extend(
|
||||||
_validate_yaml_directory(ROOT / "emulators", "emulator.schema.json")
|
_validate_yaml_directory(ROOT / "emulators", "emulator.schema.json")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -313,6 +313,39 @@ def validate_cli_modes(args, mode_attrs: list[str]) -> None:
|
|||||||
raise SystemExit(f"Options are mutually exclusive: --{' --'.join(mode_attrs)}")
|
raise SystemExit(f"Options are mutually exclusive: --{' --'.join(mode_attrs)}")
|
||||||
|
|
||||||
|
|
||||||
|
def read_from_system_dir(file_entry: dict) -> bool:
|
||||||
|
"""Whether the emulator reads this entry from the system directory.
|
||||||
|
|
||||||
|
A file loaded from the save, content or working directory is not a BIOS:
|
||||||
|
no platform lists it, no pack carries it, and the truth model must not
|
||||||
|
ask a platform to declare it.
|
||||||
|
"""
|
||||||
|
load_from = file_entry.get("load_from", "")
|
||||||
|
return not load_from or load_from == "system_dir"
|
||||||
|
|
||||||
|
|
||||||
|
def outside_gap_scope(file_entry: dict, is_standalone: bool) -> bool:
|
||||||
|
"""Whether an entry is a file the platform's pack would carry for this build.
|
||||||
|
|
||||||
|
Read by the gap report and by verify's undeclared-file pass, so both set
|
||||||
|
aside the same entries: a placeholder standing for a family of files, a
|
||||||
|
file the user imports through the UI (``path: null``), an entry of the
|
||||||
|
build the platform does not run, a file read outside the system
|
||||||
|
directory, and a filename-agnostic entry the builder scans for itself.
|
||||||
|
"""
|
||||||
|
name = file_entry.get("name", "")
|
||||||
|
if "<" in name or ">" in name or "*" in name:
|
||||||
|
return True
|
||||||
|
if "path" in file_entry and file_entry["path"] is None:
|
||||||
|
return True
|
||||||
|
mode = file_entry.get("mode")
|
||||||
|
if (mode == "standalone" and not is_standalone) or (
|
||||||
|
mode == "libretro" and is_standalone
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
return not read_from_system_dir(file_entry) or bool(file_entry.get("agnostic"))
|
||||||
|
|
||||||
|
|
||||||
def filter_files_by_mode(files: list[dict], standalone: bool) -> list[dict]:
|
def filter_files_by_mode(files: list[dict], standalone: bool) -> list[dict]:
|
||||||
"""Filter file entries by libretro/standalone mode."""
|
"""Filter file entries by libretro/standalone mode."""
|
||||||
result = []
|
result = []
|
||||||
@@ -490,3 +523,84 @@ def settle_mismatch(
|
|||||||
):
|
):
|
||||||
return "frontend_digest_exact"
|
return "frontend_digest_exact"
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
def destination_owners(profiles: dict) -> dict[str, list[tuple[str, str]]]:
|
||||||
|
"""For each file name, the path tail each emulator reads it under."""
|
||||||
|
owners: dict[str, list[tuple[str, str]]] = {}
|
||||||
|
for emu_name, profile in profiles.items():
|
||||||
|
for f in profile.get("files", []):
|
||||||
|
name = str(f.get("name") or "")
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
for declared in {f.get("path"), f.get("standalone_path")} - {None, ""} or {name}:
|
||||||
|
owners.setdefault(name.lower(), []).append(
|
||||||
|
(str(declared).replace("\\", "/").lower(), emu_name)
|
||||||
|
)
|
||||||
|
return owners
|
||||||
|
|
||||||
|
|
||||||
|
def _owners_of(destination: str, name: str, owners: dict) -> set[str]:
|
||||||
|
"""Emulators whose declared path is the longest tail of the destination."""
|
||||||
|
destination = destination.replace("\\", "/").lower()
|
||||||
|
matching = [
|
||||||
|
(tail, emu) for tail, emu in owners.get(name.lower(), [])
|
||||||
|
if destination == tail or destination.endswith("/" + tail)
|
||||||
|
]
|
||||||
|
if not matching:
|
||||||
|
return set()
|
||||||
|
longest = max(len(tail) for tail, _emu in matching)
|
||||||
|
return {emu for tail, emu in matching if len(tail) == longest}
|
||||||
|
|
||||||
|
|
||||||
|
def validated_choice(
|
||||||
|
file_entry: dict,
|
||||||
|
local_path: str | None,
|
||||||
|
db: dict,
|
||||||
|
validation_index: dict,
|
||||||
|
bios_dir: str,
|
||||||
|
platform_digest: str | None,
|
||||||
|
destination: str = "",
|
||||||
|
owners: dict | None = None,
|
||||||
|
) -> tuple[str | None, str | None]:
|
||||||
|
"""The file a platform destination ships, and the disagreement if any.
|
||||||
|
|
||||||
|
A frontend's own check can pass while an emulator it runs rejects the
|
||||||
|
file; a held variant both accept replaces it. The pack and the install
|
||||||
|
manifest both read this, so they name the same file. A destination
|
||||||
|
another emulator declares more precisely is that emulator's: ZEsarUX's
|
||||||
|
48 KB cpc6128.rom check has no say over ep128emu/roms/cpc6128.rom.
|
||||||
|
"""
|
||||||
|
if not local_path or not validation_index:
|
||||||
|
return local_path, None
|
||||||
|
name = file_entry.get("name", "")
|
||||||
|
rules = validation_index.get(name)
|
||||||
|
if rules and owners and destination:
|
||||||
|
owning = _owners_of(destination, name, owners)
|
||||||
|
if owning and not owning & set(rules["emulators"]):
|
||||||
|
return local_path, None
|
||||||
|
check = check_file_validation(
|
||||||
|
local_path, file_entry.get("name", ""), validation_index, bios_dir
|
||||||
|
)
|
||||||
|
if not check:
|
||||||
|
return local_path, None
|
||||||
|
better = find_validated_variant(
|
||||||
|
file_entry, db, local_path, validation_index, bios_dir,
|
||||||
|
platform_digest=platform_digest,
|
||||||
|
)
|
||||||
|
if better:
|
||||||
|
return better, None
|
||||||
|
reason, emulators = check
|
||||||
|
return local_path, f"{', '.join(emulators)} says {reason}"
|
||||||
|
|
||||||
|
|
||||||
|
def existence_discrepancy(file_entry: dict, local_path: str, platform_display: str) -> str:
|
||||||
|
"""What an existence platform is told when its declared hash contradicts
|
||||||
|
the file it loads: the file is kept, the gap is said out loud. The pack
|
||||||
|
and verify both print this one line."""
|
||||||
|
declared = file_entry.get("md5", "") or file_entry.get("sha1", "")
|
||||||
|
actual = compute_hashes(local_path)
|
||||||
|
return (
|
||||||
|
f"packed per {platform_display} existence check; declared hash {declared}, "
|
||||||
|
f"file md5 {actual['md5']} sha1 {actual['sha1']}"
|
||||||
|
)
|
||||||
+84
-66
@@ -69,6 +69,9 @@ from nativemode import (
|
|||||||
reads_file_contents,
|
reads_file_contents,
|
||||||
)
|
)
|
||||||
from validation import (
|
from validation import (
|
||||||
|
existence_discrepancy,
|
||||||
|
destination_owners,
|
||||||
|
validated_choice,
|
||||||
agnostic_substitute,
|
agnostic_substitute,
|
||||||
_build_validation_index,
|
_build_validation_index,
|
||||||
_parse_validation,
|
_parse_validation,
|
||||||
@@ -76,10 +79,14 @@ from validation import (
|
|||||||
check_file_validation,
|
check_file_validation,
|
||||||
filter_files_by_mode,
|
filter_files_by_mode,
|
||||||
find_validated_variant,
|
find_validated_variant,
|
||||||
|
outside_gap_scope,
|
||||||
)
|
)
|
||||||
|
|
||||||
DEFAULT_DB = "database.json"
|
DEFAULT_DB = "database.json"
|
||||||
DEFAULT_PLATFORMS_DIR = "platforms"
|
DEFAULT_PLATFORMS_DIR = "platforms"
|
||||||
|
# The repository's platforms wherever the script runs from, for library calls
|
||||||
|
# that pass no directory.
|
||||||
|
_REPO_PLATFORMS = os.path.join(os.path.dirname(__file__), "..", "platforms")
|
||||||
DEFAULT_EMULATORS_DIR = "emulators"
|
DEFAULT_EMULATORS_DIR = "emulators"
|
||||||
|
|
||||||
|
|
||||||
@@ -117,6 +124,10 @@ def verify_entry_existence(
|
|||||||
local_path: str | None,
|
local_path: str | None,
|
||||||
validation_index: dict[str, dict] | None = None,
|
validation_index: dict[str, dict] | None = None,
|
||||||
db: dict | None = None,
|
db: dict | None = None,
|
||||||
|
destination: str = "",
|
||||||
|
owners: dict | None = None,
|
||||||
|
resolve_status: str = "",
|
||||||
|
platform_display: str = "",
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""RetroArch verification: path_is_valid() -file exists = OK."""
|
"""RetroArch verification: path_is_valid() -file exists = OK."""
|
||||||
name = file_entry.get("name", "")
|
name = file_entry.get("name", "")
|
||||||
@@ -124,22 +135,27 @@ def verify_entry_existence(
|
|||||||
if not local_path:
|
if not local_path:
|
||||||
return {"name": name, "status": Status.MISSING, "required": required}
|
return {"name": name, "status": Status.MISSING, "required": required}
|
||||||
result = {"name": name, "status": Status.OK, "required": required}
|
result = {"name": name, "status": Status.OK, "required": required}
|
||||||
if validation_index:
|
if resolve_status == "hash_mismatch":
|
||||||
|
# The frontend reads no bytes, so the file stays; the declared hash
|
||||||
|
# it contradicts is reported, as the pack does.
|
||||||
|
result["discrepancy"] = existence_discrepancy(
|
||||||
|
file_entry, local_path, platform_display
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
if not validation_index:
|
||||||
|
return result
|
||||||
|
if db:
|
||||||
|
# The pack ships the variant this returns, so a disagreement it
|
||||||
|
# settles is not reported.
|
||||||
|
_chosen, disagreement = validated_choice(
|
||||||
|
file_entry, local_path, db, validation_index, "bios", None,
|
||||||
|
destination, owners,
|
||||||
|
)
|
||||||
|
else:
|
||||||
check = check_file_validation(local_path, name, validation_index)
|
check = check_file_validation(local_path, name, validation_index)
|
||||||
if check:
|
disagreement = f"{', '.join(check[1])} says {check[0]}" if check else None
|
||||||
reason, emus_list = check
|
if disagreement:
|
||||||
suppressed = False
|
result["discrepancy"] = f"file present (OK) but {disagreement}"
|
||||||
if db:
|
|
||||||
better = find_validated_variant(
|
|
||||||
file_entry, db, local_path, validation_index,
|
|
||||||
)
|
|
||||||
if better:
|
|
||||||
suppressed = True
|
|
||||||
if not suppressed:
|
|
||||||
emus = ", ".join(emus_list)
|
|
||||||
result["discrepancy"] = (
|
|
||||||
f"file present (OK) but {emus} says {reason}"
|
|
||||||
)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -378,23 +394,7 @@ def _candidate_verdict(
|
|||||||
"""
|
"""
|
||||||
if file_entry.get("unsourceable"):
|
if file_entry.get("unsourceable"):
|
||||||
return "settled"
|
return "settled"
|
||||||
# Placeholders stand for a family of files, not a file.
|
if outside_gap_scope(file_entry, is_standalone):
|
||||||
if "<" in fname or ">" in fname or "*" in fname:
|
|
||||||
return "skip"
|
|
||||||
# An explicit null path means the user imports it through the UI.
|
|
||||||
if "path" in file_entry and file_entry["path"] is None:
|
|
||||||
return "skip"
|
|
||||||
file_mode = file_entry.get("mode")
|
|
||||||
if file_mode == "standalone" and not is_standalone:
|
|
||||||
return "skip"
|
|
||||||
if file_mode == "libretro" and is_standalone:
|
|
||||||
return "skip"
|
|
||||||
# Read from somewhere other than the system directory: not a BIOS gap.
|
|
||||||
load_from = file_entry.get("load_from", "")
|
|
||||||
if load_from and load_from != "system_dir":
|
|
||||||
return "skip"
|
|
||||||
# Filename-agnostic entries are answered by the builder's own scan.
|
|
||||||
if file_entry.get("agnostic"):
|
|
||||||
return "skip"
|
return "skip"
|
||||||
if not include_all:
|
if not include_all:
|
||||||
archive = file_entry.get("archive")
|
archive = file_entry.get("archive")
|
||||||
@@ -802,6 +802,7 @@ def verify_platform(
|
|||||||
if f.get("hle_fallback")
|
if f.get("hle_fallback")
|
||||||
}
|
}
|
||||||
validation_index = _build_validation_index(platform_profiles)
|
validation_index = _build_validation_index(platform_profiles)
|
||||||
|
validation_owners = destination_owners(platform_profiles)
|
||||||
|
|
||||||
# Filter systems by target
|
# Filter systems by target
|
||||||
if not target_cores:
|
if not target_cores:
|
||||||
@@ -857,12 +858,19 @@ def verify_platform(
|
|||||||
file_entry, sys_id, db, zip_contents, data_dir_registry,
|
file_entry, sys_id, db, zip_contents, data_dir_registry,
|
||||||
slot_overrides, mode, platform_profiles,
|
slot_overrides, mode, platform_profiles,
|
||||||
)
|
)
|
||||||
|
destination = sanitize_pack_path(
|
||||||
|
file_entry.get("destination", file_entry.get("name", ""))
|
||||||
|
)
|
||||||
if not reads_file_contents(mode):
|
if not reads_file_contents(mode):
|
||||||
result = verify_entry_existence(
|
result = verify_entry_existence(
|
||||||
file_entry,
|
file_entry,
|
||||||
local_path,
|
local_path,
|
||||||
validation_index,
|
validation_index,
|
||||||
db,
|
db,
|
||||||
|
destination,
|
||||||
|
validation_owners,
|
||||||
|
resolve_status,
|
||||||
|
config.get("platform", ""),
|
||||||
)
|
)
|
||||||
elif digest_algorithm(mode) == "sha1":
|
elif digest_algorithm(mode) == "sha1":
|
||||||
result = verify_entry_sha1(file_entry, local_path)
|
result = verify_entry_sha1(file_entry, local_path)
|
||||||
@@ -871,25 +879,13 @@ def verify_platform(
|
|||||||
# Emulator-level validation: informational for platform packs.
|
# Emulator-level validation: informational for platform packs.
|
||||||
# Platform verification (MD5) is the authority. Emulator
|
# Platform verification (MD5) is the authority. Emulator
|
||||||
# mismatches are reported as discrepancies, not failures.
|
# mismatches are reported as discrepancies, not failures.
|
||||||
if result["status"] == Status.OK and local_path and validation_index:
|
if result["status"] == Status.OK:
|
||||||
fname = file_entry.get("name", "")
|
_chosen, disagreement = validated_choice(
|
||||||
check = check_file_validation(
|
file_entry, local_path, db, validation_index, "bios",
|
||||||
local_path, fname, validation_index,
|
digest_algorithm(mode), destination, validation_owners,
|
||||||
)
|
)
|
||||||
if check:
|
if disagreement:
|
||||||
reason, emus_list = check
|
result["discrepancy"] = f"{platform} says OK but {disagreement}"
|
||||||
better = find_validated_variant(
|
|
||||||
file_entry,
|
|
||||||
db,
|
|
||||||
local_path,
|
|
||||||
validation_index,
|
|
||||||
platform_digest=digest_algorithm(mode),
|
|
||||||
)
|
|
||||||
if not better:
|
|
||||||
emus = ", ".join(emus_list)
|
|
||||||
result["discrepancy"] = (
|
|
||||||
f"{platform} says OK but {emus} says {reason}"
|
|
||||||
)
|
|
||||||
result["system"] = sys_id
|
result["system"] = sys_id
|
||||||
result["hle_fallback"] = hle_index.get(file_entry.get("name", ""), False)
|
result["hle_fallback"] = hle_index.get(file_entry.get("name", ""), False)
|
||||||
result["ground_truth"] = build_ground_truth(
|
result["ground_truth"] = build_ground_truth(
|
||||||
@@ -1126,8 +1122,11 @@ def _print_undeclared_section(result: dict, verbose: bool) -> None:
|
|||||||
if not undeclared:
|
if not undeclared:
|
||||||
return
|
return
|
||||||
|
|
||||||
bios_files = [u for u in undeclared if u.get("category", "bios") == "bios"]
|
# Everything that is not game data is firmware the core loads, archives
|
||||||
|
# included: a bios_zip sat in neither list, so a required one that was
|
||||||
|
# missing was never printed.
|
||||||
game_data = [u for u in undeclared if u.get("category", "bios") == "game_data"]
|
game_data = [u for u in undeclared if u.get("category", "bios") == "game_data"]
|
||||||
|
bios_files = [u for u in undeclared if u.get("category", "bios") != "game_data"]
|
||||||
|
|
||||||
req_not_in_repo = [
|
req_not_in_repo = [
|
||||||
u
|
u
|
||||||
@@ -1259,12 +1258,15 @@ def verify_emulator(
|
|||||||
db: dict,
|
db: dict,
|
||||||
standalone: bool = False,
|
standalone: bool = False,
|
||||||
regions: list[str] | None = None,
|
regions: list[str] | None = None,
|
||||||
|
platforms_dir: str = _REPO_PLATFORMS,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Verify files for specific emulator profiles.
|
"""Verify files for specific emulator profiles.
|
||||||
|
|
||||||
A region priority list narrows the report the same way a pack built with the
|
A region priority list narrows the report the same way a pack built with the
|
||||||
same list would be narrowed, through the emulator pack's own drop set.
|
same list would be narrowed, through the emulator pack's own drop set.
|
||||||
"""
|
"""
|
||||||
|
from packpaths import _resolve_destination
|
||||||
|
|
||||||
load_emulator_profiles(emulators_dir)
|
load_emulator_profiles(emulators_dir)
|
||||||
zip_contents = build_zip_contents_index(db)
|
zip_contents = build_zip_contents_index(db)
|
||||||
|
|
||||||
@@ -1276,9 +1278,7 @@ def verify_emulator(
|
|||||||
# Build validation index from selected profiles only
|
# Build validation index from selected profiles only
|
||||||
selected_profiles = {n: p for n, p in selected}
|
selected_profiles = {n: p for n, p in selected}
|
||||||
validation_index = _build_validation_index(selected_profiles)
|
validation_index = _build_validation_index(selected_profiles)
|
||||||
data_registry = load_data_dir_registry(
|
data_registry = load_data_dir_registry(platforms_dir)
|
||||||
os.path.join(os.path.dirname(__file__), "..", "platforms")
|
|
||||||
)
|
|
||||||
|
|
||||||
details = []
|
details = []
|
||||||
file_status: dict[str, str] = {}
|
file_status: dict[str, str] = {}
|
||||||
@@ -1300,8 +1300,6 @@ def verify_emulator(
|
|||||||
standalone,
|
standalone,
|
||||||
)
|
)
|
||||||
if region_drops:
|
if region_drops:
|
||||||
from packpaths import _resolve_destination
|
|
||||||
|
|
||||||
structure = profile.get("pack_structure")
|
structure = profile.get("pack_structure")
|
||||||
files = [
|
files = [
|
||||||
fe
|
fe
|
||||||
@@ -1489,7 +1487,11 @@ def verify_emulator(
|
|||||||
result["system"] = file_entry.get("system", "")
|
result["system"] = file_entry.get("system", "")
|
||||||
result["hle_fallback"] = hle
|
result["hle_fallback"] = hle
|
||||||
result["ground_truth"] = build_ground_truth(name, validation_index)
|
result["ground_truth"] = build_ground_truth(name, validation_index)
|
||||||
dest = file_entry.get("path", "") or name
|
# The slot the emulator pack places this entry in: standalone_path
|
||||||
|
# under --standalone, the pack_structure prefix, the sanitised path.
|
||||||
|
dest = _resolve_destination(
|
||||||
|
file_entry, profile.get("pack_structure"), standalone
|
||||||
|
) or name
|
||||||
emu_results.setdefault(dest, []).append((result, required, hle))
|
emu_results.setdefault(dest, []).append((result, required, hle))
|
||||||
|
|
||||||
for dest, alternatives in emu_results.items():
|
for dest, alternatives in emu_results.items():
|
||||||
@@ -1556,6 +1558,7 @@ def verify_system(
|
|||||||
db: dict,
|
db: dict,
|
||||||
standalone: bool = False,
|
standalone: bool = False,
|
||||||
regions: list[str] | None = None,
|
regions: list[str] | None = None,
|
||||||
|
platforms_dir: str = _REPO_PLATFORMS,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Verify files for all emulators supporting given system IDs."""
|
"""Verify files for all emulators supporting given system IDs."""
|
||||||
profiles = load_emulator_profiles(emulators_dir)
|
profiles = load_emulator_profiles(emulators_dir)
|
||||||
@@ -1590,7 +1593,9 @@ def verify_system(
|
|||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
return verify_emulator(matching, emulators_dir, db, standalone, regions=regions)
|
return verify_emulator(
|
||||||
|
matching, emulators_dir, db, standalone, regions=regions, platforms_dir=platforms_dir
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def print_emulator_result(result: dict, verbose: bool = False) -> None:
|
def print_emulator_result(result: dict, verbose: bool = False) -> None:
|
||||||
@@ -1718,11 +1723,29 @@ def _refuse_listing_narrowings(
|
|||||||
("--system", args.system),
|
("--system", args.system),
|
||||||
("--region", getattr(args, "region", None)),
|
("--region", getattr(args, "region", None)),
|
||||||
("--target", getattr(args, "target", None)),
|
("--target", getattr(args, "target", None)),
|
||||||
|
("--standalone", args.standalone),
|
||||||
|
("--include-archived", args.include_archived),
|
||||||
|
("--json", args.json),
|
||||||
):
|
):
|
||||||
if on:
|
if on:
|
||||||
parser.error(f"{flag} is incompatible with {listing}")
|
parser.error(f"{flag} is incompatible with {listing}")
|
||||||
|
|
||||||
|
|
||||||
|
def _refuse_mode_flags(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None:
|
||||||
|
"""A flag the chosen mode does not read is refused, never ignored."""
|
||||||
|
for refused, message in (
|
||||||
|
(args.standalone and not (args.emulator or args.system),
|
||||||
|
"--standalone requires --emulator or --system"),
|
||||||
|
(args.include_archived and not args.all, "--include-archived requires --all"),
|
||||||
|
(args.target and not (args.platform or args.all),
|
||||||
|
"--target requires --platform or --all"),
|
||||||
|
(args.target and (args.emulator or args.system),
|
||||||
|
"--target is incompatible with --emulator and --system"),
|
||||||
|
):
|
||||||
|
if refused:
|
||||||
|
parser.error(message)
|
||||||
|
|
||||||
|
|
||||||
def _run_listing(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None:
|
def _run_listing(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None:
|
||||||
if args.list_emulators:
|
if args.list_emulators:
|
||||||
list_emulator_profiles(args.emulators_dir)
|
list_emulator_profiles(args.emulators_dir)
|
||||||
@@ -1805,12 +1828,7 @@ def main():
|
|||||||
parser.error(
|
parser.error(
|
||||||
"--platform, --all, --emulator, and --system are mutually exclusive"
|
"--platform, --all, --emulator, and --system are mutually exclusive"
|
||||||
)
|
)
|
||||||
if args.standalone and not (args.emulator or args.system):
|
_refuse_mode_flags(args, parser)
|
||||||
parser.error("--standalone requires --emulator or --system")
|
|
||||||
if args.target and not (args.platform or args.all):
|
|
||||||
parser.error("--target requires --platform or --all")
|
|
||||||
if args.target and (args.emulator or args.system):
|
|
||||||
parser.error("--target is incompatible with --emulator and --system")
|
|
||||||
|
|
||||||
with open(args.db) as f:
|
with open(args.db) as f:
|
||||||
db = json.load(f)
|
db = json.load(f)
|
||||||
@@ -1820,7 +1838,7 @@ def main():
|
|||||||
names = [n.strip() for n in args.emulator.split(",") if n.strip()]
|
names = [n.strip() for n in args.emulator.split(",") if n.strip()]
|
||||||
result = verify_emulator(
|
result = verify_emulator(
|
||||||
names, args.emulators_dir, db, args.standalone,
|
names, args.emulators_dir, db, args.standalone,
|
||||||
regions=requested_regions,
|
regions=requested_regions, platforms_dir=args.platforms_dir,
|
||||||
)
|
)
|
||||||
if args.json:
|
if args.json:
|
||||||
result["details"] = [
|
result["details"] = [
|
||||||
@@ -1836,7 +1854,7 @@ def main():
|
|||||||
system_ids = [s.strip() for s in args.system.split(",") if s.strip()]
|
system_ids = [s.strip() for s in args.system.split(",") if s.strip()]
|
||||||
result = verify_system(
|
result = verify_system(
|
||||||
system_ids, args.emulators_dir, db, args.standalone,
|
system_ids, args.emulators_dir, db, args.standalone,
|
||||||
regions=requested_regions,
|
regions=requested_regions, platforms_dir=args.platforms_dir,
|
||||||
)
|
)
|
||||||
if args.json:
|
if args.json:
|
||||||
result["details"] = [
|
result["details"] = [
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from pathlib import Path
|
|||||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||||
|
|
||||||
from common import ArtifactLockBusy, artifact_lock # noqa: E402
|
from common import ArtifactLockBusy, artifact_lock, hold_artifact_lock # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
class ArtifactLockTest(unittest.TestCase):
|
class ArtifactLockTest(unittest.TestCase):
|
||||||
@@ -156,5 +156,42 @@ class PackLockCliTest(unittest.TestCase):
|
|||||||
self.assertIn("is in use by another run", proc.stdout)
|
self.assertIn("is in use by another run", proc.stdout)
|
||||||
|
|
||||||
|
|
||||||
|
class HeldLockTest(unittest.TestCase):
|
||||||
|
"""pipeline.py took the lock step by step: between the pack build and the
|
||||||
|
integrity check dist/ was free, another run's purge emptied it, and the
|
||||||
|
check reported every platform SKIP and passed."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.dir = tempfile.mkdtemp()
|
||||||
|
|
||||||
|
def _child_locks(self, env: dict) -> subprocess.CompletedProcess:
|
||||||
|
code = (
|
||||||
|
"import sys; sys.path.insert(0, 'scripts')\n"
|
||||||
|
"from common import artifact_lock\n"
|
||||||
|
f"with artifact_lock({self.dir!r}):\n pass\n"
|
||||||
|
)
|
||||||
|
return subprocess.run(
|
||||||
|
[sys.executable, "-c", code], cwd=str(REPO_ROOT), env=env,
|
||||||
|
capture_output=True, text=True, timeout=60, check=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_a_spawned_step_inherits_the_hold(self):
|
||||||
|
with hold_artifact_lock(self.dir):
|
||||||
|
self.assertEqual(self._child_locks(dict(os.environ)).returncode, 0)
|
||||||
|
|
||||||
|
def test_any_other_run_is_refused_for_the_whole_hold(self):
|
||||||
|
with hold_artifact_lock(self.dir):
|
||||||
|
env = {k: v for k, v in os.environ.items() if not k.startswith("RETROBIOS_")}
|
||||||
|
proc = self._child_locks(env)
|
||||||
|
self.assertNotEqual(proc.returncode, 0)
|
||||||
|
self.assertIn("ArtifactLockBusy", proc.stderr)
|
||||||
|
self.assertNotIn("RETROBIOS_HELD_ARTIFACT_DIR", os.environ)
|
||||||
|
self.assertEqual(self._child_locks(dict(os.environ)).returncode, 0)
|
||||||
|
|
||||||
|
def test_the_pipeline_holds_its_output_for_the_run(self):
|
||||||
|
source = (REPO_ROOT / "scripts" / "pipeline.py").read_text(encoding="utf-8")
|
||||||
|
self.assertIn("hold_artifact_lock(args.output_dir)", source)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -511,6 +511,16 @@ class PipelineRegressions(unittest.TestCase):
|
|||||||
])
|
])
|
||||||
self.assertEqual(pipeline.parse_pack_counts(output), {"RetroArch": (535, 535)})
|
self.assertEqual(pipeline.parse_pack_counts(output), {"RetroArch": (535, 535)})
|
||||||
|
|
||||||
|
def test_exclusions_read_only_the_full_variant(self):
|
||||||
|
"""--all-variants printed six packs under one label; the last won."""
|
||||||
|
output = "\n".join([
|
||||||
|
"Generating pack for Batocera [source=full]...",
|
||||||
|
" a.zip: 5 files packed (5 baseline + 0 from cores), 5/5 files OK, 3 unsafe excluded",
|
||||||
|
"Generating pack for Batocera [source=truth, required]...",
|
||||||
|
" b.zip: 2 files packed (0 baseline + 2 from cores), 2/2 files OK",
|
||||||
|
])
|
||||||
|
self.assertEqual(pipeline.parse_pack_exclusions(output), {"Batocera": 3})
|
||||||
|
|
||||||
def test_slot_step_reaches_the_summary(self):
|
def test_slot_step_reaches_the_summary(self):
|
||||||
source = Path(pipeline.__file__).read_text(encoding="utf-8")
|
source = Path(pipeline.__file__).read_text(encoding="utf-8")
|
||||||
self.assertIn('results["slots"] = ok', source)
|
self.assertIn('results["slots"] = ok', source)
|
||||||
@@ -560,6 +570,26 @@ class PipelineRegressions(unittest.TestCase):
|
|||||||
self.assertTrue(full_models, truth_runs)
|
self.assertTrue(full_models, truth_runs)
|
||||||
|
|
||||||
|
|
||||||
|
class SchemaFormatsAreChecked(unittest.TestCase):
|
||||||
|
"""date-time and uri need jsonschema's format extras; without them the
|
||||||
|
published format constraints were accepted unread."""
|
||||||
|
|
||||||
|
@unittest.skipUnless(HAS_JSONSCHEMA, "validating a schema needs jsonschema")
|
||||||
|
def test_a_missing_checker_is_named(self):
|
||||||
|
import validate_schemas # noqa: PLC0415
|
||||||
|
|
||||||
|
empty = mock.Mock(checkers={})
|
||||||
|
with mock.patch.object(validate_schemas, "FormatChecker", return_value=empty):
|
||||||
|
self.assertEqual(validate_schemas.unchecked_formats(), ["date-time", "uri"])
|
||||||
|
|
||||||
|
def test_every_install_asks_for_the_extras(self):
|
||||||
|
for path in (".github/workflows/validate.yml", ".github/workflows/deploy-site.yml",
|
||||||
|
"pyproject.toml"):
|
||||||
|
text = (ROOT / path).read_text(encoding="utf-8")
|
||||||
|
with self.subTest(path=path):
|
||||||
|
self.assertNotRegex(text, r"jsonschema(?!\[format)[=>]")
|
||||||
|
|
||||||
|
|
||||||
class ResolverRegressions(unittest.TestCase):
|
class ResolverRegressions(unittest.TestCase):
|
||||||
def _database(self, entries: dict[str, Path], suffix: str | None = None) -> dict:
|
def _database(self, entries: dict[str, Path], suffix: str | None = None) -> dict:
|
||||||
files = {}
|
files = {}
|
||||||
|
|||||||
@@ -304,5 +304,27 @@ class RepositoryWiringTests(unittest.TestCase):
|
|||||||
self.assertIn("[data]", text)
|
self.assertIn("[data]", text)
|
||||||
|
|
||||||
|
|
||||||
|
class RepeatedDestinations(unittest.TestCase):
|
||||||
|
def test_a_change_to_an_earlier_declaration_is_seen(self):
|
||||||
|
from check_freshness import diff_platform # noqa: PLC0415
|
||||||
|
|
||||||
|
def platform(first_md5: str) -> dict:
|
||||||
|
return {"systems": {"pce": {"files": [
|
||||||
|
{"name": "syscard3.pce", "destination": "syscard3.pce", "md5": first_md5},
|
||||||
|
{"name": "syscard3.pce", "destination": "syscard3.pce", "md5": "b" * 32},
|
||||||
|
]}}}
|
||||||
|
|
||||||
|
diff = diff_platform(platform("a" * 32), platform("c" * 32))
|
||||||
|
self.assertEqual(diff.files_changed, ["pce/syscard3.pce"])
|
||||||
|
|
||||||
|
|
||||||
|
class PinsWithExtras(unittest.TestCase):
|
||||||
|
def test_an_extras_bracket_keeps_its_pin(self):
|
||||||
|
from check_freshness import parse_pip_pins # noqa: PLC0415
|
||||||
|
|
||||||
|
pins = parse_pip_pins(' run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0"')
|
||||||
|
self.assertEqual(pins["jsonschema"], "==4.26.0")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
"""CONTRIBUTING.md and the site's contributing page say the same thing.
|
||||||
|
|
||||||
|
Two generators wrote the page and drifted: four steps to add a platform in
|
||||||
|
the repository, five on the site, under two different titles.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||||
|
|
||||||
|
import generate_readme # noqa: E402
|
||||||
|
import generate_site # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def _normalized(text: str) -> str:
|
||||||
|
text = re.sub(r"\]\([^)]*\)", "](link)", text)
|
||||||
|
return "\n".join(text.splitlines()[1:]).replace("on this site", "on the documentation site")
|
||||||
|
|
||||||
|
|
||||||
|
class OneContributingText(unittest.TestCase):
|
||||||
|
def test_repository_and_site_pages_match(self):
|
||||||
|
self.assertEqual(
|
||||||
|
_normalized(generate_readme.generate_contributing()),
|
||||||
|
_normalized(generate_site.generate_contributing()),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CoverageReadsTheGivenProfiles(unittest.TestCase):
|
||||||
|
"""generate_site --emulators-dir changed the emulator pages but not the
|
||||||
|
platform coverage, which read ./emulators whatever was given."""
|
||||||
|
|
||||||
|
def test_the_directory_reaches_verify(self):
|
||||||
|
seen: list[str] = []
|
||||||
|
|
||||||
|
def fake_verify(_config, _db, emulators_dir, **_kwargs):
|
||||||
|
seen.append(emulators_dir)
|
||||||
|
return {"status_counts": {}, "total_files": 0, "undeclared_files": [], "details": []}
|
||||||
|
|
||||||
|
with mock.patch.object(generate_readme, "verify_platform", fake_verify), \
|
||||||
|
mock.patch.object(generate_readme, "load_emulator_profiles", return_value={}):
|
||||||
|
generate_readme.compute_coverage(
|
||||||
|
"retroarch", str(REPO_ROOT / "platforms"), {"files": {}}, emulators_dir="elsewhere"
|
||||||
|
)
|
||||||
|
self.assertEqual(seen, ["elsewhere"])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
"""The gap report reads the collection the way the resolver does.
|
||||||
|
|
||||||
|
Three ways it called held files missing, or a missing file held: system IDs
|
||||||
|
compared verbatim (sega-megacd against sega-mega-cd), no sha256 in the hash
|
||||||
|
fallback (rust_dos names its SC-55 ROMs by sha256 alone), and directory names
|
||||||
|
indexed as files (BasiliskII's required ROM read as held because cpcemu keeps
|
||||||
|
a directory called ROM).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||||
|
|
||||||
|
from cross_reference import ( # noqa: E402
|
||||||
|
_build_supplemental_index,
|
||||||
|
cross_reference,
|
||||||
|
entry_source,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _index(**over) -> dict:
|
||||||
|
base = {
|
||||||
|
"by_name": {}, "by_name_lower": {}, "by_path_suffix": {}, "by_md5": {},
|
||||||
|
"by_sha256": {}, "by_crc32": {}, "db_files": {}, "data_names": set(),
|
||||||
|
}
|
||||||
|
base.update(over)
|
||||||
|
return base
|
||||||
|
|
||||||
|
|
||||||
|
class GapReportSources(unittest.TestCase):
|
||||||
|
def test_a_sha256_alone_finds_the_file(self):
|
||||||
|
entry = {"name": "wave1.bin", "sha256": "AB" * 32}
|
||||||
|
self.assertEqual(entry_source(entry, _index(by_sha256={"ab" * 32: "s"})), "bios")
|
||||||
|
|
||||||
|
def test_a_directory_does_not_stand_for_a_file(self):
|
||||||
|
with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp") as tmp:
|
||||||
|
(Path(tmp) / "bios" / "cpcemu" / "ROM").mkdir(parents=True)
|
||||||
|
names = _build_supplemental_index(str(Path(tmp) / "data"), str(Path(tmp) / "bios"))
|
||||||
|
self.assertIsNone(entry_source({"name": "ROM"}, _index(data_names=names)))
|
||||||
|
directory = {"name": "ROM", "type": "directory"}
|
||||||
|
self.assertEqual(entry_source(directory, _index(data_names=names)), "data")
|
||||||
|
|
||||||
|
def test_a_system_spelled_otherwise_is_still_declared(self):
|
||||||
|
profiles = {"gpgx": {"emulator": "gpgx", "systems": ["sega-megacd"],
|
||||||
|
"files": [{"name": "bios_CD_U.bin"}]}}
|
||||||
|
declared = {"megacd": {"bios_CD_U.bin"}} # sega-mega-cd, normalized
|
||||||
|
db = {"files": {}, "indexes": {}}
|
||||||
|
report = cross_reference(profiles, declared, db)
|
||||||
|
self.assertEqual(report["gpgx"]["gaps"], 0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -201,7 +201,7 @@ class PackCarriesADirectory(DirectoryFixture):
|
|||||||
out.mkdir()
|
out.mkdir()
|
||||||
zip_path = builder.generate_pack(
|
zip_path = builder.generate_pack(
|
||||||
"demo", str(self.platforms), self._db(), str(self.bios), str(out),
|
"demo", str(self.platforms), self._db(), str(self.bios), str(out),
|
||||||
include_extras=True, emulators_dir=str(self.emulators),
|
emulators_dir=str(self.emulators),
|
||||||
emu_profiles=profiles, offline=True,
|
emu_profiles=profiles, offline=True,
|
||||||
)
|
)
|
||||||
with zipfile.ZipFile(zip_path) as archive:
|
with zipfile.ZipFile(zip_path) as archive:
|
||||||
|
|||||||
+19
-2
@@ -5562,11 +5562,13 @@ struct BurnDriver BurnDrvneogeo = {
|
|||||||
with open(os.path.join(plat_dir, f"{name}.yml"), "w") as f:
|
with open(os.path.join(plat_dir, f"{name}.yml"), "w") as f:
|
||||||
yaml.dump(cfg, f)
|
yaml.dump(cfg, f)
|
||||||
|
|
||||||
|
# Keyed by the normalized system ID, as profiles spell systems
|
||||||
|
# differently from platforms.
|
||||||
everything, _ = load_platform_files(plat_dir)
|
everything, _ = load_platform_files(plat_dir)
|
||||||
self.assertEqual(everything["shared-system"], {"a.bin", "b.bin"})
|
self.assertEqual(everything["sharedsystem"], {"a.bin", "b.bin"})
|
||||||
|
|
||||||
only_alpha, _ = load_platform_files(plat_dir, ["alpha"])
|
only_alpha, _ = load_platform_files(plat_dir, ["alpha"])
|
||||||
self.assertEqual(only_alpha["shared-system"], {"a.bin"})
|
self.assertEqual(only_alpha["sharedsystem"], {"a.bin"})
|
||||||
|
|
||||||
|
|
||||||
def test_227_manifest_injected_compressed(self):
|
def test_227_manifest_injected_compressed(self):
|
||||||
@@ -6004,6 +6006,21 @@ struct BurnDriver BurnDrvneogeo = {
|
|||||||
"refuse"),
|
"refuse"),
|
||||||
(["--list-systems"], ["--region", "us"], "refuse"),
|
(["--list-systems"], ["--region", "us"], "refuse"),
|
||||||
(["--list-emulators"], ["--target", "switch"], "refuse"),
|
(["--list-emulators"], ["--target", "switch"], "refuse"),
|
||||||
|
# Only the platform path refreshes data directories.
|
||||||
|
(["--emulator", "dolphin"], ["--refresh-data"], "refuse"),
|
||||||
|
(["--system", "sony-playstation"], ["--refresh-data"], "refuse"),
|
||||||
|
(["--from-md5", "d8f1"], ["--refresh-data"], "refuse"),
|
||||||
|
(["--platform", "retroarch", "--offline"], ["--refresh-data"], "refuse"),
|
||||||
|
# Only --all chooses among platforms, as verify.py already says.
|
||||||
|
(["--platform", "retroarch"], ["--include-archived"], "refuse"),
|
||||||
|
(["--emulator", "handy"], ["--include-archived"], "refuse"),
|
||||||
|
(["--system", "atari-lynx"], ["--include-archived"], "refuse"),
|
||||||
|
(["--from-md5", "d8f1"], ["--include-archived"], "refuse"),
|
||||||
|
(["--platform", "retroarch", "--verify-packs"], ["--include-archived"],
|
||||||
|
"refuse"),
|
||||||
|
# Documented as adding core files, read by nothing.
|
||||||
|
(["--emulator", "handy"], ["--include-extras"], "refuse"),
|
||||||
|
(["--system", "atari-lynx"], ["--include-extras"], "refuse"),
|
||||||
]
|
]
|
||||||
for mode, flag, expected in matrix:
|
for mode, flag, expected in matrix:
|
||||||
with self.subTest(mode=mode, flag=flag):
|
with self.subTest(mode=mode, flag=flag):
|
||||||
|
|||||||
Loaded 100 of 139 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user