mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 13:33:24 -05:00
feat: refuse a citation no repository can hold
kenji-nx cited tmp/es-de/ANDROID.md:470-474, a path from the machine of whoever profiled it. No revision of any declared repository holds it, so profile_sync could only report it missing, every pass, forever, and no amount of reading would ever settle it. validate_schemas now refuses a scratch directory, an absolute path, a Windows drive path and one climbing out of the tree, and names the offending citation rather than the scalar that carries it. Offline, so it runs on every push and every pull request rather than waiting for a network pass. The ES-DE citation reads as external now, which is what it always was. kenji-nx is at 37 refs, all anchored: the three changed blocks were var giving way to explicit types.
This commit is contained in:
1 parent
d124f6c516
commit
6f27a296f7
3 files changed
+88
-8
No files matched your search
@@ -5,7 +5,7 @@ source: "https://git.ryujinx.app/projects/Kenji-NX"
|
|||||||
source_branch: libryujinx_bionic
|
source_branch: libryujinx_bionic
|
||||||
upstream: "https://git.ryujinx.app/projects/Ryubing"
|
upstream: "https://git.ryujinx.app/projects/Ryubing"
|
||||||
profiled_date: "2026-08-11"
|
profiled_date: "2026-08-11"
|
||||||
source_commit: "8d7d8f344cb01257c5bc59bb02a74bb89ab9bb5f"
|
source_commit: "53d32a761e61dc8e9b5c0744199a890fc8d934d5"
|
||||||
core_version: "2.1.0-pr.2"
|
core_version: "2.1.0-pr.2"
|
||||||
display_name: "Nintendo - Switch (Kenji-NX)"
|
display_name: "Nintendo - Switch (Kenji-NX)"
|
||||||
cores:
|
cores:
|
||||||
@@ -35,7 +35,7 @@ notes: |
|
|||||||
~/.switch fallback that ReloadKeySet consults in UserProfile mode never fires
|
~/.switch fallback that ReloadKeySet consults in UserProfile mode never fires
|
||||||
there. games, profiles and system are created under the root on every start.
|
there. games, profiles and system are created under the root on every start.
|
||||||
ref: src/Ryujinx.Common/Configuration/AppDataManager.cs:11-17,47,50-104,223-230,
|
ref: src/Ryujinx.Common/Configuration/AppDataManager.cs:11-17,47,50-104,223-230,
|
||||||
src/LibKenjinx/LibKenjinx.cs:54-95,
|
src/LibKenjinx/LibKenjinx.cs:59-100,
|
||||||
src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt:264-298
|
src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt:264-298
|
||||||
|
|
||||||
Firmware is installed rather than placed. ContentManager takes a directory, a
|
Firmware is installed rather than placed. ContentManager takes a directory, a
|
||||||
@@ -46,7 +46,7 @@ notes: |
|
|||||||
homebrew NRO goes through ProcessLoader without touching the key set, and the
|
homebrew NRO goes through ProcessLoader without touching the key set, and the
|
||||||
failure is deferred to the first service that needs a system archive.
|
failure is deferred to the first service that needs a system archive.
|
||||||
ref: src/Ryujinx.HLE/FileSystem/ContentManager.cs:431-498,675-749,893-903,
|
ref: src/Ryujinx.HLE/FileSystem/ContentManager.cs:431-498,675-749,893-903,
|
||||||
src/LibKenjinx/Android/JniExportedMethods.cs:459-495,
|
src/LibKenjinx/Android/JniExportedMethods.cs:462-498,
|
||||||
src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs:189-205
|
src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs:189-205
|
||||||
|
|
||||||
Two read paths belong to the desktop build alone, because the Android build
|
Two read paths belong to the desktop build alone, because the Android build
|
||||||
@@ -59,7 +59,7 @@ notes: |
|
|||||||
key_retail.bin, while the Android entry buffers the raw tag in
|
key_retail.bin, while the Android entry buffers the raw tag in
|
||||||
KenjinxAmiiboShim and INfp reads the figure id straight out of offset 0x1DC.
|
KenjinxAmiiboShim and INfp reads the figure id straight out of offset 0x1DC.
|
||||||
ref: src/LibKenjinx/LibKenjinx.csproj:11-13,
|
ref: src/LibKenjinx/LibKenjinx.csproj:11-13,
|
||||||
src/LibKenjinx/Android/JniExportedMethods.cs:347-348,1046-1060,
|
src/LibKenjinx/Android/JniExportedMethods.cs:350-351,1049-1063,
|
||||||
src/LibKenjinx/LibKenjinx.Input.cs:28-39,
|
src/LibKenjinx/LibKenjinx.Input.cs:28-39,
|
||||||
src/Ryujinx.HLE/Kenjinx/AmiiboBridge.cs:10-44,
|
src/Ryujinx.HLE/Kenjinx/AmiiboBridge.cs:10-44,
|
||||||
src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/KenjinxAmiiboShim.cs:15-41,
|
src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/KenjinxAmiiboShim.cs:15-41,
|
||||||
@@ -79,7 +79,7 @@ notes: |
|
|||||||
under the Nintendo notice of 2026-02-12, and both the source and the builds
|
under the Nintendo notice of 2026-02-12, and both the source and the builds
|
||||||
are served by the Forgejo instance, which is where ES-DE points as well.
|
are served by the Forgejo instance, which is where ES-DE points as well.
|
||||||
ref: https://github.com/github/dmca/blob/master/2026/02/2026-02-12-nintendo.md,
|
ref: https://github.com/github/dmca/blob/master/2026/02/2026-02-12-nintendo.md,
|
||||||
tmp/es-de/ANDROID.md:470-474
|
es-de ANDROID.md:470-474
|
||||||
|
|
||||||
files:
|
files:
|
||||||
- name: prod.keys
|
- name: prod.keys
|
||||||
@@ -142,7 +142,7 @@ files:
|
|||||||
of title 0100000000000809. The NCAs are then registered under
|
of title 0100000000000809. The NCAs are then registered under
|
||||||
bis/system/Contents/registered and read from there, never as loose files.
|
bis/system/Contents/registered and read from there, never as loose files.
|
||||||
No digest of the package itself is compared against anything.
|
No digest of the package itself is compared against anything.
|
||||||
source_ref: "src/Ryujinx.HLE/FileSystem/ContentManager.cs:431-498,675-749,751-1045, src/LibKenjinx/Android/JniExportedMethods.cs:459-495, src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/SettingsViewModel.kt:308-364"
|
source_ref: "src/Ryujinx.HLE/FileSystem/ContentManager.cs:431-498, 675-749, 751-1045, src/LibKenjinx/Android/JniExportedMethods.cs:462-498, src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/SettingsViewModel.kt:308-364"
|
||||||
|
|
||||||
- name: key_retail.bin
|
- name: key_retail.bin
|
||||||
required: false
|
required: false
|
||||||
@@ -183,7 +183,7 @@ files:
|
|||||||
Android slots read the bytes through the content resolver and buffer them
|
Android slots read the bytes through the content resolver and buffer them
|
||||||
for the NFP service, which takes the figure id from offset 0x1DC without
|
for the NFP service, which takes the figure id from offset 0x1DC without
|
||||||
decrypting anything.
|
decrypting anything.
|
||||||
source_ref: "src/Ryujinx.HLE/HOS/Horizon.cs:355-363, src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs:22-44,151-283, src/LibKenjinx/LibKenjinx.cs:700-729, src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/GameViews.kt:328-349"
|
source_ref: "src/Ryujinx.HLE/HOS/Horizon.cs:355-363, src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs:22-44, 151-283, src/LibKenjinx/LibKenjinx.cs:705-734, src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/GameViews.kt:328-349"
|
||||||
|
|
||||||
- name: "<CaCertificateId>.der"
|
- name: "<CaCertificateId>.der"
|
||||||
required: false
|
required: false
|
||||||
@@ -211,7 +211,7 @@ files:
|
|||||||
because the external filesystem cannot carry an executable mapping, loaded
|
because the external filesystem cannot carry an executable mapping, loaded
|
||||||
through the rootless Adreno driver loader, and the resulting handle is
|
through the rootless Adreno driver loader, and the resulting handle is
|
||||||
passed to the Vulkan loader. An empty selection uses the system loader.
|
passed to the Vulkan loader. An empty selection uses the system loader.
|
||||||
source_ref: "src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/VulkanDriverViewModel.kt:13-167, src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt:202-243, src/LibKenjinx/Android/JniExportedMethods.cs:532-555, src/LibKenjinx/VulkanLoader.cs:27-29"
|
source_ref: "src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/VulkanDriverViewModel.kt:13-167, src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt:202-243, src/LibKenjinx/Android/JniExportedMethods.cs:535-558, src/LibKenjinx/VulkanLoader.cs:27-29"
|
||||||
|
|
||||||
# System archives read out of the installed firmware, never as loose files.
|
# System archives read out of the installed firmware, never as loose files.
|
||||||
firmware_titles:
|
firmware_titles:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import zipfile
|
import zipfile
|
||||||
from pathlib import Path, PurePosixPath
|
from pathlib import Path, PurePosixPath
|
||||||
@@ -55,6 +56,7 @@ def _validate_yaml_directory(
|
|||||||
out.append(f"{path.relative_to(ROOT)}: {exc}")
|
out.append(f"{path.relative_to(ROOT)}: {exc}")
|
||||||
continue
|
continue
|
||||||
out.extend(_errors(validator, data, str(path.relative_to(ROOT))))
|
out.extend(_errors(validator, data, str(path.relative_to(ROOT))))
|
||||||
|
out.extend(_unreachable_citations(path, data))
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
@@ -132,6 +134,37 @@ def _scan_pack_manifests(dist: Path) -> list[str]:
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
_UNREACHABLE_REF = re.compile(
|
||||||
|
r"(?:^|[\s,;(])(?:tmp/|/|[A-Za-z]:[\\/]|\.{1,2}/)[\w.\\/+-]*[\w+-]:\d+(?:-\d+)?"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _unreachable_citations(path: Path, document: object) -> list[str]:
|
||||||
|
"""Refs naming a place no repository can hold.
|
||||||
|
|
||||||
|
A citation is read against a revision of a declared repository, so a
|
||||||
|
scratch directory, an absolute path or one climbing out of the tree can
|
||||||
|
never resolve and never will. kenji-nx carried tmp/es-de/ANDROID.md:470,
|
||||||
|
a path from whoever profiled it, which profile_sync could only report
|
||||||
|
missing forever.
|
||||||
|
"""
|
||||||
|
out: list[str] = []
|
||||||
|
stack: list[object] = [document]
|
||||||
|
while stack:
|
||||||
|
node = stack.pop()
|
||||||
|
if isinstance(node, dict):
|
||||||
|
stack.extend(node.values())
|
||||||
|
elif isinstance(node, list):
|
||||||
|
stack.extend(node)
|
||||||
|
elif isinstance(node, str):
|
||||||
|
for match in _UNREACHABLE_REF.finditer(node):
|
||||||
|
out.append(
|
||||||
|
f"{path.relative_to(ROOT)}: citation names a path outside "
|
||||||
|
f"any repository: {match.group().strip(' ,;(')}"
|
||||||
|
)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
def _semantic_envelope_checks(path: Path, document: dict) -> list[str]:
|
def _semantic_envelope_checks(path: Path, document: dict) -> list[str]:
|
||||||
if document.get("count") != len(document.get("items", [])):
|
if document.get("count") != len(document.get("items", [])):
|
||||||
return [f"{path.relative_to(ROOT)}: count does not equal len(items)"]
|
return [f"{path.relative_to(ROOT)}: count does not equal len(items)"]
|
||||||
|
|||||||
@@ -184,6 +184,53 @@ class WorkflowRegressions(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UnreachableCitationRegressions(unittest.TestCase):
|
||||||
|
"""A citation must name a place a declared repository can hold.
|
||||||
|
|
||||||
|
kenji-nx carried tmp/es-de/ANDROID.md:470-474, a path from the machine
|
||||||
|
of whoever profiled it. No revision of any declared repository holds it,
|
||||||
|
so profile_sync could only report it missing, every pass, forever. The
|
||||||
|
check is offline and runs on every push.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _hits(self, text: str) -> bool:
|
||||||
|
import validate_schemas
|
||||||
|
|
||||||
|
return bool(validate_schemas._UNREACHABLE_REF.search(" " + text))
|
||||||
|
|
||||||
|
def test_a_scratch_directory_is_rejected(self):
|
||||||
|
self.assertTrue(self._hits("tmp/es-de/ANDROID.md:470-474"))
|
||||||
|
|
||||||
|
def test_an_absolute_path_is_rejected(self):
|
||||||
|
self.assertTrue(self._hits("/home/someone/src/a.c:12"))
|
||||||
|
self.assertTrue(self._hits(r"C:\work\src\a.c:12"))
|
||||||
|
|
||||||
|
def test_a_path_climbing_out_of_the_tree_is_rejected(self):
|
||||||
|
self.assertTrue(self._hits("../outside/src/a.c:12"))
|
||||||
|
|
||||||
|
def test_ordinary_citations_pass(self):
|
||||||
|
for good in (
|
||||||
|
"src/core/main.cpp:210",
|
||||||
|
"libretro.c:5293-5337",
|
||||||
|
"PCE.emu/src/main/Main.cc:80-91",
|
||||||
|
"es-de ANDROID.md:470-474",
|
||||||
|
"see tmp files for details",
|
||||||
|
):
|
||||||
|
with self.subTest(citation=good):
|
||||||
|
self.assertFalse(self._hits(good))
|
||||||
|
|
||||||
|
def test_the_corpus_carries_none(self):
|
||||||
|
import validate_schemas
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
for path in sorted((ROOT / "emulators").glob("*.yml")):
|
||||||
|
with path.open(encoding="utf-8") as handle:
|
||||||
|
errors.extend(
|
||||||
|
validate_schemas._unreachable_citations(path, yaml.safe_load(handle))
|
||||||
|
)
|
||||||
|
self.assertEqual(errors, [])
|
||||||
|
|
||||||
|
|
||||||
class FaqRegressions(unittest.TestCase):
|
class FaqRegressions(unittest.TestCase):
|
||||||
"""The FAQ states facts the code owns; these tie it back to the source.
|
"""The FAQ states facts the code owns; these tie it back to the source.
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user