mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-09 11:33:47 -06:00
Compare commits
132
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fe961a1ea | ||
|
|
ce239d32c1 | ||
|
|
13995047fc | ||
|
|
31e86b6db5 | ||
|
|
da5e95cd0f | ||
|
|
3dce7b5f09 | ||
|
|
97ccf9027b | ||
|
|
1caf5c4b5e | ||
|
|
acfd80c928 | ||
|
|
4d8ea05494 | ||
|
|
3df423e84f | ||
|
|
8a9924f1d8 | ||
|
|
52fbea5faa | ||
|
|
421a7afb3f | ||
|
|
0b7acf1e7f | ||
|
|
c4dab556e7 | ||
|
|
0390f2d19b | ||
|
|
e6afd06191 | ||
|
|
03929ddaf0 | ||
|
|
2b71e47510 | ||
|
|
cd90f46937 | ||
|
|
d02672c17b | ||
|
|
c6ba139493 | ||
|
|
f0bab9766c | ||
|
|
c55e3af5ac | ||
|
|
1c34503f4e | ||
|
|
72834837a5 | ||
|
|
ae0bbb9d3c | ||
|
|
e4cc978614 | ||
|
|
f51c3a3fc7 | ||
|
|
c62a34b1b2 | ||
|
|
ed18e61580 | ||
|
|
2cdb98239e | ||
|
|
623b0a6d47 | ||
|
|
82583fe17a | ||
|
|
85501c41e8 | ||
|
|
7273f5c8b9 | ||
|
|
e723c8c1b9 | ||
|
|
4db5866bbe | ||
|
|
dc3a35bdc8 | ||
|
|
0c881820c7 | ||
|
|
d839ce0a1c | ||
|
|
1b885632a6 | ||
|
|
ad9f834f55 | ||
|
|
49e0f83ce9 | ||
|
|
2b6dbb5e92 | ||
|
|
3d31a887cc | ||
|
|
e3f773dd82 | ||
|
|
c1b3e1fc33 | ||
|
|
b3c7e76adf | ||
|
|
b2208d889e | ||
|
|
af357a2931 | ||
|
|
1b40211366 | ||
|
|
a484754c76 | ||
|
|
46268bea47 | ||
|
|
0fbe99a14a | ||
|
|
16f4c071fe | ||
|
|
a8415c33c3 | ||
|
|
7e054fbcaf | ||
|
|
5784d45531 | ||
|
|
c526180351 | ||
|
|
36f7ee0b2b | ||
|
|
cf7d9ecb89 | ||
|
|
ccc51b861b | ||
|
|
3ae9729143 | ||
|
|
26ce416163 | ||
|
|
bd3bcc36ea | ||
|
|
f631174c85 | ||
|
|
22648e8a5f | ||
|
|
0de03b5e4c | ||
|
|
3bd19b9e97 | ||
|
|
ccf1a1573a | ||
|
|
ca86236c12 | ||
|
|
994836938c | ||
|
|
71cbbd9b21 | ||
|
|
65c0f5ddc1 | ||
|
|
ef16ed2af2 | ||
|
|
da0c843bdd | ||
|
|
e32df743d5 | ||
|
|
72d3654245 | ||
|
|
bdcf134877 | ||
|
|
aec1755c61 | ||
|
|
53ee1bfd0e | ||
|
|
d2d88cf6b8 | ||
|
|
fbdee43e6d | ||
|
|
284f1aa6b7 | ||
|
|
10f3379751 | ||
|
|
76387d1962 | ||
|
|
9465a250cc | ||
|
|
3bc80b2e06 | ||
|
|
dcdfe5936e | ||
|
|
ed47f437fc | ||
|
|
15abbec663 | ||
|
|
077dcda775 | ||
|
|
27610c1d33 | ||
|
|
25034cd958 | ||
|
|
480c02882d | ||
|
|
056953d833 | ||
|
|
49d425aa8a | ||
|
|
58cb61a08f | ||
|
|
485e5bc2b3 | ||
|
|
c5165ffb96 | ||
|
|
183793481c | ||
|
|
a762152c12 | ||
|
|
b650f9eb8a | ||
|
|
4ca4592f65 | ||
|
|
0fb09c6c94 | ||
|
|
34e382eae8 | ||
|
|
9e327a05ed | ||
|
|
f010e737c5 | ||
|
|
edce60bae1 | ||
|
|
44663db144 | ||
|
|
757fad69bc | ||
|
|
ca6462c510 | ||
|
|
3f5d825ac6 | ||
|
|
151dd5ea04 | ||
|
|
eaf315ea8f | ||
|
|
366598e662 | ||
|
|
5f3e9fbacd | ||
|
|
045372992c | ||
|
|
38ffccc465 | ||
|
|
68bcef544d | ||
|
|
81995a4c18 | ||
|
|
95d14819ce | ||
|
|
dc31a90dca | ||
|
|
8e3a486107 | ||
|
|
a3d5e6c048 | ||
|
|
8e0c7bad38 | ||
|
|
67f1b29fec | ||
|
|
b9644f720c | ||
|
|
0a9d36cae3 | ||
|
|
5816562bf6 |
No files matched your search
@@ -84,7 +84,7 @@ jobs:
|
||||
|
||||
# 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.
|
||||
- run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0" "mkdocs-material>=9.7.5,<10" "pymdown-extensions>=10.14"
|
||||
- run: pip install pyyaml jsonschema==4.26.0 "mkdocs-material>=9.7.5,<10" "pymdown-extensions>=10.14"
|
||||
|
||||
- name: Validate data contracts
|
||||
run: python scripts/validate_schemas.py
|
||||
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0"
|
||||
run: pip install pyyaml jsonschema==4.26.0
|
||||
|
||||
- name: Validate platform configs and emulator profiles
|
||||
run: python scripts/validate_schemas.py --source-only
|
||||
@@ -127,7 +127,7 @@ jobs:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0"
|
||||
run: pip install pyyaml jsonschema==4.26.0
|
||||
|
||||
# The exporter fidelity cases compare what we hand back against the
|
||||
# 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) |
|
||||
| RetroDECK | 3,519 | 6.2 GB | `~/retrodeck/` | [Download](../../releases/latest) |
|
||||
| RetroPie * | 4,525 | 5.5 GB | `~/RetroPie/BIOS/` | [Download](../../releases/latest) |
|
||||
| RomM | 617 | 1.3 GB | `library/bios/` | [Download](../../releases/latest) |
|
||||
| RomM | 617 | 1.3 GB | `bios/{platform_slug}/` | [Download](../../releases/latest) |
|
||||
|
||||
The RetroDECK pack already carries its own `bios/` folder, so it extracts into `~/retrodeck/` rather than into the BIOS folder.
|
||||
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, 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
|
||||
- **506 emulators** profiled from source (RetroArch cores + standalone)
|
||||
- **546 systems** handled by those emulators (NES, SNES, PlayStation, Saturn, Dreamcast, ...)
|
||||
- **19,002 files in the collection**, each with its SHA1, MD5, SHA256, CRC32 and Adler-32 fingerprints: 5,266 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,266 system files** matched to dump-preservation catalogs (No-Intro, Redump, TOSEC); arcade sets and engine data fall outside what those catalogs index
|
||||
- **16687 MB** total collection size
|
||||
- **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
|
||||
- **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
|
||||
- **16672 MB** total collection size
|
||||
|
||||
## 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 |
|
||||
|----------|-----------------:|-------------------------:|------------|
|
||||
| Batocera | 353/353 | 12,287/12,718 | MD5 hash |
|
||||
| BizHawk | 118/118 | 800/801 | SHA1 hash |
|
||||
| EmuDeck | 168/168 | 430/432 | MD5 hash |
|
||||
| Lakka | 526/526 | 5,256/5,286 | file presence |
|
||||
| Batocera | 353/353 | 12,307/12,738 | MD5 hash |
|
||||
| BizHawk | 118/118 | 649/650 | SHA1 hash |
|
||||
| EmuDeck | 168/168 | 381/383 | MD5 hash |
|
||||
| Lakka | 527/527 | 5,263/5,293 | file presence |
|
||||
| MiSTer FPGA | 81/81 | - | MD5 hash |
|
||||
| ROCKNIX | 38/38 | 5,695/5,725 | MD5 hash |
|
||||
| Recalbox | 351/351 | 2,721/2,729 | MD5 hash |
|
||||
| RetroArch | 526/526 | 5,256/5,286 | file presence |
|
||||
| RetroBat | 348/348 | 11,875/12,305 | MD5 hash |
|
||||
| RetroDECK | 2,008/2,008 | 5,387/5,437 | MD5 hash |
|
||||
| RetroPie * | 526/526 | 9,171/9,313 | file presence |
|
||||
| RomM | 381/381 | 746/752 | MD5 hash |
|
||||
| ROCKNIX | 38/38 | 5,702/5,732 | MD5 hash |
|
||||
| Recalbox | 351/351 | 2,728/2,736 | MD5 hash |
|
||||
| RetroArch | 527/527 | 5,263/5,293 | file presence |
|
||||
| RetroBat | 348/348 | 11,749/12,179 | MD5 hash |
|
||||
| RetroDECK | 2,008/2,008 | 5,296/5,346 | MD5 hash |
|
||||
| RetroPie * | 527/527 | 9,182/9,324 | file presence |
|
||||
| RomM | 381/381 | 624/630 | 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/).
|
||||
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 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-09T21:03:52Z*
|
||||
*Auto-generated on 2026-10-06T03:31:49Z*
|
||||
File renamed without changes.
File renamed without changes.
File diff suppressed because it is too large.
Load diff
File renamed without changes.
File diff suppressed because it is too large.
Load diff
Binary file not shown.
File diff suppressed because it is too large.
Load diff
Binary file not shown.
@@ -1,498 +0,0 @@
|
||||
// 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
@@ -1,470 +0,0 @@
|
||||
// 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
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+129
-414
File diff suppressed because it is too large.
Load diff
@@ -148,9 +148,6 @@ files:
|
||||
path: resources/GameIndex.yaml
|
||||
required: false
|
||||
bundled: true
|
||||
sha1:
|
||||
- 997fdfb1c1873c1a43972b2984ac0da61fd47b14
|
||||
- 71fc274cbe27f382afb0324024ebdc6f59aa666d
|
||||
description: "Game database"
|
||||
note: "Read from the resources folder, keyed by serial, and carrying the per-game fixes, patches and settings overrides. Parsed into gamedb.cache in the cache folder and reparsed whenever the modification time moves."
|
||||
source_ref: "aethersx2/pcsx2/GameDatabase.cpp:35-36, aethersx2/pcsx2/GameDatabase.cpp:348-352, aethersx2/pcsx2/GameDatabase.cpp:523-535"
|
||||
|
||||
@@ -93,6 +93,7 @@ files:
|
||||
- name: colecovision.rom
|
||||
system: colecovision
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "990bf1956f10207d8781b619eb74f89b00d921c8d45c95c334c16c8cceca09ad"
|
||||
validation: [sha256]
|
||||
description: "ColecoVision BIOS"
|
||||
@@ -102,6 +103,7 @@ files:
|
||||
- name: disksys.rom
|
||||
system: famicom-disk-system
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "fdc1a76e654feea993fcb38366e05ee5f4eb641f86fe6bebaeefd412e112dd72"
|
||||
validation: [sha256]
|
||||
description: "Famicom Disk System BIOS"
|
||||
@@ -111,6 +113,7 @@ files:
|
||||
- name: gba_bios.bin
|
||||
system: game-boy-advance
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570"
|
||||
validation: [sha256]
|
||||
description: "GBA BIOS"
|
||||
@@ -120,6 +123,7 @@ files:
|
||||
- name: bios.gg
|
||||
system: game-gear
|
||||
required: false
|
||||
mode: standalone
|
||||
sha256: "8c8a21335038285cfa03dc076100c1f0bfadf3e4ff70796f11f3dfaaab60eee2"
|
||||
validation: [sha256]
|
||||
description: "Game Gear BIOS"
|
||||
@@ -130,6 +134,7 @@ files:
|
||||
system: master-system
|
||||
region: [north-america]
|
||||
required: false
|
||||
mode: standalone
|
||||
sha256: "477617917a12a30f9f43844909dc2de6e6a617430f5c9a36306c86414a670d50"
|
||||
validation: [sha256]
|
||||
description: "Master System BIOS (US)"
|
||||
@@ -139,6 +144,7 @@ files:
|
||||
system: master-system
|
||||
region: [japan]
|
||||
required: false
|
||||
mode: standalone
|
||||
sha256: "67846e26764bd862f19179294347f7353a4166b62ac4198a5ec32933b7da486e"
|
||||
validation: [sha256]
|
||||
description: "Master System BIOS (Japan)"
|
||||
@@ -148,6 +154,7 @@ files:
|
||||
system: master-system
|
||||
region: [europe]
|
||||
required: false
|
||||
mode: standalone
|
||||
sha256: "477617917a12a30f9f43844909dc2de6e6a617430f5c9a36306c86414a670d50"
|
||||
validation: [sha256]
|
||||
description: "Master System BIOS (Europe), same binary as US"
|
||||
@@ -159,6 +166,7 @@ files:
|
||||
system: mega-cd
|
||||
region: [north-america]
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "fb477cdbf94c84424c2feca4fe40656d85393fe7b7b401911b45ad2eb991258c"
|
||||
validation: [sha256]
|
||||
description: "Mega CD / Sega CD BIOS (US)"
|
||||
@@ -168,6 +176,7 @@ files:
|
||||
system: mega-cd
|
||||
region: [japan]
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "7133fc2dd2fe5b7d0acd53a5f10f3d00b5d31270239ad20d74ef32393e24af88"
|
||||
validation: [sha256]
|
||||
description: "Mega CD BIOS (Japan)"
|
||||
@@ -177,6 +186,7 @@ files:
|
||||
system: mega-cd
|
||||
region: [europe]
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "fe608a2a07676a23ab5fd5eee2f53c9e2526d69a28aa16ccd85c0ec42e6933cb"
|
||||
validation: [sha256]
|
||||
description: "Mega CD BIOS (Europe)"
|
||||
@@ -187,6 +197,7 @@ files:
|
||||
system: mega-ld
|
||||
region: [north-america]
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "e89b5a319f66406611ec82fe5c4aa6827c175a05135bd7bd177366cba0465021"
|
||||
validation: [sha256]
|
||||
description: "LaserActive SEGA PAC BIOS (US), v1.04"
|
||||
@@ -196,6 +207,7 @@ files:
|
||||
system: mega-ld
|
||||
region: [japan]
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "dca942d977217f703d8d1c6eb1aeb6b32c78ecc421486bbb46c459d385161c94"
|
||||
validation: [sha256]
|
||||
description: "LaserActive SEGA PAC BIOS (Japan), v1.02"
|
||||
@@ -205,6 +217,7 @@ files:
|
||||
- name: MSX.ROM
|
||||
system: msx
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "413a2b601a94b3792e054be2439cc77a1819cceadbfa9542f88d51c7480f2ef0"
|
||||
validation: [sha256]
|
||||
description: "MSX BIOS ROM (Japan)"
|
||||
@@ -214,6 +227,7 @@ files:
|
||||
- name: MSX2.ROM
|
||||
system: msx2
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "0c672d86ead61a97f49a583b88b7c1905da120645cd44f0c9f2baf4f4631e0b1"
|
||||
validation: [sha256]
|
||||
description: "MSX2 main BIOS ROM (Japan)"
|
||||
@@ -222,6 +236,7 @@ files:
|
||||
- name: MSX2EXT.ROM
|
||||
system: msx2
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "6c6f421a10c428d960b7ecc990f99af1c638147f747bddca7b0bf0e2ab738300"
|
||||
validation: [sha256]
|
||||
description: "MSX2 sub ROM (Japan)"
|
||||
@@ -231,6 +246,7 @@ files:
|
||||
- name: neo-epo.bin
|
||||
system: neo-geo
|
||||
required: true
|
||||
mode: standalone
|
||||
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"
|
||||
|
||||
@@ -238,6 +254,7 @@ files:
|
||||
- name: sp-45.sp1
|
||||
system: neo-geo
|
||||
required: true
|
||||
mode: standalone
|
||||
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"
|
||||
|
||||
@@ -245,6 +262,7 @@ files:
|
||||
- name: ngp_bios.rom
|
||||
system: neo-geo-pocket
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "0293555b21c4fac516d25199df7809b26beeae150e1d4504a050db32264a6ad7"
|
||||
validation: [sha256]
|
||||
description: "Neo Geo Pocket BIOS"
|
||||
@@ -254,6 +272,7 @@ files:
|
||||
- name: ngpc_bios.rom
|
||||
system: neo-geo-pocket-color
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "8fb845a2f71514cec20728e2f0fecfade69444f8d50898b92c2259f1ba63e10d"
|
||||
validation: [sha256]
|
||||
description: "Neo Geo Pocket Color BIOS"
|
||||
@@ -264,6 +283,7 @@ files:
|
||||
system: nintendo-64dd
|
||||
region: [japan]
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "806400ec0df94b0755de6c5b8249d6b6a9866124c5ddbdac198bde22499bfb8b"
|
||||
validation: [sha256]
|
||||
description: "Nintendo 64DD IPL ROM (Japan retail)"
|
||||
@@ -273,6 +293,7 @@ files:
|
||||
system: nintendo-64dd
|
||||
region: [north-america]
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "e9fec87a45fba02399e88064b9e2f8cf0f2106e351c58279a87f05da5bc984ad"
|
||||
validation: [sha256]
|
||||
description: "Nintendo 64DD IPL ROM (US dev kit)"
|
||||
@@ -281,6 +302,7 @@ files:
|
||||
- name: 64dd_ipl_DEV.bin
|
||||
system: nintendo-64dd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "9c2962a8b994a29e4cd04b3a6e4ed730a751414655ab6a9799ebf5fc08b79d44"
|
||||
validation: [sha256]
|
||||
description: "Nintendo 64DD IPL ROM (development)"
|
||||
@@ -290,6 +312,7 @@ files:
|
||||
- name: syscard1.pce
|
||||
system: pc-engine-cd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "afe9f27f91ac918348555b86298b4f984643eafa2773196f2c5441ea84f0c3bb"
|
||||
validation: [sha256]
|
||||
description: "PC Engine CD-ROM2 System Card v1.0 (Japan). Also used by PC Engine LD."
|
||||
@@ -298,6 +321,7 @@ files:
|
||||
- name: syscard3.pce
|
||||
system: pc-engine-cd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce"
|
||||
validation: [sha256]
|
||||
description: "Super CD-ROM2 System Card v3.0 (Japan). Also used by SuperGrafx CD as Arcade Card."
|
||||
@@ -306,6 +330,7 @@ files:
|
||||
- name: syscard3u.pce
|
||||
system: pc-engine-cd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "cadac2725711b3c442bcf237b02f5a5210c96f17625c35fa58f009e0ed39e4db"
|
||||
validation: [sha256]
|
||||
description: "TurboGrafx-CD Super System Card v3.0 (US)"
|
||||
@@ -314,6 +339,7 @@ files:
|
||||
- name: games_express.pce
|
||||
system: pc-engine-cd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d"
|
||||
validation: [sha256]
|
||||
description: "Games Express CD Card (Japan). Also used by PC Engine LD."
|
||||
@@ -323,6 +349,7 @@ files:
|
||||
- name: pac-n10.pce
|
||||
system: pc-engine-ld
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "0e87a3385a27b3a4cac51934819b7eefa5b3d690768d2495633838488cd0e2e4"
|
||||
validation: [sha256]
|
||||
description: "NEC PAC-N10 LaserActive module (US)"
|
||||
@@ -331,6 +358,7 @@ files:
|
||||
- name: pac-n1.pce
|
||||
system: pc-engine-ld
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "459325690a458baebd77495c91e37c4dddfdd542ba13a821ce954e5bb245627f"
|
||||
validation: [sha256]
|
||||
description: "NEC PAC-N1 LaserActive module (Japan)"
|
||||
@@ -339,6 +367,7 @@ files:
|
||||
- name: pce-lp1.pce
|
||||
system: pc-engine-ld
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "3f43b3b577117d84002e99cb0baeb97b0d65b1d70b4adadc68817185c6a687f0"
|
||||
validation: [sha256]
|
||||
description: "NEC PCE-LP1 LaserActive module (Japan)"
|
||||
@@ -348,6 +377,7 @@ files:
|
||||
- name: scph5501.bin
|
||||
system: sony-playstation
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "11052b6499e466bbf0a709b1f9cb6834a9418e66680387912451e971cf8a1fef"
|
||||
validation: [sha256]
|
||||
description: "PlayStation BIOS (US)"
|
||||
@@ -356,6 +386,7 @@ files:
|
||||
- name: scph5500.bin
|
||||
system: sony-playstation
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "9c0421858e217805f4abe18698afea8d5aa36ff0727eb8484944e00eb5e7eadb"
|
||||
validation: [sha256]
|
||||
description: "PlayStation BIOS (Japan)"
|
||||
@@ -364,6 +395,7 @@ files:
|
||||
- name: scph5502.bin
|
||||
system: sony-playstation
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "1faaa18fa820a0225e488d9f086296b8e6c46df739666093987ff7d8fd352c09"
|
||||
validation: [sha256]
|
||||
description: "PlayStation BIOS (Europe)"
|
||||
@@ -374,6 +406,7 @@ files:
|
||||
system: saturn
|
||||
region: [north-america]
|
||||
required: true
|
||||
mode: standalone
|
||||
description: "Sega Saturn BIOS (US)"
|
||||
source_ref: "desktop-ui/emulator/saturn.cpp:14"
|
||||
|
||||
@@ -381,6 +414,7 @@ files:
|
||||
system: saturn
|
||||
region: [japan]
|
||||
required: true
|
||||
mode: standalone
|
||||
description: "Sega Saturn BIOS (Japan)"
|
||||
source_ref: "desktop-ui/emulator/saturn.cpp:15"
|
||||
|
||||
@@ -388,6 +422,7 @@ files:
|
||||
system: saturn
|
||||
region: [europe]
|
||||
required: true
|
||||
mode: standalone
|
||||
description: "Sega Saturn BIOS (Europe)"
|
||||
source_ref: "desktop-ui/emulator/saturn.cpp:16"
|
||||
|
||||
@@ -395,6 +430,7 @@ files:
|
||||
- name: SGB1.sfc
|
||||
system: super-famicom
|
||||
required: false
|
||||
mode: standalone
|
||||
category: game_data
|
||||
description: "Super Game Boy cartridge ROM. Loaded via CLI as SFC cartridge alongside GB game."
|
||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||
@@ -402,6 +438,7 @@ files:
|
||||
- name: SGB2.sfc
|
||||
system: super-famicom
|
||||
required: false
|
||||
mode: standalone
|
||||
category: game_data
|
||||
description: "Super Game Boy 2 cartridge ROM. Loaded via CLI as SFC cartridge alongside GB game."
|
||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||
@@ -409,6 +446,7 @@ files:
|
||||
- name: STBIOS.bin
|
||||
system: super-famicom
|
||||
required: false
|
||||
mode: standalone
|
||||
category: game_data
|
||||
description: "SuFami Turbo adapter BIOS. Loaded via CLI as SFC cartridge alongside SuFami games."
|
||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||
@@ -416,6 +454,7 @@ files:
|
||||
- name: BS-X.bin
|
||||
system: super-famicom
|
||||
required: false
|
||||
mode: standalone
|
||||
category: game_data
|
||||
description: "Satellaview BS-X Town cartridge ROM. Loaded via CLI as SFC cartridge for BS games."
|
||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||
@@ -272,8 +272,6 @@ files:
|
||||
standalone_path: resources/GameIndex.yaml
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: 72bfdf91fa11a86e2810a7c167cdcf11e20418a8
|
||||
size: 2690355
|
||||
description: "Game database"
|
||||
note: >-
|
||||
Read from the resources folder, keyed by serial, carrying the per-game
|
||||
@@ -299,8 +297,6 @@ files:
|
||||
standalone_path: resources/RedumpDatabase.yaml
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: bf2c73d668cd671df8adfe2ec3c7074e8ba9176a
|
||||
size: 1917242
|
||||
description: "Disc track hash database"
|
||||
note: >-
|
||||
Loaded on the first hash lookup and used to tell the user whether a dump
|
||||
@@ -312,8 +308,6 @@ files:
|
||||
standalone_path: resources/patches.zip
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: f7d0f1606c87019f00109ebdf0f260299f0a72da
|
||||
size: 2007224
|
||||
description: "Game patch archive"
|
||||
note: >-
|
||||
Per-game pnach entries matched on serial and disc CRC. The patches and
|
||||
@@ -793,8 +787,6 @@ files:
|
||||
standalone_path: resources/sounds/achievements/unlock.wav
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: da328a0a3074ec86fee2d6741bfc2e5a1f68d063
|
||||
size: 202702
|
||||
description: "Achievement unlock sound"
|
||||
source_ref: "pcsx2/Achievements.cpp:69, pcsx2/Achievements.cpp:1278-1284, pcsx2/Achievements.cpp:1581"
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ files:
|
||||
|
||||
- name: "psxonpsp660.bin"
|
||||
description: "PSP embedded PS1 BIOS (region-free override)"
|
||||
region: [world]
|
||||
required: false
|
||||
size: 524288
|
||||
sha1: "96880d1ca92a016ff054be5159bb06fe03cb4e14"
|
||||
@@ -113,6 +114,7 @@ files:
|
||||
|
||||
- name: "ps1_rom.bin"
|
||||
description: "PS3 embedded PS1 BIOS, first 512KB extracted (region-free override)"
|
||||
region: [world]
|
||||
required: false
|
||||
sha1: "c40146361eb8cf670b19fdc9759190257803cab7"
|
||||
md5: "81bbe60ba7a3d1cea1d48c14cbcc647b"
|
||||
@@ -124,6 +126,7 @@ files:
|
||||
|
||||
- name: "openbios.bin"
|
||||
description: "OpenBIOS (open-source PS1 HLE BIOS, region-free override)"
|
||||
region: [world]
|
||||
required: false
|
||||
size: 524288
|
||||
mode: libretro
|
||||
|
||||
+5
-6
@@ -14,14 +14,13 @@ notes: >
|
||||
Doom 3 source port based on dhewm3 (GPL Doom 3 engine). The core accepts
|
||||
.pk4 extensions (retro_get_system_info sets valid_extensions = "pk4").
|
||||
Content is loaded directly from the .pk4 file path - the engine extracts
|
||||
the grandparent directory as fs_basepath (libretro.cpp:2381,2449-2450
|
||||
the grandparent directory as fs_basepath (libretro.cpp:1097,1135-1136
|
||||
via extract_directory called twice on info->path) and expects the standard
|
||||
Doom 3 directory layout with base/ containing pak000.pk4 through pak008.pk4.
|
||||
BASE_GAMEDIR is hardcoded to "base" (Licensee.h:58). One binary serves both
|
||||
games: content in a directory named d3xp, or the doom_game core option,
|
||||
passes "+set fs_game d3xp" (libretro.cpp:1635-1643, 2384-2405) to load the
|
||||
Resurrection of Evil expansion from a d3xp/ subdirectory alongside base/.
|
||||
The filesystem code (FileSystem.cpp:2207)
|
||||
BASE_GAMEDIR is hardcoded to "base" (Licensee.h:58). The boom3_xp variant
|
||||
compiles with _D3XP defined, which passes "+set fs_game d3xp" at startup
|
||||
(libretro.cpp:538-544) to load the Resurrection of Evil expansion from a
|
||||
d3xp/ subdirectory alongside base/. The filesystem code (FileSystem.cpp:2125)
|
||||
scans for .pk4 files in the game directory and loads them in order, with
|
||||
higher-numbered paks overriding lower ones. The core requires OpenGL
|
||||
(RETRO_ENVIRONMENT_SET_HW_RENDER) and XRGB8888 pixel format. All required
|
||||
|
||||
@@ -25,18 +25,21 @@ files:
|
||||
- name: "prod.keys"
|
||||
required: true
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:655-656"
|
||||
|
||||
- name: "title.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:659-660"
|
||||
|
||||
- name: "console.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Console-specific keys (BIS, SD seed)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:661-662"
|
||||
|
||||
@@ -44,6 +47,7 @@ files:
|
||||
required: false
|
||||
path: "switch/"
|
||||
size: 160
|
||||
mode: standalone
|
||||
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
||||
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:274-292"
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ files:
|
||||
size: 2097152
|
||||
crc32: "54c0c963"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:58-65"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -74,6 +75,7 @@ files:
|
||||
crc32: "79434781"
|
||||
required: true
|
||||
aliases: [basic2.rom]
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:78-85"
|
||||
|
||||
- name: os12.rom
|
||||
@@ -82,6 +84,7 @@ files:
|
||||
size: 16384
|
||||
crc32: "3c14fc70"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:436-443"
|
||||
|
||||
- name: dfs09.rom
|
||||
@@ -91,6 +94,7 @@ files:
|
||||
crc32: "3ce609cf"
|
||||
required: false
|
||||
note: "Conditional: only when using 8271 disk interface"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:444-451"
|
||||
|
||||
- name: dfs-2.26.rom
|
||||
@@ -100,6 +104,7 @@ files:
|
||||
crc32: "5ae33e94"
|
||||
required: false
|
||||
note: "Conditional: needed for 1770 DFS or tube processor"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:452-459"
|
||||
|
||||
- name: adfs-1.30.rom
|
||||
@@ -109,6 +114,7 @@ files:
|
||||
crc32: "d3855588"
|
||||
required: false
|
||||
note: "Conditional: needed for ADFS support"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:460-467"
|
||||
|
||||
- name: ADT-1.40.rom
|
||||
@@ -118,6 +124,7 @@ files:
|
||||
crc32: "8314fed0"
|
||||
required: false
|
||||
note: "Optional: fetched separately, silently skipped if absent"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:468-475"
|
||||
|
||||
- name: TUBE110.rom
|
||||
@@ -127,6 +134,7 @@ files:
|
||||
crc32: "9ec2dbd0"
|
||||
required: false
|
||||
note: "Conditional: needed for 6502 tube processor"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:476-483"
|
||||
|
||||
- name: Z80_122.rom
|
||||
@@ -136,6 +144,7 @@ files:
|
||||
crc32: "b5851efc"
|
||||
required: false
|
||||
note: "Conditional: needed for Z80 tube processor"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:484-491"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -147,6 +156,7 @@ files:
|
||||
size: 16384
|
||||
crc32: "79434781"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:90-97"
|
||||
|
||||
- name: os.rom
|
||||
@@ -155,6 +165,7 @@ files:
|
||||
size: 16384
|
||||
crc32: "bf63fb1f"
|
||||
required: true
|
||||
mode: standalone
|
||||
note: >
|
||||
The catalogue accepts five images: 406a42ce, 0886741b, bf63fb1f, 6fea0978
|
||||
and d61135d7. They differ only in the credits text, which lives on the IO
|
||||
@@ -168,6 +179,7 @@ files:
|
||||
crc32: "51523993"
|
||||
required: false
|
||||
note: "Conditional: needed for PRES ADFS"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:98-105"
|
||||
|
||||
- name: ADFS-E00_2.rom
|
||||
@@ -177,6 +189,7 @@ files:
|
||||
crc32: "8d17de0e"
|
||||
required: false
|
||||
note: "Conditional: needed for PRES ADFS"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:106-113"
|
||||
|
||||
- name: adfs.rom
|
||||
@@ -186,6 +199,7 @@ files:
|
||||
crc32: "3289bdc6"
|
||||
required: false
|
||||
note: "Conditional: needed for Acorn ADFS"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:114-121"
|
||||
|
||||
- name: DFS-1770-2.20.rom
|
||||
@@ -195,6 +209,7 @@ files:
|
||||
crc32: "f3dc9bc5"
|
||||
required: false
|
||||
note: "Conditional: needed for DFS disk images"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:122-129"
|
||||
|
||||
- name: ELK130E.rom
|
||||
@@ -204,6 +219,7 @@ files:
|
||||
crc32: "a923368d"
|
||||
required: false
|
||||
note: "Conditional: needed for IDE interface"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:130-137"
|
||||
|
||||
- name: AP6v133.rom
|
||||
@@ -213,6 +229,7 @@ files:
|
||||
crc32: "e0013cfc"
|
||||
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."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:138-145, Machines/Utility/ROMCatalogue.cpp:1484-1499, OSBindings/SDL/main.cpp:756-783"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -224,6 +241,7 @@ files:
|
||||
size: 262144
|
||||
crc32: "c4f0f55f"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:193-200"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -237,6 +255,7 @@ files:
|
||||
crc32: "815752df"
|
||||
required: false
|
||||
note: "Built into binary. Required for CPC 464 model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:261-268, Machines/Utility/ROMLibrary.cpp:14535"
|
||||
|
||||
- name: basic464.rom
|
||||
@@ -246,6 +265,7 @@ files:
|
||||
crc32: "7d9a3bac"
|
||||
required: false
|
||||
note: "Built into binary. Required for CPC 464 model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:269-276, Machines/Utility/ROMLibrary.cpp:14532"
|
||||
|
||||
- name: os664.rom
|
||||
@@ -255,6 +275,7 @@ files:
|
||||
crc32: "3f5a6dc4"
|
||||
required: false
|
||||
note: "Built into binary. Required for CPC 664 model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:277-283, Machines/Utility/ROMLibrary.cpp:14537"
|
||||
|
||||
- name: basic664.rom
|
||||
@@ -264,6 +285,7 @@ files:
|
||||
crc32: "32fee492"
|
||||
required: false
|
||||
note: "Built into binary. Required for CPC 664 model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:284-291, Machines/Utility/ROMLibrary.cpp:14534"
|
||||
|
||||
- name: os6128.rom
|
||||
@@ -273,6 +295,7 @@ files:
|
||||
crc32: "0219bb74"
|
||||
required: false
|
||||
note: "Built into binary. Required for CPC 6128 model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:292-299, Machines/Utility/ROMLibrary.cpp:14536"
|
||||
|
||||
- name: basic6128.rom
|
||||
@@ -282,6 +305,7 @@ files:
|
||||
crc32: "ca6af63d"
|
||||
required: false
|
||||
note: "Built into binary. Required for CPC 6128 model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:300-307, Machines/Utility/ROMLibrary.cpp:14533"
|
||||
|
||||
- name: amsdos.rom
|
||||
@@ -291,6 +315,7 @@ files:
|
||||
crc32: "1fe22ecd"
|
||||
required: false
|
||||
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"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -303,6 +328,7 @@ files:
|
||||
crc32: "ba210588"
|
||||
required: true
|
||||
note: "Required for original Apple II model"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:336-343"
|
||||
|
||||
- name: apple2.rom
|
||||
@@ -312,6 +338,7 @@ files:
|
||||
crc32: "f66f9c26"
|
||||
required: true
|
||||
note: "Required for Apple II+ model"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:328-335"
|
||||
|
||||
- name: apple2eu.rom
|
||||
@@ -321,6 +348,7 @@ files:
|
||||
crc32: "e12be18d"
|
||||
required: true
|
||||
note: "Required for Apple IIe model"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:320-327"
|
||||
|
||||
- name: apple2e.rom
|
||||
@@ -330,6 +358,7 @@ files:
|
||||
crc32: "65989942"
|
||||
required: true
|
||||
note: "Required for Enhanced IIe model"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:312-319"
|
||||
|
||||
- name: apple2-character.rom
|
||||
@@ -339,6 +368,7 @@ files:
|
||||
crc32: "64f415c6"
|
||||
required: true
|
||||
note: "Required for Apple II and II+ models"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:344-351"
|
||||
|
||||
- name: apple2eu-character.rom
|
||||
@@ -348,6 +378,7 @@ files:
|
||||
crc32: "816a86f1"
|
||||
required: true
|
||||
note: "Required for Apple IIe model"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:352-359"
|
||||
|
||||
- name: apple2e-character.rom
|
||||
@@ -357,6 +388,7 @@ files:
|
||||
crc32: "2651014d"
|
||||
required: true
|
||||
note: "Required for Enhanced IIe model. Also used by Apple IIgs."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:360-367"
|
||||
|
||||
- name: scsi.rom
|
||||
@@ -366,6 +398,7 @@ files:
|
||||
crc32: "5aff85d3"
|
||||
required: false
|
||||
note: "Conditional: needed for SCSI controller"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:368-375"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -378,6 +411,7 @@ files:
|
||||
crc32: "42f124b0"
|
||||
required: true
|
||||
note: "Required for ROM01 model"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:380-387"
|
||||
|
||||
- name: apple2gs.rom2
|
||||
@@ -387,6 +421,7 @@ files:
|
||||
crc32: "de7ddf29"
|
||||
required: true
|
||||
note: "Required for ROM03 model (default)"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:388-395"
|
||||
|
||||
- name: apple2gs.chr
|
||||
@@ -395,6 +430,7 @@ files:
|
||||
size: 4096
|
||||
crc32: "91e53cd8"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:396-403"
|
||||
|
||||
- name: "341s0632-2"
|
||||
@@ -403,6 +439,7 @@ files:
|
||||
size: 4096
|
||||
crc32: "e1c11fb0"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:404-411"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -414,6 +451,7 @@ files:
|
||||
size: 196608
|
||||
crc32: "1a586c64"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:416-423"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -425,6 +463,7 @@ files:
|
||||
size: 8192
|
||||
crc32: "3aa93ef3"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:496-503"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -438,6 +477,7 @@ files:
|
||||
crc32: "718d42b1"
|
||||
required: false
|
||||
note: "Conditional: needed when VIC-20 has 1540 drive"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:508-515"
|
||||
|
||||
- name: 1541.bin
|
||||
@@ -447,6 +487,7 @@ files:
|
||||
crc32: "fb760019"
|
||||
required: false
|
||||
note: "Conditional: needed when Plus/4 has 1541 drive"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:516-523"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -460,6 +501,7 @@ files:
|
||||
crc32: "ce7144f6"
|
||||
required: false
|
||||
note: "Conditional: needed for 16-sector disk controller"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:528-535"
|
||||
|
||||
- name: state-machine-16.rom
|
||||
@@ -476,6 +518,7 @@ files:
|
||||
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
|
||||
the same internal table.
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:536-543, Components/DiskII/DiskII.cpp:187-239"
|
||||
|
||||
- name: boot-13.rom
|
||||
@@ -485,6 +528,7 @@ files:
|
||||
crc32: "d34eb2ff"
|
||||
required: false
|
||||
note: "Conditional: needed for 13-sector disk controller"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:544-551"
|
||||
|
||||
- name: state-machine-13.rom
|
||||
@@ -494,6 +538,7 @@ files:
|
||||
crc32: "62e22620"
|
||||
required: false
|
||||
note: "Conditional: needed for 13-sector disk controller"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:552-559"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -507,6 +552,7 @@ files:
|
||||
required: true
|
||||
aliases: ["Exos (198x)(Enterprise).bin"]
|
||||
note: "One EXOS version required. Selected by target or any available."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:600-607"
|
||||
|
||||
- name: exos20.bin
|
||||
@@ -517,6 +563,7 @@ files:
|
||||
required: true
|
||||
aliases: ["Expandible OS v2.0 (1984)(Intelligent Software).bin"]
|
||||
note: "Alternative EXOS version"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:611-618"
|
||||
|
||||
- name: exos21.bin
|
||||
@@ -527,6 +574,7 @@ files:
|
||||
required: true
|
||||
aliases: ["Expandible OS v2.1 (1985)(Intelligent Software).bin"]
|
||||
note: "Alternative EXOS version"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:622-629"
|
||||
|
||||
- name: exos23.bin
|
||||
@@ -537,6 +585,7 @@ files:
|
||||
required: true
|
||||
aliases: ["Expandible OS v2.3 (1987)(Intelligent Software).bin"]
|
||||
note: "Alternative EXOS version"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:633-640"
|
||||
|
||||
- name: basic10.bin
|
||||
@@ -546,6 +595,7 @@ files:
|
||||
crc32: "d62e4fb7"
|
||||
required: true
|
||||
note: "One BASIC version required. v1.0 single-file version."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:645-652"
|
||||
|
||||
- name: "BASIC 1.0 - EPROM 1-2 (198x)(Enterprise).bin"
|
||||
@@ -555,6 +605,7 @@ files:
|
||||
crc32: "37bf48e1"
|
||||
required: false
|
||||
note: "Alternative: both parts needed together as substitute for basic10.bin"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:653-660"
|
||||
|
||||
- name: "BASIC 1.0 - EPROM 2-2 (198x)(Enterprise).bin"
|
||||
@@ -564,6 +615,7 @@ files:
|
||||
crc32: "c5298c79"
|
||||
required: false
|
||||
note: "Alternative: both parts needed together as substitute for basic10.bin"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:661-668"
|
||||
|
||||
- name: basic11.bin
|
||||
@@ -573,6 +625,7 @@ files:
|
||||
crc32: "683cf455"
|
||||
required: true
|
||||
note: "Alternative BASIC version"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:669-676"
|
||||
|
||||
- name: "BASIC 1.1 - EPROM 1.1 (198x)(Enterprise).bin"
|
||||
@@ -582,6 +635,7 @@ files:
|
||||
crc32: "c96b7602"
|
||||
required: false
|
||||
note: "Alternative format of BASIC v1.1 with trailing byte"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:677-684"
|
||||
|
||||
- name: basic21.bin
|
||||
@@ -594,6 +648,7 @@ files:
|
||||
- "BASIC Interpreter v2.1 (1985)(Intelligent Software).bin"
|
||||
- "BASIC Interpreter v2.1 (1985)(Intelligent Software)[a].bin"
|
||||
note: "Alternative BASIC version"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:685-693"
|
||||
|
||||
- name: epdos.bin
|
||||
@@ -604,6 +659,7 @@ files:
|
||||
required: false
|
||||
aliases: ["EPDOS v1.7 (19xx)(Haluska, Laszlo).bin"]
|
||||
note: "Conditional: needed for EPDOS disk interface"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:698-705"
|
||||
|
||||
- name: exdos.bin
|
||||
@@ -614,6 +670,7 @@ files:
|
||||
required: false
|
||||
aliases: ["EX-DOS EPROM (198x)(Enterprise).bin"]
|
||||
note: "Conditional: needed when DOS is set to EXDOS"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:709-716"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -626,6 +683,7 @@ files:
|
||||
crc32: "6d0c8a28"
|
||||
required: true
|
||||
note: "Required for Macintosh 128k model"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:724-731"
|
||||
|
||||
- name: mac512k.rom
|
||||
@@ -635,6 +693,7 @@ files:
|
||||
crc32: "cf759e0d"
|
||||
required: true
|
||||
note: "Required for Macintosh 512k model"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:732-739"
|
||||
|
||||
- name: macplus.rom
|
||||
@@ -649,6 +708,7 @@ files:
|
||||
checksum 4D1F8172 of the third Mac Plus revision. The
|
||||
machine copies whichever is supplied into its 128 KiB ROM without
|
||||
reading the revision.
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:740-751, Machines/Apple/Macintosh/Macintosh.cpp:127-144"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -662,6 +722,7 @@ files:
|
||||
crc32: "0072ed54"
|
||||
required: false
|
||||
note: "Optional. Boots cartridge directly if absent."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:764-771"
|
||||
|
||||
- name: japanese-bios.sms
|
||||
@@ -672,6 +733,7 @@ files:
|
||||
crc32: "48d44a13"
|
||||
required: false
|
||||
note: "Optional. Used for Japan region. Boots without if absent."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:756-763"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -685,6 +747,7 @@ files:
|
||||
crc32: "94ee12f3"
|
||||
required: true
|
||||
note: "Required for MSX1. Region bytes patched in memory if regional BIOS absent."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:777-784"
|
||||
|
||||
- name: msx-japanese.rom
|
||||
@@ -695,6 +758,7 @@ files:
|
||||
crc32: "ee229390"
|
||||
required: false
|
||||
note: "Alternative to msx.rom for Japanese region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:785-792"
|
||||
|
||||
- name: msx-american.rom
|
||||
@@ -704,6 +768,7 @@ files:
|
||||
size: 32768
|
||||
required: false
|
||||
note: "Alternative to msx.rom for American region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:793-800"
|
||||
|
||||
- name: msx-european.rom
|
||||
@@ -713,6 +778,7 @@ files:
|
||||
size: 32768
|
||||
required: false
|
||||
note: "Alternative to msx.rom for European region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:801-808"
|
||||
|
||||
- name: disk.rom
|
||||
@@ -722,6 +788,7 @@ files:
|
||||
crc32: "721f61df"
|
||||
required: false
|
||||
note: "Conditional: needed for disk drive support"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:809-816"
|
||||
|
||||
- name: msx2.rom
|
||||
@@ -732,6 +799,7 @@ files:
|
||||
crc32: "6cdaf3a5"
|
||||
required: true
|
||||
note: "Required for MSX2"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:818-825"
|
||||
|
||||
- name: msx2ext.rom
|
||||
@@ -742,6 +810,7 @@ files:
|
||||
crc32: "66237ecf"
|
||||
required: true
|
||||
note: "Required for MSX2"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:826-833"
|
||||
|
||||
- name: fmpac.rom
|
||||
@@ -751,6 +820,7 @@ files:
|
||||
crc32: "0e84505d"
|
||||
required: false
|
||||
note: "Conditional: needed for MSX-Music support"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:834-841"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -763,6 +833,7 @@ files:
|
||||
crc32: "f18710b4"
|
||||
required: true
|
||||
note: "Required for Oric-1 model"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:870-877"
|
||||
|
||||
- name: basic11.rom
|
||||
@@ -772,6 +843,7 @@ files:
|
||||
crc32: "c3a92bef"
|
||||
required: true
|
||||
note: "Required for Oric Atmos model"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:878-885"
|
||||
|
||||
- name: pravetz.rom
|
||||
@@ -781,6 +853,7 @@ files:
|
||||
crc32: "58079502"
|
||||
required: true
|
||||
note: "Required for Pravetz 8D model"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:886-893"
|
||||
|
||||
- name: colour.rom
|
||||
@@ -790,6 +863,7 @@ files:
|
||||
crc32: "d50fca65"
|
||||
required: false
|
||||
note: "Optional. Enables colour video output."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:846-853"
|
||||
|
||||
- name: ORIC.PAL-PROM-TBP24S10-1ab9b572.bin
|
||||
@@ -799,6 +873,7 @@ files:
|
||||
crc32: "1ab9b572"
|
||||
required: false
|
||||
note: "Optional. Alternative colour ROM."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:854-861"
|
||||
|
||||
- name: PRAVETZ-8D.PAL-PROM-82S129-1576a69b.bin
|
||||
@@ -808,6 +883,7 @@ files:
|
||||
crc32: "1576a69b"
|
||||
required: false
|
||||
note: "Optional. Colour ROM for Pravetz model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:862-869"
|
||||
|
||||
- name: bd500.rom
|
||||
@@ -817,6 +893,7 @@ files:
|
||||
crc32: "61952e34"
|
||||
required: false
|
||||
note: "Conditional: needed for Byte Drive 500 disk interface"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:894-901"
|
||||
|
||||
- name: jasmin.rom
|
||||
@@ -826,6 +903,7 @@ files:
|
||||
crc32: "37220e89"
|
||||
required: false
|
||||
note: "Conditional: needed for Jasmin disk interface"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:902-909"
|
||||
|
||||
- name: microdisc.rom
|
||||
@@ -835,6 +913,7 @@ files:
|
||||
crc32: "a9664a9c"
|
||||
required: false
|
||||
note: "Conditional: needed for Microdisc disk interface"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:910-917"
|
||||
|
||||
- name: 8dos.rom
|
||||
@@ -844,6 +923,7 @@ files:
|
||||
crc32: "49a74c06"
|
||||
required: false
|
||||
note: "Conditional: needed for Pravetz 8-DOS disk interface"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:918-925"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -856,6 +936,7 @@ files:
|
||||
crc32: "9576944c"
|
||||
required: true
|
||||
note: "Required for XT-class emulation"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:930-937"
|
||||
|
||||
- name: GLaTICK_0.8.5_AT.ROM
|
||||
@@ -865,6 +946,7 @@ files:
|
||||
crc32: "371ea3f1"
|
||||
required: false
|
||||
note: "XT-class RTC option ROM"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:938-945"
|
||||
|
||||
- name: "Phoenix 80286 ROM BIOS Version 3.05.bin"
|
||||
@@ -874,6 +956,7 @@ files:
|
||||
crc32: "8d0d318a"
|
||||
required: true
|
||||
note: "Required for AT-class emulation (alternative to IBM AT BIOS)"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:946-953"
|
||||
|
||||
- name: at-bios.bin
|
||||
@@ -883,6 +966,7 @@ files:
|
||||
crc32: "674426be"
|
||||
required: true
|
||||
note: "AT-class: single-file IBM AT BIOS (alternative to split U27/U47)"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:954-961"
|
||||
|
||||
- name: BIOS_5170_15NOV85_U27_61X9266_27256.BIN
|
||||
@@ -892,6 +976,7 @@ files:
|
||||
crc32: "4995be7a"
|
||||
required: false
|
||||
note: "AT-class: split BIOS, used with U47 chip"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:962-969"
|
||||
|
||||
- name: BIOS_5170_15NOV85_U47_61X9265_27256.BIN
|
||||
@@ -901,6 +986,7 @@ files:
|
||||
crc32: "c32713e4"
|
||||
required: false
|
||||
note: "AT-class: split BIOS, used with U27 chip"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:970-977"
|
||||
|
||||
- name: CGA.F08
|
||||
@@ -910,6 +996,7 @@ files:
|
||||
crc32: "a362ffe6"
|
||||
required: true
|
||||
note: "Required for CGA video adapter"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:978-985"
|
||||
|
||||
- name: EUMDA9.F14
|
||||
@@ -919,6 +1006,7 @@ files:
|
||||
crc32: "7754882a"
|
||||
required: true
|
||||
note: "Required for MDA video adapter"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:986-993"
|
||||
|
||||
- name: ibm_6277356_ega_card_u44_27128.bin
|
||||
@@ -928,6 +1016,7 @@ files:
|
||||
crc32: "2f2fbc40"
|
||||
required: false
|
||||
note: "Needed for EGA video adapter"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:994-1001"
|
||||
|
||||
- name: ibm_vga.bin
|
||||
@@ -937,6 +1026,7 @@ files:
|
||||
crc32: "03b3f90d"
|
||||
required: false
|
||||
note: "Needed for VGA video adapter"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1002-1009"
|
||||
|
||||
- name: ibm-basic-1.10.rom
|
||||
@@ -946,6 +1036,7 @@ files:
|
||||
crc32: "ebacb791"
|
||||
required: false
|
||||
note: "Required for AT-class with IBM BIOS (not needed with Phoenix)"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1010-1017"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -957,6 +1048,7 @@ files:
|
||||
size: 16384
|
||||
crc32: "74eaae87"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1046-1053"
|
||||
|
||||
- name: kernal.318004-05.bin
|
||||
@@ -966,6 +1058,7 @@ files:
|
||||
crc32: "71c07bd4"
|
||||
required: true
|
||||
note: "Default kernel version requested by machine code"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1038-1045"
|
||||
|
||||
- name: kernal.318004-03.bin
|
||||
@@ -975,6 +1068,7 @@ files:
|
||||
crc32: "77bab934"
|
||||
required: false
|
||||
note: "Alternative kernel version (in catalogue but not default)"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1022-1029"
|
||||
|
||||
- name: kernal.318004-04.bin
|
||||
@@ -984,6 +1078,7 @@ files:
|
||||
crc32: "be54ed79"
|
||||
required: false
|
||||
note: "Alternative kernel version (in catalogue but not default)"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1030-1037"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -995,6 +1090,7 @@ files:
|
||||
size: 16384
|
||||
crc32: "f62d2f3a"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1157-1164"
|
||||
|
||||
- name: mo5-v1.1.rom
|
||||
@@ -1003,6 +1099,7 @@ files:
|
||||
size: 16384
|
||||
crc32: "237c60bf"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1165-1172"
|
||||
|
||||
- name: mo6-v1.rom
|
||||
@@ -1011,6 +1108,7 @@ files:
|
||||
size: 65536
|
||||
crc32: "45e859c6"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1173-1180"
|
||||
|
||||
- name: mo6-v2.rom
|
||||
@@ -1019,6 +1117,7 @@ files:
|
||||
size: 65536
|
||||
crc32: "b4430a3b"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1181-1188"
|
||||
|
||||
- name: mo6-v3.rom
|
||||
@@ -1027,6 +1126,7 @@ files:
|
||||
size: 65536
|
||||
crc32: "7a012322"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1189-1196"
|
||||
|
||||
- name: pc128.rom
|
||||
@@ -1035,6 +1135,7 @@ files:
|
||||
size: 65536
|
||||
crc32: "fc173504"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1197-1204"
|
||||
|
||||
- name: cd90-640.rom
|
||||
@@ -1044,6 +1145,7 @@ files:
|
||||
crc32: "5114c0a5"
|
||||
required: false
|
||||
note: "Requested on top of the machine ROM when a disk is attached."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1206-1213"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -1055,6 +1157,7 @@ files:
|
||||
size: 8192
|
||||
crc32: "db4c43c1"
|
||||
required: true
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1218-1225"
|
||||
|
||||
- name: characters-english.bin
|
||||
@@ -1065,6 +1168,7 @@ files:
|
||||
crc32: "83e032a6"
|
||||
required: true
|
||||
note: "Required for English region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1226-1233"
|
||||
|
||||
- name: kernel-pal.bin
|
||||
@@ -1075,6 +1179,7 @@ files:
|
||||
crc32: "4be07cb4"
|
||||
required: true
|
||||
note: "Required for English PAL region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1234-1241"
|
||||
|
||||
- name: kernel-ntsc.bin
|
||||
@@ -1085,6 +1190,7 @@ files:
|
||||
crc32: "e5e7c174"
|
||||
required: true
|
||||
note: "Required for English NTSC region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1242-1249"
|
||||
|
||||
- name: characters-danish.bin
|
||||
@@ -1095,6 +1201,7 @@ files:
|
||||
crc32: "7fc11454"
|
||||
required: false
|
||||
note: "Required for Danish region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1250-1257"
|
||||
|
||||
- name: kernel-danish.bin
|
||||
@@ -1105,6 +1212,7 @@ files:
|
||||
crc32: "02adaf16"
|
||||
required: false
|
||||
note: "Required for Danish region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1258-1265"
|
||||
|
||||
- name: characters-japanese.bin
|
||||
@@ -1115,6 +1223,7 @@ files:
|
||||
crc32: "fcfd8a4b"
|
||||
required: false
|
||||
note: "Required for Japanese region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1266-1273"
|
||||
|
||||
- name: kernel-japanese.bin
|
||||
@@ -1125,6 +1234,7 @@ files:
|
||||
crc32: "336900d7"
|
||||
required: false
|
||||
note: "Required for Japanese region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1274-1281"
|
||||
|
||||
- name: characters-swedish.bin
|
||||
@@ -1135,6 +1245,7 @@ files:
|
||||
crc32: "d808551d"
|
||||
required: false
|
||||
note: "Required for Swedish region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1282-1289"
|
||||
|
||||
- name: kernel-swedish.bin
|
||||
@@ -1145,6 +1256,7 @@ files:
|
||||
crc32: "b2a60662"
|
||||
required: false
|
||||
note: "Required for Swedish region"
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1290-1297"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -1158,6 +1270,7 @@ files:
|
||||
crc32: "ddee531f"
|
||||
required: false
|
||||
note: "Built into binary. Required for 16K/48K model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1302-1309, Machines/Utility/ROMLibrary.cpp:14539"
|
||||
|
||||
- name: 128.rom
|
||||
@@ -1167,6 +1280,7 @@ files:
|
||||
crc32: "2cbe8995"
|
||||
required: false
|
||||
note: "Built into binary. Required for 128K model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1310-1317, Machines/Utility/ROMLibrary.cpp:14540"
|
||||
|
||||
- name: plus2.rom
|
||||
@@ -1176,6 +1290,7 @@ files:
|
||||
crc32: "e7a517dc"
|
||||
required: false
|
||||
note: "Built into binary. Required for +2 model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1318-1325, Machines/Utility/ROMLibrary.cpp:14541"
|
||||
|
||||
- name: plus3.rom
|
||||
@@ -1185,6 +1300,7 @@ files:
|
||||
crc32: "96e3c17a"
|
||||
required: false
|
||||
note: "Built into binary. Required for +2a/+3 model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1326-1336, Machines/Utility/ROMLibrary.cpp:14542"
|
||||
|
||||
# -------------------------------------------------------
|
||||
@@ -1198,6 +1314,7 @@ files:
|
||||
crc32: "4c7fc597"
|
||||
required: false
|
||||
note: "Built into binary. Required for ZX80 model."
|
||||
mode: standalone
|
||||
source_ref: "Machines/Utility/ROMCatalogue.cpp:1341-1348, Machines/Utility/ROMLibrary.cpp:14544"
|
||||
|
||||
- name: zx81.rom
|
||||
@@ -1207,4 +1324,5 @@ files:
|
||||
crc32: "4b1dd6eb"
|
||||
required: false
|
||||
note: "Built into binary. Required for ZX81 model."
|
||||
mode: standalone
|
||||
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[7] (upper ROM slot 7, 16KB): romdisc_bin (AMSDOS)
|
||||
|
||||
Called from HardResetCPC() (crocods-core/platform.c:1024-1035) using
|
||||
Called from HardResetCPC() (crocods-core/platform.c:1009-1020) using
|
||||
the extern byte arrays directly. No filesystem ROM loading exists in
|
||||
the libretro path.
|
||||
|
||||
A Multiface ROM loader exists (crocods-core/multiface.c:53-64) that
|
||||
A Multiface ROM loader exists (crocods-core/multiface.c:51-61) that
|
||||
reads LocRomMulti via fopen(), but InitMultiface() is never called
|
||||
from the libretro build or platform.c. Dead code in libretro context.
|
||||
|
||||
@@ -45,7 +45,7 @@ files: []
|
||||
|
||||
platform_details:
|
||||
bios_mapping:
|
||||
source_ref: "crocods-core/vga.c:185-200, crocods-core/platform.c:1024-1035"
|
||||
source_ref: "crocods-core/vga.c:185-200, crocods-core/platform.c:1009-1020"
|
||||
note: |
|
||||
All ROMs are embedded. InitMemCPC() copies cpc6128_bin and romdisc_bin
|
||||
into ROMINF and ROMEXT arrays via memcpy. No filesystem lookup occurs.
|
||||
|
||||
@@ -148,7 +148,7 @@ files:
|
||||
required: false
|
||||
hle_fallback: true
|
||||
note: "BootMii NAND backup. Can be imported to populate Wii NAND with channels, saves, system menu"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:27-45,47-92"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:26-39,42-89"
|
||||
|
||||
- name: "keys.bin"
|
||||
path: null
|
||||
@@ -156,7 +156,7 @@ files:
|
||||
required: false
|
||||
hle_fallback: true
|
||||
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:77-91, Source/Core/DiscIO/NANDImporter.h:35-38"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:20, Source/Core/DiscIO/NANDImporter.cpp:82-94, Source/Core/DiscIO/NANDImporter.h:46"
|
||||
|
||||
# -- Wii SD Card Image --
|
||||
- name: "WiiSD.raw"
|
||||
@@ -189,21 +189,21 @@ files:
|
||||
required: false
|
||||
hle_fallback: true
|
||||
note: "SSL client certificate. Auto-extracted from IOS13 content during NAND import"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:220-304"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:239-323"
|
||||
|
||||
- name: "clientcakey.pem"
|
||||
path: "Wii/clientcakey.pem"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
note: "SSL client private key. Auto-extracted from IOS13 content during NAND import"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:256"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:275"
|
||||
|
||||
- name: "rootca.pem"
|
||||
path: "Wii/rootca.pem"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
note: "SSL root CA certificate. Auto-extracted from IOS13 content during NAND import"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:257"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:276"
|
||||
|
||||
# -- Realtek Bluetooth firmware (Wiimote passthrough) --
|
||||
# Required for real Wiimote connectivity via USB Bluetooth adapters with Realtek chipsets.
|
||||
|
||||
@@ -28,24 +28,28 @@ files:
|
||||
- name: "prod.keys"
|
||||
required: true
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:574-575, 853"
|
||||
|
||||
- name: "dev.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
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"
|
||||
|
||||
- name: "title.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:577-578, 850"
|
||||
|
||||
- name: "console.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Console-specific keys (BIS, SD seed)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:579-580"
|
||||
|
||||
@@ -53,6 +57,7 @@ files:
|
||||
required: false
|
||||
path: "switch/"
|
||||
size: 160
|
||||
mode: standalone
|
||||
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
||||
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:281-302"
|
||||
|
||||
|
||||
@@ -289,8 +289,6 @@ files:
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: 2d9878fdd70979ecf6dd0da7652efd3797c26039
|
||||
size: 2697551
|
||||
category: game_data
|
||||
description: "per-title compatibility and fix database"
|
||||
note: >-
|
||||
@@ -305,8 +303,6 @@ files:
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: dceb4536fba7fb89452864fc512a4d071a19eae0
|
||||
size: 1906463
|
||||
category: game_data
|
||||
description: "disc hash database"
|
||||
note: >-
|
||||
@@ -319,8 +315,6 @@ files:
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: a3d939af27fe0929e61b8e20d49ef58316c0a66c
|
||||
size: 2091144
|
||||
category: game_data
|
||||
description: "built-in game patch archive"
|
||||
note: >-
|
||||
@@ -560,8 +554,6 @@ files:
|
||||
|
||||
- name: unlock.wav
|
||||
path: resources/sounds/achievements/unlock.wav
|
||||
sha1: da328a0a3074ec86fee2d6741bfc2e5a1f68d063
|
||||
size: 202702
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
description: "achievement unlock sound"
|
||||
|
||||
@@ -79,6 +79,7 @@ files:
|
||||
description: "Sega CD BIOS (US)"
|
||||
region: [north-america]
|
||||
required: true
|
||||
mode: standalone
|
||||
size: 131072
|
||||
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)"
|
||||
@@ -89,6 +90,7 @@ files:
|
||||
description: "Mega CD BIOS (Europe)"
|
||||
region: [europe]
|
||||
required: true
|
||||
mode: standalone
|
||||
size: 131072
|
||||
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"
|
||||
@@ -98,6 +100,7 @@ files:
|
||||
description: "Mega CD BIOS (Japan)"
|
||||
region: [japan]
|
||||
required: true
|
||||
mode: standalone
|
||||
size: 131072
|
||||
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"
|
||||
@@ -109,6 +112,7 @@ files:
|
||||
system: nintendo-gba
|
||||
description: "Game Boy Advance BIOS"
|
||||
required: true
|
||||
mode: standalone
|
||||
size: 16384
|
||||
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)"
|
||||
@@ -122,6 +126,7 @@ files:
|
||||
system: nintendo-snes
|
||||
description: "NEC uPD77C25 firmware for DSP-1"
|
||||
required: true
|
||||
mode: standalone
|
||||
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"
|
||||
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."
|
||||
@@ -131,6 +136,7 @@ files:
|
||||
system: nintendo-snes
|
||||
description: "NEC uPD77C25 firmware for DSP-2"
|
||||
required: true
|
||||
mode: standalone
|
||||
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"
|
||||
|
||||
@@ -139,6 +145,7 @@ files:
|
||||
system: nintendo-snes
|
||||
description: "NEC uPD77C25 firmware for DSP-3"
|
||||
required: true
|
||||
mode: standalone
|
||||
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"
|
||||
|
||||
@@ -147,6 +154,7 @@ files:
|
||||
system: nintendo-snes
|
||||
description: "NEC uPD77C25 firmware for DSP-4"
|
||||
required: true
|
||||
mode: standalone
|
||||
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"
|
||||
|
||||
@@ -155,6 +163,7 @@ files:
|
||||
system: nintendo-snes
|
||||
description: "NEC uPD96050 firmware for ST010"
|
||||
required: true
|
||||
mode: standalone
|
||||
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"
|
||||
note: "0xC000 program plus 0x1000 data. Used by F1 ROC II."
|
||||
@@ -164,6 +173,7 @@ files:
|
||||
system: nintendo-snes
|
||||
description: "NEC uPD96050 firmware for ST011"
|
||||
required: true
|
||||
mode: standalone
|
||||
size: 53248
|
||||
source_ref: "backend/snes-core/src/memory/cartridge.rs:298-300 (MissingSt011Rom), :938-943 (Hayazashi Nidan Morita Shougi CRC32 match)"
|
||||
|
||||
@@ -172,6 +182,7 @@ files:
|
||||
system: nintendo-snes
|
||||
description: "ARM firmware for ST018"
|
||||
required: true
|
||||
mode: standalone
|
||||
min_size: 163840
|
||||
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)"
|
||||
@@ -184,6 +195,7 @@ files:
|
||||
system: sega-mastersystem
|
||||
description: "Master System BIOS"
|
||||
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"
|
||||
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."
|
||||
|
||||
@@ -191,6 +203,7 @@ files:
|
||||
system: sega-gamegear
|
||||
description: "Game Gear BIOS"
|
||||
required: false
|
||||
mode: standalone
|
||||
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."
|
||||
|
||||
@@ -201,6 +214,7 @@ files:
|
||||
system: nintendo-gb
|
||||
description: "Game Boy boot ROM"
|
||||
required: false
|
||||
mode: standalone
|
||||
size: 256
|
||||
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)"
|
||||
@@ -210,6 +224,7 @@ files:
|
||||
system: nintendo-gbc
|
||||
description: "Game Boy Color boot ROM"
|
||||
required: false
|
||||
mode: standalone
|
||||
min_size: 2048
|
||||
max_size: 2304
|
||||
validation: [size]
|
||||
@@ -224,6 +239,7 @@ files:
|
||||
system: nintendo-nes
|
||||
description: "External NES color palette"
|
||||
required: false
|
||||
mode: standalone
|
||||
min_size: 192
|
||||
max_size: 1536
|
||||
validation: [size]
|
||||
|
||||
@@ -61,7 +61,7 @@ notes: |
|
||||
the JVM default soundbank in place. VlcDir points at a VLC installation that
|
||||
vlcj scans for libvlc.
|
||||
ref: src/media/emulator/media/EmulatorMIDI.java:26-36,
|
||||
src/main/emulator/ui/swt/Property.java:1863-1904
|
||||
src/main/emulator/ui/swt/Property.java:1860-1901
|
||||
|
||||
files:
|
||||
- name: M7_EmuSmw7.dll
|
||||
|
||||
@@ -2,10 +2,9 @@ emulator: Kronos
|
||||
type: libretro
|
||||
core_classification: official_port
|
||||
source: "https://github.com/libretro/yabause"
|
||||
source_branch: kronos
|
||||
upstream: "https://github.com/FCare/Kronos"
|
||||
profiled_date: "2026-03-24"
|
||||
source_commit: "3791ffb2221fda61d70aec5cca376418caf05669"
|
||||
source_commit: "8926b0c6c347f8c5c755911ddb0ac695420ffbf8"
|
||||
core_version: "v2.6.0"
|
||||
display_name: "Sega - Saturn/ST-V (Kronos)"
|
||||
cores:
|
||||
@@ -245,7 +244,7 @@ files:
|
||||
size: 2097152
|
||||
validation: [size]
|
||||
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/sys/memory/src/cs0.c:1526-1535"
|
||||
source_ref: "yabause/src/utils/src/db.c:43-44, yabause/src/cs0.c:1207-1216"
|
||||
|
||||
- name: "mpr-19367-mx.ic1"
|
||||
system: sega-saturn
|
||||
@@ -253,4 +252,4 @@ files:
|
||||
size: 2097152
|
||||
validation: [size]
|
||||
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/sys/memory/src/cs0.c:1526-1535"
|
||||
source_ref: "yabause/src/utils/src/db.c:45, yabause/src/cs0.c:1207-1216"
|
||||
+5
-5
@@ -36,7 +36,7 @@ files:
|
||||
min_size: 4194304
|
||||
max_size: 8388608
|
||||
validation: [size]
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:33-34,63-180,249-274,286-348"
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:231-254,267-322"
|
||||
note: >
|
||||
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
|
||||
@@ -47,7 +47,7 @@ files:
|
||||
required: false
|
||||
max_size: 4194304
|
||||
description: "DVD player ROM"
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:189-229,339"
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:190-211,314"
|
||||
note: >
|
||||
DVD player ROM. Tries <biospath>.rom1 (appended) then <biosbase>.rom1
|
||||
(extension replaced). Silently skipped if not found.
|
||||
@@ -57,7 +57,7 @@ files:
|
||||
required: false
|
||||
max_size: 524288
|
||||
description: "Chinese ROM extension"
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:189-229,340"
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:190-211,315"
|
||||
note: >
|
||||
Chinese region ROM extension. Same naming convention as rom1.
|
||||
Only present on Chinese region consoles.
|
||||
@@ -68,7 +68,7 @@ files:
|
||||
hle_fallback: true
|
||||
size: 1024
|
||||
description: "NVRAM / EEPROM data"
|
||||
source_ref: "pcsx2/CDVD/CDVD.cpp:196-227"
|
||||
source_ref: "pcsx2/CDVD/CDVD.cpp:224-267"
|
||||
note: >
|
||||
Console EEPROM data (language, timezone, region, iLink ID, OSD settings).
|
||||
Path derived from BIOS path with extension replaced to .nvm.
|
||||
@@ -80,7 +80,7 @@ files:
|
||||
hle_fallback: true
|
||||
size: 4
|
||||
description: "Mechacon version"
|
||||
source_ref: "pcsx2/CDVD/CDVD.cpp:229-245"
|
||||
source_ref: "pcsx2/CDVD/CDVD.cpp:255-266"
|
||||
note: >
|
||||
Mechacon (disc drive controller) version as u32.
|
||||
Auto-created with default 0x00020603 if missing.
|
||||
|
||||
+1047
-1154
File diff suppressed because it is too large.
Load diff
+430
-443
File diff suppressed because it is too large.
Load diff
@@ -680,7 +680,7 @@ files:
|
||||
required: true
|
||||
category: bios_zip
|
||||
system: casio-loopy
|
||||
source_ref: "src/mame/casio/casloopy.cpp:2097-2105"
|
||||
source_ref: "src/mame/casio/casloopy.cpp:2105-2462"
|
||||
contents:
|
||||
- name: hd6437021.lsi302
|
||||
description: SH-1 CPU internal mask ROM
|
||||
|
||||
@@ -292,7 +292,7 @@ files:
|
||||
- name: "scph5502.bin"
|
||||
system: sony-playstation
|
||||
description: "SCPH-5502 / v3.0E BIOS"
|
||||
region: [europe, oceania]
|
||||
region: [europe]
|
||||
required: true
|
||||
size: 524288
|
||||
sha256: "1faaa18fa820a0225e488d9f086296b8e6c46df739666093987ff7d8fd352c09"
|
||||
@@ -376,7 +376,7 @@ files:
|
||||
- name: "epr-17952a.ic8"
|
||||
system: sega-stv
|
||||
description: "ST-V BIOS (North America)"
|
||||
region: [north-america, south-korea, asia-pal, brazil, latin-america]
|
||||
region: [north-america]
|
||||
required: true
|
||||
size: 524288
|
||||
validation: [size]
|
||||
|
||||
@@ -23,24 +23,28 @@ notes: >
|
||||
files:
|
||||
- name: "syscard3.pce"
|
||||
description: "CD-ROM System Card 3.0 (Japan)"
|
||||
region: [japan]
|
||||
required: true
|
||||
source_ref: "libretro.c:65, libretro.c:1896-1901"
|
||||
note: "Default value for pce_fast_cdbios option."
|
||||
|
||||
- name: "syscard2.pce"
|
||||
description: "CD-ROM System Card 2.0 (Japan)"
|
||||
region: [japan]
|
||||
required: false
|
||||
source_ref: "libretro.c:1903"
|
||||
note: "pce_fast_cdbios = 'System Card 2'."
|
||||
|
||||
- name: "syscard1.pce"
|
||||
description: "CD-ROM System Card 1.0 (Japan)"
|
||||
region: [japan]
|
||||
required: false
|
||||
source_ref: "libretro.c:1905"
|
||||
note: "pce_fast_cdbios = 'System Card 1'."
|
||||
|
||||
- name: "gexpress.pce"
|
||||
description: "Games Express CD Card"
|
||||
region: [japan]
|
||||
required: false
|
||||
aliases: ["gecard.pce"]
|
||||
source_ref: "libretro.c:1906-1907"
|
||||
@@ -48,12 +52,14 @@ files:
|
||||
|
||||
- name: "syscard3u.pce"
|
||||
description: "CD-ROM System Card 3.0 (US / TurboGrafx-CD)"
|
||||
region: [north-america]
|
||||
required: false
|
||||
source_ref: "libretro.c:1908-1909"
|
||||
note: "pce_fast_cdbios = 'System Card 3 US'. Not in .info."
|
||||
|
||||
- name: "syscard2u.pce"
|
||||
description: "CD-ROM System Card 2.0 (US / TurboGrafx-CD)"
|
||||
region: [north-america]
|
||||
required: false
|
||||
source_ref: "libretro.c:1910-1911"
|
||||
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
|
||||
|
||||
The libretro port resolves BIOS filenames in retro_load_game()
|
||||
(src/platform/libretro/libretro.c:2060-2118). It picks the filename
|
||||
(src/platform/libretro/libretro.c:2091-2148). It picks the filename
|
||||
based on detected model:
|
||||
GBA -> gba_bios.bin
|
||||
GB (DMG/MGB) -> gb_bios.bin
|
||||
@@ -38,7 +38,7 @@ notes: |
|
||||
SGB -> sgb_bios.bin
|
||||
SGB2 -> gb_bios.bin (falls to default, missing case)
|
||||
|
||||
The standalone core (src/gb/core.c:649-696) correctly maps SGB2 to
|
||||
The standalone core (src/gb/core.c:637-684) correctly maps SGB2 to
|
||||
sgb_bios.bin (marked TODO for a dedicated file). The libretro port
|
||||
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)
|
||||
validation: [size]
|
||||
note: "GBA BIOS. HLE replacement built in. Real BIOS adds boot logo and full SWI accuracy."
|
||||
source_ref: "src/platform/libretro/libretro.c:2068"
|
||||
source_ref: "src/platform/libretro/libretro.c:2099"
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Game Boy - boot ROM (optional, skip fallback)
|
||||
@@ -68,7 +68,7 @@ files:
|
||||
size: 256 # 256 B (0x100)
|
||||
validation: [crc32]
|
||||
note: "DMG/MGB boot ROM. Scrolling Nintendo logo. Skipped if absent."
|
||||
source_ref: "src/platform/libretro/libretro.c:2104"
|
||||
source_ref: "src/platform/libretro/libretro.c:971"
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Game Boy Color - boot ROM (optional, skip fallback)
|
||||
@@ -79,7 +79,7 @@ files:
|
||||
size: 2304 # 2304 B (0x900)
|
||||
validation: [crc32]
|
||||
note: "CGB boot ROM. Used for CGB, AGB (GBA in GB mode), and SCGB models."
|
||||
source_ref: "src/platform/libretro/libretro.c:2097"
|
||||
source_ref: "src/platform/libretro/libretro.c:2127"
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Super Game Boy - boot ROM (optional, skip fallback)
|
||||
@@ -89,14 +89,14 @@ files:
|
||||
required: false
|
||||
size: 256 # 256 B (0x100)
|
||||
validation: [crc32]
|
||||
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:2100"
|
||||
note: "SGB boot ROM. SGB2 maps here in standalone (src/gb/core.c:643) but falls to gb_bios.bin in libretro."
|
||||
source_ref: "src/platform/libretro/libretro.c:2130"
|
||||
|
||||
platform_details:
|
||||
gba:
|
||||
bios_size: 16384 # 16 KB
|
||||
hle_bios: true
|
||||
source_ref: "src/gba/hle-bios.c, src/gba/gba.c:513-552"
|
||||
source_ref: "src/gba/hle-bios.c, src/gba/gba.c:520-551"
|
||||
note: |
|
||||
GBALoadBIOS() validates size (must be exactly 0x4000) then computes
|
||||
an internal checksum. Two official checksums are recognized:
|
||||
@@ -125,7 +125,7 @@ platform_details:
|
||||
sgb:
|
||||
bios_size: 256 # 256 B
|
||||
hle_bios: false
|
||||
source_ref: "src/gb/gb.c:570-605, src/gb/core.c:654-656"
|
||||
source_ref: "src/gb/gb.c:570-605, src/gb/core.c:642-644"
|
||||
note: |
|
||||
Accepted SGB CRC32 checksums:
|
||||
SGB = 0xEC8A83B9, SGB2 = 0x53D0DD63
|
||||
|
||||
@@ -98,7 +98,7 @@ notes: |
|
||||
MSX.emu/src/blueMSX/Memory/romMapperDRAM.c:88-99), and the Philips FDC, Panasonic
|
||||
FDC and MoonSound mappers test nothing at all
|
||||
(MSX.emu/src/blueMSX/Memory/romMapperPhilipsFdc.c:139-146,
|
||||
MSX.emu/src/blueMSX/Memory/romMapperTC8566AF.c:228-255,
|
||||
MSX.emu/src/blueMSX/Memory/romMapperTC8566AF.c:236-262,
|
||||
MSX.emu/src/blueMSX/Memory/romMapperMoonsound.c:129-158).
|
||||
|
||||
The ROM database is compiled in. mediaDbGuessRom matches a cartridge's SHA-1 against
|
||||
@@ -396,7 +396,7 @@ files:
|
||||
required: true
|
||||
size: 16384
|
||||
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:228-255"
|
||||
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"
|
||||
|
||||
- name: "MSXTR.rom"
|
||||
path: "Machines/Shared Roms/MSXTR.rom"
|
||||
@@ -436,7 +436,7 @@ files:
|
||||
required: true
|
||||
size: 65536
|
||||
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:228-255"
|
||||
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"
|
||||
|
||||
- name: "coleco.rom"
|
||||
path: "Machines/COL - ColecoVision/coleco.rom"
|
||||
|
||||
@@ -151,18 +151,16 @@ files:
|
||||
source_ref: "cbus/ideio.c:1928-1930, cbus/ideio.c:1944-1981"
|
||||
|
||||
# -- SCSI BIOS ROM --
|
||||
# Read into a 0x4000 buffer; any non-empty read is kept. Falls back to the
|
||||
# scsibios[] stub.
|
||||
# Loaded by scsiio.c:219. 16 KB (0x4000). Falls back to scsibios[] stub.
|
||||
- name: "scsi.rom"
|
||||
path: "np2kai/scsi.rom"
|
||||
max_size: 16384
|
||||
size: 16384
|
||||
required: false
|
||||
hle_fallback: true
|
||||
note: >
|
||||
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 controller BIOS ROM (16 KB). The core includes a built-in
|
||||
SCSI BIOS stub as fallback.
|
||||
source_ref: "cbus/scsiio.c:720-732"
|
||||
source_ref: "cbus/scsiio.c:214-231"
|
||||
|
||||
# -- SASI BIOS ROM --
|
||||
# Loaded by sasiio.c:455. 4 KB (0x1000). Falls back to sasibios[] stub.
|
||||
|
||||
+16
-26
@@ -25,7 +25,7 @@ files:
|
||||
min_size: 4194304
|
||||
max_size: 8388608
|
||||
validation: [size]
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:16-17,80-197,258-283,317-370"
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:258-362"
|
||||
note: "Accepts any file 4-8 MB with valid romdir (RESET + ROMVER). Naming convention ps2-VVVVr-YYYYMMDD.bin (version, region, date)."
|
||||
|
||||
- name: ps2-0230a-20080220.rom1
|
||||
@@ -48,7 +48,7 @@ files:
|
||||
size: 1024
|
||||
description: "Console NVRAM"
|
||||
note: "Read at the BIOS path with the extension replaced by nvm, opened case-insensitively. Built in memory from region defaults when absent or blank."
|
||||
source_ref: "pcsx2/CDVD/CDVD.cpp:165-189"
|
||||
source_ref: "pcsx2/CDVD/CDVD.cpp:160-188"
|
||||
|
||||
- name: ps2-0230a-20080220.mec
|
||||
required: false
|
||||
@@ -66,32 +66,22 @@ files:
|
||||
note: "DEV9 network adapter EEPROM. Falls back to built-in defaults if missing."
|
||||
|
||||
- name: GameIndex.yaml
|
||||
path: resources/GameIndex.yaml
|
||||
path: pcsx2/resources/GameIndex.yaml
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: 5e2d50a3376dc6a0750505bec68626d39a5bd4c1
|
||||
size: 2683897
|
||||
category: game_data
|
||||
description: "per-title compatibility and fix database"
|
||||
source_ref: "pcsx2/GameDatabase.cpp:39, pcsx2/GameDatabase.cpp:1026-1035"
|
||||
note: "Read from the resources directory shipped with the application. Console error and no per-title fixes if missing."
|
||||
mode: libretro
|
||||
source_ref: "pcsx2/GameDatabase.cpp:46, pcsx2/GameDatabase.cpp:896"
|
||||
note: "Game compatibility database. OSD warning if missing."
|
||||
|
||||
- name: RedumpDatabase.yaml
|
||||
path: resources/RedumpDatabase.yaml
|
||||
- name: cheats_ws.zip
|
||||
path: pcsx2/resources/cheats_ws.zip
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: bf2c73d668cd671df8adfe2ec3c7074e8ba9176a
|
||||
size: 1917242
|
||||
category: game_data
|
||||
description: "disc hash database"
|
||||
source_ref: "pcsx2/GameDatabase.cpp:1133, pcsx2/GameDatabase.cpp:1188-1197"
|
||||
note: "Read from the resources directory shipped with the application, used to name a disc from its dump."
|
||||
mode: libretro
|
||||
source_ref: "pcsx2/VMManager.cpp:348-361"
|
||||
note: "Widescreen patches archive."
|
||||
|
||||
- name: patches.zip
|
||||
path: resources/patches.zip
|
||||
- name: cheats_ni.zip
|
||||
path: pcsx2/resources/cheats_ni.zip
|
||||
required: false
|
||||
bundled: true
|
||||
category: game_data
|
||||
description: "built-in game patch archive"
|
||||
source_ref: "pcsx2/Patch.cpp:117, pcsx2/Patch.cpp:289-318"
|
||||
note: "Opened read only from the resources directory shipped with the application and searched for the pnach matching the running serial and CRC. Widescreen and no-interlacing patches live inside it."
|
||||
mode: libretro
|
||||
source_ref: "pcsx2/VMManager.cpp:383-396"
|
||||
note: "No-interlacing patches archive."
|
||||
@@ -59,14 +59,14 @@ files:
|
||||
# -- Fallback BIOS filenames (searched in order after scph550x) --
|
||||
|
||||
- name: "psxonpsp660.bin"
|
||||
description: "PSP embedded PS1 BIOS, filed as the Japanese BIOS"
|
||||
region: [japan]
|
||||
description: "PSP embedded PS1 BIOS (region detected at runtime)"
|
||||
region: [world]
|
||||
size: 524288
|
||||
required: false
|
||||
hle_fallback: true
|
||||
validation: [size]
|
||||
source_ref: "frontend/libretro.c:3864-3867 (listed_bios[3]), frontend/libretro.c:3654-3669 (try_use_bios region guess)"
|
||||
note: "The core reads the region out of the image: bytes 0x7ff51 are ' J' in this dump, so it takes the JP slot (' E' takes EU, anything else US), one image per slot, first found kept. The disc licence picks the slot at boot (Config.Bios[Config.PsxRegion]); a US or EU disc only reaches this file when its own slot is empty. Must be 512 KB."
|
||||
source_ref: "frontend/libretro.c:3864-3867 (listed_bios[3])"
|
||||
note: "Region detected from content at runtime. Must be 512 KB in libretro context."
|
||||
|
||||
- name: "scph101.bin"
|
||||
description: "SCPH-101 (v4.4 03-24-00 A) - PSone US"
|
||||
|
||||
@@ -84,7 +84,7 @@ platform_details:
|
||||
size: 4096
|
||||
|
||||
load_priority:
|
||||
source_ref: "source/PokeMini.c:500-509"
|
||||
source_ref: "source/PokeMini.c:515-523"
|
||||
note: |
|
||||
During PokeMini_Reset(), the core first reloads FreeBIOS as a
|
||||
safe baseline, then attempts to load bios.min from disk. If
|
||||
|
||||
@@ -37,6 +37,7 @@ files:
|
||||
region: [north-america]
|
||||
size: 2097152
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
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"
|
||||
@@ -46,6 +47,7 @@ files:
|
||||
region: [europe]
|
||||
size: 2097152
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "GameCube PAL boot ROM"
|
||||
source_ref: "Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:184"
|
||||
@@ -55,6 +57,7 @@ files:
|
||||
region: [japan]
|
||||
size: 2097152
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "GameCube NTSC-J boot ROM. JAP is the legacy directory name"
|
||||
source_ref: "Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:186"
|
||||
@@ -67,6 +70,7 @@ files:
|
||||
path: "GC/dsp_rom.bin"
|
||||
size: 8192
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
validation: [size]
|
||||
known_hash_adler32: "0x66f334fe"
|
||||
@@ -78,6 +82,7 @@ files:
|
||||
path: "GC/dsp_coef.bin"
|
||||
size: 4096
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
validation: [size]
|
||||
known_hash_adler32: "0xf3b93527"
|
||||
@@ -93,6 +98,7 @@ files:
|
||||
path: "GC/font_western.bin"
|
||||
size: 9589
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
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"
|
||||
@@ -101,6 +107,7 @@ files:
|
||||
path: "GC/font_japanese.bin"
|
||||
size: 303693
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
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"
|
||||
@@ -111,6 +118,7 @@ files:
|
||||
- name: "gba_bios.bin"
|
||||
path: "GBA/gba_bios.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
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"
|
||||
@@ -119,6 +127,7 @@ files:
|
||||
- name: "SYSCONF"
|
||||
path: "Wii/shared2/sys/SYSCONF"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
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"
|
||||
@@ -127,6 +136,7 @@ files:
|
||||
path: "Wii/title/00000001/00000002/data/setting.txt"
|
||||
size: 256
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
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"
|
||||
@@ -135,6 +145,7 @@ files:
|
||||
- name: "nand.bin"
|
||||
path: null
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
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"
|
||||
@@ -143,6 +154,7 @@ files:
|
||||
path: null
|
||||
size: 1024
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
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"
|
||||
@@ -151,6 +163,7 @@ files:
|
||||
- name: "WiiSD.raw"
|
||||
path: "Load/WiiSD.raw"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "Virtual SD card image for Wii homebrew. Auto-created, supports SD/SDHC up to 4GB"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:149"
|
||||
@@ -159,6 +172,7 @@ files:
|
||||
- name: "codehandler.bin"
|
||||
path: null
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
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"
|
||||
@@ -167,6 +181,7 @@ files:
|
||||
- name: "Wii System Menu"
|
||||
path: null
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
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"
|
||||
@@ -175,6 +190,7 @@ files:
|
||||
- name: "clientca.pem"
|
||||
path: "Wii/clientca.pem"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "SSL client certificate. Auto-extracted from IOS13 content during NAND import"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:201-285"
|
||||
@@ -182,6 +198,7 @@ files:
|
||||
- name: "clientcakey.pem"
|
||||
path: "Wii/clientcakey.pem"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "SSL client private key. Auto-extracted from IOS13 content during NAND import"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:237"
|
||||
@@ -189,6 +206,7 @@ files:
|
||||
- name: "rootca.pem"
|
||||
path: "Wii/rootca.pem"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "SSL root CA certificate. Auto-extracted from IOS13 content during NAND import"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:238"
|
||||
@@ -202,90 +220,105 @@ files:
|
||||
- name: "rtl8723a_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8723a_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8723A BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:379"
|
||||
|
||||
- name: "rtl8723b_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8723b_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8723B BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:516-519"
|
||||
|
||||
- name: "rtl8723d_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8723d_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8723D BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:383"
|
||||
|
||||
- name: "rtl8761a_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8761a_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8761A BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:385"
|
||||
|
||||
- name: "rtl8761bu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8761bu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8761BU BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:387"
|
||||
|
||||
- name: "rtl8821a_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8821a_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8821A BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:389"
|
||||
|
||||
- name: "rtl8821c_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8821c_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8821C BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:391"
|
||||
|
||||
- name: "rtl8822b_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8822b_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8822B BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:395"
|
||||
|
||||
- name: "rtl8822cu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8822cu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8822CU BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:393"
|
||||
|
||||
- name: "rtl8851bu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8851bu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8851BU BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:397"
|
||||
|
||||
- name: "rtl8852au_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8852au_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8852AU BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:399"
|
||||
|
||||
- name: "rtl8852bu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8852bu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8852BU BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:401"
|
||||
|
||||
- name: "rtl8852cu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8852cu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8852CU BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:403"
|
||||
|
||||
- name: "rtl8852btu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8852btu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8852BT/8852BE-VT BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:405"
|
||||
|
||||
- name: "rtl8922au_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8922au_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8922AU BT firmware for Wiimote passthrough"
|
||||
source_ref: "Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:407"
|
||||
|
||||
|
||||
@@ -21,18 +21,21 @@ files:
|
||||
- name: "prod.keys"
|
||||
required: true
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Switch production keys for NCA decryption"
|
||||
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:239"
|
||||
|
||||
- name: "title.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Per-title decryption keys, needed for specific game content"
|
||||
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:240"
|
||||
|
||||
- name: "console.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Console-specific keys for encrypted content"
|
||||
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:241"
|
||||
|
||||
|
||||
@@ -26,18 +26,21 @@ files:
|
||||
- name: "prod.keys"
|
||||
required: true
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:654"
|
||||
|
||||
- name: "title.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:657"
|
||||
|
||||
- name: "console.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Console-specific keys (BIS, SD seed)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:658"
|
||||
|
||||
@@ -45,6 +48,7 @@ files:
|
||||
required: false
|
||||
path: "switch/"
|
||||
size: 160
|
||||
mode: standalone
|
||||
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
||||
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294"
|
||||
|
||||
|
||||
@@ -27,18 +27,21 @@ files:
|
||||
- name: "prod.keys"
|
||||
required: true
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:655-656"
|
||||
|
||||
- name: "title.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:659-660"
|
||||
|
||||
- name: "console.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Console-specific keys (BIS, SD seed)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:661-662"
|
||||
|
||||
@@ -46,6 +49,7 @@ files:
|
||||
required: false
|
||||
path: "switch/"
|
||||
size: 160
|
||||
mode: standalone
|
||||
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
||||
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294"
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ param(
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "df0adf1fd5088ef9509429fb1d5720e5ca5e7bf46aff9ad0021851dce7349107"
|
||||
$defaultInstallSha256 = "42b1747ae867d41da39b11daf783c0106daa6ed18861a2214c196f3a5c9a3c60"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
$temporary = $null
|
||||
|
||||
+11
-49
@@ -103,36 +103,8 @@ DEFAULT_DESTS = {
|
||||
"rocknix": Path("/storage/roms/bios"),
|
||||
"misterfpga": Path("/media/fat/games"),
|
||||
"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's installer defaults to C:\RetroBat (installer.iss).
|
||||
"retrobat": Path("C:/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
|
||||
# the pool: every future is submitted before the first one completes.
|
||||
_stop = threading.Event()
|
||||
@@ -1295,7 +1267,7 @@ def download_files(
|
||||
def do_standalone_copies(
|
||||
manifest: dict, bios_path: Path, os_type: str,
|
||||
extra_dirs: dict[str, Path] | None = None,
|
||||
) -> tuple[int, int, int]:
|
||||
) -> tuple[int, int]:
|
||||
"""Copy BIOS files to standalone emulator directories.
|
||||
|
||||
Supports:
|
||||
@@ -1308,19 +1280,16 @@ def do_standalone_copies(
|
||||
default per-OS locations, for setups such as LaunchBox that keep their
|
||||
emulators outside them. The layout below the root is the same.
|
||||
|
||||
Returns (copied, skipped, failed): skipped is a target directory that
|
||||
does not exist, failed a copy the system refused (permission, disk
|
||||
full, read-only mount), which used to be counted as "dir not found".
|
||||
Returns (copied_count, skipped_count).
|
||||
"""
|
||||
from fnmatch import fnmatch
|
||||
|
||||
copies = manifest.get("standalone_copies", [])
|
||||
if not copies:
|
||||
return 0, 0, 0
|
||||
return 0, 0
|
||||
|
||||
copied = 0
|
||||
skipped = 0
|
||||
failed = 0
|
||||
|
||||
for entry in copies:
|
||||
# Note entries: print message if emulator detected
|
||||
@@ -1379,11 +1348,10 @@ def do_standalone_copies(
|
||||
try:
|
||||
shutil.copy2(src, dest)
|
||||
copied += 1
|
||||
except OSError as exc:
|
||||
print(f" ERROR: {dest}: {exc.strerror or exc}")
|
||||
failed += 1
|
||||
except OSError:
|
||||
skipped += 1
|
||||
|
||||
return copied, skipped, failed
|
||||
return copied, skipped
|
||||
|
||||
|
||||
def format_size(n: int) -> str:
|
||||
@@ -1417,7 +1385,7 @@ def _prompt_manual_platform(os_type: str) -> list[tuple[str, Path]]:
|
||||
plat = AVAILABLE_PLATFORMS[int(choice) - 1]
|
||||
elif choice in AVAILABLE_PLATFORMS:
|
||||
plat = choice
|
||||
default_dest = _default_dest(os_type, plat)
|
||||
default_dest = DEFAULT_DESTS.get(plat, Path.home() / "bios")
|
||||
try:
|
||||
raw = input(f"BIOS directory [{default_dest}]: ").strip()
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
@@ -1437,9 +1405,7 @@ def _default_dest(os_type: str, platform_name: str) -> Path:
|
||||
"""
|
||||
if os_type == "android" and platform_name == "retroarch":
|
||||
return _android_storage_root() / "RetroArch" / "system"
|
||||
return _os_default_dests(os_type).get(
|
||||
platform_name, DEFAULT_DESTS.get(platform_name, Path.home() / "bios")
|
||||
)
|
||||
return DEFAULT_DESTS.get(platform_name, Path.home() / "bios")
|
||||
|
||||
|
||||
def _manual_usage_hint(os_type: str) -> list[str]:
|
||||
@@ -1743,15 +1709,11 @@ def main() -> None:
|
||||
print("\nStandalone emulators:")
|
||||
lb_root = launchbox_root(os_type)
|
||||
extra_dirs = launchbox_bios_dirs(lb_root) if lb_root else None
|
||||
copied, skipped, copy_failed = do_standalone_copies(
|
||||
copied, skipped = do_standalone_copies(
|
||||
manifest, bios_path, os_type, extra_dirs
|
||||
)
|
||||
if copied or skipped or copy_failed:
|
||||
print(
|
||||
f" {copied} copied, {skipped} skipped (dir not found), "
|
||||
f"{copy_failed} failed"
|
||||
)
|
||||
total_errors += copy_failed
|
||||
if copied or skipped:
|
||||
print(f" {copied} copied, {skipped} skipped (dir not found)")
|
||||
elif manifest.get("standalone_copies") and not args.check:
|
||||
print(
|
||||
"\nStandalone copies skipped "
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
||||
TEMP_INSTALLER=""
|
||||
TEMP_DIRECTORY=""
|
||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
DEFAULT_INSTALL_SHA256="df0adf1fd5088ef9509429fb1d5720e5ca5e7bf46aff9ad0021851dce7349107"
|
||||
DEFAULT_INSTALL_SHA256="42b1747ae867d41da39b11daf783c0106daa6ed18861a2214c196f3a5c9a3c60"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
+207
-27
@@ -5,7 +5,7 @@
|
||||
"platform": "batocera",
|
||||
"display_name": "Batocera",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T03:26:07Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -237,10 +237,10 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 12808,
|
||||
"total_size": 8059515793,
|
||||
"pack_files": 12810,
|
||||
"pack_size": 8059515793,
|
||||
"total_files": 12826,
|
||||
"total_size": 8065364335,
|
||||
"pack_files": 12828,
|
||||
"pack_size": 8065364335,
|
||||
"total_omitted": 30,
|
||||
"omitted_files": [
|
||||
{
|
||||
@@ -3467,6 +3467,36 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32.rom",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32ext.rom",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
"sha256": "d964826432559994ffe329f643fd4e42dbbff313362d9434945725d7119cae87",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtv.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
"sha256": "6e48bb768539e8dae352553e5fdfa860682edaf9e26b105fadd8e26103171765",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-130-cdtv.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtvext.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -40939,20 +40969,20 @@
|
||||
},
|
||||
{
|
||||
"dest": "switch/prod.keys",
|
||||
"sha1": "c146b651696d25287241f4bab18226e488bc45b3",
|
||||
"sha256": "039895c6e93a0c6208c3af27da8c3fc408083873a96f2320dc334a6fa252c112",
|
||||
"size": 16025,
|
||||
"repo_path": "bios/Nintendo/Switch/prod.keys",
|
||||
"sha1": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
||||
"sha256": "76df55f85c6628cd924f227a72a301bbb731833c0a0f00c440e01e6b34480990",
|
||||
"size": 15039,
|
||||
"repo_path": "bios/Arcade/Arcade/switch/prod.keys",
|
||||
"cores": [
|
||||
"citron"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "switch/title.keys",
|
||||
"sha1": "bfee8e708dc4c8f27bfe51a12d57caaa0b7f74de",
|
||||
"sha256": "3ed66f7e82df79922739c6c2d34de90bc36a48d13acdff24f9d51013bd8c0677",
|
||||
"size": 1224,
|
||||
"repo_path": "bios/Nintendo/Switch/title.keys",
|
||||
"sha1": "8e6c2873d671f482d3fe0f7b96e18e0f84d02c10",
|
||||
"sha256": "dc6e31aae8daa9067dafcf2bca6d9d3aa691a63474eb698ad8c54be3e360753b",
|
||||
"size": 19584,
|
||||
"repo_path": "bios/Arcade/Arcade/switch/title.keys",
|
||||
"cores": [
|
||||
"citron"
|
||||
]
|
||||
@@ -40987,6 +41017,16 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Acorn/basic.rom",
|
||||
"sha1": "4a7393f3a45ea309f744441c16723e2ef447a281",
|
||||
"sha256": "45bd55dc0f6f0f8f1fe9e2481de7def206565eec8f600ba3068b849ca4132079",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Acorn/BBC Micro/basic2.rom",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "BBCMicro/dfs09.rom",
|
||||
"sha1": "5cc0f14b8f46855c70eaa653cca4ad079b458732",
|
||||
@@ -41567,6 +41607,16 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/MSX.ROM",
|
||||
"sha1": "409e82adac40f6bdd18eb6c84e8b2fbdc7fb5498",
|
||||
"sha256": "999564a371dd2fdf7fbe8d853e82a68d557c27b7d87417639b2fa17704b83f78",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/msx-japanese.rom",
|
||||
"sha1": "302afb5d8be26c758309ca3df611ae69cced2821",
|
||||
@@ -41607,6 +41657,26 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/MSX2.ROM",
|
||||
"sha1": "6103b39f1e38d1aa2d84b1c3219c44f1abb5436e",
|
||||
"sha256": "4bc4ae85ca5f28246cd3e7b7e017d298ddd375603657f84ef2c7954bc2d9b919",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX2.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/MSX2EXT.ROM",
|
||||
"sha1": "5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02",
|
||||
"sha256": "6c6f421a10c428d960b7ecc990f99af1c638147f747bddca7b0bf0e2ab738300",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX2EXT.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/fmpac.rom",
|
||||
"sha1": "9d789166e3caf28e4742fe933d962e99618c633d",
|
||||
@@ -84703,6 +84773,36 @@
|
||||
"mesen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dmg_boot.bin",
|
||||
"sha1": "1db57a1e8b6e4096f811587f9eab0c6675fd9755",
|
||||
"sha256": "6f64da4cecd7e54e2f928eb3e3ba7810a7a567d0d247cc71737d1771e073a916",
|
||||
"size": 256,
|
||||
"repo_path": "bios/Nintendo/Game Boy/dmg_boot.bin",
|
||||
"cores": [
|
||||
"mesen-s"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cgb_boot.bin",
|
||||
"sha1": "80e3ee82416c1a74827a5459fde32f04bf51a1f6",
|
||||
"sha256": "119470a9cf9f0d85f0c7ba5a6cadffeec07eb9862719086d1a9808189605e388",
|
||||
"size": 2304,
|
||||
"repo_path": "bios/Nintendo/Game Boy Color/cgb_boot.bin",
|
||||
"cores": [
|
||||
"mesen-s"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Mupen64plus/IPL.n64",
|
||||
"sha1": "bf861922dcb78c316360e3e742f4f70ff63c9bc3",
|
||||
"sha256": "806400ec0df94b0755de6c5b8249d6b6a9866124c5ddbdac198bde22499bfb8b",
|
||||
"size": 4194304,
|
||||
"repo_path": "bios/Nintendo/64DD/64DD_IPL_v12_JPN.bin",
|
||||
"cores": [
|
||||
"mupen64plus_next"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Mupen64plus/font.ttf",
|
||||
"sha1": "1d7f6fa0c34e3d50be52c16a0816bebac309fe41",
|
||||
@@ -85525,10 +85625,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "np2kai/scsi.rom",
|
||||
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
@@ -89473,6 +89573,86 @@
|
||||
"px68k"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88.rom",
|
||||
"sha1": "3518193b8207bdebf22c1380c2db8c554baff329",
|
||||
"sha256": "d76f9118f9994eb391a0dc9c1fbfbfa2e9bc584a65da75f3ba7065d3c03cab76",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/NEC/PC-98/n88.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_0.rom",
|
||||
"sha1": "d1ae642aed4f0584eeb81ff50180db694e5101d4",
|
||||
"sha256": "8b584cd9143a670ddfce657ca65067c86f6be1db757a3085f48b1479ac6e8cf3",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_0.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_1.rom",
|
||||
"sha1": "4ae4d37409ff99411a623da9f6a44192170a854e",
|
||||
"sha256": "810936574cdf68849822a1570046091f0bad0590dee3635dbc89a4cc4e099555",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_1.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_2.rom",
|
||||
"sha1": "e94278682ef9e9bbb82201f72c50382748dcea2a",
|
||||
"sha256": "7d361b1bfd8523cd580bd9ca73902154556fbf45f60f0f539beef397d9462646",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_2.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_3.rom",
|
||||
"sha1": "af452aed79b072c4d17985830b7c5dca64d4b412",
|
||||
"sha256": "3dc431af6e0c724582a3aa49e5c22cb9cc99d8298a4cce500e62bbaf1bc4bffb",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_3.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88n.rom",
|
||||
"sha1": "063609dd518c124a4fc9ba35d1bae35771666a34",
|
||||
"sha256": "4db0b25bf25fd063b08fde9e76d7504440cd322a8ab6b72c4ee7d8ea98ce0839",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/NEC/PC-98/n88n.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88sub.rom",
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-98/N88SUB.ROM",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88knj1.rom",
|
||||
"sha1": "82e11a177af6a5091dd67f50a2f4bafda84d6556",
|
||||
"sha256": "7608040cffb1951e5cc567abb63f75b5746777a1ba96196c1b75606b793bb4bb",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/NEC/PC-88/KANJI1.ROM",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88knj2.rom",
|
||||
"sha1": "b86b932cb4ce34472e0fe0f01a4a4350e15dcb53",
|
||||
@@ -89493,6 +89673,16 @@
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font2.rom",
|
||||
"sha1": "0a056b6d28b4b9e29a2d255bdaa25c24f176cf1e",
|
||||
@@ -126021,16 +126211,6 @@
|
||||
"fbneo"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Acorn/basic.rom",
|
||||
"sha1": "4a7393f3a45ea309f744441c16723e2ef447a281",
|
||||
"sha256": "45bd55dc0f6f0f8f1fe9e2481de7def206565eec8f600ba3068b849ca4132079",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Acorn/BBC Micro/basic2.rom",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Triforce/segaboot.gcm",
|
||||
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||
|
||||
+245
-1555
File diff suppressed because it is too large.
Load diff
+37
-527
@@ -5,7 +5,7 @@
|
||||
"platform": "emudeck",
|
||||
"display_name": "EmuDeck",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T03:26:07Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -250,10 +250,10 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 618,
|
||||
"total_size": 3472900342,
|
||||
"pack_files": 620,
|
||||
"pack_size": 3472900342,
|
||||
"total_files": 569,
|
||||
"total_size": 3452229095,
|
||||
"pack_files": 571,
|
||||
"pack_size": 3452229095,
|
||||
"total_omitted": 0,
|
||||
"omitted_files": [],
|
||||
"files": [
|
||||
@@ -331,18 +331,18 @@
|
||||
},
|
||||
{
|
||||
"dest": "disksys.rom",
|
||||
"sha1": "af5af53f66982e749643fdf8b2acbb7d4d3ed229",
|
||||
"sha256": "fdc1a76e654feea993fcb38366e05ee5f4eb641f86fe6bebaeefd412e112dd72",
|
||||
"sha1": "57fe1bdee955bb48d357e463ccbf129496930b62",
|
||||
"sha256": "99c18490ed9002d9c6d999b9d8d15be5c051bdfa7cc7e73318053c9a994b0178",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/Nintendo/Famicom Disk System/.variants/disksys.rom.af5af53f",
|
||||
"repo_path": "bios/Arcade/FBNeo/fdsbios.nes",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "prod.keys",
|
||||
"sha1": "c146b651696d25287241f4bab18226e488bc45b3",
|
||||
"sha256": "039895c6e93a0c6208c3af27da8c3fc408083873a96f2320dc334a6fa252c112",
|
||||
"size": 16025,
|
||||
"repo_path": "bios/Nintendo/Switch/prod.keys",
|
||||
"sha1": "d6cedce971a2a4ab2588e39285a9d2fb37b62924",
|
||||
"sha256": "76df55f85c6628cd924f227a72a301bbb731833c0a0f00c440e01e6b34480990",
|
||||
"size": 15039,
|
||||
"repo_path": "bios/Arcade/Arcade/switch/prod.keys",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -387,26 +387,26 @@
|
||||
},
|
||||
{
|
||||
"dest": "bios_CD_E.bin",
|
||||
"sha1": "7063192ae9f6b696c5b81bc8f0a9fe6f0c400e58",
|
||||
"sha256": "fe608a2a07676a23ab5fd5eee2f53c9e2526d69a28aa16ccd85c0ec42e6933cb",
|
||||
"sha1": "f891e0ea651e2232af0c5c4cb46a0cae2ee8f356",
|
||||
"sha256": "16735df92fcc1d0fc6a1eced244867e95281c5006340926082b74e363e073dc4",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Sega/Mega CD/eu_mcd2_9303.bin",
|
||||
"repo_path": "bios/Sega/Mega CD/MCD_eu_100.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "bios_CD_U.bin",
|
||||
"sha1": "5a8c4b91d3034c1448aac4b5dc9a6484fce51636",
|
||||
"sha256": "fb477cdbf94c84424c2feca4fe40656d85393fe7b7b401911b45ad2eb991258c",
|
||||
"sha1": "f4f315adcef9b8feb0364c21ab7f0eaf5457f3ed",
|
||||
"sha256": "807384678ef259494d943eddb6954dfc7069693d0dde0c424e2028488d88e81a",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Sega/Mega CD/SegaCDBIOS9303.bin",
|
||||
"repo_path": "bios/Sega/Mega CD/bios_CD_U.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "bios_CD_J.bin",
|
||||
"sha1": "d203cfe22c03ae479dd8ca33840cf8d9776eb3ff",
|
||||
"sha256": "7133fc2dd2fe5b7d0acd53a5f10f3d00b5d31270239ad20d74ef32393e24af88",
|
||||
"sha1": "4846f448160059a7da0215a5df12ca160f26dd69",
|
||||
"sha256": "923eb9da740f98ab1572085e3d351e2c0982df0906b0b99efb68a9997c94cf8f",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Sega/Mega CD/jp_mcd2_921222.bin",
|
||||
"repo_path": "bios/Sega/Mega CD/MCD_jp_100p.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -529,316 +529,6 @@
|
||||
"repo_path": "bios/Microsoft/Xbox/Complex_4627.bin",
|
||||
"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",
|
||||
"sha1": "60022d6d026c8df950722478aab25702a0dfa067",
|
||||
@@ -1009,36 +699,6 @@
|
||||
"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",
|
||||
"sha1": "f4f683a49d7eb4155566f793f2c1c27e90159992",
|
||||
@@ -2182,13 +1842,13 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "switch/dev.keys",
|
||||
"sha1": "fc916ff462b842983d4061d09230163ac4b38f16",
|
||||
"sha256": "72685b5ce6c7ea3e18e3e0d98be1a99de8197299ee15429687b27df614ec92b9",
|
||||
"size": 781,
|
||||
"repo_path": "bios/Other/eden/switch/dev.keys",
|
||||
"dest": "dc/dc_nvmem.bin",
|
||||
"sha1": "72c65f2a11ee7b72b77b9945187b7a332632e14a",
|
||||
"sha256": "15a9bc3aa59fa998c03bfe0b1cf6cb1958c1660e8e72b3d7195b653f44038407",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Sega/Dreamcast/dc_nvmem.bin",
|
||||
"cores": [
|
||||
"eden"
|
||||
"flycast"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3891,6 +3551,16 @@
|
||||
"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",
|
||||
"sha1": "4ed31ec6b0b175bb109c0eb5fd3d193da823339f",
|
||||
@@ -3961,156 +3631,6 @@
|
||||
"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",
|
||||
"sha1": "093f8698b54b78dcb701de2043f82639de51d63b",
|
||||
@@ -5047,16 +4567,6 @@
|
||||
"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",
|
||||
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
||||
|
||||
+91
-33
@@ -5,7 +5,7 @@
|
||||
"platform": "lakka",
|
||||
"display_name": "Lakka",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T03:26:07Z",
|
||||
"base_destination": "system",
|
||||
"detect": [
|
||||
{
|
||||
@@ -16,10 +16,10 @@
|
||||
}
|
||||
],
|
||||
"standalone_copies": [],
|
||||
"total_files": 5861,
|
||||
"total_size": 6398490526,
|
||||
"pack_files": 8504,
|
||||
"pack_size": 6439023690,
|
||||
"total_files": 5867,
|
||||
"total_size": 6399851574,
|
||||
"pack_files": 8510,
|
||||
"pack_size": 6440384738,
|
||||
"total_omitted": 0,
|
||||
"omitted_files": [],
|
||||
"files": [
|
||||
@@ -961,10 +961,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "galaksija/ROM2.BIN",
|
||||
"sha1": "5d5ab4313a2d0effe7572bb129193b64cab002c1",
|
||||
"sha256": "7d93fe474fcc98c06a6d96e533f8935e4e707edf0792d3e9d679ea68404375de",
|
||||
"size": 4096,
|
||||
"repo_path": "bios/Galaksija/ROM2.BIN",
|
||||
"sha1": "1bae895fbdd658cfb56c53cc2139282cc1e778de",
|
||||
"sha256": "9fc01e6beab84e61e0267fa39e23c649cdf2ccd25ad1cbfc2de0facfd160e723",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/Sony/PlayStation 2/ROM2.BIN",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -1319,6 +1319,14 @@
|
||||
"repo_path": "bios/NEC/PC-98/n88knj2.rom",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88SUB.ROM",
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-98/N88SUB.ROM",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88EXT0.ROM",
|
||||
"sha1": "d239c26ad7ac5efac6e947b0e9549b1534aa970d",
|
||||
@@ -1763,10 +1771,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
||||
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||
"sha256": "49d987ee1eab29a157425b82d54516957a81e1bac247c8834e494642605c3e8c",
|
||||
"sha1": "f4f683a49d7eb4155566f793f2c1c27e90159992",
|
||||
"sha256": "7f33e6600722791ccee5fd15d4330af8d8f74165f7d1ded6738684c0294aaffe",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_rom.bin.3c6cc6e0",
|
||||
"repo_path": "bios/Nintendo/GameCube/Sys/GC/dsp_rom.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -1859,10 +1867,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "BS-X.bin",
|
||||
"sha1": "604556b2e62860af18db5a77f2956ebc75450020",
|
||||
"sha256": "3ce321496edc5d77038de2034eb3fb354d7724afd0bc7fd0319f3eb5d57b984d",
|
||||
"sha1": "4891d739a8a8b67923681bad4fb67edab2e90e50",
|
||||
"sha256": "a49827b45ff9ac9cf5b4658190e1428e59251bc82d8a63d8e9e0f71e439f008f",
|
||||
"size": 1048576,
|
||||
"repo_path": "bios/Nintendo/SNES/Satellaview_BS-X.sfc",
|
||||
"repo_path": "bios/Nintendo/SNES/BS-X.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4107,10 +4115,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||
"sha256": "3bf3c54d6f16d88d3669be698179bb8c2fb981ee5ce71bcb952719429a838cc1",
|
||||
"size": 2693579,
|
||||
"repo_path": "bios/Other/yaps2/GameIndex.yaml",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4261,6 +4269,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32.rom",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32ext.rom",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
@@ -4271,6 +4289,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtv.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
"sha256": "6e48bb768539e8dae352553e5fdfa860682edaf9e26b105fadd8e26103171765",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-130-cdtv.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtvext.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -25511,6 +25539,16 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SufamiTurbo.sfc",
|
||||
"sha1": "ef86ea192eed03d5c413fdbbfd46043be1d7a127",
|
||||
"sha256": "edacb453da14f825f05d1134d6035f4bf034e55f7cfb97c70c4ee107eabc7342",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Nintendo/SuFami Turbo/STBIOS.bin",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "ymf288_adpcm_rom.bin",
|
||||
"sha1": "50b6c3e288eaa12ad275d4f323267bb72b0445df",
|
||||
@@ -25541,6 +25579,26 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Super CD-ROM System (Japan) (v3.0).pce",
|
||||
"sha1": "79f5ff55dd10187c7fd7b8daab0b3ffbd1f56a2c",
|
||||
"sha256": "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/NEC/PC Engine/syscard3.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Games Express CD Card (Japan).pce",
|
||||
"sha1": "014881a959e045e00f4db8f52955200865d40280",
|
||||
"sha256": "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/NEC/PC Engine/gecard.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Satellaview/BSX0120-0.bin",
|
||||
"sha1": "f51c52bb26f2546fb167ab1084a56457475ff0a0",
|
||||
@@ -47433,10 +47491,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "np2kai/scsi.rom",
|
||||
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
@@ -53027,6 +53085,16 @@
|
||||
"qemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font2.rom",
|
||||
"sha1": "0a056b6d28b4b9e29a2d255bdaa25c24f176cf1e",
|
||||
@@ -56529,16 +56597,6 @@
|
||||
"nekop2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "scummvm/extra/MT32_CONTROL.ROM",
|
||||
"sha1": "b083518fffb7f66b03c23b7eb4f868e62dc5a987",
|
||||
|
||||
+103
-13
@@ -5,7 +5,7 @@
|
||||
"platform": "recalbox",
|
||||
"display_name": "Recalbox",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T03:26:07Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -237,10 +237,10 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 3220,
|
||||
"total_size": 4192446979,
|
||||
"pack_files": 3222,
|
||||
"pack_size": 4192446979,
|
||||
"total_files": 3229,
|
||||
"total_size": 4193670403,
|
||||
"pack_files": 3231,
|
||||
"pack_size": 4193670403,
|
||||
"total_omitted": 1,
|
||||
"omitted_files": [
|
||||
{
|
||||
@@ -1815,10 +1815,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "pcenginecd/syscard3.pce",
|
||||
"sha1": "79f5ff55dd10187c7fd7b8daab0b3ffbd1f56a2c",
|
||||
"sha256": "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/NEC/PC Engine/syscard3.pce",
|
||||
"sha1": "1b4c260326d905bc718812dad0f68089977f427b",
|
||||
"sha256": "df4f75feebb95e53dfef72dea0787df743e3474ba046ff5a4cb88e34dda93ff1",
|
||||
"size": 262656,
|
||||
"repo_path": "bios/NEC/PC Engine/.variants/syscard3.pce.1b4c2603",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -3383,6 +3383,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32.rom",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32ext.rom",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
@@ -3393,6 +3403,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtv.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
"sha256": "6e48bb768539e8dae352553e5fdfa860682edaf9e26b105fadd8e26103171765",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-130-cdtv.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtvext.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -4093,6 +4113,16 @@
|
||||
"beetle_saturn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bk/BASIC10.ROM",
|
||||
"sha1": "af7eae90c398ec12942a408fa5ac2722e331738c",
|
||||
"sha256": "24be347356715352fcab480ed1559302d47d598255980a32150eba1431b462b8",
|
||||
"size": 24576,
|
||||
"repo_path": "bios/Elektronika/BK/BASIC10.ROM",
|
||||
"cores": [
|
||||
"bk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bk/TERAK.ROM",
|
||||
"sha1": "273a9933b68a290c5aedcd6d69faa7b1d22c0344",
|
||||
@@ -21423,6 +21453,16 @@
|
||||
"fceumm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/dc_nvmem.bin",
|
||||
"sha1": "72c65f2a11ee7b72b77b9945187b7a332632e14a",
|
||||
"sha256": "15a9bc3aa59fa998c03bfe0b1cf6cb1958c1660e8e72b3d7195b653f44038407",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Sega/Dreamcast/dc_nvmem.bin",
|
||||
"cores": [
|
||||
"flycast"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/font.bin",
|
||||
"sha1": "94c6ff701f79742ee23c0a028ac0be5aead77c73",
|
||||
@@ -21823,6 +21863,16 @@
|
||||
"fuse"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "colecovision.rom",
|
||||
"sha1": "45bedc4cbdeac66c7df59e9e599195c778d86a92",
|
||||
"sha256": "990bf1956f10207d8781b619eb74f89b00d921c8d45c95c334c16c8cceca09ad",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/Coleco/ColecoVision/BIOS.col",
|
||||
"cores": [
|
||||
"gearcoleco"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "eos.rom",
|
||||
"sha1": "ad3c20ef444f10af7ae8eb75c81e500d9b1bba3d",
|
||||
@@ -21943,6 +21993,16 @@
|
||||
"gsplus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "c600.rom",
|
||||
"sha1": "d4181c9f046aafc3fb326b381baac809d9e38d16",
|
||||
"sha256": "de1e3e035878bab43d0af8fe38f5839c527e9548647036598ee6fe7ec74d2a7d",
|
||||
"size": 256,
|
||||
"repo_path": "bios/Apple/Apple II/AppleIIe_DiskII.rom",
|
||||
"cores": [
|
||||
"gsplus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "howard.o",
|
||||
"sha1": "bdca7bbb647066cf7555ce5d555ce09de4ebc41b",
|
||||
@@ -24965,10 +25025,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "np2kai/scsi.rom",
|
||||
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
@@ -27983,6 +28043,16 @@
|
||||
"px68k"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88sub.rom",
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-98/N88SUB.ROM",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88knj2.rom",
|
||||
"sha1": "b86b932cb4ce34472e0fe0f01a4a4350e15dcb53",
|
||||
@@ -28863,6 +28933,26 @@
|
||||
"virtualjaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "jagboot.rom",
|
||||
"sha1": "af643900fea84234e08b616a6d27f03f0a61db96",
|
||||
"sha256": "f0bd29599b3dfca7f5620c011256c1dc45c9d82c428a318f965c2ba3ded2e9f9",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Atari/Jaguar/boot.rom",
|
||||
"cores": [
|
||||
"virtualjaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "jagboot_m.rom",
|
||||
"sha1": "af643900fea84234e08b616a6d27f03f0a61db96",
|
||||
"sha256": "f0bd29599b3dfca7f5620c011256c1dc45c9d82c428a318f965c2ba3ded2e9f9",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Atari/Jaguar/boot.rom",
|
||||
"cores": [
|
||||
"virtualjaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "pinmame/roms/gts1.zip",
|
||||
"sha1": "d4b0c8277c187a92279e2d2b71fc91b72d581257",
|
||||
|
||||
+91
-33
@@ -5,7 +5,7 @@
|
||||
"platform": "retroarch",
|
||||
"display_name": "RetroArch",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T03:26:07Z",
|
||||
"base_destination": "system",
|
||||
"detect": [
|
||||
{
|
||||
@@ -255,10 +255,10 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 5861,
|
||||
"total_size": 6398490526,
|
||||
"pack_files": 8504,
|
||||
"pack_size": 6439023690,
|
||||
"total_files": 5867,
|
||||
"total_size": 6399851574,
|
||||
"pack_files": 8510,
|
||||
"pack_size": 6440384738,
|
||||
"total_omitted": 0,
|
||||
"omitted_files": [],
|
||||
"files": [
|
||||
@@ -1200,10 +1200,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "galaksija/ROM2.BIN",
|
||||
"sha1": "5d5ab4313a2d0effe7572bb129193b64cab002c1",
|
||||
"sha256": "7d93fe474fcc98c06a6d96e533f8935e4e707edf0792d3e9d679ea68404375de",
|
||||
"size": 4096,
|
||||
"repo_path": "bios/Galaksija/ROM2.BIN",
|
||||
"sha1": "1bae895fbdd658cfb56c53cc2139282cc1e778de",
|
||||
"sha256": "9fc01e6beab84e61e0267fa39e23c649cdf2ccd25ad1cbfc2de0facfd160e723",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/Sony/PlayStation 2/ROM2.BIN",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -1558,6 +1558,14 @@
|
||||
"repo_path": "bios/NEC/PC-98/n88knj2.rom",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88SUB.ROM",
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-98/N88SUB.ROM",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88EXT0.ROM",
|
||||
"sha1": "d239c26ad7ac5efac6e947b0e9549b1534aa970d",
|
||||
@@ -2002,10 +2010,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
||||
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||
"sha256": "49d987ee1eab29a157425b82d54516957a81e1bac247c8834e494642605c3e8c",
|
||||
"sha1": "f4f683a49d7eb4155566f793f2c1c27e90159992",
|
||||
"sha256": "7f33e6600722791ccee5fd15d4330af8d8f74165f7d1ded6738684c0294aaffe",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_rom.bin.3c6cc6e0",
|
||||
"repo_path": "bios/Nintendo/GameCube/Sys/GC/dsp_rom.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -2098,10 +2106,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "BS-X.bin",
|
||||
"sha1": "604556b2e62860af18db5a77f2956ebc75450020",
|
||||
"sha256": "3ce321496edc5d77038de2034eb3fb354d7724afd0bc7fd0319f3eb5d57b984d",
|
||||
"sha1": "4891d739a8a8b67923681bad4fb67edab2e90e50",
|
||||
"sha256": "a49827b45ff9ac9cf5b4658190e1428e59251bc82d8a63d8e9e0f71e439f008f",
|
||||
"size": 1048576,
|
||||
"repo_path": "bios/Nintendo/SNES/Satellaview_BS-X.sfc",
|
||||
"repo_path": "bios/Nintendo/SNES/BS-X.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4346,10 +4354,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||
"sha256": "3bf3c54d6f16d88d3669be698179bb8c2fb981ee5ce71bcb952719429a838cc1",
|
||||
"size": 2693579,
|
||||
"repo_path": "bios/Other/yaps2/GameIndex.yaml",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4500,6 +4508,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32.rom",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32ext.rom",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
@@ -4510,6 +4528,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtv.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
"sha256": "6e48bb768539e8dae352553e5fdfa860682edaf9e26b105fadd8e26103171765",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-130-cdtv.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtvext.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -25750,6 +25778,16 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SufamiTurbo.sfc",
|
||||
"sha1": "ef86ea192eed03d5c413fdbbfd46043be1d7a127",
|
||||
"sha256": "edacb453da14f825f05d1134d6035f4bf034e55f7cfb97c70c4ee107eabc7342",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Nintendo/SuFami Turbo/STBIOS.bin",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "ymf288_adpcm_rom.bin",
|
||||
"sha1": "50b6c3e288eaa12ad275d4f323267bb72b0445df",
|
||||
@@ -25780,6 +25818,26 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Super CD-ROM System (Japan) (v3.0).pce",
|
||||
"sha1": "79f5ff55dd10187c7fd7b8daab0b3ffbd1f56a2c",
|
||||
"sha256": "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/NEC/PC Engine/syscard3.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Games Express CD Card (Japan).pce",
|
||||
"sha1": "014881a959e045e00f4db8f52955200865d40280",
|
||||
"sha256": "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/NEC/PC Engine/gecard.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Satellaview/BSX0120-0.bin",
|
||||
"sha1": "f51c52bb26f2546fb167ab1084a56457475ff0a0",
|
||||
@@ -47672,10 +47730,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "np2kai/scsi.rom",
|
||||
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
@@ -53266,6 +53324,16 @@
|
||||
"qemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font2.rom",
|
||||
"sha1": "0a056b6d28b4b9e29a2d255bdaa25c24f176cf1e",
|
||||
@@ -56768,16 +56836,6 @@
|
||||
"nekop2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "scummvm/extra/MT32_CONTROL.ROM",
|
||||
"sha1": "b083518fffb7f66b03c23b7eb4f868e62dc5a987",
|
||||
|
||||
+189
-1454
File diff suppressed because it is too large.
Load diff
+232
-1252
File diff suppressed because it is too large.
Load diff
+131
-43
@@ -5,7 +5,7 @@
|
||||
"platform": "retropie",
|
||||
"display_name": "RetroPie",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T03:26:07Z",
|
||||
"base_destination": "BIOS",
|
||||
"detect": [
|
||||
{
|
||||
@@ -15,10 +15,10 @@
|
||||
}
|
||||
],
|
||||
"standalone_copies": [],
|
||||
"total_files": 9890,
|
||||
"total_size": 8732256943,
|
||||
"pack_files": 12533,
|
||||
"pack_size": 8772790107,
|
||||
"total_files": 9899,
|
||||
"total_size": 8731282254,
|
||||
"pack_files": 12542,
|
||||
"pack_size": 8771815418,
|
||||
"total_omitted": 8,
|
||||
"omitted_files": [
|
||||
{
|
||||
@@ -1041,10 +1041,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "galaksija/ROM2.BIN",
|
||||
"sha1": "5d5ab4313a2d0effe7572bb129193b64cab002c1",
|
||||
"sha256": "7d93fe474fcc98c06a6d96e533f8935e4e707edf0792d3e9d679ea68404375de",
|
||||
"size": 4096,
|
||||
"repo_path": "bios/Galaksija/ROM2.BIN",
|
||||
"sha1": "1bae895fbdd658cfb56c53cc2139282cc1e778de",
|
||||
"sha256": "9fc01e6beab84e61e0267fa39e23c649cdf2ccd25ad1cbfc2de0facfd160e723",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/Sony/PlayStation 2/ROM2.BIN",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -1399,6 +1399,14 @@
|
||||
"repo_path": "bios/NEC/PC-98/n88knj2.rom",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88SUB.ROM",
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-98/N88SUB.ROM",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88EXT0.ROM",
|
||||
"sha1": "d239c26ad7ac5efac6e947b0e9549b1534aa970d",
|
||||
@@ -1843,10 +1851,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
||||
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||
"sha256": "49d987ee1eab29a157425b82d54516957a81e1bac247c8834e494642605c3e8c",
|
||||
"sha1": "f4f683a49d7eb4155566f793f2c1c27e90159992",
|
||||
"sha256": "7f33e6600722791ccee5fd15d4330af8d8f74165f7d1ded6738684c0294aaffe",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_rom.bin.3c6cc6e0",
|
||||
"repo_path": "bios/Nintendo/GameCube/Sys/GC/dsp_rom.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -1939,10 +1947,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "BS-X.bin",
|
||||
"sha1": "604556b2e62860af18db5a77f2956ebc75450020",
|
||||
"sha256": "3ce321496edc5d77038de2034eb3fb354d7724afd0bc7fd0319f3eb5d57b984d",
|
||||
"sha1": "4891d739a8a8b67923681bad4fb67edab2e90e50",
|
||||
"sha256": "a49827b45ff9ac9cf5b4658190e1428e59251bc82d8a63d8e9e0f71e439f008f",
|
||||
"size": 1048576,
|
||||
"repo_path": "bios/Nintendo/SNES/Satellaview_BS-X.sfc",
|
||||
"repo_path": "bios/Nintendo/SNES/BS-X.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4187,10 +4195,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||
"sha256": "3bf3c54d6f16d88d3669be698179bb8c2fb981ee5ce71bcb952719429a838cc1",
|
||||
"size": 2693579,
|
||||
"repo_path": "bios/Other/yaps2/GameIndex.yaml",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4571,6 +4579,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32.rom",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32ext.rom",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
@@ -4581,6 +4599,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtv.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
"sha256": "6e48bb768539e8dae352553e5fdfa860682edaf9e26b105fadd8e26103171765",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-130-cdtv.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtvext.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -41945,6 +41973,16 @@
|
||||
"storage": "release",
|
||||
"release_asset": "easyrpg.soundfont"
|
||||
},
|
||||
{
|
||||
"dest": "system.rom",
|
||||
"sha1": "1f5f7013f18c08ff50d7942e76c4fbd782412414",
|
||||
"sha256": "863df0975a305110a7da4591b704e8f5dc7302f03c63603aa2dcd03e84c36ff9",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/IBM/PC/ibmpcjr/bios.rom",
|
||||
"cores": [
|
||||
"dosbox-staging"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "IBM PC Cassette BASIC ROM",
|
||||
"sha1": "07449ebca18f979b9ab748582b736e402f2bf940",
|
||||
@@ -41985,6 +42023,16 @@
|
||||
"dosbox_pure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "system/nds_bios_arm9.bin",
|
||||
"sha1": "bfaac75f101c135e32e2aaf541de6b1be4c8c62d",
|
||||
"sha256": "1693983a7707ae394786fa526c0552457888a51d4e410d715ef07acd5a540555",
|
||||
"size": 4096,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Bios9.bin",
|
||||
"cores": [
|
||||
"drastic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "system/drastic_bios_arm9.bin",
|
||||
"sha1": "4dc29a1560e1cbd7afef1998f7a6c25c5db1cc3a",
|
||||
@@ -41995,6 +42043,16 @@
|
||||
"drastic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "system/nds_bios_arm7.bin",
|
||||
"sha1": "24f67bdea115a2c847c8813a262502ee1607b7df",
|
||||
"sha256": "ba65f690eb04ec92db67c0e299e21ad71de087d6d5de8a9cb17a62eaab563c17",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Bios7.bin",
|
||||
"cores": [
|
||||
"drastic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "system/drastic_bios_arm7.bin",
|
||||
"sha1": "31ae916c85d012582c7cdf47de6943ac6778253b",
|
||||
@@ -42005,6 +42063,16 @@
|
||||
"drastic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "system/nds_firmware.bin",
|
||||
"sha1": "87dae2500e889737af51f4a5b5845770a62482f5",
|
||||
"sha256": "330c59198faec64b40e96cc875b422d340fc9ab1a9e32e9bd9b7baa1dbe6ba7f",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Lite_Firmware.bin",
|
||||
"cores": [
|
||||
"drastic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "game_database.xml",
|
||||
"sha1": "b0ae5480c61bdb701b9a444a1a81c4970db06d77",
|
||||
@@ -62783,6 +62851,16 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SufamiTurbo.sfc",
|
||||
"sha1": "ef86ea192eed03d5c413fdbbfd46043be1d7a127",
|
||||
"sha256": "edacb453da14f825f05d1134d6035f4bf034e55f7cfb97c70c4ee107eabc7342",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Nintendo/SuFami Turbo/STBIOS.bin",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "ymf288_adpcm_rom.bin",
|
||||
"sha1": "50b6c3e288eaa12ad275d4f323267bb72b0445df",
|
||||
@@ -62813,6 +62891,26 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Super CD-ROM System (Japan) (v3.0).pce",
|
||||
"sha1": "79f5ff55dd10187c7fd7b8daab0b3ffbd1f56a2c",
|
||||
"sha256": "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/NEC/PC Engine/syscard3.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Games Express CD Card (Japan).pce",
|
||||
"sha1": "014881a959e045e00f4db8f52955200865d40280",
|
||||
"sha256": "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/NEC/PC Engine/gecard.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Satellaview/BSX0120-0.bin",
|
||||
"sha1": "f51c52bb26f2546fb167ab1084a56457475ff0a0",
|
||||
@@ -84705,10 +84803,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "np2kai/scsi.rom",
|
||||
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
@@ -90569,6 +90667,16 @@
|
||||
"qemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font2.rom",
|
||||
"sha1": "0a056b6d28b4b9e29a2d255bdaa25c24f176cf1e",
|
||||
@@ -95707,26 +95815,6 @@
|
||||
"nekop2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "resources/GameIndex.yaml",
|
||||
"sha1": "5e2d50a3376dc6a0750505bec68626d39a5bd4c1",
|
||||
"sha256": "6531239315d759d0588985b31576696d2672c1a0e192633ea2d4f3d011bfdb37",
|
||||
"size": 2683897,
|
||||
"repo_path": "bios/Sony/PlayStation 2/resources/.variants/GameIndex.yaml.fc3c1420",
|
||||
"cores": [
|
||||
"pcsx2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "scummvm/extra/MT32_CONTROL.ROM",
|
||||
"sha1": "b083518fffb7f66b03c23b7eb4f868e62dc5a987",
|
||||
|
||||
+39
-19
@@ -5,7 +5,7 @@
|
||||
"platform": "rocknix",
|
||||
"display_name": "ROCKNIX",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T03:26:07Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -17,10 +17,10 @@
|
||||
}
|
||||
],
|
||||
"standalone_copies": [],
|
||||
"total_files": 5802,
|
||||
"total_size": 6570592972,
|
||||
"pack_files": 5804,
|
||||
"pack_size": 6570592972,
|
||||
"total_files": 5804,
|
||||
"total_size": 6570732492,
|
||||
"pack_files": 5806,
|
||||
"pack_size": 6570732492,
|
||||
"total_omitted": 0,
|
||||
"omitted_files": [],
|
||||
"files": [
|
||||
@@ -15720,6 +15720,16 @@
|
||||
"fceumm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/dc_nvmem.bin",
|
||||
"sha1": "72c65f2a11ee7b72b77b9945187b7a332632e14a",
|
||||
"sha256": "15a9bc3aa59fa998c03bfe0b1cf6cb1958c1660e8e72b3d7195b653f44038407",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Sega/Dreamcast/dc_nvmem.bin",
|
||||
"cores": [
|
||||
"flycast"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/font.bin",
|
||||
"sha1": "94c6ff701f79742ee23c0a028ac0be5aead77c73",
|
||||
@@ -45694,10 +45704,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "np2kai/scsi.rom",
|
||||
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||
"sha256": "9809ed07796454d5524cce240c4f815496b82fae3644aba80778612c618d5cf7",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/scsi.rom",
|
||||
"sha1": "451c85c46b92e6ad2ad930f055ccf0fe3049936d",
|
||||
"sha256": "5e1efe51b2c8be901ab4283c671bd84364a574845b7269f92bdddc4dae9ae5d5",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Other/Clock Signal/AppleII/scsi.rom",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
@@ -53048,6 +53058,26 @@
|
||||
"sdlpal"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SkyEmu/cgb_boot.bin",
|
||||
"sha1": "80e3ee82416c1a74827a5459fde32f04bf51a1f6",
|
||||
"sha256": "119470a9cf9f0d85f0c7ba5a6cadffeec07eb9862719086d1a9808189605e388",
|
||||
"size": 2304,
|
||||
"repo_path": "bios/Nintendo/Game Boy Color/cgb_boot.bin",
|
||||
"cores": [
|
||||
"skyemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SkyEmu/dmg_rom.bin",
|
||||
"sha1": "4ed31ec6b0b175bb109c0eb5fd3d193da823339f",
|
||||
"sha256": "cf053eccb4ccafff9e67339d4e78e98dce7d1ed59be819d2a1ba2232c6fce1c7",
|
||||
"size": 256,
|
||||
"repo_path": "bios/Nintendo/Game Boy/GB_dmg.bin",
|
||||
"cores": [
|
||||
"skyemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SkyEmu/nds7.bin",
|
||||
"sha1": "24f67bdea115a2c847c8813a262502ee1607b7df",
|
||||
@@ -56080,16 +56110,6 @@
|
||||
"kronos"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SkyEmu/cgb_boot.bin",
|
||||
"sha1": "80e3ee82416c1a74827a5459fde32f04bf51a1f6",
|
||||
"sha256": "119470a9cf9f0d85f0c7ba5a6cadffeec07eb9862719086d1a9808189605e388",
|
||||
"size": 2304,
|
||||
"repo_path": "bios/Nintendo/Game Boy Color/cgb_boot.bin",
|
||||
"cores": [
|
||||
"skyemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SkyEmu/gba_bios.bin",
|
||||
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
||||
|
||||
+21
-1822
File diff suppressed because it is too large.
Load diff
@@ -148,7 +148,6 @@ platforms:
|
||||
retroarch:
|
||||
config: retroarch.yml
|
||||
status: active
|
||||
extract_to: system/
|
||||
logo: https://raw.githubusercontent.com/libretro/RetroArch/master/media/retroarch-vector_invader-only.svg
|
||||
scraper: libretro
|
||||
source_url: https://raw.githubusercontent.com/libretro/libretro-database/master/dat/System.dat
|
||||
@@ -183,7 +182,6 @@ platforms:
|
||||
batocera:
|
||||
config: batocera.yml
|
||||
status: active
|
||||
extract_to: /userdata/bios/
|
||||
logo: https://raw.githubusercontent.com/batocera-linux/batocera-emulationstation/master/resources/splash_batocera.svg
|
||||
scraper: batocera
|
||||
source_url: https://raw.githubusercontent.com/batocera-linux/batocera.linux/master/package/batocera/core/batocera-scripts/scripts/batocera-systems
|
||||
@@ -409,7 +407,6 @@ platforms:
|
||||
recalbox:
|
||||
config: recalbox.yml
|
||||
status: active
|
||||
extract_to: /recalbox/share/bios/
|
||||
logo: https://raw.githubusercontent.com/homarr-labs/dashboard-icons/main/svg/recalbox.svg
|
||||
scraper: recalbox
|
||||
source_url: https://gitlab.com/recalbox/recalbox/-/raw/master/board/recalbox/fsoverlay/recalbox/share_init/system/.emulationstation/es_bios.xml
|
||||
@@ -623,7 +620,6 @@ platforms:
|
||||
retrobat:
|
||||
config: retrobat.yml
|
||||
status: active
|
||||
extract_to: bios/
|
||||
logo: https://raw.githubusercontent.com/RetroBat-Official/retrobat/main/system/resources/retrobat_logo_notext.png
|
||||
scraper: retrobat
|
||||
source_url: https://raw.githubusercontent.com/RetroBat-Official/emulatorlauncher/master/batocera-systems/Resources/batocera-systems.json
|
||||
@@ -805,10 +801,6 @@ platforms:
|
||||
target_source: null
|
||||
install:
|
||||
detect:
|
||||
# installer.iss: DefaultDirName=C:\RetroBat, the first place to look.
|
||||
- os: windows
|
||||
method: path_exists
|
||||
path: 'C:\RetroBat\bios'
|
||||
- os: windows
|
||||
method: path_exists
|
||||
path: '%USERPROFILE%\RetroBat\bios'
|
||||
@@ -816,7 +808,6 @@ platforms:
|
||||
emudeck:
|
||||
config: emudeck.yml
|
||||
status: active
|
||||
extract_to: ~/Emulation/bios/
|
||||
logo: https://raw.githubusercontent.com/dragoonDorise/EmuDeck/main/icons/EmuDeck.png
|
||||
scraper: emudeck
|
||||
source_url: https://raw.githubusercontent.com/dragoonDorise/EmuDeck/main/functions/checkBIOS.sh
|
||||
@@ -847,7 +838,6 @@ platforms:
|
||||
lakka:
|
||||
config: lakka.yml
|
||||
status: active
|
||||
extract_to: /storage/system/
|
||||
logo: https://raw.githubusercontent.com/libretro/retroarch-assets/master/src/xmb/flatui/lakka.svg
|
||||
scraper: libretro
|
||||
inherits_from: retroarch
|
||||
@@ -864,7 +854,6 @@ platforms:
|
||||
retrodeck:
|
||||
config: retrodeck.yml
|
||||
status: active
|
||||
extract_to: ~/retrodeck/
|
||||
logo: https://raw.githubusercontent.com/RetroDECK/RetroDECK/main/res/icon.svg
|
||||
scraper: retrodeck
|
||||
source_url: https://github.com/RetroDECK/components
|
||||
@@ -909,7 +898,6 @@ platforms:
|
||||
romm:
|
||||
config: romm.yml
|
||||
status: active
|
||||
extract_to: library/bios/
|
||||
logo: https://avatars.githubusercontent.com/u/168586850
|
||||
scraper: romm
|
||||
source_url: https://raw.githubusercontent.com/rommapp/romm/master/backend/models/fixtures/known_bios_files.json
|
||||
@@ -957,7 +945,6 @@ platforms:
|
||||
rocknix:
|
||||
config: rocknix.yml
|
||||
status: active
|
||||
extract_to: /storage/roms/bios/
|
||||
logo: https://avatars.githubusercontent.com/u/152618023
|
||||
scraper: rocknix
|
||||
cores: all_libretro
|
||||
@@ -972,7 +959,6 @@ platforms:
|
||||
retropie:
|
||||
config: retropie.yml
|
||||
status: archived
|
||||
extract_to: ~/RetroPie/BIOS/
|
||||
logo: https://avatars.githubusercontent.com/u/11378204
|
||||
scraper: retropie
|
||||
source: https://github.com/RetroPie/RetroPie-Setup
|
||||
@@ -989,7 +975,6 @@ platforms:
|
||||
bizhawk:
|
||||
config: bizhawk.yml
|
||||
status: active
|
||||
extract_to: Firmware/
|
||||
logo: https://avatars.githubusercontent.com/u/11743303
|
||||
scraper: bizhawk
|
||||
source_url: https://raw.githubusercontent.com/TASEmulators/BizHawk/master/src/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs
|
||||
@@ -1054,7 +1039,6 @@ platforms:
|
||||
misterfpga:
|
||||
config: misterfpga.yml
|
||||
status: active
|
||||
extract_to: /media/fat/games/
|
||||
logo: https://avatars.githubusercontent.com/u/47264183
|
||||
scraper: misterfpga
|
||||
source_url: https://raw.githubusercontent.com/ajgowans/BiosDB_MiSTer/db/bios_db.json.zip
|
||||
|
||||
+37
-9
@@ -163,15 +163,22 @@ shared_groups:
|
||||
# QUASI88 searches "quasi88/" subfolder first
|
||||
# ref: quasi88-libretro/src/LIBRETRO/libretro.c:463 — snprintf(..., "%s%cquasi88%c%s", ...)
|
||||
quasi88:
|
||||
# Only what System.dat does not declare for nec-pc-88: the mkII SR
|
||||
# extension ROMs that match N88.ROM (MAME pc8801mk2sr: 710a63ec, c0bd2aa6,
|
||||
# af2b6efa, 7713c519). The core reads N88EXTn.ROM before n88_n.rom
|
||||
# (src/LIBRETRO/libretro.c:109-112), so they win over the n88_n.rom dumps
|
||||
# System.dat pairs with that main ROM, which come from other models (n88_0
|
||||
# 61984bab is the original PC-8801's). N88SUB.ROM, N88.ROM, N88N.ROM and
|
||||
# N88KNJ1.ROM are the native disk.rom, n88.rom, n88n.rom and n88knj1.rom
|
||||
# under the other spelling the core tries: aliases in the profile, not
|
||||
# files of their own here, or the pack carried disk.rom twice.
|
||||
- name: N88.ROM
|
||||
destination: quasi88/N88.ROM
|
||||
required: true
|
||||
md5: "4f984e04a99d56c4cfe36115415d6eb8"
|
||||
- name: N88SUB.ROM
|
||||
destination: quasi88/N88SUB.ROM
|
||||
required: true
|
||||
md5: "793f86784e5608352a5d7f03f03e0858"
|
||||
- name: N88N.ROM
|
||||
destination: quasi88/N88N.ROM
|
||||
required: true
|
||||
md5: "2ff07b8769367321128e03924af668a0"
|
||||
- name: N88KNJ1.ROM
|
||||
destination: quasi88/N88KNJ1.ROM
|
||||
required: true
|
||||
md5: "d81c6d5d7ad1a4bbbd6ae22a01257603"
|
||||
- name: N88EXT0.ROM
|
||||
destination: quasi88/N88EXT0.ROM
|
||||
required: true
|
||||
@@ -188,6 +195,27 @@ shared_groups:
|
||||
destination: quasi88/N88EXT3.ROM
|
||||
required: true
|
||||
md5: "fc4b76a402ba501e6ba6de4b3e8b4273"
|
||||
# N88EXT0-3.ROM above are the mkII SR extension ROMs that match N88.ROM
|
||||
# (MAME pc8801mk2sr: 710a63ec, c0bd2aa6, af2b6efa, 7713c519). The core
|
||||
# reads N88EXTn.ROM before n88_n.rom (src/LIBRETRO/libretro.c:109-112),
|
||||
# so they win over the n88_n.rom dumps System.dat pairs with that main ROM,
|
||||
# which come from other models (n88_0 61984bab is the original PC-8801's).
|
||||
- name: n88_0.rom
|
||||
destination: quasi88/n88_0.rom
|
||||
required: false
|
||||
md5: "e28fe3f520bea594350ea8fb00395370"
|
||||
- name: n88_1.rom
|
||||
destination: quasi88/n88_1.rom
|
||||
required: false
|
||||
md5: "a8e298da7ac947669bcb1ff25cee0a83"
|
||||
- name: n88_2.rom
|
||||
destination: quasi88/n88_2.rom
|
||||
required: false
|
||||
md5: "9d03154fd9abfc28c4e6d4dc705e6e23"
|
||||
- name: n88_3.rom
|
||||
destination: quasi88/n88_3.rom
|
||||
required: false
|
||||
md5: "e1791f8154f1cdf22b576a1a365b6e1f"
|
||||
|
||||
# Sinclair ZX Spectrum — Fuse core (Pentagon/Scorpion ROMs)
|
||||
# VERIFIED: fuse-libretro/src/compat/paths.c compat_get_next_path()
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ dependencies = [
|
||||
# and the CI contract check are the only callers, and no user path imports it.
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"jsonschema[format-nongpl]>=4.23",
|
||||
"jsonschema>=4.23",
|
||||
]
|
||||
# The site build the repo generates and documents. Both were hard
|
||||
# requirements of deploy-site.yml and of wiki/release-process.md while being
|
||||
|
||||
+24
-105
@@ -4,136 +4,55 @@
|
||||
"title": "RetroBIOS content database",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"generated_at",
|
||||
"total_files",
|
||||
"total_size",
|
||||
"files",
|
||||
"indexes"
|
||||
],
|
||||
"required": ["schema_version", "generated_at", "total_files", "total_size", "files", "indexes"],
|
||||
"properties": {
|
||||
"schema_version": {
|
||||
"const": 1
|
||||
},
|
||||
"generated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"total_files": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"total_size": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"bios_dir": {
|
||||
"type": "string",
|
||||
"description": "Directory the files were scanned from; _mame_clones.json sits beside it."
|
||||
},
|
||||
"schema_version": { "const": 1 },
|
||||
"generated_at": { "type": "string", "format": "date-time" },
|
||||
"total_files": { "type": "integer", "minimum": 0 },
|
||||
"total_size": { "type": "integer", "minimum": 0 },
|
||||
"files": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"pattern": "^[a-f0-9]{40}$"
|
||||
},
|
||||
"propertyNames": { "pattern": "^[a-f0-9]{40}$" },
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"path",
|
||||
"name",
|
||||
"size",
|
||||
"sha1",
|
||||
"md5",
|
||||
"sha256",
|
||||
"crc32",
|
||||
"adler32"
|
||||
],
|
||||
"required": ["path", "name", "size", "sha1", "md5", "sha256", "crc32", "adler32"],
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{40}$"
|
||||
},
|
||||
"md5": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{32}$"
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{64}$"
|
||||
},
|
||||
"crc32": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{8}$"
|
||||
},
|
||||
"adler32": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{8}$"
|
||||
},
|
||||
"provenance": {
|
||||
"type": "object"
|
||||
}
|
||||
"path": { "type": "string", "minLength": 1 },
|
||||
"name": { "type": "string", "minLength": 1 },
|
||||
"size": { "type": "integer", "minimum": 0 },
|
||||
"sha1": { "type": "string", "pattern": "^[a-f0-9]{40}$" },
|
||||
"md5": { "type": "string", "pattern": "^[a-f0-9]{32}$" },
|
||||
"sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
||||
"crc32": { "type": "string", "pattern": "^[a-f0-9]{8}$" },
|
||||
"adler32": { "type": "string", "pattern": "^[a-f0-9]{8}$" },
|
||||
"provenance": { "type": "object" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"by_md5",
|
||||
"by_name",
|
||||
"by_crc32",
|
||||
"by_sha256",
|
||||
"by_path_suffix"
|
||||
],
|
||||
"required": ["by_md5", "by_name", "by_crc32", "by_sha256", "by_path_suffix"],
|
||||
"properties": {
|
||||
"by_md5": {
|
||||
"$ref": "#/$defs/hashIndex"
|
||||
},
|
||||
"by_crc32": {
|
||||
"$ref": "#/$defs/hashIndex"
|
||||
},
|
||||
"by_sha256": {
|
||||
"$ref": "#/$defs/hashIndex"
|
||||
},
|
||||
"by_name": {
|
||||
"$ref": "#/$defs/listIndex"
|
||||
},
|
||||
"by_path_suffix": {
|
||||
"$ref": "#/$defs/listIndex"
|
||||
}
|
||||
"by_md5": { "$ref": "#/$defs/hashIndex" },
|
||||
"by_crc32": { "$ref": "#/$defs/hashIndex" },
|
||||
"by_sha256": { "$ref": "#/$defs/hashIndex" },
|
||||
"by_name": { "$ref": "#/$defs/listIndex" },
|
||||
"by_path_suffix": { "$ref": "#/$defs/listIndex" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"hashIndex": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{40}$"
|
||||
}
|
||||
"additionalProperties": { "type": "string", "pattern": "^[a-f0-9]{40}$" }
|
||||
},
|
||||
"listIndex": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{40}$"
|
||||
},
|
||||
"items": { "type": "string", "pattern": "^[a-f0-9]{40}$" },
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
|
||||
@@ -31,37 +31,6 @@
|
||||
"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": {
|
||||
|
||||
@@ -129,8 +129,7 @@
|
||||
"hash_mismatch",
|
||||
"not_found",
|
||||
"external",
|
||||
"user_provided",
|
||||
"no_platform_slug"
|
||||
"user_provided"
|
||||
]
|
||||
},
|
||||
"cores": {
|
||||
|
||||
+6
-58
@@ -43,18 +43,11 @@ def write_if_changed(path: str, content: str, normalize=None) -> bool:
|
||||
)
|
||||
if _strip_timestamps(before) == _strip_timestamps(after):
|
||||
return False
|
||||
write_text_atomic(path, content)
|
||||
return True
|
||||
|
||||
|
||||
def write_text_atomic(path: str, content: str) -> None:
|
||||
"""Write a whole file or nothing.
|
||||
|
||||
Truncate-then-write leaves a half-written artifact behind an interrupt:
|
||||
a partial database.json, manifest or README.md is committed-looking and
|
||||
silently wrong. The scratch file sits beside the target so the rename
|
||||
stays on one filesystem, which is what makes it atomic.
|
||||
"""
|
||||
# Truncate-then-write leaves a half-written artifact behind an interrupt,
|
||||
# and every generator in the repo funnels through here: a partial
|
||||
# database.json or README.md is committed-looking and silently wrong.
|
||||
# The scratch file sits beside the target so the rename stays on one
|
||||
# filesystem, which is what makes it atomic.
|
||||
directory = os.path.dirname(os.path.abspath(path))
|
||||
handle, scratch = tempfile.mkstemp(
|
||||
dir=directory, prefix=f".{os.path.basename(path)}.", suffix=".tmp"
|
||||
@@ -67,24 +60,7 @@ def write_text_atomic(path: str, content: str) -> None:
|
||||
with contextlib.suppress(OSError):
|
||||
os.unlink(scratch)
|
||||
raise
|
||||
|
||||
|
||||
def copy_file_atomic(source: str, path: str) -> None:
|
||||
"""Copy a file into place whole or not at all, metadata included."""
|
||||
import shutil
|
||||
|
||||
directory = os.path.dirname(os.path.abspath(path))
|
||||
handle, scratch = tempfile.mkstemp(
|
||||
dir=directory, prefix=f".{os.path.basename(path)}.", suffix=".tmp"
|
||||
)
|
||||
os.close(handle)
|
||||
try:
|
||||
shutil.copy2(source, scratch)
|
||||
os.replace(scratch, path)
|
||||
except BaseException:
|
||||
with contextlib.suppress(OSError):
|
||||
os.unlink(scratch)
|
||||
raise
|
||||
return True
|
||||
|
||||
def _strip_timestamps(text: str) -> str:
|
||||
"""Remove known timestamp patterns for content comparison."""
|
||||
@@ -96,11 +72,6 @@ def _strip_timestamps(text: str) -> str:
|
||||
class ArtifactLockBusy(RuntimeError):
|
||||
"""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
|
||||
def artifact_lock(directory: str, exclusive: bool = True):
|
||||
"""Serialize access to a shared artifact directory across processes.
|
||||
@@ -117,9 +88,6 @@ def artifact_lock(directory: str, exclusive: bool = True):
|
||||
return
|
||||
|
||||
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")
|
||||
mode = fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH
|
||||
with open(lock_path, "w") as handle:
|
||||
@@ -135,26 +103,6 @@ def artifact_lock(directory: str, exclusive: bool = True):
|
||||
finally:
|
||||
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:
|
||||
"""Timestamp for generated artifacts.
|
||||
|
||||
|
||||
@@ -152,13 +152,12 @@ def print_report(report: dict) -> None:
|
||||
print(f"\nSummary: {', '.join(parts)}")
|
||||
|
||||
|
||||
def update_changed(report: dict) -> list[str]:
|
||||
"""Refresh entries that have changed; the keys whose refresh failed."""
|
||||
failed: list[str] = []
|
||||
def update_changed(report: dict) -> None:
|
||||
"""Refresh entries that have changed."""
|
||||
for e in report.get("entries", []):
|
||||
if e["status"] == "UPDATED" and e.get("key"):
|
||||
log.info("refreshing %s ...", e["key"])
|
||||
result = subprocess.run(
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/refresh_data_dirs.py",
|
||||
@@ -168,10 +167,6 @@ def update_changed(report: dict) -> list[str]:
|
||||
],
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
log.error("refresh of %s failed (exit %d)", e["key"], result.returncode)
|
||||
failed.append(e["key"])
|
||||
return failed
|
||||
|
||||
|
||||
def main() -> None:
|
||||
@@ -199,10 +194,7 @@ def main() -> None:
|
||||
print_report(report)
|
||||
|
||||
if args.update:
|
||||
failed = update_changed(report)
|
||||
if failed:
|
||||
print(f"ERROR: refresh failed for {', '.join(failed)}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
update_changed(report)
|
||||
|
||||
# Unreachable upstream means the freshness question was not answered, and
|
||||
# a zero exit says it was answered "fresh".
|
||||
|
||||
@@ -157,22 +157,6 @@ def _core_names(config: dict) -> set[str]:
|
||||
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:
|
||||
"""Compare two platform files the way a reviewer reads the diff.
|
||||
|
||||
@@ -188,8 +172,12 @@ def diff_platform(old: dict, new: dict) -> PlatformDiff:
|
||||
diff.systems_added = sorted(set(new_systems) - set(old_systems))
|
||||
diff.systems_removed = sorted(set(old_systems) - set(new_systems))
|
||||
for system in sorted(set(old_systems) & set(new_systems)):
|
||||
old_files = _keyed_files((old_systems[system] or {}).get("files") or [])
|
||||
new_files = _keyed_files((new_systems[system] or {}).get("files") or [])
|
||||
old_files = {
|
||||
_file_key(f): f for f in (old_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(
|
||||
f"{system}/{k}" for k in sorted(set(new_files) - set(old_files))
|
||||
)
|
||||
@@ -804,9 +792,7 @@ def _check_fbneo(cache_dir: str, offline: bool) -> Finding:
|
||||
|
||||
# --- 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+))?")
|
||||
|
||||
|
||||
|
||||
+38
-127
@@ -480,60 +480,6 @@ 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 _foreign_tree(path: str, owners: set[str]) -> bool:
|
||||
"""Whether a path lives in the tree of an emulator that is not the asker.
|
||||
|
||||
bios/Other/<emulator>/ holds what one emulator ships. A same-named file
|
||||
found there answers that emulator's entry, not another's: EmuCoreX's
|
||||
unlock.wav was served from NetherSX2's tree, and upstream PCSX2's
|
||||
GameIndex.yaml from EmuCoreX's, while the system directory held a copy.
|
||||
"""
|
||||
parts = path.split("/")
|
||||
if not owners or len(parts) < 4 or parts[0] != "bios" or parts[1] != "Other":
|
||||
return False
|
||||
return not (owners & _affinity_tokens(parts[2]))
|
||||
|
||||
|
||||
def _rank_tail_candidates(
|
||||
candidates: list[str], start: int, tail: str, files_db: dict, owners: set[str]
|
||||
) -> list[str]:
|
||||
"""Order the files a destination tail designates, the likeliest first.
|
||||
|
||||
A shortened tail found only in another emulator's tree is that
|
||||
emulator's asset, not evidence about this entry. A full tail claimed
|
||||
twice goes to the file whose path carries the least on top of the
|
||||
destination, then to the owner's own copy (super3's Games.xml was served
|
||||
to Supermodel-Dojo by index order alone), then to any copy outside
|
||||
another emulator's tree (upstream PCSX2 took EmuCoreX's GameIndex.yaml
|
||||
over the system directory's).
|
||||
"""
|
||||
def path_of(sha1: str) -> str:
|
||||
return files_db.get(sha1, {}).get("path", "")
|
||||
|
||||
if start:
|
||||
candidates = [h for h in candidates if not _foreign_tree(path_of(h), owners)]
|
||||
if len(candidates) < 2:
|
||||
return candidates
|
||||
depth = len(tail.split("/"))
|
||||
return sorted(
|
||||
candidates,
|
||||
key=lambda h: (
|
||||
len(path_of(h).split("/")) - depth,
|
||||
not owners & _affinity_tokens(path_of(h).rsplit("/", 1)[0]),
|
||||
_foreign_tree(path_of(h), owners),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _by_affinity(paths: list[str], file_entry: dict, dest_hint: str) -> list[str]:
|
||||
"""Same-named candidates, the likeliest first.
|
||||
|
||||
@@ -549,18 +495,16 @@ def _by_affinity(paths: list[str], file_entry: dict, dest_hint: str) -> list[str
|
||||
return paths
|
||||
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 "")
|
||||
owners = _owner_tokens(file_entry)
|
||||
for owner in (file_entry.get("source_profile"), file_entry.get("source_emulator")):
|
||||
if owner:
|
||||
wanted |= _affinity_tokens(str(owner).replace(" ", ""))
|
||||
sized = any(file_entry.get(k) for k in ("size", "min_size", "max_size"))
|
||||
|
||||
def score(path: str) -> tuple[int, bool, bool, int]:
|
||||
def score(path: str) -> tuple[int, int]:
|
||||
fits = 0
|
||||
if sized and os.path.exists(path):
|
||||
fits = int(size_fits(file_entry, os.path.getsize(path)))
|
||||
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.
|
||||
# Another emulator's own tree ranks below everything else.
|
||||
return fits, bool(owners & tokens), not _foreign_tree(path, owners | wanted), len(wanted & tokens)
|
||||
return fits, len(wanted & _affinity_tokens(path.rsplit("/", 1)[0]))
|
||||
|
||||
return sorted(paths, key=score, reverse=True)
|
||||
|
||||
@@ -762,14 +706,9 @@ def resolve_local_file(
|
||||
# stops describing this file and starts describing whichever emulator
|
||||
# happens to store the same asset tree.
|
||||
hint_parts = dest_hint.split("/")
|
||||
# The asking profile and the destination's own directories both name
|
||||
# whose tree a copy may come from.
|
||||
owners = _owner_tokens(file_entry) | _affinity_tokens("/".join(hint_parts[:-1]))
|
||||
for start in range(min(2, len(hint_parts) - 1)):
|
||||
tail = "/".join(hint_parts[start:])
|
||||
candidates = _rank_tail_candidates(
|
||||
by_path_suffix.get(tail, []), start, tail, files_db, owners
|
||||
)
|
||||
candidates = by_path_suffix.get(tail, [])
|
||||
if start and len(candidates) > 1:
|
||||
continue
|
||||
# The index names the file this destination designates. Not on
|
||||
@@ -780,6 +719,15 @@ def resolve_local_file(
|
||||
for h in candidates
|
||||
):
|
||||
own_file_absent = True
|
||||
if len(candidates) > 1:
|
||||
depth = len(tail.split("/"))
|
||||
candidates = sorted(
|
||||
candidates,
|
||||
key=lambda h: len(
|
||||
files_db.get(h, {}).get("path", "").split("/")
|
||||
)
|
||||
- depth,
|
||||
)
|
||||
for match_sha1 in candidates:
|
||||
if match_sha1 not in files_db:
|
||||
continue
|
||||
@@ -912,7 +860,7 @@ def resolve_local_file(
|
||||
|
||||
# MAME clone fallback: if a file was deduped, resolve via canonical
|
||||
if _depth < 3 and not has_strong_hash:
|
||||
clone_map = get_mame_clone_map(db.get("bios_dir"))
|
||||
clone_map = get_mame_clone_map()
|
||||
canonical = clone_map.get(name)
|
||||
if canonical and canonical != name:
|
||||
canonical_entry = {"name": canonical}
|
||||
@@ -960,34 +908,28 @@ def resolve_local_file(
|
||||
return None, "not_found"
|
||||
|
||||
|
||||
_mame_clone_map_cache: dict[str, dict[str, str]] = {}
|
||||
_mame_clone_map_cache: dict[str, str] | None = None
|
||||
|
||||
|
||||
def get_mame_clone_map(bios_dir: str | None = None) -> dict[str, str]:
|
||||
"""The MAME clone map (clone_name -> canonical_name) of a scanned tree.
|
||||
|
||||
dedup.py writes _mame_clones.json beside the tree it scanned, and the
|
||||
map of one tree says nothing about another: read from the repository
|
||||
root whatever ``--bios-dir`` said, an alternate tree's renamed clones
|
||||
fell out of its packs. Without a tree the repository's own is read.
|
||||
"""
|
||||
if bios_dir is None:
|
||||
bios_dir = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "bios"
|
||||
)
|
||||
clone_path = os.path.join(os.path.dirname(os.path.abspath(bios_dir)), "_mame_clones.json")
|
||||
cached = _mame_clone_map_cache.get(clone_path)
|
||||
if cached is not None:
|
||||
return cached
|
||||
clone_map: dict[str, str] = {}
|
||||
def get_mame_clone_map() -> dict[str, str]:
|
||||
"""Load and cache the MAME clone map (clone_name -> canonical_name)."""
|
||||
global _mame_clone_map_cache
|
||||
if _mame_clone_map_cache is not None:
|
||||
return _mame_clone_map_cache
|
||||
clone_path = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
"_mame_clones.json",
|
||||
)
|
||||
if os.path.exists(clone_path):
|
||||
with open(clone_path) as f:
|
||||
data = json.load(f)
|
||||
_mame_clone_map_cache = {}
|
||||
for canonical, info in data.items():
|
||||
for clone in info.get("clones", []):
|
||||
clone_map[clone] = canonical
|
||||
_mame_clone_map_cache[clone_path] = clone_map
|
||||
return clone_map
|
||||
_mame_clone_map_cache[clone] = canonical
|
||||
else:
|
||||
_mame_clone_map_cache = {}
|
||||
return _mame_clone_map_cache
|
||||
|
||||
|
||||
|
||||
@@ -1000,27 +942,6 @@ class ProfileSelectionError(ValueError):
|
||||
"""A named profile cannot answer for itself."""
|
||||
|
||||
|
||||
def profiles_for_systems(
|
||||
profiles: dict, system_ids: list[str], standalone: bool = False
|
||||
) -> list[str]:
|
||||
"""Profile keys that serve any of the systems, spelled either way.
|
||||
|
||||
Compared on the normalized id: stella2014 writes atari_2600 where every
|
||||
other profile writes atari-2600, and an exact match built two different
|
||||
packs under one file name, the second replacing the first.
|
||||
"""
|
||||
wanted = {_norm_system_id(sid) for sid in system_ids}
|
||||
matching = []
|
||||
for name, profile in sorted(profiles.items()):
|
||||
if profile.get("type") in ("launcher", "alias", "test"):
|
||||
continue
|
||||
if standalone and "standalone" not in profile.get("type", "libretro"):
|
||||
continue
|
||||
if wanted & {_norm_system_id(sid) for sid in profile.get("systems", [])}:
|
||||
matching.append(name)
|
||||
return matching
|
||||
|
||||
|
||||
def select_emulator_profiles(
|
||||
profile_names: list[str],
|
||||
all_profiles: dict,
|
||||
@@ -1709,24 +1630,17 @@ def list_emulator_profiles(emulators_dir: str, skip_aliases: bool = True) -> Non
|
||||
|
||||
|
||||
def list_system_ids(emulators_dir: str) -> None:
|
||||
"""Print available system IDs with emulator count.
|
||||
|
||||
Counted on the normalized id, as --system selects: listed per spelling,
|
||||
3do showed two emulators and panasonic-3do six for one system.
|
||||
"""
|
||||
"""Print available system IDs with emulator count."""
|
||||
profiles = load_emulator_profiles(emulators_dir)
|
||||
system_emus: dict[str, set[str]] = {}
|
||||
spelling: dict[str, str] = {}
|
||||
for name, p in sorted(profiles.items()):
|
||||
system_emus: dict[str, list[str]] = {}
|
||||
for name, p in profiles.items():
|
||||
if p.get("type") in ("alias", "test", "launcher"):
|
||||
continue
|
||||
for sys_id in p.get("systems", []):
|
||||
key = _norm_system_id(sys_id)
|
||||
system_emus.setdefault(key, set()).add(name)
|
||||
spelling.setdefault(key, sys_id)
|
||||
for key in sorted(system_emus, key=lambda k: spelling[k]):
|
||||
count = len(system_emus[key])
|
||||
print(f" {spelling[key]:35s} ({count} emulator{'s' if count > 1 else ''})")
|
||||
system_emus.setdefault(sys_id, []).append(name)
|
||||
for sys_id in sorted(system_emus):
|
||||
count = len(system_emus[sys_id])
|
||||
print(f" {sys_id:35s} ({count} emulator{'s' if count > 1 else ''})")
|
||||
|
||||
|
||||
def list_platform_system_ids(platform_name: str, platforms_dir: str) -> None:
|
||||
@@ -1802,11 +1716,8 @@ from ziptools import ( # noqa: E402,F401
|
||||
)
|
||||
from artifacts import ( # noqa: E402,F401
|
||||
write_if_changed,
|
||||
write_text_atomic,
|
||||
copy_file_atomic,
|
||||
ArtifactLockBusy,
|
||||
artifact_lock,
|
||||
hold_artifact_lock,
|
||||
_TIMESTAMP_PATTERNS,
|
||||
_strip_timestamps,
|
||||
)
|
||||
|
||||
+96
-181
@@ -20,7 +20,6 @@ from pathlib import Path
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
from common import (
|
||||
_norm_system_id,
|
||||
get_mame_clone_map,
|
||||
list_registered_platforms,
|
||||
load_database,
|
||||
@@ -29,10 +28,7 @@ from common import (
|
||||
name_match_size_ok,
|
||||
parse_md5_list,
|
||||
require_yaml,
|
||||
resolve_local_file,
|
||||
runs_standalone,
|
||||
)
|
||||
from validation import outside_gap_scope
|
||||
|
||||
yaml = require_yaml()
|
||||
|
||||
@@ -48,9 +44,6 @@ def load_platform_files(
|
||||
"""Collect declared filenames + data_directories per system.
|
||||
|
||||
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 = {}
|
||||
platform_data_dirs = {}
|
||||
@@ -62,87 +55,58 @@ def load_platform_files(
|
||||
for fe in system.get("files", []):
|
||||
name = fe.get("name", "")
|
||||
if name:
|
||||
declared.setdefault(_norm_system_id(sys_id), set()).add(name)
|
||||
declared.setdefault(sys_id, set()).add(name)
|
||||
for dd in system.get("data_directories", []):
|
||||
ref = dd.get("ref", "")
|
||||
if ref:
|
||||
platform_data_dirs.setdefault(_norm_system_id(sys_id), set()).add(ref)
|
||||
platform_data_dirs.setdefault(sys_id, set()).add(ref)
|
||||
return declared, platform_data_dirs
|
||||
|
||||
|
||||
def declared_names(platforms_dir: str, platforms: list[str] | None, db: dict) -> set[str]:
|
||||
"""Every name the platforms declare, under every spelling the database knows.
|
||||
|
||||
The report judged "undeclared" per normalized system id, which does not
|
||||
bring bk and elektronika-bk, or nintendo-sgb and nintendo-super-game-boy,
|
||||
together: 646 entries whose exact name a platform declares were counted
|
||||
undeclared, while verify and the site read one flat set.
|
||||
"""
|
||||
from common import expand_platform_declared_names
|
||||
|
||||
names: set[str] = set()
|
||||
for platform_name in platforms or list_registered_platforms(
|
||||
platforms_dir, include_archived=True
|
||||
):
|
||||
config = load_platform_config(platform_name, platforms_dir)
|
||||
names.update(expand_platform_declared_names(config, db))
|
||||
return names
|
||||
|
||||
|
||||
def _build_supplemental_index(
|
||||
data_root: str = "data", bios_root: str = "bios"
|
||||
) -> set[str]:
|
||||
"""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.
|
||||
"""
|
||||
"""Build a set of filenames and directory names in data/ and inside bios/ ZIPs."""
|
||||
names: set[str] = set()
|
||||
root_path = Path(data_root)
|
||||
if root_path.is_dir():
|
||||
for fpath in root_path.rglob("*"):
|
||||
if fpath.name.startswith("."):
|
||||
continue
|
||||
if fpath.is_file():
|
||||
names.add(fpath.name)
|
||||
names.add(fpath.name.lower())
|
||||
else:
|
||||
names.add(fpath.name + "/")
|
||||
names.add(fpath.name.lower() + "/")
|
||||
names.add(fpath.name)
|
||||
names.add(fpath.name.lower())
|
||||
if fpath.is_dir():
|
||||
# Also index relative path from data/subdir/ for directory entries
|
||||
parts = fpath.relative_to(root_path).parts
|
||||
if len(parts) > 1:
|
||||
rel = "/".join(parts[1:])
|
||||
names.add(rel)
|
||||
names.add(rel + "/")
|
||||
names.add(rel.lower())
|
||||
names.add(rel.lower() + "/")
|
||||
bios_path = Path(bios_root)
|
||||
if bios_path.is_dir():
|
||||
# Index directory names for directory-type entries (e.g., "nestopia/samples/moepro/")
|
||||
for dpath in bios_path.rglob("*"):
|
||||
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.lower() + "/")
|
||||
names |= _zip_member_names(bios_path)
|
||||
return names
|
||||
import zipfile
|
||||
|
||||
|
||||
def _zip_member_names(bios_path: Path) -> set[str]:
|
||||
"""Basenames of the files inside every ZIP under bios/, both cases."""
|
||||
import zipfile
|
||||
|
||||
names: set[str] = set()
|
||||
for zpath in bios_path.rglob("*.zip"):
|
||||
try:
|
||||
with zipfile.ZipFile(zpath) as zf:
|
||||
members = [m for m in zf.namelist() if not m.endswith("/")]
|
||||
except (zipfile.BadZipFile, OSError) as exc:
|
||||
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()))
|
||||
for zpath in bios_path.rglob("*.zip"):
|
||||
try:
|
||||
with zipfile.ZipFile(zpath) as zf:
|
||||
for member in zf.namelist():
|
||||
if not member.endswith("/"):
|
||||
basename = (
|
||||
member.rsplit("/", 1)[-1] if "/" in member else member
|
||||
)
|
||||
names.add(basename)
|
||||
names.add(basename.lower())
|
||||
except (zipfile.BadZipFile, OSError):
|
||||
pass
|
||||
return names
|
||||
|
||||
|
||||
@@ -154,7 +118,6 @@ def _resolve_source(
|
||||
by_path_suffix: dict | None = None,
|
||||
file_entry: dict | None = None,
|
||||
db_files: dict | None = None,
|
||||
bios_dir: str | None = None,
|
||||
) -> str | None:
|
||||
"""Return the source category for a file, or None if not found.
|
||||
|
||||
@@ -197,7 +160,7 @@ def _resolve_source(
|
||||
return "bios"
|
||||
# bios/ under the canonical MAME set name, as resolve_local_file does:
|
||||
# a renamed archive is held once, under the name the dedup kept.
|
||||
canonical = get_mame_clone_map(bios_dir).get(fname)
|
||||
canonical = get_mame_clone_map().get(fname)
|
||||
if canonical and canonical != fname:
|
||||
if canonical in by_name and _name_hit(canonical):
|
||||
return "bios"
|
||||
@@ -205,109 +168,15 @@ def _resolve_source(
|
||||
by_name_lower[canonical.lower()]
|
||||
):
|
||||
return "bios"
|
||||
if data_names and _data_hit(fname, basename, file_entry, data_names):
|
||||
return "data"
|
||||
# data/ supplemental index
|
||||
if data_names:
|
||||
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
|
||||
|
||||
|
||||
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,
|
||||
index.get("bios_dir"),
|
||||
)
|
||||
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(
|
||||
archive_name: str,
|
||||
by_name: dict[str, list],
|
||||
@@ -328,7 +197,6 @@ def _cross_reference_profile(
|
||||
declared: dict[str, set[str]],
|
||||
report: dict,
|
||||
index: dict,
|
||||
standalone_cores: set[str] | None = None,
|
||||
) -> None:
|
||||
"""Compare one emulator profile against what the platforms declare.
|
||||
|
||||
@@ -339,7 +207,10 @@ def _cross_reference_profile(
|
||||
"""
|
||||
by_name = index["by_name"]
|
||||
by_name_lower = index["by_name_lower"]
|
||||
by_md5 = index["by_md5"]
|
||||
by_crc32 = index["by_crc32"]
|
||||
by_path_suffix = index["by_path_suffix"]
|
||||
db_files = index["db_files"]
|
||||
data_names = index["data_names"]
|
||||
all_declared = index["all_declared"]
|
||||
emu_files = profile.get("files", [])
|
||||
@@ -354,11 +225,8 @@ def _cross_reference_profile(
|
||||
else:
|
||||
platform_names = set()
|
||||
for sys_id in systems:
|
||||
platform_names.update(declared.get(_norm_system_id(sys_id), set()))
|
||||
platform_names.update(declared.get(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 = []
|
||||
covered = []
|
||||
unsourceable_list: list[dict] = []
|
||||
@@ -390,7 +258,26 @@ def _cross_reference_profile(
|
||||
})
|
||||
continue
|
||||
|
||||
if outside_gap_scope(f, is_standalone):
|
||||
# Skip pattern placeholders (e.g., <bios>.bin, <user-selected>.bin)
|
||||
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
|
||||
|
||||
archive = f.get("archive")
|
||||
@@ -452,7 +339,44 @@ def _cross_reference_profile(
|
||||
if storage in ("release", "large_file"):
|
||||
source = "large_file"
|
||||
else:
|
||||
source = entry_source(f, index)
|
||||
source = _resolve_source(
|
||||
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:
|
||||
source = "missing"
|
||||
|
||||
@@ -497,7 +421,6 @@ def cross_reference(
|
||||
platform_data_dirs: dict[str, set[str]] | None = None,
|
||||
data_names: set[str] | None = None,
|
||||
all_declared: set[str] | None = None,
|
||||
standalone_cores: set[str] | None = None,
|
||||
) -> dict:
|
||||
"""Compare emulator profiles against platform declarations.
|
||||
|
||||
@@ -519,7 +442,6 @@ def cross_reference(
|
||||
by_name_lower = {k.lower(): k for k in by_name}
|
||||
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
||||
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", {})
|
||||
db_files = db.get("files", {})
|
||||
report = {}
|
||||
@@ -529,16 +451,14 @@ def cross_reference(
|
||||
"by_name_lower": by_name_lower,
|
||||
"by_md5": by_md5,
|
||||
"by_crc32": by_crc32,
|
||||
"by_sha256": by_sha256,
|
||||
"by_path_suffix": by_path_suffix,
|
||||
"db_files": db_files,
|
||||
"data_names": data_names,
|
||||
"all_declared": all_declared,
|
||||
"bios_dir": db.get("bios_dir"),
|
||||
}
|
||||
for emu_name, profile in profiles.items():
|
||||
_cross_reference_profile(
|
||||
emu_name, profile, declared, report, index, standalone_cores
|
||||
emu_name, profile, declared, report, index
|
||||
)
|
||||
|
||||
return report
|
||||
@@ -615,7 +535,6 @@ def main():
|
||||
if args.target and not args.platform:
|
||||
parser.error("--target requires --platform")
|
||||
|
||||
standalone_cores: set[str] = set()
|
||||
if args.platform:
|
||||
from common import load_target_config, resolve_platform_cores
|
||||
|
||||
@@ -627,21 +546,17 @@ def main():
|
||||
config = load_platform_config(args.platform, args.platforms_dir)
|
||||
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
||||
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:
|
||||
print("No emulator profiles found.", file=sys.stderr)
|
||||
return
|
||||
|
||||
selected = [args.platform] if args.platform else None
|
||||
declared, plat_data_dirs = load_platform_files(args.platforms_dir, selected)
|
||||
declared, plat_data_dirs = load_platform_files(
|
||||
args.platforms_dir, [args.platform] if args.platform else None
|
||||
)
|
||||
db = load_database(args.db)
|
||||
data_names = _build_supplemental_index()
|
||||
report = cross_reference(
|
||||
profiles, declared, db, plat_data_dirs, data_names,
|
||||
all_declared=declared_names(args.platforms_dir, selected, db),
|
||||
standalone_cores=standalone_cores,
|
||||
)
|
||||
report = cross_reference(profiles, declared, db, plat_data_dirs, data_names)
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(report, indent=2))
|
||||
|
||||
@@ -38,19 +38,6 @@ def _is_placeholder(name: str) -> bool:
|
||||
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
|
||||
class NativeFile:
|
||||
"""One file as the platform will read it, after correction."""
|
||||
@@ -74,26 +61,19 @@ class NativeFile:
|
||||
"""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
|
||||
platform's sha1, a record no file satisfies. The truth decides alone
|
||||
only on a contradiction: a hash both sides declare without a value in
|
||||
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.
|
||||
platform's sha1, a record no file satisfies. Both sides are merged
|
||||
only where they agree on some hash; where they contradict, the truth
|
||||
decides alone; where the truth declares none, the platform does.
|
||||
"""
|
||||
truth, plat = _field_sets(self.truth), _field_sets(self.platform)
|
||||
truth = {f: set(_hash_values(self.truth or {}, f)) for f in HASH_FIELDS}
|
||||
plat = {f: set(_hash_values(self.platform or {}, f)) for f in HASH_FIELDS}
|
||||
if not any(truth.values()):
|
||||
return "platform"
|
||||
if not any(plat.values()):
|
||||
return "truth"
|
||||
shared = [f for f in HASH_FIELDS if truth[f] and plat[f]]
|
||||
if any(truth[f] & plat[f] for f in shared):
|
||||
if any(truth[f] & plat[f] for f in HASH_FIELDS):
|
||||
return "both"
|
||||
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
|
||||
return "truth"
|
||||
|
||||
def hashes(self, field_name: str) -> list[str]:
|
||||
"""Accepted values for a hash, truth first when it has an opinion.
|
||||
@@ -156,7 +136,7 @@ class NativeFile:
|
||||
order = (self.platform, self.truth) if source == "platform" else (self.truth, self.platform)
|
||||
for entry in order:
|
||||
if entry and entry.get("size"):
|
||||
return _single_size(entry["size"])
|
||||
return int(entry["size"])
|
||||
return None
|
||||
|
||||
def native(self, key: str, default: object = "") -> object:
|
||||
@@ -322,27 +302,14 @@ def build_native_model(
|
||||
if candidate.truth is None
|
||||
]
|
||||
|
||||
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.
|
||||
def by_destination(candidate: NativeFile) -> bool:
|
||||
theirs = _match_key(candidate.platform or {})[0]
|
||||
return bool(t_dest) and (theirs == t_dest or theirs.endswith("/" + t_dest))
|
||||
return bool(t_dest) and theirs == t_dest
|
||||
|
||||
def by_name(
|
||||
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 {})
|
||||
def by_name(candidate: NativeFile) -> bool:
|
||||
theirs = _match_key(candidate.platform or {})[1]
|
||||
if not t_name or theirs != t_name:
|
||||
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
|
||||
# 480-byte boot.bin is not RomM's 2 MB Dreamcast boot.bin.
|
||||
t_size = truth_entry.get("size")
|
||||
@@ -351,7 +318,7 @@ def build_native_model(
|
||||
isinstance(t_size, int) and isinstance(p_size, int) and t_size != p_size
|
||||
)
|
||||
|
||||
def by_hash(candidate: NativeFile, t_hashes: set = t_hashes) -> bool:
|
||||
def by_hash(candidate: NativeFile) -> bool:
|
||||
if not t_hashes:
|
||||
return False
|
||||
theirs = {
|
||||
|
||||
@@ -41,18 +41,6 @@ class Exporter(BaseExporter):
|
||||
def carries() -> frozenset[str]:
|
||||
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
|
||||
def native_sources() -> dict[str, str]:
|
||||
return {"batocera-systems": SOURCE_URL}
|
||||
@@ -62,29 +50,10 @@ class Exporter(BaseExporter):
|
||||
# The data block is a fraction of the file; the rest is the checker.
|
||||
return True
|
||||
|
||||
def _bios_files(self, files: list[NativeFile], before: dict | None = None) -> str:
|
||||
return ", ".join(self._bios_items(files, before))
|
||||
def _bios_files(self, files: list[NativeFile]) -> str:
|
||||
return ", ".join(self._bios_items(files))
|
||||
|
||||
@staticmethod
|
||||
def _runner_keys(item: dict | None) -> list[str]:
|
||||
"""The emulator and core checkBios reads through coreExists().
|
||||
|
||||
Not in our model, so they travel from the maintainer's own entry: a
|
||||
rewritten item without them made the checker treat the BIOS as
|
||||
wanted by every build (vectrex, tvc, macintosh, bk).
|
||||
"""
|
||||
return [
|
||||
f'"{key}": "{item[key]}"'
|
||||
for key in ("emulator", "core")
|
||||
if item and isinstance(item.get(key), str)
|
||||
]
|
||||
|
||||
def _bios_items(self, files: list[NativeFile], before: dict | None = None) -> list[str]:
|
||||
originals = {
|
||||
str(item.get("file", "")): item
|
||||
for item in (before or {}).get("biosFiles", [])
|
||||
if isinstance(item, dict)
|
||||
}
|
||||
def _bios_items(self, files: list[NativeFile]) -> list[str]:
|
||||
parts: list[str] = []
|
||||
for fe in files:
|
||||
# The platform states an unhashed file as an empty md5 rather
|
||||
@@ -99,25 +68,14 @@ class Exporter(BaseExporter):
|
||||
zipped = fe.native("zipped_file", "")
|
||||
if zipped:
|
||||
item.append(f'"zippedFile": "{zipped}"')
|
||||
item.extend(self._runner_keys(originals.get(path)))
|
||||
parts.append("{ " + ", ".join(item) + " }")
|
||||
return parts
|
||||
|
||||
@staticmethod
|
||||
def _own_entry(system: NativeSystem, before: dict | None) -> dict:
|
||||
"""The maintainer's entry for this system, out of the parsed pair."""
|
||||
inner = (before or {}).get(system.native_id)
|
||||
return inner if isinstance(inner, dict) else {}
|
||||
|
||||
def _entry_line(
|
||||
self, system: NativeSystem, files: list[NativeFile], before: dict | None = None
|
||||
) -> str:
|
||||
own = self._own_entry(system, before)
|
||||
runner = "".join(f"{key}, " for key in self._runner_keys(own))
|
||||
def _entry_line(self, system: NativeSystem, files: list[NativeFile]) -> str:
|
||||
return (
|
||||
f'{_INDENT}"{system.native_id}": '
|
||||
f'{{ "name": "{self.display_name(system)}", {runner}'
|
||||
f'"biosFiles": [ {self._bios_files(files, own)} ] }},'
|
||||
f'{{ "name": "{self.display_name(system)}", '
|
||||
f'"biosFiles": [ {self._bios_files(files)} ] }},'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -157,17 +115,14 @@ class Exporter(BaseExporter):
|
||||
) -> list[str]:
|
||||
"""One entry, keeping the layout and the notes it was written with."""
|
||||
trailing, leading = self._item_notes(original)
|
||||
before = self._parse_entry(original)
|
||||
own = self._own_entry(system, before)
|
||||
items = self._bios_items(files, own)
|
||||
items = self._bios_items(files)
|
||||
one_line = len(original) == 1 and not trailing and not leading
|
||||
if one_line:
|
||||
return [self._entry_line(system, files, before)]
|
||||
return [self._entry_line(system, files)]
|
||||
|
||||
runner = "".join(f"{key}, " for key in self._runner_keys(own))
|
||||
head = (
|
||||
f'{_INDENT}"{system.native_id}": '
|
||||
f'{{ "name": "{self.display_name(system)}", {runner}"biosFiles": ['
|
||||
f'{{ "name": "{self.display_name(system)}", "biosFiles": ['
|
||||
)
|
||||
pad = " " * (len(_INDENT) + 4)
|
||||
lines = [head]
|
||||
@@ -277,8 +232,8 @@ class Exporter(BaseExporter):
|
||||
continue
|
||||
written.add(key)
|
||||
original_lines = body[start:end + 1]
|
||||
replacement = self._entry_line(*pair)
|
||||
before = self._parse_entry(original_lines)
|
||||
replacement = self._entry_line(*pair, before)
|
||||
after = self._parse_entry([replacement])
|
||||
if before is not None and before == after:
|
||||
# Nothing changed: keep the maintainer's own lines, comments
|
||||
|
||||
@@ -11,7 +11,7 @@ from __future__ import annotations
|
||||
import re
|
||||
|
||||
from .base_exporter import BaseExporter
|
||||
from .baseline import NativeFile, NativeSystem, Report, _hash_values
|
||||
from .baseline import NativeFile, NativeSystem, Report
|
||||
|
||||
SOURCE_URL = (
|
||||
"https://raw.githubusercontent.com/TASEmulators/BizHawk/master"
|
||||
@@ -60,14 +60,6 @@ class Exporter(BaseExporter):
|
||||
# hang off exist nowhere else.
|
||||
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
|
||||
def _unambiguous(systems: dict[str, NativeSystem]) -> dict[str, NativeFile]:
|
||||
"""Files whose name identifies exactly one entry with a SHA1.
|
||||
@@ -80,17 +72,7 @@ class Exporter(BaseExporter):
|
||||
seen: dict[str, list[NativeFile]] = {}
|
||||
for system in systems.values():
|
||||
for fe in system.files:
|
||||
# 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. An entry the
|
||||
# database never declared (can_add refuses it) names no call
|
||||
# of its own either: by name it would rewrite the homonym of
|
||||
# another system.
|
||||
if (
|
||||
fe.platform is not None
|
||||
and fe.hash("sha1")
|
||||
and not _hash_values(fe.platform, "sha1")
|
||||
):
|
||||
if fe.hash("sha1"):
|
||||
seen.setdefault(fe.name.casefold(), []).append(fe)
|
||||
resolved: dict[str, NativeFile] = {}
|
||||
for name, entries in seen.items():
|
||||
@@ -114,7 +96,6 @@ class Exporter(BaseExporter):
|
||||
)
|
||||
|
||||
index = self._unambiguous(systems)
|
||||
self._touchable = set(index)
|
||||
|
||||
def commented_out(text: str, position: int) -> bool:
|
||||
"""Whether the call sits on a line the compiler never sees.
|
||||
|
||||
@@ -90,35 +90,8 @@ class Exporter(BaseExporter):
|
||||
def states(self, fe: NativeFile, field_name: str) -> bool:
|
||||
if field_name in fe.filled:
|
||||
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)
|
||||
|
||||
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
|
||||
def _md5s(cls, systems: dict[str, NativeSystem], system_id: str) -> list[str]:
|
||||
"""Every MD5 the system accepts, in a stable order, deduplicated."""
|
||||
@@ -178,9 +151,7 @@ class Exporter(BaseExporter):
|
||||
# and our model does not was deleted: a user whose dump matched
|
||||
# it would stop passing the check. A rewrite that withdraws one
|
||||
# is refused and reported instead.
|
||||
# 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)
|
||||
withdrawn = theirs - set(md5s)
|
||||
if withdrawn:
|
||||
self._withdrawn.setdefault(name, set()).update(withdrawn)
|
||||
elif set(md5s) != theirs:
|
||||
@@ -208,7 +179,7 @@ class Exporter(BaseExporter):
|
||||
if name not in defined:
|
||||
issues.append(f"check absent from the output: {name}")
|
||||
continue
|
||||
md5s = self._expected_md5s(systems, name, system_id)
|
||||
md5s = self._md5s(systems, system_id)
|
||||
if not md5s:
|
||||
continue
|
||||
body = next(
|
||||
|
||||
@@ -13,7 +13,7 @@ import zipfile
|
||||
from collections import OrderedDict
|
||||
|
||||
from .base_exporter import BaseExporter
|
||||
from .baseline import NativeFile, NativeSystem, Report
|
||||
from .baseline import NativeSystem, Report
|
||||
|
||||
SOURCE_URL = (
|
||||
"https://raw.githubusercontent.com/ajgowans/BiosDB_MiSTer/db/bios_db.json.zip"
|
||||
@@ -57,13 +57,6 @@ class Exporter(BaseExporter):
|
||||
with zipfile.ZipFile(io.BytesIO(raw)) as archive:
|
||||
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]:
|
||||
indexed: dict[str, object] = {}
|
||||
for system in systems.values():
|
||||
@@ -87,13 +80,11 @@ class Exporter(BaseExporter):
|
||||
)
|
||||
database = json.loads(original, object_pairs_hook=OrderedDict)
|
||||
indexed = self._by_path(systems)
|
||||
self._written = set()
|
||||
|
||||
for path, entry in database.get("files", {}).items():
|
||||
fe = indexed.get(path)
|
||||
if fe is None:
|
||||
continue
|
||||
self._written.add(id(fe))
|
||||
md5 = fe.hash("md5")
|
||||
if md5:
|
||||
entry["hash"] = md5
|
||||
|
||||
@@ -176,8 +176,8 @@ class Exporter(BaseExporter):
|
||||
return nested, "bios"
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def _merge(cls, existing: object, ours: list[OrderedDict]) -> list[OrderedDict]:
|
||||
@staticmethod
|
||||
def _merge(existing: object, ours: list[OrderedDict]) -> list[OrderedDict]:
|
||||
"""Correct the component's own list; never replace it.
|
||||
|
||||
Assigning our entries wholesale dropped every file RetroDECK declares
|
||||
@@ -206,9 +206,6 @@ class Exporter(BaseExporter):
|
||||
for system in _systems_of(entry):
|
||||
key = (str(entry.get("filename", "")), system)
|
||||
seen_keys[key] = seen_keys.get(key, 0) + 1
|
||||
# A hash no revision shares is a revision the platform does not
|
||||
# know: appended, not counted corrected against entries kept as
|
||||
# they were (the 64DD IPL declared twice, both contradicted).
|
||||
for entry in entries:
|
||||
keys = _matching_keys(entry, by_key)
|
||||
other_revision = (
|
||||
@@ -216,17 +213,18 @@ class Exporter(BaseExporter):
|
||||
and seen_keys.get(keys[0], 0) > 1
|
||||
and not _md5_set(entry.get("md5")) & _md5_set(by_key[keys[0]].get("md5"))
|
||||
)
|
||||
if other_revision or not keys:
|
||||
if other_revision:
|
||||
merged.append(OrderedDict(entry))
|
||||
corrected.update(keys)
|
||||
continue
|
||||
if not keys:
|
||||
merged.append(OrderedDict(entry))
|
||||
continue
|
||||
declared = entry.get("system")
|
||||
combined = OrderedDict(entry)
|
||||
# Only what the format says it corrects: the maintainer's
|
||||
# description and search paths are theirs (neogeo.zip lost its
|
||||
# four roms directories to a path that does not exist).
|
||||
combined.update(
|
||||
(field, value) for field, value in by_key[keys[0]].items()
|
||||
if field in cls.carries() and not (field == "system" and declared)
|
||||
if not (field == "system" and declared)
|
||||
)
|
||||
merged.append(combined)
|
||||
corrected.update(keys)
|
||||
|
||||
@@ -29,9 +29,6 @@ SOURCE_URL = (
|
||||
class Exporter(BaseExporter):
|
||||
"""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
|
||||
def platform_name() -> str:
|
||||
return "romm"
|
||||
@@ -64,20 +61,16 @@ class Exporter(BaseExporter):
|
||||
"""
|
||||
return native_id in SLUG_MAP
|
||||
|
||||
def writable(self, fe: NativeFile, require: str = "") -> bool: # noqa: ARG002
|
||||
@classmethod
|
||||
def writable(cls, fe: NativeFile, require: str = "") -> bool:
|
||||
"""What RomM already ships stays; the conditions gate additions.
|
||||
|
||||
An entry of theirs that could never verify is still theirs, and the
|
||||
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.
|
||||
round trip is not the place to decide otherwise.
|
||||
"""
|
||||
if fe.platform is not None:
|
||||
return True
|
||||
if (fe.native_system, fe.name) in self._platform_keys:
|
||||
return False
|
||||
return self._verifiable(fe) and self._known_platform(fe.native_system)
|
||||
return cls._verifiable(fe) and cls._known_platform(fe.native_system)
|
||||
|
||||
def render(
|
||||
self,
|
||||
@@ -87,12 +80,6 @@ class Exporter(BaseExporter):
|
||||
scraped: dict | None = None,
|
||||
) -> dict[str, str]:
|
||||
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 fe in sorted(system.files, key=lambda f: f.name):
|
||||
|
||||
@@ -6,8 +6,6 @@ libretro gives them, matching libretro-database/dat/System.dat.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -23,17 +21,9 @@ SOURCE_URL = (
|
||||
)
|
||||
|
||||
|
||||
_QUOTED_NAME = re.compile(r'rom \( 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
|
||||
def _quote(name: str) -> str:
|
||||
"""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
|
||||
|
||||
|
||||
class Exporter(BaseExporter):
|
||||
@@ -118,7 +108,6 @@ class Exporter(BaseExporter):
|
||||
) -> dict[str, str]:
|
||||
lines = self._header(originals, scraped)
|
||||
lines.extend(["game (", '\tname "System"', '\tcomment "System"'])
|
||||
quoted = frozenset(_QUOTED_NAME.findall(originals.get(self.native_filename(), "")))
|
||||
|
||||
for system, files in sorted(
|
||||
self.exportable(systems), key=lambda pair: pair[0].native_id
|
||||
@@ -127,7 +116,7 @@ class Exporter(BaseExporter):
|
||||
for fe in files:
|
||||
if not any(fe.hash(h) for h in ("crc32", "md5", "sha1")):
|
||||
continue
|
||||
parts = [f"name {_quote(self._rom_name(fe), quoted)}"]
|
||||
parts = [f"name {_quote(self._rom_name(fe))}"]
|
||||
size = fe.size()
|
||||
if size:
|
||||
parts.append(f"size {size}")
|
||||
|
||||
+2
-23
@@ -15,7 +15,6 @@ import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
@@ -271,24 +270,6 @@ 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:
|
||||
"""Keep the entries of release assets the checkout does not hold.
|
||||
|
||||
@@ -328,7 +309,8 @@ def _preserve_large_file_entries(files: dict, db_path: str) -> int:
|
||||
expected_md5=entry.get("md5", ""),
|
||||
)
|
||||
if cached and not os.path.exists(path):
|
||||
_restore(cached, path)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
shutil.copy2(cached, path)
|
||||
files[sha1] = entry
|
||||
count += 1
|
||||
return count
|
||||
@@ -386,9 +368,6 @@ def main():
|
||||
"generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"total_files": len(files),
|
||||
"total_size": total_size,
|
||||
# The tree these paths belong to: dedup writes _mame_clones.json
|
||||
# beside it, and the resolver reads the map there, not at the repo root.
|
||||
"bios_dir": os.path.normpath(args.bios_dir),
|
||||
"files": files,
|
||||
"indexes": indexes,
|
||||
}
|
||||
|
||||
+140
-255
@@ -31,8 +31,6 @@ sys.path.insert(0, os.path.dirname(__file__))
|
||||
from common import resolve_platform_cores as _platform_cores
|
||||
from common import (
|
||||
apply_target_overrides,
|
||||
profiles_for_systems,
|
||||
write_text_atomic,
|
||||
artifact_lock,
|
||||
ArtifactLockBusy,
|
||||
build_target_cores_cache,
|
||||
@@ -75,9 +73,6 @@ from nativemode import (
|
||||
reads_file_contents,
|
||||
)
|
||||
from validation import (
|
||||
existence_discrepancy,
|
||||
destination_owners,
|
||||
validated_choice,
|
||||
inner_rom_check,
|
||||
settle_mismatch,
|
||||
agnostic_substitute,
|
||||
@@ -206,18 +201,6 @@ def _system_tag(system_filter: list[str] | None) -> str:
|
||||
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(
|
||||
group_platforms: list[str], representative: str, platforms_dir: str
|
||||
) -> str:
|
||||
@@ -228,14 +211,14 @@ def _platform_pack_stem(
|
||||
"""
|
||||
rep_cfg = load_platform_config(representative, platforms_dir)
|
||||
version = rep_cfg.get("version", rep_cfg.get("dat_version", ""))
|
||||
version_tag = f"_{_name_part(str(version))}" if version else ""
|
||||
version_tag = f"_{version.replace(' ', '')}" if version else ""
|
||||
if len(group_platforms) <= 1:
|
||||
return _name_part(rep_cfg.get("platform", representative), "_") + version_tag
|
||||
return rep_cfg.get("platform", representative).replace(" ", "_") + version_tag
|
||||
names = [
|
||||
load_platform_config(p, platforms_dir).get("platform", p)
|
||||
for p in group_platforms
|
||||
]
|
||||
return "_".join(_name_part(n) for n in names) + version_tag
|
||||
return "_".join(n.replace(" ", "") for n in names) + version_tag
|
||||
|
||||
|
||||
|
||||
@@ -579,30 +562,13 @@ def _select_variants(
|
||||
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(
|
||||
platform_name: str,
|
||||
platforms_dir: str,
|
||||
db: dict,
|
||||
bios_dir: str,
|
||||
output_dir: str,
|
||||
include_extras: bool = False,
|
||||
emulators_dir: str = "emulators",
|
||||
zip_contents: dict | None = None,
|
||||
data_registry: dict | None = None,
|
||||
@@ -611,21 +577,15 @@ def generate_pack(
|
||||
required_only: bool = False,
|
||||
system_filter: list[str] | None = None,
|
||||
precomputed_extras: list[dict] | None = None,
|
||||
extras_only: bool = False,
|
||||
source: str = "full",
|
||||
flatten: bool = True,
|
||||
regions: list[str] | None = None,
|
||||
target_name: str | None = None,
|
||||
one_per_slot: bool = False,
|
||||
offline: bool | None = None,
|
||||
pack_name: str | None = None,
|
||||
) -> str | None:
|
||||
"""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.
|
||||
"""
|
||||
config = load_platform_config(platform_name, platforms_dir)
|
||||
@@ -643,12 +603,10 @@ def generate_pack(
|
||||
narrowings = _narrowings(
|
||||
source, regions, target_name, one_per_slot, required_only
|
||||
)
|
||||
zip_path = os.path.join(
|
||||
output_dir,
|
||||
_platform_pack_name(
|
||||
platform_name, platforms_dir, narrowings, system_filter, pack_name
|
||||
),
|
||||
)
|
||||
narrow_tags = "".join(tag for tag, _label in narrowings)
|
||||
stem = _platform_pack_stem([platform_name], platform_name, platforms_dir)
|
||||
zip_name = f"{stem}{narrow_tags}_BIOS_Pack{_system_tag(system_filter)}.zip"
|
||||
zip_path = os.path.join(output_dir, zip_name)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Case-insensitive dedup only for platforms targeting Windows/macOS.
|
||||
@@ -685,7 +643,6 @@ def generate_pack(
|
||||
for name in resolve_platform_cores(config, emu_profiles)
|
||||
}
|
||||
validation_index = _build_validation_index(platform_profiles)
|
||||
validation_owners = destination_owners(platform_profiles)
|
||||
slot_overrides = slots.pack_overrides(
|
||||
config, emu_profiles or {}, db, zip_contents, data_registry
|
||||
)
|
||||
@@ -718,20 +675,13 @@ def generate_pack(
|
||||
)
|
||||
return None
|
||||
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] = {}
|
||||
if source != "truth":
|
||||
# Decided among the declarations this pack keeps: a preferred entry
|
||||
# that --required-only drops must not silence its required twin, or
|
||||
# the destination leaves the pack without a trace (RetroDECK's four
|
||||
# Atari 5200 ROMs, optional under atari-400-800 and required under
|
||||
# atari-5200). No data directories: the choice is about the baseline.
|
||||
# A manifest lists the full set and carries no data directories, so
|
||||
# the preference is resolved with neither narrowing nor a cache.
|
||||
preferred_entries = _preferred_entries(
|
||||
pack_systems, db, bios_dir, base_dest, required_only,
|
||||
pack_systems, db, bios_dir, base_dest, False,
|
||||
zip_contents, None, offline,
|
||||
)
|
||||
|
||||
@@ -901,12 +851,8 @@ def generate_pack(
|
||||
file_reasons[dedup_key] = "not found"
|
||||
continue
|
||||
|
||||
# Batocera hashes the member, never the ZIP: a zipped_file entry
|
||||
# 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
|
||||
if status == "hash_mismatch" and hash_mismatch_excludes_file(
|
||||
verification_mode
|
||||
):
|
||||
zf_name = file_entry.get("zipped_file")
|
||||
if zf_name and local_path:
|
||||
@@ -942,9 +888,15 @@ def generate_pack(
|
||||
# their code never reads the bytes. An upstream hash that
|
||||
# contradicts a local dump is reported, not acted on.
|
||||
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(
|
||||
dedup_key,
|
||||
existence_discrepancy(file_entry, local_path, platform_display),
|
||||
f"packed per {platform_display} existence check; "
|
||||
f"declared hash {declared}, file md5 "
|
||||
f"{actual['md5']} sha1 {actual['sha1']}",
|
||||
)
|
||||
file_status.setdefault(dedup_key, "ok")
|
||||
|
||||
@@ -952,15 +904,33 @@ def generate_pack(
|
||||
# Platform verification (existence/md5) is the authority for pack status.
|
||||
# Emulator checks are supplementary -logged but don't downgrade.
|
||||
# When a discrepancy is found, try to find a file satisfying both.
|
||||
if file_status.get(dedup_key) == "ok":
|
||||
local_path, disagreement = validated_choice(
|
||||
file_entry, local_path, db, validation_index, bios_dir,
|
||||
digest_algorithm(verification_mode), dest, validation_owners,
|
||||
if (
|
||||
file_status.get(dedup_key) == "ok"
|
||||
and local_path
|
||||
and validation_index
|
||||
):
|
||||
fname = file_entry.get("name", "")
|
||||
check = check_file_validation(
|
||||
local_path, fname, validation_index, bios_dir
|
||||
)
|
||||
if disagreement:
|
||||
file_reasons.setdefault(
|
||||
dedup_key, f"{platform_display} says OK but {disagreement}"
|
||||
if check:
|
||||
reason, emus_list = check
|
||||
better = find_validated_variant(
|
||||
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:
|
||||
continue
|
||||
@@ -1252,38 +1222,6 @@ 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(
|
||||
profile_names: list[str],
|
||||
emulators_dir: str,
|
||||
@@ -1295,7 +1233,6 @@ def generate_emulator_pack(
|
||||
required_only: bool = False,
|
||||
regions: list[str] | None = None,
|
||||
offline: bool | None = None,
|
||||
platforms_dir: str | None = None,
|
||||
) -> str | None:
|
||||
"""Generate a ZIP pack for specific emulator profiles."""
|
||||
all_profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
|
||||
@@ -1309,7 +1246,7 @@ def generate_emulator_pack(
|
||||
return None
|
||||
|
||||
# ZIP naming
|
||||
display_names = [_name_part(p.get("emulator", n)) for n, p in selected]
|
||||
display_names = [p.get("emulator", n).replace(" ", "") for n, p in selected]
|
||||
validation_index = _build_validation_index(dict(selected))
|
||||
narrow_tags = "".join(
|
||||
tag
|
||||
@@ -1322,9 +1259,7 @@ def generate_emulator_pack(
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
region_drops: set[str] = (
|
||||
emulator_region_drops(selected, standalone, regions, required_only)
|
||||
if regions
|
||||
else set()
|
||||
emulator_region_drops(selected, standalone, regions) if regions else set()
|
||||
)
|
||||
|
||||
total_files = 0
|
||||
@@ -1337,7 +1272,7 @@ def generate_emulator_pack(
|
||||
seen_hashes: set[str] = set() # SHA1 dedup for same file, different path
|
||||
data_dir_notices: list[str] = []
|
||||
data_registry = load_data_dir_registry(
|
||||
platforms_dir or os.path.join(os.path.dirname(__file__), "..", "platforms")
|
||||
os.path.join(os.path.dirname(__file__), "..", "platforms")
|
||||
)
|
||||
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
@@ -1368,9 +1303,20 @@ def generate_emulator_pack(
|
||||
prefix = pack_structure.get(mode_key, "")
|
||||
if prefix:
|
||||
dd_dest = f"{prefix}/{dd_dest}" if dd_dest else prefix
|
||||
total_files += _pack_data_tree(
|
||||
zf, local_cache, dd_dest, seen_destinations, seen_parents, seen_lower
|
||||
)
|
||||
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:
|
||||
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:
|
||||
print(f" No files needed for {profile.get('emulator', emu_name)}")
|
||||
@@ -1557,11 +1503,19 @@ def generate_system_pack(
|
||||
required_only: bool = False,
|
||||
regions: list[str] | None = None,
|
||||
offline: bool | None = None,
|
||||
platforms_dir: str | None = None,
|
||||
) -> str | None:
|
||||
"""Generate a ZIP pack for all emulators supporting given system IDs."""
|
||||
profiles = load_emulator_profiles(emulators_dir)
|
||||
matching = profiles_for_systems(profiles, system_ids, standalone)
|
||||
matching = []
|
||||
for name, profile in sorted(profiles.items()):
|
||||
if profile.get("type") in ("launcher", "alias", "test"):
|
||||
continue
|
||||
emu_systems = set(profile.get("systems", []))
|
||||
if emu_systems & set(system_ids):
|
||||
ptype = profile.get("type", "libretro")
|
||||
if standalone and "standalone" not in ptype:
|
||||
continue
|
||||
matching.append(name)
|
||||
|
||||
if not matching:
|
||||
all_systems: set[str] = set()
|
||||
@@ -1583,42 +1537,38 @@ def generate_system_pack(
|
||||
)
|
||||
return None
|
||||
|
||||
# Named like the platform's --system tag: one spelling of a system is
|
||||
# one file name, and one selection.
|
||||
sys_display = "_".join(_system_display_name(sid) for sid in system_ids)
|
||||
# Built in a scratch directory: under its emulator name in output_dir it
|
||||
# overwrote, then carried away, an --emulator pack already there.
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
scratch = tempfile.mkdtemp(prefix=".system-", dir=output_dir)
|
||||
try:
|
||||
result = generate_emulator_pack(
|
||||
matching,
|
||||
emulators_dir,
|
||||
db,
|
||||
bios_dir,
|
||||
scratch,
|
||||
standalone,
|
||||
zip_contents,
|
||||
required_only=required_only,
|
||||
regions=regions,
|
||||
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.
|
||||
# Use system-based ZIP name
|
||||
sys_display = "_".join(
|
||||
"_".join(w.title() for w in sid.split("-")) for sid in system_ids
|
||||
)
|
||||
result = generate_emulator_pack(
|
||||
matching,
|
||||
emulators_dir,
|
||||
db,
|
||||
bios_dir,
|
||||
output_dir,
|
||||
standalone,
|
||||
zip_contents,
|
||||
required_only=required_only,
|
||||
regions=regions,
|
||||
offline=offline,
|
||||
)
|
||||
if result:
|
||||
# Rename to system-based name. 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(
|
||||
tag
|
||||
for tag, _label in _narrowings(
|
||||
"full", regions, None, False, required_only, standalone=standalone
|
||||
)
|
||||
)
|
||||
new_path = os.path.join(output_dir, f"{sys_display}{tags}_BIOS_Pack.zip")
|
||||
os.replace(result, new_path)
|
||||
return new_path
|
||||
finally:
|
||||
shutil.rmtree(scratch, ignore_errors=True)
|
||||
new_name = f"{sys_display}{tags}_BIOS_Pack.zip"
|
||||
new_path = os.path.join(output_dir, new_name)
|
||||
if new_path != result:
|
||||
os.rename(result, new_path)
|
||||
result = new_path
|
||||
return result
|
||||
|
||||
|
||||
def list_platforms(platforms_dir: str) -> list[str]:
|
||||
@@ -1703,13 +1653,10 @@ def generate_split_packs(
|
||||
# Two groupings write different files; without the tag they accumulate in
|
||||
# one directory under one SHA256SUMS.txt.
|
||||
group_tag = "" if group_by == "system" else f"_By{group_by.title()}"
|
||||
# The parts carry the version, so the directory does too: a rescrape that
|
||||
# moves the version would otherwise leave the old parts beside the new
|
||||
# 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")
|
||||
split_dir = os.path.join(
|
||||
output_dir,
|
||||
f"{platform_display.replace(' ', '_')}{split_tags}{group_tag}_Split",
|
||||
)
|
||||
os.makedirs(split_dir, exist_ok=True)
|
||||
|
||||
systems = config.get("systems", {})
|
||||
@@ -1755,14 +1702,25 @@ def generate_split_packs(
|
||||
target_name=target_name,
|
||||
one_per_slot=one_per_slot,
|
||||
offline=offline,
|
||||
pack_name=f"{part_prefix}_{_name_part(group_name, '_')}_BIOS_Pack.zip",
|
||||
)
|
||||
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)
|
||||
|
||||
# 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).
|
||||
# Warn about extras that couldn't be distributed (emulators without systems: field)
|
||||
distributed = {
|
||||
id(fe)
|
||||
for fe in _extras_for_systems(
|
||||
@@ -1771,17 +1729,12 @@ def generate_split_packs(
|
||||
}
|
||||
undistributed = [fe for fe in all_extras if id(fe) not in distributed]
|
||||
if undistributed:
|
||||
zip_path = generate_pack(
|
||||
platform_name, platforms_dir, db, bios_dir, split_dir,
|
||||
emulators_dir=emulators_dir, zip_contents=zip_contents,
|
||||
data_registry=data_registry, emu_profiles=emu_profiles,
|
||||
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",
|
||||
emus = sorted({fe.get("source_emulator", "?") for fe in undistributed})
|
||||
print(
|
||||
f" NOTE: {len(undistributed)} core extras from {len(emus)} emulators "
|
||||
f"not in split packs (missing systems: field in profiles: "
|
||||
f"{', '.join(emus[:5])}{'...' if len(emus) > 5 else ''})"
|
||||
)
|
||||
if zip_path:
|
||||
results.append(zip_path)
|
||||
|
||||
return results
|
||||
|
||||
@@ -1821,16 +1774,7 @@ def generate_md5_pack(
|
||||
if emulator_name and emulators_dir:
|
||||
profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
|
||||
if emulator_name in profiles:
|
||||
# The same gate as --emulator: a core with no standalone build
|
||||
# has no standalone layout, and a pack named for one would be
|
||||
# the libretro pack under a name that promises otherwise.
|
||||
try:
|
||||
(_name, profile), = select_emulator_profiles(
|
||||
[emulator_name], profiles, standalone
|
||||
)
|
||||
except ProfileSelectionError as exc:
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
return None
|
||||
profile = profiles[emulator_name]
|
||||
emu_display = profile.get("emulator", emulator_name)
|
||||
emu_pack_structure = profile.get("pack_structure")
|
||||
for fe in profile.get("files", []):
|
||||
@@ -1848,7 +1792,7 @@ def generate_md5_pack(
|
||||
)
|
||||
)
|
||||
zip_name = (
|
||||
f"{_name_part(context_name, '_')}_Custom{custom_tags}_BIOS_Pack.zip"
|
||||
f"{context_name.replace(' ', '_')}_Custom{custom_tags}_BIOS_Pack.zip"
|
||||
)
|
||||
zip_path = os.path.join(output_dir, zip_name)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
@@ -2003,7 +1947,9 @@ def generate_target_manifests(targets_dir: str, output_dir: str) -> None:
|
||||
result[alias] = result[target_name]
|
||||
|
||||
out_path = Path(output_dir) / f"{yml_file.stem}.json"
|
||||
write_text_atomic(out_path, json.dumps(result, indent=2, sort_keys=True) + "\n")
|
||||
with open(out_path, "w") as f:
|
||||
json.dump(result, f, indent=2, sort_keys=True)
|
||||
f.write("\n")
|
||||
count += 1
|
||||
print(f" {yml_file.stem}: {len(result)} targets")
|
||||
print(f"Generated {count} target manifest(s) in {output_dir}")
|
||||
@@ -2105,7 +2051,8 @@ def _write_manifest_if_changed(path: str, manifest: dict) -> None:
|
||||
new_cmp = {k: v for k, v in manifest.items() if k != "generated"}
|
||||
if old_cmp == new_cmp:
|
||||
return # no content change, keep existing timestamp
|
||||
write_text_atomic(path, new_json)
|
||||
with open(path, "w") as f:
|
||||
f.write(new_json)
|
||||
|
||||
|
||||
def _run_manifest_mode(
|
||||
@@ -2298,11 +2245,6 @@ def _pack_label(source: str, required_only: bool) -> str:
|
||||
return f"[source={source}, required]" if required_only else f"[source={source}]"
|
||||
|
||||
|
||||
def _produced(split: bool, zip_path, zip_paths) -> bool:
|
||||
"""Whether the build wrote what the run asked for."""
|
||||
return bool(zip_paths if split else zip_path)
|
||||
|
||||
|
||||
def _run_platform_packs(
|
||||
args,
|
||||
groups,
|
||||
@@ -2337,8 +2279,6 @@ def _run_platform_packs(
|
||||
else:
|
||||
print(f"\nGenerating pack for {representative} {_pack_label(source, required_only)}...")
|
||||
|
||||
zip_path = None
|
||||
zip_paths: list = []
|
||||
try:
|
||||
tc = target_cores_cache.get(representative) if args.target else None
|
||||
if args.split:
|
||||
@@ -2369,6 +2309,7 @@ def _run_platform_packs(
|
||||
db,
|
||||
args.bios_dir,
|
||||
args.output_dir,
|
||||
include_extras=args.include_extras,
|
||||
emulators_dir=args.emulators_dir,
|
||||
zip_contents=zip_contents,
|
||||
data_registry=data_registry,
|
||||
@@ -2407,12 +2348,6 @@ def _run_platform_packs(
|
||||
except (FileNotFoundError, OSError, yaml.YAMLError) as e:
|
||||
print(f" ERROR: {e}")
|
||||
failed.append(representative)
|
||||
else:
|
||||
# A builder that declined (no system left under the target)
|
||||
# produced nothing: the run must say so, not verify whatever
|
||||
# the output directory already held and exit 0.
|
||||
if not _produced(args.split, zip_path, zip_paths):
|
||||
failed.append(representative)
|
||||
|
||||
print("\nVerifying packs and generating manifests...")
|
||||
skip_conf = bool(system_filter or args.split)
|
||||
@@ -2476,31 +2411,6 @@ def _refuse_for_all_variants(args, parser) -> None:
|
||||
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:
|
||||
"""Refuse every flag the requested mode would not apply.
|
||||
|
||||
@@ -2508,7 +2418,6 @@ def _refuse_unapplied_flags(args, parser) -> None:
|
||||
about an artifact the caller did not name. Run before any quick-exit
|
||||
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
|
||||
# still needs the region priority list to narrow its expectation.
|
||||
args.regions = []
|
||||
@@ -2521,7 +2430,6 @@ def _refuse_unapplied_flags(args, parser) -> None:
|
||||
parser.error("--region is incompatible with --manifest-targets")
|
||||
if args.one_per_slot and args.manifest_targets:
|
||||
parser.error("--one-per-slot is incompatible with --manifest-targets")
|
||||
_refuse_refresh_data(args, parser)
|
||||
|
||||
_refuse_for_all_variants(args, parser)
|
||||
mode = next(
|
||||
@@ -2615,6 +2523,11 @@ def main():
|
||||
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("--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(
|
||||
"--offline",
|
||||
@@ -2787,7 +2700,6 @@ def main():
|
||||
required_only=args.required_only,
|
||||
regions=getattr(args, "regions", None),
|
||||
offline=args.offline,
|
||||
platforms_dir=args.platforms_dir,
|
||||
)
|
||||
if not built:
|
||||
sys.exit(1)
|
||||
@@ -2808,7 +2720,6 @@ def main():
|
||||
required_only=args.required_only,
|
||||
regions=getattr(args, "regions", None),
|
||||
offline=args.offline,
|
||||
platforms_dir=args.platforms_dir,
|
||||
)
|
||||
if not built:
|
||||
sys.exit(1)
|
||||
@@ -3092,18 +3003,6 @@ def _manifest_region_drops(
|
||||
)
|
||||
|
||||
|
||||
def _record_unplaceable(unplaceable: list[dict], record_omission) -> None:
|
||||
"""Held by the collection, nowhere to put it on this platform.
|
||||
|
||||
Said in the manifest, where RomM's DOSBox Pure ROMs were simply absent.
|
||||
"""
|
||||
for u in unplaceable:
|
||||
record_omission(
|
||||
u.get("path") or u.get("name", ""), u, u.get("system") or "",
|
||||
"no_platform_slug", [u.get("emulator", "")],
|
||||
)
|
||||
|
||||
|
||||
def generate_manifest(
|
||||
platform_name: str,
|
||||
platforms_dir: str,
|
||||
@@ -3140,8 +3039,6 @@ def generate_manifest(
|
||||
name: emu_profiles[name]
|
||||
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
|
||||
registry: dict = {}
|
||||
@@ -3213,10 +3110,10 @@ def generate_manifest(
|
||||
# constrained sibling claim it; without the same rule here the
|
||||
# manifest named whatever answered to the name, so the pack and the
|
||||
# installer disagreed about which file a destination means.
|
||||
# Decided among the declarations this manifest keeps, as the pack
|
||||
# does; a manifest carries no data directories.
|
||||
# A manifest lists the full set and carries no data directories, so
|
||||
# the preference is resolved with neither narrowing nor a cache.
|
||||
preferred_entries = _preferred_entries(
|
||||
pack_systems, db, bios_dir, base_dest, required_only,
|
||||
pack_systems, db, bios_dir, base_dest, False,
|
||||
zip_contents, None, offline,
|
||||
)
|
||||
for sys_id, system in sorted(pack_systems.items()):
|
||||
@@ -3282,12 +3179,6 @@ def generate_manifest(
|
||||
):
|
||||
record_omission(full_dest, file_entry, sys_id, status, None)
|
||||
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
|
||||
# the copy this repo holds: an upstream hash carried by no
|
||||
@@ -3345,7 +3236,6 @@ def generate_manifest(
|
||||
seen_lower.add(dedup_key.lower())
|
||||
|
||||
# Phase 2: core complement (emulator extras)
|
||||
unplaceable: list[dict] = []
|
||||
if source != "platform":
|
||||
core_files = _collect_emulator_extras(
|
||||
config,
|
||||
@@ -3356,11 +3246,9 @@ def generate_manifest(
|
||||
emu_profiles,
|
||||
target_cores=target_cores,
|
||||
include_all=(source == "truth"),
|
||||
unplaceable=unplaceable,
|
||||
)
|
||||
else:
|
||||
core_files = []
|
||||
_record_unplaceable(unplaceable, record_omission)
|
||||
total_size += _manifest_core_entries(
|
||||
core_files, config, db, bios_dir, base_dest, repo_root,
|
||||
zip_contents, offline, region_drops, case_insensitive,
|
||||
@@ -3466,15 +3354,12 @@ def _target_cores_for(
|
||||
) -> set[str] | None:
|
||||
"""Cores a target leaves available, or None when no target was asked for.
|
||||
|
||||
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.
|
||||
None means no narrowing, which is what an unfiltered pack expects.
|
||||
"""
|
||||
if not target_name:
|
||||
return None
|
||||
cache, _kept = build_target_cores_cache(
|
||||
[platform_name], target_name, platforms_dir, is_all=True
|
||||
[platform_name], target_name, platforms_dir
|
||||
)
|
||||
return cache.get(platform_name)
|
||||
|
||||
|
||||
+24
-69
@@ -15,7 +15,6 @@ import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
from common import (
|
||||
@@ -34,24 +33,17 @@ import release_record
|
||||
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(
|
||||
platform_name: str,
|
||||
platforms_dir: str,
|
||||
db: dict,
|
||||
data_registry: dict | None = None,
|
||||
supplemental_names: set[str] | None = None,
|
||||
emulators_dir: str = _EMULATORS_DIR,
|
||||
) -> dict:
|
||||
config = load_platform_config(platform_name, platforms_dir)
|
||||
result = verify_platform(
|
||||
config,
|
||||
db,
|
||||
emulators_dir,
|
||||
data_dir_registry=data_registry,
|
||||
supplemental_names=supplemental_names,
|
||||
)
|
||||
@@ -67,7 +59,7 @@ def compute_coverage(
|
||||
core_missing = len(undeclared) - core_present
|
||||
# Files a profile marks unsourceable never reach the undeclared list, so
|
||||
# the gap they represent has to be counted back in.
|
||||
profiles = load_emulator_profiles(emulators_dir)
|
||||
profiles = load_emulator_profiles("emulators")
|
||||
unsourceable_names = {
|
||||
f.get("name", "")
|
||||
for emu in resolve_platform_cores(config, profiles)
|
||||
@@ -118,44 +110,6 @@ def release_totals(platform_name: str, record: dict) -> tuple[int | None, int |
|
||||
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(
|
||||
coverages: dict,
|
||||
archived: set[str],
|
||||
@@ -402,7 +356,20 @@ def generate_readme(db: dict, platforms_dir: str) -> str:
|
||||
# Where the pack itself is extracted, which is not always the BIOS folder:
|
||||
# a pack whose entries already carry their own root (RetroDECK) extracts
|
||||
# one level above it.
|
||||
extract_paths = _extract_cells(platforms_dir)
|
||||
extract_paths = {
|
||||
"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 = {
|
||||
name
|
||||
for name, entry in load_platform_registry(platforms_dir).items()
|
||||
@@ -415,10 +382,12 @@ def generate_readme(db: dict, platforms_dir: str) -> str:
|
||||
)
|
||||
)
|
||||
|
||||
lines.extend(_paragraphs(extract_notes(platforms_dir)))
|
||||
if archived:
|
||||
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,"
|
||||
" but upstream is no longer scraped on a schedule.",
|
||||
@@ -672,14 +641,8 @@ def generate_readme(db: dict, platforms_dir: str) -> str:
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def contributing_text(link, title: str, site_words: str) -> str:
|
||||
"""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}
|
||||
def generate_contributing() -> str:
|
||||
return f"""# Contributing to RetroBIOS
|
||||
|
||||
## Add a BIOS file
|
||||
|
||||
@@ -688,7 +651,7 @@ def contributing_text(link, title: str, site_words: str) -> str:
|
||||
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
|
||||
|
||||
The [dump provenance]({link("provenance")}) page lists catalogued dumps still
|
||||
The [dump provenance]({SITE_URL}provenance/) page lists catalogued dumps still
|
||||
missing from the collection, with their hashes. A file matching one of those is
|
||||
the most useful contribution.
|
||||
|
||||
@@ -699,7 +662,7 @@ the most useful contribution.
|
||||
3. Register it in `platforms/_registry.yml`
|
||||
4. Generate the platform YAML and test: `python scripts/verify.py --platform <name>`
|
||||
|
||||
Full walkthrough: [adding a platform]({link("wiki/adding-a-platform")}).
|
||||
Full walkthrough: [adding a platform]({SITE_URL}wiki/adding-a-platform/).
|
||||
|
||||
## Add an emulator profile
|
||||
|
||||
@@ -709,7 +672,7 @@ Full walkthrough: [adding a platform]({link("wiki/adding-a-platform")}).
|
||||
4. Write the YAML to `emulators/<name>.yml`
|
||||
5. Test: `python scripts/cross_reference.py --emulator <name>`
|
||||
|
||||
Full walkthrough: [profiling guide]({link("wiki/profiling")}).
|
||||
Full walkthrough: [profiling guide]({SITE_URL}wiki/profiling/).
|
||||
|
||||
## File conventions
|
||||
|
||||
@@ -734,18 +697,10 @@ 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,
|
||||
{site_words}, and in the BIOS packs.
|
||||
on the documentation site, 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():
|
||||
parser = argparse.ArgumentParser(description="Generate slim README.md")
|
||||
parser.add_argument("--db", default="database.json")
|
||||
|
||||
+233
-138
@@ -31,20 +31,20 @@ from pathlib import Path
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
from common import (
|
||||
build_zip_contents_index,
|
||||
compute_composition,
|
||||
count_catalog_matched,
|
||||
list_registered_platforms,
|
||||
load_database,
|
||||
load_emulator_profiles,
|
||||
load_provenance_snapshots,
|
||||
parse_md5_list,
|
||||
preferred_profile,
|
||||
require_yaml,
|
||||
resolve_platform_cores,
|
||||
unique_emulator_profiles,
|
||||
upstream_profile_index,
|
||||
write_if_changed as _write_artifact,
|
||||
yaml_load,
|
||||
)
|
||||
from cross_reference import FileConsumers
|
||||
from nativemode import reads_file_contents
|
||||
|
||||
|
||||
@@ -53,13 +53,7 @@ from nativemode import reads_file_contents
|
||||
|
||||
|
||||
yaml = require_yaml()
|
||||
from generate_readme import (
|
||||
compute_coverage,
|
||||
contributing_text,
|
||||
extract_notes,
|
||||
extract_targets,
|
||||
release_totals,
|
||||
)
|
||||
from generate_readme import compute_coverage, release_totals
|
||||
from profile_sync import source_ref_values, split_source_ref
|
||||
from provenance_report import build_report
|
||||
import release_record
|
||||
@@ -207,22 +201,8 @@ def _timestamp() -> str:
|
||||
# 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(
|
||||
db: dict,
|
||||
coverages: dict,
|
||||
profiles: dict,
|
||||
registry: dict | None = None,
|
||||
platforms_dir: str = "platforms",
|
||||
db: dict, coverages: dict, profiles: dict, registry: dict | None = None
|
||||
) -> str:
|
||||
total_files = db.get("total_files", 0)
|
||||
total_size = db.get("total_size", 0)
|
||||
@@ -338,9 +318,24 @@ def generate_home(
|
||||
"",
|
||||
'??? info "Where to extract"',
|
||||
"",
|
||||
*_extract_table(platforms_dir),
|
||||
" Every other pack extracts straight into the BIOS folder."
|
||||
" [Full instructions per setup](which-pack.md).",
|
||||
" | Platform | Extract to |",
|
||||
" |----------|-----------|",
|
||||
" | RetroArch | `system/` |",
|
||||
" | 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).",
|
||||
"",
|
||||
]
|
||||
)
|
||||
@@ -530,7 +525,7 @@ def build_emulator_gap_report(
|
||||
The gap analysis page and the published gaps export must not compute this
|
||||
twice and drift; both read this one report.
|
||||
"""
|
||||
from common import _norm_system_id, expand_platform_declared_names
|
||||
from common import expand_platform_declared_names
|
||||
from cross_reference import cross_reference as run_cross_reference
|
||||
|
||||
all_declared: set[str] = set()
|
||||
@@ -543,7 +538,7 @@ def build_emulator_gap_report(
|
||||
for fe in system.get("files", []):
|
||||
fname = fe.get("name", "")
|
||||
if fname:
|
||||
declared.setdefault(_norm_system_id(sys_id), set()).add(fname)
|
||||
declared.setdefault(sys_id, set()).add(fname)
|
||||
|
||||
unique_profiles = {
|
||||
k: v
|
||||
@@ -1652,7 +1647,8 @@ def generate_provenance_page(db: dict, report: dict) -> str:
|
||||
def generate_system_page(
|
||||
manufacturer: str,
|
||||
consoles: dict[str, list],
|
||||
consumers: FileConsumers,
|
||||
platform_files: dict[str, set],
|
||||
emulator_files: dict[str, dict],
|
||||
) -> str:
|
||||
manufacturer.lower().replace(" ", "-")
|
||||
lines = [
|
||||
@@ -1675,9 +1671,13 @@ def generate_system_page(
|
||||
md5_full = f.get("md5", "unknown")
|
||||
size = _fmt_size(f.get("size", 0))
|
||||
|
||||
# Who reads this file, not who reads a file of this name
|
||||
plats = sorted(consumers.platforms.get(sha1_full, ()))
|
||||
emus = sorted(consumers.emulators.get(sha1_full, ()))
|
||||
# Cross-reference
|
||||
plats = sorted(p for p, names in platform_files.items() if name in names)
|
||||
emus = sorted(
|
||||
e
|
||||
for e, data in emulator_files.items()
|
||||
if name in data.get("files", set())
|
||||
)
|
||||
|
||||
# Truncated hashes for readability
|
||||
sha1_short = sha1_full[:12] if sha1_full != "unknown" else "-"
|
||||
@@ -1873,18 +1873,14 @@ def _file_badges(f: dict, in_repo: bool) -> list[str]:
|
||||
badges.append(
|
||||
'<span class="rb-badge rb-badge-muted">optional</span>'
|
||||
)
|
||||
if in_repo:
|
||||
badges.append(
|
||||
'<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:
|
||||
if not in_repo:
|
||||
badges.append(
|
||||
'<span class="rb-badge rb-badge-warning">missing</span>'
|
||||
)
|
||||
elif in_repo:
|
||||
badges.append(
|
||||
'<span class="rb-badge rb-badge-success">in repo</span>'
|
||||
)
|
||||
if hle:
|
||||
badges.append(
|
||||
'<span class="rb-badge rb-badge-info">HLE fallback</span>'
|
||||
@@ -1935,7 +1931,7 @@ def _file_badges(f: dict, in_repo: bool) -> list[str]:
|
||||
def _render_emulator_file(
|
||||
f: dict,
|
||||
profile: dict,
|
||||
consumers: FileConsumers | None,
|
||||
platform_files: dict | None,
|
||||
files: list,
|
||||
_file_available,
|
||||
) -> list[str]:
|
||||
@@ -2049,9 +2045,10 @@ def _render_emulator_file(
|
||||
details.append(
|
||||
f"Source: {_source_ref_markdown(profile, source_ref)}"
|
||||
)
|
||||
if consumers:
|
||||
sha1 = consumers.sha1_of(f, f.get("path") or "")
|
||||
plats = sorted(consumers.platforms.get(sha1, ())) if sha1 else []
|
||||
if platform_files:
|
||||
plats = sorted(
|
||||
p for p, names in platform_files.items() if fname in names
|
||||
)
|
||||
if plats:
|
||||
plat_links = [_platform_link(p, p, "../") for p in plats]
|
||||
details.append(f"Platforms: {', '.join(plat_links)}")
|
||||
@@ -2220,75 +2217,55 @@ def _availability_check(db: dict, data_names):
|
||||
missing while the gap report called it held would describe a
|
||||
different collection on two pages of the same site.
|
||||
"""
|
||||
from cross_reference import entry_source
|
||||
from cross_reference import _resolve_source
|
||||
|
||||
by_name = db.get("indexes", {}).get("by_name", {})
|
||||
by_name_lower = {k.lower(): k for k in by_name}
|
||||
by_path_suffix = db.get("indexes", {}).get("by_path_suffix", {})
|
||||
|
||||
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,
|
||||
}
|
||||
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
||||
db_files = db.get("files", {})
|
||||
|
||||
def _file_available(f: dict) -> bool:
|
||||
"""Check if a file is available using the same resolution as cross_reference."""
|
||||
# cross_reference sets these aside before resolving: the file found
|
||||
# under the name belongs to another game or release.
|
||||
if not f.get("name") or f.get("unsourceable"):
|
||||
fname = f.get("name", "")
|
||||
if not fname:
|
||||
return False
|
||||
if f.get("storage", "") in ("release", "large_file"):
|
||||
storage = f.get("storage", "")
|
||||
if storage in ("release", "large_file"):
|
||||
return True
|
||||
return entry_source(f, index) is not None
|
||||
src = _resolve_source(
|
||||
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
|
||||
|
||||
|
||||
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(
|
||||
name: str,
|
||||
profile: dict,
|
||||
db: dict,
|
||||
consumers: FileConsumers | None = None,
|
||||
platform_files: dict | None = None,
|
||||
data_names: set[str] | None = None,
|
||||
) -> str:
|
||||
if profile.get("type") == "alias":
|
||||
@@ -2400,13 +2377,40 @@ def generate_emulator_page(
|
||||
else:
|
||||
_file_available = _availability_check(db, data_names)
|
||||
|
||||
lines.extend(_emulator_file_summary(files, _file_available))
|
||||
# Stats by category
|
||||
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
|
||||
for f in files:
|
||||
lines.extend(
|
||||
_render_emulator_file(
|
||||
f, profile, consumers, files, _file_available
|
||||
f, profile, platform_files, files, _file_available
|
||||
)
|
||||
)
|
||||
|
||||
@@ -2937,7 +2941,7 @@ def generate_gap_analysis(
|
||||
|
||||
|
||||
def _render_xref_by_platform(
|
||||
coverages: dict, unique: dict
|
||||
coverages: dict, unique: dict, core_to_profile: dict
|
||||
) -> list[str]:
|
||||
"""Which cores each platform pulls in, and what they need."""
|
||||
lines: list[str] = []
|
||||
@@ -2956,14 +2960,26 @@ def _render_xref_by_platform(
|
||||
)
|
||||
lines.append("")
|
||||
|
||||
# The cores the builder and verify resolve: a hand copy here dropped
|
||||
# the libretro set a list naming retroarch pulls in, and the system
|
||||
# fallback EmuDeck relies on.
|
||||
matched = {
|
||||
key: unique[key]
|
||||
for key in resolve_platform_cores(config, unique)
|
||||
if key in unique
|
||||
}
|
||||
# Resolve which profiles this platform uses
|
||||
if platform_cores == "all_libretro":
|
||||
matched = {
|
||||
k: v for k, v in unique.items() if "libretro" in v.get("type", "")
|
||||
}
|
||||
elif isinstance(platform_cores, list):
|
||||
matched = {}
|
||||
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":
|
||||
lines.append(f" **{len(matched)} cores** (all libretro)")
|
||||
@@ -3027,7 +3043,7 @@ def _render_xref_by_platform(
|
||||
|
||||
|
||||
def _render_xref_by_upstream(
|
||||
coverages: dict, unique: dict
|
||||
coverages: dict, unique: dict, core_to_profile: dict
|
||||
) -> list[str]:
|
||||
"""The same relation read the other way: one row per upstream."""
|
||||
lines: list[str] = []
|
||||
@@ -3052,8 +3068,20 @@ def _render_xref_by_upstream(
|
||||
# Build platform membership per core
|
||||
platform_membership: dict[str, set[str]] = {}
|
||||
for pname, cov in coverages.items():
|
||||
for key in resolve_platform_cores(cov["config"], unique):
|
||||
platform_membership.setdefault(key, set()).add(pname)
|
||||
config = cov["config"]
|
||||
pcores = config.get("cores", [])
|
||||
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()):
|
||||
cores = by_upstream[upstream_url]
|
||||
@@ -3090,6 +3118,12 @@ def generate_cross_reference(
|
||||
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_upstreams = len({
|
||||
p.get("upstream", p.get("source", ""))
|
||||
@@ -3113,17 +3147,72 @@ def generate_cross_reference(
|
||||
"",
|
||||
]
|
||||
|
||||
lines.extend(_render_xref_by_platform(coverages, unique))
|
||||
lines.extend(_render_xref_by_upstream(coverages, unique))
|
||||
lines.extend(_render_xref_by_platform(coverages, unique, core_to_profile))
|
||||
lines.extend(_render_xref_by_upstream(coverages, unique, core_to_profile))
|
||||
|
||||
lines.extend(["", f"*Generated on {_timestamp()}*"])
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def generate_contributing() -> str:
|
||||
return contributing_text(
|
||||
lambda page: f"{page}.md", "# Contributing - RetroBIOS", "on this site"
|
||||
)
|
||||
return """# Contributing - RetroBIOS
|
||||
|
||||
## 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
|
||||
@@ -3249,21 +3338,30 @@ def generate_wiki_data_model(db: dict, profiles: dict) -> str:
|
||||
# Build cross-reference indexes
|
||||
|
||||
|
||||
def _build_file_consumers(db: dict, coverages: dict, profiles: dict) -> FileConsumers:
|
||||
"""Which platforms and emulators read each collected file."""
|
||||
consumers = FileConsumers(db, build_zip_contents_index(db))
|
||||
consumers.add_platforms({name: cov["config"] for name, cov in coverages.items()})
|
||||
consumers.add_emulators(profiles)
|
||||
return consumers
|
||||
def _build_platform_file_index(coverages: dict) -> dict[str, set]:
|
||||
"""Map platform_name -> set of declared file names."""
|
||||
index = {}
|
||||
for name, cov in coverages.items():
|
||||
names = set()
|
||||
config = cov["config"]
|
||||
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]:
|
||||
"""Map emulator_name -> {systems: set}, for the platform pages."""
|
||||
return {
|
||||
name: {"systems": set(profile.get("systems", []))}
|
||||
for name, profile in profiles.items()
|
||||
if profile.get("type") != "alias"
|
||||
}
|
||||
"""Map emulator_name -> {files: set, systems: set} for cross-reference."""
|
||||
index = {}
|
||||
for name, profile in profiles.items():
|
||||
if profile.get("type") == "alias":
|
||||
continue
|
||||
index[name] = {
|
||||
"files": {f.get("name", "") for f in profile.get("files", [])},
|
||||
"systems": set(profile.get("systems", [])),
|
||||
}
|
||||
return index
|
||||
|
||||
|
||||
# mkdocs.yml nav generator
|
||||
@@ -3589,8 +3687,7 @@ def main():
|
||||
for name in sorted(platform_names):
|
||||
try:
|
||||
cov = compute_coverage(
|
||||
name, args.platforms_dir, db, data_registry, suppl_names,
|
||||
emulators_dir=args.emulators_dir,
|
||||
name, args.platforms_dir, db, data_registry, suppl_names
|
||||
)
|
||||
coverages[name] = cov
|
||||
print(
|
||||
@@ -3607,15 +3704,13 @@ def main():
|
||||
)
|
||||
|
||||
# Build cross-reference indexes
|
||||
consumers = _build_file_consumers(db, coverages, profiles)
|
||||
platform_files = _build_platform_file_index(coverages)
|
||||
emulator_files = _build_emulator_file_index(profiles)
|
||||
|
||||
# Generate home
|
||||
print("Generating home page...")
|
||||
write_if_changed(
|
||||
str(docs / "index.md"), generate_home(
|
||||
db, coverages, profiles, registry, args.platforms_dir
|
||||
)
|
||||
str(docs / "index.md"), generate_home(db, coverages, profiles, registry)
|
||||
)
|
||||
|
||||
stats = compute_stats(db, coverages, profiles)
|
||||
@@ -3664,7 +3759,7 @@ def main():
|
||||
)
|
||||
for mfr, consoles in manufacturers.items():
|
||||
slug = mfr.lower().replace(" ", "-")
|
||||
page = generate_system_page(mfr, consoles, consumers)
|
||||
page = generate_system_page(mfr, consoles, platform_files, emulator_files)
|
||||
write_if_changed(str(docs / "systems" / f"{slug}.md"), page)
|
||||
|
||||
# Generate emulator pages
|
||||
@@ -3678,7 +3773,7 @@ def main():
|
||||
if profile.get("type") not in ("alias", "test")
|
||||
}
|
||||
for name, profile in public_profiles.items():
|
||||
page = generate_emulator_page(name, profile, db, consumers, suppl_names)
|
||||
page = generate_emulator_page(name, profile, db, platform_files, suppl_names)
|
||||
write_if_changed(str(docs / "emulators" / f"{name}.md"), page)
|
||||
|
||||
# Generate cross-reference page
|
||||
|
||||
+16
-59
@@ -163,15 +163,11 @@ def _fetch_asset(
|
||||
hashes, expected_sha1, expected_md5
|
||||
):
|
||||
return cached
|
||||
elif not _replaced_on_release(name, cached, offline):
|
||||
# A copy of this asset that answers another hash: the caller
|
||||
# wants a different revision under the same name, and the
|
||||
# release still serves this one. Keeping it is what lets the
|
||||
# next caller for the primary find it.
|
||||
else:
|
||||
# A verified copy of this asset that answers another hash: the
|
||||
# caller wants a different revision under the same name. Keeping
|
||||
# it is what lets the next caller for the primary find it.
|
||||
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:
|
||||
return None
|
||||
@@ -183,9 +179,20 @@ def _fetch_asset(
|
||||
dir=dest_dir, prefix=os.path.basename(cached) + ".", suffix=".tmp"
|
||||
)
|
||||
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:
|
||||
downloaded = False
|
||||
for candidate, url in _asset_urls(name):
|
||||
for candidate in candidates:
|
||||
encoded_name = urllib.parse.quote(candidate)
|
||||
url = (
|
||||
f"https://github.com/{LARGE_FILES_REPO}/releases/download/"
|
||||
f"{LARGE_FILES_RELEASE}/{encoded_name}"
|
||||
)
|
||||
try:
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "retrobios/1.0"})
|
||||
with urllib.request.urlopen(req, timeout=300) as resp:
|
||||
@@ -213,56 +220,6 @@ def _fetch_asset(
|
||||
_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:
|
||||
"""Whether computed hashes answer the caller's declaration."""
|
||||
if expected_sha1 and hashes["sha1"].lower() != expected_sha1.lower():
|
||||
|
||||
+20
-64
@@ -14,7 +14,6 @@ from common import resolve_local_file
|
||||
from common import runs_standalone
|
||||
from common import sanitize_pack_path
|
||||
from common import size_fits
|
||||
from validation import read_from_system_dir
|
||||
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.
|
||||
|
||||
@@ -134,42 +133,6 @@ def _map_emulator_to_slug(
|
||||
return sys_to_slug.get(target, "")
|
||||
return ""
|
||||
|
||||
def _slug_for(
|
||||
u: dict, profiles: dict, platform_systems: set, norm_map: dict, sys_to_slug: dict
|
||||
) -> str:
|
||||
"""The platform slug a report entry's emulator files under, or empty."""
|
||||
emu_name = u.get("profile") or u.get("emulator", "")
|
||||
profile = profiles.get(emu_name, {})
|
||||
if not profile:
|
||||
# The report names the display name where the key failed.
|
||||
profile = next(
|
||||
(pp for pp in profiles.values() if pp.get("emulator") == emu_name), {}
|
||||
)
|
||||
return _map_emulator_to_slug(profile, platform_systems, norm_map, sys_to_slug)
|
||||
|
||||
|
||||
def unplaceable_extras(config: dict, undeclared: list[dict], profiles: dict) -> set:
|
||||
"""Keys of the report entries a slug-based platform cannot place.
|
||||
|
||||
RomM files every BIOS under the slug of its system and knows no slug
|
||||
for DOS or for the arcade sets MAME 2003 reads; the builder skipped
|
||||
those entries and the report counted them in the pack.
|
||||
"""
|
||||
from common import _norm_system_id
|
||||
|
||||
is_slug_based, sys_to_slug = _detect_slug_structure(config)
|
||||
if not is_slug_based:
|
||||
return set()
|
||||
platform_systems = set(config.get("systems", {}).keys())
|
||||
norm_map = {_norm_system_id(sid): sid for sid in platform_systems}
|
||||
return {
|
||||
(u.get("emulator", ""), u.get("name", ""), u.get("path") or "")
|
||||
for u in undeclared
|
||||
if u.get("in_repo")
|
||||
and not _slug_for(u, profiles, platform_systems, norm_map, sys_to_slug)
|
||||
}
|
||||
|
||||
|
||||
def _agnostic_scan_extras(
|
||||
profiles: dict,
|
||||
relevant: set,
|
||||
@@ -448,18 +411,9 @@ def _collect_emulator_extras(
|
||||
emu_profiles: dict | None = None,
|
||||
target_cores: set[str] | None = None,
|
||||
include_all: bool = False,
|
||||
claimants: dict[tuple[str, str, str], str] | None = None,
|
||||
unplaceable: list[dict] | None = None,
|
||||
) -> list[dict]:
|
||||
"""Collect core requirement files from emulator profiles not in the platform pack.
|
||||
|
||||
``claimants`` receives the destination of every report entry that
|
||||
asked for one, the ones a sibling already took included: the region
|
||||
report withdraws by that key, and an entry without one stayed listed
|
||||
after the builder had withdrawn its destination. ``unplaceable``
|
||||
receives the entries a slug-based platform cannot place, so the
|
||||
manifest and the report can say so instead of counting them packed.
|
||||
|
||||
Uses the same system-overlap matching as verify.py cross-reference:
|
||||
- Matches emulators by shared system IDs with the platform
|
||||
- Filters mode: standalone, type: launcher, type: alias
|
||||
@@ -517,18 +471,25 @@ def _collect_emulator_extras(
|
||||
|
||||
# Slug-based platforms: prefix dest with system slug
|
||||
if is_slug_based:
|
||||
slug = _slug_for(u, profiles, platform_systems, norm_map, sys_to_slug)
|
||||
emu_name = u.get("profile") or u.get("emulator", "")
|
||||
profile = profiles.get(emu_name, {})
|
||||
# Try finding profile by display name if key lookup failed
|
||||
if not profile:
|
||||
for pn, pp in profiles.items():
|
||||
if pp.get("emulator") == emu_name:
|
||||
profile = pp
|
||||
break
|
||||
slug = _map_emulator_to_slug(
|
||||
profile,
|
||||
platform_systems,
|
||||
norm_map,
|
||||
sys_to_slug,
|
||||
)
|
||||
if not slug:
|
||||
if unplaceable is not None:
|
||||
unplaceable.append(u)
|
||||
continue
|
||||
continue # can't place without slug
|
||||
dest = f"{slug}/{dest}"
|
||||
|
||||
full_dest = f"{extras_prefix}/{dest}" if extras_prefix else dest
|
||||
if claimants is not None:
|
||||
claimants[(u.get("emulator", ""), u.get("name", ""), u.get("path") or "")] = (
|
||||
sanitize_pack_path(dest)
|
||||
)
|
||||
if full_dest in seen_dests:
|
||||
continue
|
||||
seen_dests.add(full_dest)
|
||||
@@ -603,7 +564,9 @@ def _collect_emulator_extras(
|
||||
continue
|
||||
if file_mode == "libretro" and is_standalone:
|
||||
continue
|
||||
if not read_from_system_dir(f):
|
||||
# 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
|
||||
if is_standalone:
|
||||
raw = f.get("standalone_path") or f.get("path") or fname
|
||||
@@ -719,7 +682,6 @@ def platform_region_groups(
|
||||
emu_profiles,
|
||||
target_cores=target_cores,
|
||||
include_all=include_all,
|
||||
claimants=extra_dests,
|
||||
)
|
||||
for extra in _kept(extras, required_only):
|
||||
dest = sanitize_pack_path(extra.get("destination", extra.get("name", "")))
|
||||
@@ -747,10 +709,7 @@ def platform_region_groups(
|
||||
return groups, extra_dests
|
||||
|
||||
def emulator_region_drops(
|
||||
selected: list[tuple[str, dict]],
|
||||
standalone: bool,
|
||||
regions: list[str],
|
||||
required_only: bool = False,
|
||||
selected: list[tuple[str, dict]], standalone: bool, regions: list[str]
|
||||
) -> set[str]:
|
||||
"""Destinations an emulator-mode `--region` withdraws.
|
||||
|
||||
@@ -767,10 +726,7 @@ def emulator_region_drops(
|
||||
region_groups: dict[str, list[tuple[str, str]]] = {}
|
||||
for emu_name, profile in sorted(selected):
|
||||
structure = profile.get("pack_structure")
|
||||
# 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):
|
||||
for fe in filter_files_by_mode(profile.get("files", []), standalone):
|
||||
dest = _resolve_destination(fe, structure, standalone)
|
||||
if dest:
|
||||
group_id = _emulator_region_group(emu_name, profile, fe)
|
||||
|
||||
+18
-19
@@ -26,7 +26,6 @@ Usage:
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import contextlib
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -34,7 +33,7 @@ import time
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
from common import ArtifactLockBusy, artifact_lock, hold_artifact_lock
|
||||
from common import ArtifactLockBusy, artifact_lock
|
||||
|
||||
|
||||
def run(cmd: list[str], label: str) -> tuple[bool, str]:
|
||||
@@ -120,11 +119,11 @@ def parse_pack_exclusions(output: str) -> dict[str, int]:
|
||||
for line in output.splitlines():
|
||||
label_match = re.match(r"Generating (?:shared )?pack for (.+)\.\.\.", line)
|
||||
if label_match:
|
||||
# The same reading as parse_pack_counts: a reduced variant must
|
||||
# not overwrite the full pack's count under the shared label.
|
||||
current_label = _full_pack_label(label_match.group(1).strip())
|
||||
current_label = re.sub(
|
||||
r"\s+\[source=[^\]]+\]$", "", label_match.group(1).strip()
|
||||
)
|
||||
continue
|
||||
if not current_label or "files packed" not in line:
|
||||
if "files packed" not in line:
|
||||
continue
|
||||
excluded_match = re.search(r"(\d+) unsafe excluded", line)
|
||||
exclusions[current_label] = (
|
||||
@@ -198,13 +197,9 @@ def check_consistency(verify_output: str, pack_output: str) -> bool:
|
||||
f" {v_label}: verify {v_ok}/{v_total} == pack {p_ok}/{p_total} OK"
|
||||
)
|
||||
else:
|
||||
# The invariant runs both ways: a builder more optimistic
|
||||
# than the report ships files the report never vouched for.
|
||||
print(
|
||||
f" {v_label}: MISMATCH pack {p_ok}/{p_total} OK "
|
||||
f"> verify {v_ok}/{v_total}"
|
||||
f" {v_label}: verify {v_ok}/{v_total}, pack {p_ok}/{p_total} OK (pack resolves more)"
|
||||
)
|
||||
all_ok = False
|
||||
else:
|
||||
print(f" {v_label}: {v_ok}/{v_total} (no separate pack)")
|
||||
all_ok = False
|
||||
@@ -322,6 +317,12 @@ def main():
|
||||
parser.add_argument(
|
||||
"--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("--source", choices=["platform", "truth", "full"], default="full")
|
||||
parser.add_argument("--all-variants", action="store_true")
|
||||
@@ -345,14 +346,11 @@ def main():
|
||||
|
||||
# A second run on the same output directory is refused before any work:
|
||||
# the database rebuild alone takes minutes, and the reader holding the
|
||||
# directory would otherwise see the answer only after all of it. The
|
||||
# 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:
|
||||
# directory would otherwise see the answer only after all of it.
|
||||
if not args.skip_packs and Path(args.output_dir).is_dir():
|
||||
try:
|
||||
held.enter_context(hold_artifact_lock(args.output_dir))
|
||||
with artifact_lock(args.output_dir):
|
||||
pass
|
||||
except ArtifactLockBusy as exc:
|
||||
print(f"ERROR: {exc}")
|
||||
sys.exit(1)
|
||||
@@ -527,6 +525,8 @@ def main():
|
||||
pack_cmd.append("--include-archived")
|
||||
if args.offline:
|
||||
pack_cmd.append("--offline")
|
||||
if args.include_extras:
|
||||
pack_cmd.append("--include-extras")
|
||||
if args.target:
|
||||
pack_cmd.extend(["--target", args.target])
|
||||
if args.source != "full":
|
||||
@@ -667,7 +667,6 @@ def main():
|
||||
print(f"{'=' * 60}")
|
||||
print(f" Pipeline {'COMPLETE' if all_ok else 'FINISHED WITH ERRORS'}")
|
||||
print(f"{'=' * 60}")
|
||||
held.close()
|
||||
sys.exit(0 if all_ok else 1)
|
||||
|
||||
|
||||
|
||||
+67
-187
@@ -71,11 +71,6 @@ class RefPart:
|
||||
end: int | None
|
||||
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)
|
||||
class AnchorResult:
|
||||
@@ -232,24 +227,6 @@ def collect_tokens(entry: dict) -> list[str]:
|
||||
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:
|
||||
"""True when a ref names a project rather than a path in a known repo.
|
||||
|
||||
@@ -680,8 +657,8 @@ def anchor_block(
|
||||
pin = _normalize(pin_lines)
|
||||
head = _normalize(head_lines)
|
||||
lo, hi = start - 1, end
|
||||
if lo < 0 or hi > len(pin):
|
||||
return AnchorResult("GONE", None, None, [], "cited range ends past the end of the file")
|
||||
if lo < 0 or lo >= len(pin):
|
||||
return AnchorResult("GONE", None, None, [])
|
||||
if not any(pin[lo:hi]):
|
||||
return AnchorResult("CHANGED", None, None, [], "cited range is blank")
|
||||
|
||||
@@ -915,22 +892,17 @@ def anchor_part(
|
||||
reason = "written against HEAD, pin names an older revision"
|
||||
return PartResult(part, "GONE", None, None, None, [], reason, slug, url)
|
||||
|
||||
end = part.last
|
||||
if end > len(pin_lines):
|
||||
if part.start > len(pin_lines) and _written_against_head(part, head_lines):
|
||||
# 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
|
||||
# the palette and database loads at 2041 and 2063, which is where
|
||||
# HEAD carries them, over a pin four hundred lines shorter. A range
|
||||
# that only ends past the file is no better: dolphin cited 239-323 in
|
||||
# a 317-line NANDImporter.cpp, and the block found above the end
|
||||
# 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"
|
||||
# HEAD carries them, over a pin four hundred lines shorter.
|
||||
return PartResult(
|
||||
part, "GONE", None, None, None, [],
|
||||
"written against HEAD, pin names an older revision", slug, url,
|
||||
)
|
||||
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)
|
||||
note = anchored.reason
|
||||
if anchored.status in REVIEW_STATUSES:
|
||||
@@ -1178,7 +1150,7 @@ def verify_at_pin(part: RefPart, pin_lines, tokens, hash_tokens=()) -> PartResul
|
||||
)
|
||||
if part.start is None:
|
||||
return PartResult(part, "ANCHORED", None, None, None, [])
|
||||
if part.last > len(pin_lines):
|
||||
if part.start > len(pin_lines):
|
||||
return PartResult(
|
||||
part, "GONE", None, None, None, [], "beyond the end of the file"
|
||||
)
|
||||
@@ -1195,7 +1167,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.
|
||||
reach = SELF_CHECK_CONTEXT + len(tokens)
|
||||
lo = max(0, part.start - 1 - SELF_CHECK_CONTEXT)
|
||||
hi = min(len(pin_lines), part.last + reach)
|
||||
hi = min(len(pin_lines), (part.end or part.start) + reach)
|
||||
window = "\n".join(pin_lines[lo:hi]).lower()
|
||||
if any(token in window for token in tokens):
|
||||
return PartResult(part, "ANCHORED", None, None, None, [])
|
||||
@@ -1211,8 +1183,8 @@ def verify_at_pin(part: RefPart, pin_lines, tokens, hash_tokens=()) -> PartResul
|
||||
{
|
||||
index
|
||||
for index, line in enumerate(pin_lines, 1)
|
||||
for token in hash_tokens
|
||||
if token.lower() in line.lower()
|
||||
for token in tokens
|
||||
if token in line.lower()
|
||||
}
|
||||
)
|
||||
if not elsewhere:
|
||||
@@ -1698,26 +1670,12 @@ def build_report(
|
||||
view, _ = resolve_path(part.path, part.start, tuple(tokens))
|
||||
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 = []
|
||||
for entry_name, ref, tokens, hashes, ref_parts, citation in refs:
|
||||
flags = [self_checked(part, tokens) for part in ref_parts]
|
||||
judged = [
|
||||
judge(part, flag, tokens, hashes)
|
||||
verify_at_pin(part, fetch(PIN, part.path, part.start, tokens), tokens, hashes)
|
||||
if flag else anchor_across_views(part, tokens)
|
||||
for part, flag in zip(ref_parts, flags)
|
||||
]
|
||||
staged.append((entry_name, ref, citation, reconcile_self_check(judged, flags)))
|
||||
@@ -2501,15 +2459,6 @@ def bump_commit(
|
||||
if pending_recale(report, accept_changed, text):
|
||||
return False
|
||||
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(
|
||||
report.upstream_head and report.upstream_pin != report.upstream_head
|
||||
)
|
||||
@@ -2517,6 +2466,9 @@ def bump_commit(
|
||||
# Rewriting the pin to the value it already holds is not a bump, and
|
||||
# announcing it buries the profiles that did move.
|
||||
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["source_commit"] = report.head
|
||||
new_text = _set_pin(text, document, "source_commit", report.head, "profiled_date")
|
||||
@@ -2670,14 +2622,10 @@ def _realign_part(
|
||||
|
||||
def _writing_pairs(
|
||||
document: dict, repos: list, revisions: list[tuple[str, dict]], intro_sha: str
|
||||
) -> tuple[list[list], list[str]]:
|
||||
"""Every reading of where the text was written, and the ambiguities.
|
||||
|
||||
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] = []
|
||||
) -> tuple[list, list[str]]:
|
||||
"""(repo, writing pin, current pin) per declared repository, and the
|
||||
repositories whose writing pin the history cannot single out."""
|
||||
pairs = []
|
||||
ambiguous: list[str] = []
|
||||
for pin_field, repo in repos:
|
||||
current = document.get(f"{pin_field}_commit")
|
||||
@@ -2686,58 +2634,9 @@ def _writing_pairs(
|
||||
written = _writing_pins(revisions, intro_sha, f"{pin_field}_commit")
|
||||
if len(written) > 1:
|
||||
ambiguous.append(f"{pin_field}_commit {' -> '.join(written)}")
|
||||
# The current pin among the candidates reads as "no move" (None) for
|
||||
# that repository, which the other candidates must then agree with.
|
||||
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
|
||||
elif written and written[0] != current:
|
||||
pairs.append((repo, written[0], current))
|
||||
return pairs, ambiguous
|
||||
|
||||
|
||||
def realign_prose(
|
||||
@@ -2798,10 +2697,30 @@ def realign_prose(
|
||||
if intro_sha is None:
|
||||
# The scalar is not committed yet: written now, under this pin.
|
||||
continue
|
||||
readings, ambiguous = _writing_pairs(document, repos, revisions, intro_sha)
|
||||
if not any(readings):
|
||||
pairs, ambiguous = _writing_pairs(document, repos, revisions, intro_sha)
|
||||
if ambiguous:
|
||||
# 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
|
||||
moves, blocked = _realign_citation(citation, readings, ambiguous, cache_dir, offline)
|
||||
if not pairs:
|
||||
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:
|
||||
# Half a run must not move: the untouched ranges would read as
|
||||
# already realigned when they were never even located.
|
||||
@@ -2828,39 +2747,14 @@ def realign_prose(
|
||||
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:
|
||||
"""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.
|
||||
"""
|
||||
"""True when the profile directory carries uncommitted changes."""
|
||||
result = subprocess.run(
|
||||
["git", "-C", emulators_dir, "status", "--porcelain", "--", "."],
|
||||
["git", "status", "--porcelain", "--", emulators_dir],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode:
|
||||
raise RuntimeError(
|
||||
f"cannot read the git status of {emulators_dir}: {result.stderr.strip()}"
|
||||
)
|
||||
return bool(result.stdout.strip())
|
||||
|
||||
|
||||
@@ -2962,40 +2856,16 @@ def _check_quota(count: int, offline: bool) -> None:
|
||||
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:
|
||||
path = Path(args.emulators_dir) / f"{name}.yml"
|
||||
if not path.is_file():
|
||||
return
|
||||
if args.backfill_commits:
|
||||
_backfill(args, name, profile, report, path)
|
||||
if not (args.rebase_refs or args.bump_commit) or _stale_prose_blocks(name, path, args):
|
||||
if args.backfill_commits and report.pin and not profile.get("source_commit"):
|
||||
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]}")
|
||||
if not (args.rebase_refs or args.bump_commit):
|
||||
return
|
||||
with tempfile.TemporaryDirectory() as scratch:
|
||||
# Always work on a copy. A dry run reports what it left there; a real
|
||||
@@ -3253,8 +3123,18 @@ def main() -> None:
|
||||
args.backfill_commits or args.rebase_refs or args.bump_commit
|
||||
or args.realign_prose
|
||||
)
|
||||
if writes and not args.dry_run and not args.force:
|
||||
_refuse_dirty_tree(args.emulators_dir)
|
||||
if (
|
||||
writes
|
||||
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:
|
||||
for name in selected:
|
||||
|
||||
@@ -88,13 +88,6 @@ def build_report(db: dict, snapshots: dict, members: dict | None = None) -> dict
|
||||
by_md5_size = {
|
||||
(entry.get("md5", ""), entry.get("size", 0)) for entry in by_sha1.values()
|
||||
}
|
||||
# Redump lists some dumps by crc32 alone (ps2-0101jd-20030110, DTL-H10100):
|
||||
# collected as a loose file it would stay MISSING for good without this.
|
||||
by_crc32_size = {
|
||||
(str(entry.get("crc32", "")).lower(), entry.get("size", 0))
|
||||
for entry in by_sha1.values()
|
||||
if entry.get("crc32")
|
||||
}
|
||||
|
||||
report = {}
|
||||
for source, snapshot in sorted(snapshots.items()):
|
||||
@@ -105,7 +98,6 @@ def build_report(db: dict, snapshots: dict, members: dict | None = None) -> dict
|
||||
if (
|
||||
entry.get("sha1") in by_sha1
|
||||
or (entry.get("md5"), entry.get("size")) in by_md5_size
|
||||
or (str(entry.get("crc32") or "").lower(), entry.get("size")) in by_crc32_size
|
||||
or _held_in_archive(entry, members)
|
||||
):
|
||||
matched += 1
|
||||
|
||||
+5
-14
@@ -255,25 +255,16 @@ def fallback_groups(
|
||||
index: dict[str, dict],
|
||||
requested: list[str],
|
||||
) -> list[str]:
|
||||
"""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.
|
||||
"""
|
||||
"""Group IDs where no candidate matched, so the whole group was kept."""
|
||||
if not requested:
|
||||
return []
|
||||
out: list[str] = []
|
||||
for group_id, members in groups.items():
|
||||
ranked = _competing_ranks(members, index, requested)
|
||||
served = any(
|
||||
not regions or WORLD in regions
|
||||
for regions in (
|
||||
lookup_regions(index, destination, name)
|
||||
for destination, name in members
|
||||
)
|
||||
has_world = any(
|
||||
WORLD in lookup_regions(index, destination, name)
|
||||
for destination, name in members
|
||||
)
|
||||
if ranked and not served and min(r for r, _ in ranked) == len(requested):
|
||||
if ranked and not has_world and min(r for r, _ in ranked) == len(requested):
|
||||
out.append(group_id)
|
||||
return sorted(out)
|
||||
+20
-18
@@ -19,7 +19,7 @@ from pathlib import Path
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
|
||||
import region
|
||||
from common import load_database, load_emulator_profiles, resolve_local_file
|
||||
from common import load_emulator_profiles, load_database, parse_md5_list
|
||||
|
||||
# No-Intro filename tokens: full English territory names.
|
||||
NOINTRO = {
|
||||
@@ -100,21 +100,24 @@ def catalog_regions(db: dict) -> dict[str, tuple[set[str], str]]:
|
||||
return out
|
||||
|
||||
|
||||
def resolve_sha1(file_entry: dict, db: dict, by_path: dict | None = None) -> str | None:
|
||||
"""The repo SHA1 a profile entry resolves to, by the pack's own resolver.
|
||||
|
||||
A private resolver read sha1, then md5, then a lone name: entries that
|
||||
identify their file by path (Dolphin's three IPL.bin), by crc32
|
||||
(geargrafx) or by sha256 (mednafen) were never checked at all.
|
||||
"""
|
||||
if by_path is None:
|
||||
by_path = {entry.get("path", ""): sha1 for sha1, entry in db["files"].items()}
|
||||
path, status = resolve_local_file(
|
||||
file_entry, db, dest_hint=file_entry.get("path") or ""
|
||||
)
|
||||
if not path or status in ("not_found", "hash_mismatch"):
|
||||
return None
|
||||
return by_path.get(path)
|
||||
def resolve_sha1(file_entry: dict, db: dict) -> str | None:
|
||||
"""Resolve a profile file entry to a repo SHA1, or None when ambiguous."""
|
||||
files = db["files"]
|
||||
indexes = db["indexes"]
|
||||
raw_sha1 = file_entry.get("sha1")
|
||||
declared = raw_sha1 if isinstance(raw_sha1, list) else [raw_sha1]
|
||||
sha1_hits = [str(value).lower() for value in declared if value]
|
||||
sha1_hits = [value for value in sha1_hits if value in files]
|
||||
if len(sha1_hits) == 1:
|
||||
return sha1_hits[0]
|
||||
for md5 in parse_md5_list(file_entry.get("md5")):
|
||||
hit = indexes["by_md5"].get(md5)
|
||||
if hit:
|
||||
return hit
|
||||
hits = indexes["by_name"].get(file_entry.get("name", ""), [])
|
||||
if isinstance(hits, str):
|
||||
hits = [hits]
|
||||
return hits[0] if len(hits) == 1 else None
|
||||
|
||||
|
||||
def build_report(profiles: dict, db: dict) -> dict:
|
||||
@@ -127,7 +130,6 @@ def build_report(profiles: dict, db: dict) -> dict:
|
||||
for slugs, _name in catalog.values():
|
||||
vocabulary.update(slugs)
|
||||
|
||||
by_path = {entry.get("path", ""): sha1 for sha1, entry in db["files"].items()}
|
||||
for emu_name, profile in sorted(profiles.items()):
|
||||
if profile.get("type") in ("launcher", "alias"):
|
||||
continue
|
||||
@@ -135,7 +137,7 @@ def build_report(profiles: dict, db: dict) -> dict:
|
||||
if not isinstance(f, dict) or not f.get("region"):
|
||||
continue
|
||||
declared = region.normalize_declared(f["region"])
|
||||
sha1 = resolve_sha1(dict(f, source_profile=emu_name), db, by_path)
|
||||
sha1 = resolve_sha1(f, db)
|
||||
if not sha1 or sha1 not in catalog:
|
||||
unchecked += 1
|
||||
continue
|
||||
|
||||
@@ -24,7 +24,6 @@ import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import split_pack
|
||||
from common import write_text_atomic
|
||||
|
||||
RECORD = "release.json"
|
||||
|
||||
@@ -125,7 +124,7 @@ def main() -> int:
|
||||
print(f"Error: {line}", file=sys.stderr)
|
||||
if mismatches:
|
||||
return 1
|
||||
write_text_atomic(str(args.output), json.dumps(record, indent=2) + "\n")
|
||||
args.output.write_text(json.dumps(record, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"Wrote {args.output}")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -19,10 +19,11 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
from common import copy_file_atomic, load_database
|
||||
from common import load_database
|
||||
|
||||
|
||||
def gitignored_paths(gitignore: str) -> set[str]:
|
||||
@@ -71,9 +72,7 @@ def restore(
|
||||
unsatisfied.append(path)
|
||||
continue
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
# Whole or absent: a scan running beside a plain copy hashed the
|
||||
# truncated file and published that in the database.
|
||||
copy_file_atomic(source, path)
|
||||
shutil.copy2(source, path)
|
||||
print(f"Restored: {path}")
|
||||
restored += 1
|
||||
print(f"Total: {restored} files restored")
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
@@ -226,38 +225,22 @@ class BaseScraper(ABC):
|
||||
...
|
||||
|
||||
|
||||
def github_headers() -> dict[str, str]:
|
||||
"""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.
|
||||
"""
|
||||
def fetch_github_latest_version(repo: str) -> str | None:
|
||||
"""Fetch the latest release version tag from a GitHub repo."""
|
||||
url = f"https://api.github.com/repos/{repo}/releases/latest"
|
||||
try:
|
||||
req = urllib.request.Request(url, headers=github_headers())
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
headers={
|
||||
"User-Agent": "retrobios-scraper/1.0",
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
tag = json.loads(resp.read()).get("tag_name", "")
|
||||
except (urllib.error.URLError, json.JSONDecodeError) as e:
|
||||
raise RuntimeError(f"cannot read the latest release of {repo}: {e}") from e
|
||||
if not tag:
|
||||
raise RuntimeError(f"the latest release of {repo} names no tag")
|
||||
return tag
|
||||
data = json.loads(resp.read())
|
||||
return data.get("tag_name", "")
|
||||
except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError):
|
||||
return None
|
||||
|
||||
|
||||
class _PlatformDumper(yaml.SafeDumper):
|
||||
|
||||
@@ -19,12 +19,7 @@ from pathlib import Path
|
||||
|
||||
from common import yaml_load
|
||||
|
||||
from .base_scraper import (
|
||||
BaseScraper,
|
||||
BiosRequirement,
|
||||
github_headers,
|
||||
requirement_entry,
|
||||
)
|
||||
from .base_scraper import BaseScraper, BiosRequirement, requirement_entry
|
||||
|
||||
PLATFORM_NAME = "batocera"
|
||||
|
||||
@@ -43,32 +38,39 @@ def pick_stable_tag(names: list[str]) -> str | None:
|
||||
return max(stable)[1] if stable else None
|
||||
|
||||
|
||||
def fetch_stable_tag() -> str:
|
||||
"""Return the newest stable batocera-N(.M) tag name.
|
||||
def fetch_stable_tag() -> str | None:
|
||||
"""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"
|
||||
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:
|
||||
req = urllib.request.Request(url, headers=github_headers())
|
||||
req = urllib.request.Request(url, headers=headers)
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
tags = json.loads(resp.read())
|
||||
except (urllib.error.URLError, json.JSONDecodeError) as e:
|
||||
raise RuntimeError(f"cannot list batocera tags: {e}") from e
|
||||
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
|
||||
except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError):
|
||||
return None
|
||||
return pick_stable_tag([tag["name"] for tag in tags])
|
||||
|
||||
|
||||
_STABLE_TAG = fetch_stable_tag() or "master"
|
||||
|
||||
SOURCE_URL = (
|
||||
_RAW_BASE + "/{tag}"
|
||||
f"{_RAW_BASE}/{_STABLE_TAG}"
|
||||
"/package/batocera/core/batocera-scripts/scripts/batocera-systems"
|
||||
)
|
||||
|
||||
CONFIGGEN_DEFAULTS_URL = (
|
||||
_RAW_BASE + "/{tag}"
|
||||
f"{_RAW_BASE}/{_STABLE_TAG}"
|
||||
"/package/batocera/core/batocera-configgen/configs/"
|
||||
"configgen-defaults.yml"
|
||||
)
|
||||
@@ -184,9 +186,8 @@ def _resolve_truncated_md5(md5: str, md5_index: dict[str, str]) -> str:
|
||||
class Scraper(BaseScraper):
|
||||
"""Scraper for batocera-systems Python dict."""
|
||||
|
||||
def __init__(self):
|
||||
self.tag = fetch_stable_tag()
|
||||
super().__init__(url=SOURCE_URL.format(tag=self.tag))
|
||||
def __init__(self, url: str = SOURCE_URL):
|
||||
super().__init__(url=url)
|
||||
|
||||
def _fetch_cores(self) -> tuple[list[str], list[str]]:
|
||||
"""Extract core names and standalone cores from configgen-defaults.yml.
|
||||
@@ -194,17 +195,16 @@ class Scraper(BaseScraper):
|
||||
Returns (all_cores, standalone_cores) where standalone_cores are
|
||||
those with emulator != "libretro".
|
||||
"""
|
||||
url = CONFIGGEN_DEFAULTS_URL.format(tag=self.tag)
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
CONFIGGEN_DEFAULTS_URL,
|
||||
headers={"User-Agent": "retrobios-scraper/1.0"},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
raw = resp.read().decode("utf-8")
|
||||
except urllib.error.URLError as e:
|
||||
raise ConnectionError(
|
||||
f"Failed to fetch {url}: {e}"
|
||||
f"Failed to fetch {CONFIGGEN_DEFAULTS_URL}: {e}"
|
||||
) from e
|
||||
data = yaml_load(raw)
|
||||
cores: set[str] = set()
|
||||
@@ -372,12 +372,25 @@ class Scraper(BaseScraper):
|
||||
|
||||
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()
|
||||
result = {
|
||||
"platform": "Batocera",
|
||||
"version": self.tag.removeprefix("batocera-"),
|
||||
"version": batocera_version or "",
|
||||
"homepage": "https://batocera.org",
|
||||
"source": self.url,
|
||||
"source": SOURCE_URL,
|
||||
"base_destination": "bios",
|
||||
"hash_type": "md5",
|
||||
"verification_mode": "md5",
|
||||
|
||||
@@ -41,8 +41,10 @@ PLATFORM_NAME = "bizhawk"
|
||||
|
||||
GITHUB_REPO = "TASEmulators/BizHawk"
|
||||
|
||||
_STABLE_TAG = fetch_github_latest_version(GITHUB_REPO) or "master"
|
||||
|
||||
SOURCE_URL = (
|
||||
"https://raw.githubusercontent.com/TASEmulators/BizHawk/{tag}"
|
||||
f"https://raw.githubusercontent.com/TASEmulators/BizHawk/{_STABLE_TAG}"
|
||||
"/src/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs"
|
||||
)
|
||||
|
||||
@@ -330,8 +332,7 @@ class Scraper(BaseScraper):
|
||||
"""BizHawk firmware database scraper."""
|
||||
|
||||
def __init__(self):
|
||||
self.tag = fetch_github_latest_version(GITHUB_REPO)
|
||||
super().__init__(url=SOURCE_URL.format(tag=self.tag))
|
||||
super().__init__(url=SOURCE_URL)
|
||||
|
||||
def validate_format(self, raw_data: str) -> bool:
|
||||
return "FirmwareDatabase" in raw_data and "FirmwareAndOption" in raw_data
|
||||
@@ -371,11 +372,13 @@ class Scraper(BaseScraper):
|
||||
|
||||
systems[req.system]["files"].append(requirement_entry(req))
|
||||
|
||||
version = _STABLE_TAG if _STABLE_TAG != "master" else ""
|
||||
|
||||
return {
|
||||
"platform": "BizHawk",
|
||||
"version": self.tag,
|
||||
"version": version,
|
||||
"homepage": "https://tasvideos.org/BizHawk",
|
||||
"source": self.url,
|
||||
"source": SOURCE_URL,
|
||||
"base_destination": "Firmware",
|
||||
"hash_type": "sha1",
|
||||
"verification_mode": "sha1",
|
||||
|
||||
@@ -295,7 +295,7 @@ class Scraper(BaseScraper):
|
||||
def fetch_metadata(self) -> dict:
|
||||
"""Fetch version info from GitHub."""
|
||||
version = fetch_github_latest_version("libretro/libretro-core-info")
|
||||
return {"version": version}
|
||||
return {"version": version or ""}
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -22,7 +22,6 @@ try:
|
||||
BaseScraper,
|
||||
BiosRequirement,
|
||||
fetch_github_latest_version,
|
||||
github_headers,
|
||||
requirement_entry,
|
||||
)
|
||||
except ImportError:
|
||||
@@ -30,7 +29,6 @@ except ImportError:
|
||||
BaseScraper,
|
||||
BiosRequirement,
|
||||
fetch_github_latest_version,
|
||||
github_headers,
|
||||
requirement_entry,
|
||||
)
|
||||
|
||||
@@ -428,7 +426,13 @@ class Scraper(BaseScraper):
|
||||
|
||||
systems[req.system]["files"].append(requirement_entry(req))
|
||||
|
||||
version = fetch_github_latest_version("dragoonDorise/EmuDeck")
|
||||
version = ""
|
||||
try:
|
||||
v = fetch_github_latest_version("dragoonDorise/EmuDeck")
|
||||
if v:
|
||||
version = v
|
||||
except (ConnectionError, ValueError, OSError):
|
||||
pass
|
||||
|
||||
cores = self._fetch_installed_emulators()
|
||||
|
||||
@@ -464,15 +468,17 @@ class Scraper(BaseScraper):
|
||||
skip = {"retroarch_maincfg", "retroarch"}
|
||||
|
||||
try:
|
||||
req = urllib.request.Request(api_url, headers=github_headers())
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
data = json.loads(resp.read())
|
||||
except (urllib.error.URLError, json.JSONDecodeError) as e:
|
||||
raise RuntimeError(f"cannot list EmuDeck EmuScripts: {e}") from e
|
||||
req = urllib.request.Request(
|
||||
api_url,
|
||||
headers={"User-Agent": "retrobios-scraper/1.0"},
|
||||
)
|
||||
data = json.loads(urllib.request.urlopen(req, timeout=30).read())
|
||||
except (urllib.error.URLError, OSError):
|
||||
return []
|
||||
|
||||
cores: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for entry in data if isinstance(data, list) else []:
|
||||
for entry in data:
|
||||
name = entry.get("name", "")
|
||||
if not name.endswith(".sh"):
|
||||
continue
|
||||
@@ -487,8 +493,6 @@ class Scraper(BaseScraper):
|
||||
if core not in seen:
|
||||
seen.add(core)
|
||||
cores.append(core)
|
||||
if not cores:
|
||||
raise RuntimeError(f"no emulator script in {api_url}")
|
||||
return sorted(cores)
|
||||
|
||||
|
||||
|
||||
Loaded 100 of 174 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user