From 37633043dba84d880961dae6272098cf4ce36f11 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sun, 18 Oct 2026 13:40:50 +0200 Subject: [PATCH] fix: pin ryujinx to its Gitea mirror Gitea answers /raw/commit with an HTML proof-of-work page, so Forgejo-family files are fetched through the API raw route. --- emulators/ryujinx.yml | 18 +++++++++--------- scripts/find_pinned_revision.py | 2 +- scripts/upstream.py | 23 ++++++++++++++++++++++- tests/test_upstream.py | 15 +++++++++++++++ 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/emulators/ryujinx.yml b/emulators/ryujinx.yml index d438e74e..10645558 100644 --- a/emulators/ryujinx.yml +++ b/emulators/ryujinx.yml @@ -3,38 +3,38 @@ type: standalone source: "https://git.ngram.ca/mirrors/Ryujinx" upstream: "https://github.com/Ryujinx/Ryujinx" profiled_date: "2026-03-26" -core_version: "1.1.1403" +source_commit: "7d158acc3b5826a08941d6e8d50d3a3897021bcd" +core_version: "1.1.1402" display_name: "Ryujinx (Nintendo Switch)" cores: - ryujinx systems: [nintendo-switch] analysis_date: "2026-03-26" -analysis_commit: "HEAD (depth=1, ngram.ca mirror)" mode: standalone -# Key files are read from {BaseDirPath}/system/ (AppDataManager.cs:229) -# Also searched in ~/.switch/ (AppDataManager.cs:47) -# Batocera copies bios/switch/prod.keys to Ryujinx/system/prod.keys (ryujinxGenerator.py:108) -# Firmware NCAs are installed via UI (ContentManager.cs:429), not placed as files +# Key files are read from {BaseDirPath}/system/ (src/Ryujinx.Common/Configuration/AppDataManager.cs:229) +# Also searched in ~/.switch/ (src/Ryujinx.Common/Configuration/AppDataManager.cs:47) +# Batocera copied bios/switch/prod.keys to Ryujinx/system/prod.keys (batocera ryujinxGenerator.py:108 at 1f69a46e) until it dropped the package in ee8a418f +# Firmware NCAs are installed via UI (src/Ryujinx.HLE/FileSystem/ContentManager.cs:429), not placed as files files: - name: "prod.keys" required: true path: "switch/prod.keys" note: "Switch production keys for NCA decryption" - source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:239" + source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:238" - name: "title.keys" required: false path: "switch/title.keys" note: "Per-title decryption keys, needed for specific game content" - source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:240" + source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:239" - name: "console.keys" required: false path: "switch/console.keys" note: "Console-specific keys for encrypted content" - source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:241" + source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:240" notes: | Ryujinx is a standalone Nintendo Switch emulator written in C#, created by gdkchan. diff --git a/scripts/find_pinned_revision.py b/scripts/find_pinned_revision.py index b643d480..a7821311 100644 --- a/scripts/find_pinned_revision.py +++ b/scripts/find_pinned_revision.py @@ -62,7 +62,7 @@ def find_revision( """The newest revision whose copy of the path has this md5, and its bytes.""" wanted = md5.lower() for sha in path_history(repo, path, cache_dir): - data = upstream._fetch(upstream.raw_url(repo, sha, path)) + data = upstream._fetch(upstream.content_url(repo, sha, path)) if data is not None and hashlib.md5(data).hexdigest() == wanted: return sha, data return None diff --git a/scripts/upstream.py b/scripts/upstream.py index f5970d9f..96bfd29d 100644 --- a/scripts/upstream.py +++ b/scripts/upstream.py @@ -51,6 +51,13 @@ _HOSTS: dict[str, tuple[str, str, str]] = { "https://codeberg.org/api/v1", "https://codeberg.org", ), + # The Ryujinx repository left GitHub in October 2024; this Gitea pull + # mirror kept its history up to 1.1.1402. + "git.ngram.ca": ( + "forgejo", + "https://git.ngram.ca/api/v1", + "https://git.ngram.ca", + ), "git.citron-emu.org": ( "forgejo", "https://git.citron-emu.org/api/v1", @@ -180,6 +187,20 @@ def raw_url(repo: Repo, sha: str, path: str) -> str: return f"{repo.raw_base}/{repo.owner}/{repo.name}/raw/commit/{sha}/{quoted}" +def content_url(repo: Repo, sha: str, path: str) -> str: + """URL the tooling fetches one path from. + + Forgejo and Gitea hosts put their web routes, /raw/commit included, + behind a proof-of-work page (git.ngram.ca answers 200 with an HTML + challenge); the API raw route is not challenged. raw_url stays the + link a reader opens. + """ + if repo.family != "forgejo": + return raw_url(repo, sha, path) + quoted = urllib.parse.quote(path) + return f"{repo.api_base}/repos/{repo.owner}/{repo.name}/raw/{quoted}?ref={sha}" + + def _headers(url: str, accept_json: bool = False) -> dict[str, str]: """Request headers. GITHUB_TOKEN is only ever sent to GitHub.""" headers = {"User-Agent": USER_AGENT} @@ -336,7 +357,7 @@ def fetch_file( return None if text == ABSENT else text.splitlines() if offline: raise NotCachedError(f"{repo.slug}@{sha[:8]}:{path} is not in the offline cache") - text = _http_text(raw_url(repo, sha, path)) + text = _http_text(content_url(repo, sha, path)) write_cache(target, ABSENT if text is None else text) return None if text is None else text.splitlines() diff --git a/tests/test_upstream.py b/tests/test_upstream.py index dd0e5131..c27720e4 100644 --- a/tests/test_upstream.py +++ b/tests/test_upstream.py @@ -115,6 +115,21 @@ class TestRawUrl(unittest.TestCase): "https://codeberg.org/g/p/raw/commit/abc123/a.c", ) + def test_forgejo_fetches_through_the_api(self): + # git.ngram.ca serves an HTML proof-of-work page on /raw/commit, + # which fetch_file would have cached as the file's lines. + import upstream + + repo = parse_repo("https://git.ngram.ca/mirrors/Ryujinx") + self.assertEqual( + upstream.content_url(repo, "abc123", "src/a b.cs"), + "https://git.ngram.ca/api/v1/repos/mirrors/Ryujinx/raw/src/a%20b.cs?ref=abc123", + ) + github = parse_repo("https://github.com/o/n") + self.assertEqual( + upstream.content_url(github, "s", "a.c"), raw_url(github, "s", "a.c") + ) + def test_path_is_quoted(self): repo = parse_repo("https://github.com/o/n") self.assertIn("src/a%20b.cpp", raw_url(repo, "s", "src/a b.cpp"))