feat: document the category of every entry

Held entries carry their category and readers too, not only missing ones, so each platform page says which cores read every file it lists and where the code does it. Severity still applies to firmware gaps only.
This commit is contained in:
Abdessamad Derraz committed 2026-10-22 05:46:09 +02:00
1 parent a75ea05b86
commit be9036f2b2
3 files changed
+17 -16

No files matched your search

+7 -7
View File
@@ -1993,14 +1993,14 @@ def _render_platform_systems(
status_display = f"untested: {reason}" if reason else "untested" status_display = f"untested: {reason}" if reason else "untested"
elif status == "missing": elif status == "missing":
status_display = "**missing**" status_display = "**missing**"
# Not every missing entry is firmware: the platform may list a
# game's data or a file no core it runs opens.
kind = {"game_data": "game data",
"unread": "no core the platform runs opens it"}.get(f.get("category", ""))
if kind:
status_display += f", {kind}"
else: else:
status_display = status status_display = status
# Not every entry is firmware: the platform may list a game's data
# or a file no core it runs opens.
kind = {"game_data": "game data",
"unread": "no core the platform runs opens it"}.get(f.get("category", ""))
if kind:
status_display += f", {kind}"
size_str = _fmt_size(size) if size else "" size_str = _fmt_size(size) if size else ""
details = [status_display] details = [status_display]
@@ -2033,7 +2033,7 @@ def _render_platform_systems(
if sha256: if sha256:
hash_parts.append(f"SHA256: `{sha256}`") hash_parts.append(f"SHA256: `{sha256}`")
lines.append(f"{pad} {' | '.join(hash_parts)}") lines.append(f"{pad} {' | '.join(hash_parts)}")
if status == "missing" and f.get("category_evidence"): if f.get("category_evidence"):
lines.append(f"{pad} {_category_evidence_md(f['category_evidence'])}") lines.append(f"{pad} {_category_evidence_md(f['category_evidence'])}")
lines.append("") lines.append("")
+9 -8
View File
@@ -1192,8 +1192,8 @@ def verify_platform(
file_status: dict[str, str] = {} file_status: dict[str, str] = {}
file_required: dict[str, bool] = {} file_required: dict[str, bool] = {}
file_severity: dict[str, str] = {} file_severity: dict[str, str] = {}
# What a missing entry is: firmware, a game's data, or a name no core # What each entry is: firmware, a game's data, or a name no core the
# the platform runs opens. Only firmware gaps carry a severity. # platform runs opens. Only firmware gaps carry a severity.
file_category: dict[str, str] = {} file_category: dict[str, str] = {}
categories = EntryCategories(config, profiles, data_dir_registry) categories = EntryCategories(config, profiles, data_dir_registry)
@@ -1356,13 +1356,14 @@ def verify_platform(
prev = file_status.get(dest) prev = file_status.get(dest)
hle = hle_index.get(file_entry.get("name", ""), False) hle = hle_index.get(file_entry.get("name", ""), False)
sev = compute_severity(cur, required, mode, hle) sev = compute_severity(cur, required, mode, hle)
if cur == Status.MISSING: explained = categories.explain(file_entry, destination, sys_id)
explained = categories.explain(file_entry, destination, sys_id) category = explained.pop("category")
category = explained.pop("category")
result["category_evidence"] = explained
else:
category = BIOS
result["category"] = category result["category"] = category
result["category_evidence"] = explained
if cur != Status.MISSING:
# A held entry is firmware for the counts: its category only
# documents it.
category = BIOS
if file_category.get(dest, category) == BIOS: if file_category.get(dest, category) == BIOS:
category = BIOS category = BIOS
file_category[dest] = category file_category[dest] = category
+1 -1
View File
@@ -39,7 +39,7 @@ Every module in `tests/`, by the area it guards.
|------|----------|---------| |------|----------|---------|
| Core regression suites | synthetic | `test_e2e`, `test_audit_regressions`, `test_tool_messages` | | Core regression suites | synthetic | `test_e2e`, `test_audit_regressions`, `test_tool_messages` |
| File resolution and identity | synthetic + collection | `test_adler32_resolution`, `test_agnostic_scan`, `test_agnostic_scan_mode`, `test_agnostic_scope`, `test_agnostic_substitute`, `test_archive_choice`, `test_bios_companions`, `test_bundled_identity`, `test_casefold_cache`, `test_db_aliases`, `test_declared_names`, `test_derivations`, `test_directory_entries`, `test_extra_identity`, `test_foreign_tree`, `test_foreign_tree_names`, `test_frontend_digest`, `test_game_data_homonyms`, `test_hashing`, `test_identity`, `test_md5_pack_context`, `test_name_tiebreak`, `test_nec_filing`, `test_owner_resolution`, `test_path_suffix`, `test_resolution_evidence`, `test_sega_filing`, `test_shipped_content`, `test_twin_declarations`, `test_validated_choice`, `test_variant_choice`, `test_variant_identity`, `test_zipped_member` | | File resolution and identity | synthetic + collection | `test_adler32_resolution`, `test_agnostic_scan`, `test_agnostic_scan_mode`, `test_agnostic_scope`, `test_agnostic_substitute`, `test_archive_choice`, `test_bios_companions`, `test_bundled_identity`, `test_casefold_cache`, `test_db_aliases`, `test_declared_names`, `test_derivations`, `test_directory_entries`, `test_extra_identity`, `test_foreign_tree`, `test_foreign_tree_names`, `test_frontend_digest`, `test_game_data_homonyms`, `test_hashing`, `test_identity`, `test_md5_pack_context`, `test_name_tiebreak`, `test_nec_filing`, `test_owner_resolution`, `test_path_suffix`, `test_resolution_evidence`, `test_sega_filing`, `test_shipped_content`, `test_twin_declarations`, `test_validated_choice`, `test_variant_choice`, `test_variant_identity`, `test_zipped_member` |
| Verification, cross-reference and profile checks | synthetic + profiles | `test_case_insensitive_platforms`, `test_checks_have_values`, `test_core_file_fills_declaration`, `test_core_size_refusal`, `test_cross_reference_sources`, `test_crypto_aes`, `test_crypto_unique_data`, `test_crypto_verify`, `test_file_consumers`, `test_fileless_audit`, `test_flycast_layout`, `test_gap_settlement`, `test_hidden_extras`, `test_launcher_aliases`, `test_launcher_layer`, `test_native_mode`, `test_no_case_collisions`, `test_path_conflict_status`, `test_path_null_copies`, `test_profile_checks`, `test_profile_repositories`, `test_romm_hash_rule`, `test_shared_groups`, `test_standalone_links`, `test_standalone_mode`, `test_undeclared_dedup`, `test_unplaced_extras`, `test_validation_scope`, `test_verify_all_dir`, `test_verify_emulator_evidence`, `test_verify_listing`, `test_verify_pack_choice`, `test_verify_release_asset` | | Verification, cross-reference and profile checks | synthetic + profiles | `test_case_insensitive_platforms`, `test_checks_have_values`, `test_core_file_fills_declaration`, `test_core_size_refusal`, `test_cross_reference_sources`, `test_crypto_aes`, `test_crypto_unique_data`, `test_crypto_verify`, `test_entry_categories`, `test_file_consumers`, `test_fileless_audit`, `test_flycast_layout`, `test_gap_settlement`, `test_hidden_extras`, `test_launcher_aliases`, `test_launcher_layer`, `test_native_mode`, `test_no_case_collisions`, `test_path_conflict_status`, `test_path_null_copies`, `test_profile_checks`, `test_profile_repositories`, `test_romm_hash_rule`, `test_shared_groups`, `test_standalone_links`, `test_standalone_mode`, `test_undeclared_dedup`, `test_unplaced_extras`, `test_validation_scope`, `test_verify_all_dir`, `test_verify_emulator_evidence`, `test_verify_listing`, `test_verify_pack_choice`, `test_verify_release_asset` |
| Packs and manifests | synthetic + real packs | `test_artifact_lock`, `test_atomic_artifacts`, `test_batocera_cores`, `test_deterministic_zip`, `test_emulator_pack_counts`, `test_finalize_pack_names`, `test_large_file_cache`, `test_libretro_layout`, `test_manifest_entry`, `test_minui_platform`, `test_pack_conformance_dirs`, `test_pack_core_extras`, `test_pack_counts`, `test_pack_destinations`, `test_pack_failures`, `test_pack_file_names`, `test_pack_grouping`, `test_pack_integrity`, `test_pack_member_identity`, `test_pack_readme`, `test_platform_inheritance`, `test_platform_keys`, `test_required_only_archives`, `test_required_only_twins`, `test_split_extras`, `test_split_names`, `test_split_pack`, `test_system_pack_extras`, `test_system_pack_parity`, `test_torrentzip`, `test_which_pack` | | Packs and manifests | synthetic + real packs | `test_artifact_lock`, `test_atomic_artifacts`, `test_batocera_cores`, `test_deterministic_zip`, `test_emulator_pack_counts`, `test_finalize_pack_names`, `test_large_file_cache`, `test_libretro_layout`, `test_manifest_entry`, `test_minui_platform`, `test_pack_conformance_dirs`, `test_pack_core_extras`, `test_pack_counts`, `test_pack_destinations`, `test_pack_failures`, `test_pack_file_names`, `test_pack_grouping`, `test_pack_integrity`, `test_pack_member_identity`, `test_pack_readme`, `test_platform_inheritance`, `test_platform_keys`, `test_required_only_archives`, `test_required_only_twins`, `test_split_extras`, `test_split_names`, `test_split_pack`, `test_system_pack_extras`, `test_system_pack_parity`, `test_torrentzip`, `test_which_pack` |
| Slots and regions | synthetic + profiles | `test_region`, `test_region_agnostic_scan`, `test_region_groups`, `test_region_held`, `test_region_platform_index`, `test_region_required_only`, `test_slot`, `test_slots`, `test_slots_shipped_file` | | Slots and regions | synthetic + profiles | `test_region`, `test_region_agnostic_scan`, `test_region_groups`, `test_region_held`, `test_region_platform_index`, `test_region_required_only`, `test_slot`, `test_slots`, `test_slots_shipped_file` |
| Hardware targets and systems | synthetic + target files | `test_extract_targets`, `test_lakka_targets`, `test_libretro_slugs`, `test_system_ids`, `test_system_selection`, `test_target_names`, `test_target_scrapers`, `test_target_systems`, `test_target_verify` | | Hardware targets and systems | synthetic + target files | `test_extract_targets`, `test_lakka_targets`, `test_libretro_slugs`, `test_system_ids`, `test_system_selection`, `test_target_names`, `test_target_scrapers`, `test_target_systems`, `test_target_verify` |