mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-09-10 11:55:16 +00:00
Compare commits
18 Commits
Canary-1.3
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
b000f91dad | ||
|
a60b2a0ba3 | ||
|
4c9b48b754 | ||
|
3bd7d5904e | ||
|
23eb9a3043 | ||
|
931ec44406 | ||
|
d68efa98ba | ||
|
ded76801d1 | ||
|
6084df7473 | ||
|
f3953c6039 | ||
|
91f5247e7f | ||
|
5658402c6b | ||
|
1b2c93e188 | ||
|
9e599ff325 | ||
|
7a5f430b59 | ||
|
1e340ce2f3 | ||
|
dbb4e63e1e | ||
|
d00ab52fa2 |
@@ -19,8 +19,8 @@
|
|||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
|
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
|
||||||
<PackageVersion Include="Concentus" Version="2.2.2" />
|
<PackageVersion Include="Concentus" Version="2.2.2" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
|
||||||
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
<PackageVersion Include="DynamicData" Version="9.4.1" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||||
<PackageVersion Include="Humanizer" Version="2.14.1" />
|
<PackageVersion Include="Humanizer" Version="2.14.1" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
@@ -42,11 +42,11 @@
|
|||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.116" />
|
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.116" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||||
<PackageVersion Include="Ryujinx.UpdateClient" Version="1.0.29" />
|
<PackageVersion Include="Ryujinx.UpdateClient" Version="1.0.44" />
|
||||||
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="1.0.29" />
|
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="1.0.44" />
|
||||||
<PackageVersion Include="Gommon" Version="2.7.1.1" />
|
<PackageVersion Include="Gommon" Version="2.7.2.1" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="Sep" Version="0.6.0" />
|
<PackageVersion Include="Sep" Version="0.11.1" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
|
||||||
|
@@ -79,7 +79,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Editor di Mii",
|
"it_IT": "Editor di Mii",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "",
|
"ko_KR": "Mii 편집기",
|
||||||
"no_NO": "Mii-redigerer",
|
"no_NO": "Mii-redigerer",
|
||||||
"pl_PL": "Edytor Mii",
|
"pl_PL": "Edytor Mii",
|
||||||
"pt_BR": "Editor de Mii",
|
"pt_BR": "Editor de Mii",
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Редактор Mii",
|
"uk_UA": "Редактор Mii",
|
||||||
"zh_CN": "",
|
"zh_CN": "Mii 编辑器",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -779,7 +779,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Scansiona un Amiibo (da file .bin)",
|
"it_IT": "Scansiona un Amiibo (da file .bin)",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "Amiibo 스캔(빈에서)",
|
"ko_KR": "Amiibo 스캔(.bin에서)",
|
||||||
"no_NO": "Skann en Amiibo (fra bin fil)",
|
"no_NO": "Skann en Amiibo (fra bin fil)",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Escaneie um Amiibo (de um .bin)",
|
"pt_BR": "Escaneie um Amiibo (de um .bin)",
|
||||||
@@ -829,7 +829,7 @@
|
|||||||
"he_IL": "התקן קושחה (.XCI או .ZIP)",
|
"he_IL": "התקן קושחה (.XCI או .ZIP)",
|
||||||
"it_IT": "Installa firmware (.XCI o .ZIP)",
|
"it_IT": "Installa firmware (.XCI o .ZIP)",
|
||||||
"ja_JP": "ファームウェアをインストール(.XCI または .ZIP)",
|
"ja_JP": "ファームウェアをインストール(.XCI または .ZIP)",
|
||||||
"ko_KR": "펌웨어 (.XCI 또는 .ZIP) 설치",
|
"ko_KR": "펌웨어(.XCI 또는 .ZIP) 설치",
|
||||||
"no_NO": "Installer firmware (.XCI eller .ZIP)",
|
"no_NO": "Installer firmware (.XCI eller .ZIP)",
|
||||||
"pl_PL": "Zainstaluj firmware (.XCI lub .ZIP)",
|
"pl_PL": "Zainstaluj firmware (.XCI lub .ZIP)",
|
||||||
"pt_BR": "Instalar firmware (.XCI ou .ZIP)",
|
"pt_BR": "Instalar firmware (.XCI ou .ZIP)",
|
||||||
@@ -854,7 +854,7 @@
|
|||||||
"he_IL": "התקן קושחה (תיקייה)",
|
"he_IL": "התקן קושחה (תיקייה)",
|
||||||
"it_IT": "Installa firmware (cartella)",
|
"it_IT": "Installa firmware (cartella)",
|
||||||
"ja_JP": "ファームウェアをインストール(フォルダー)",
|
"ja_JP": "ファームウェアをインストール(フォルダー)",
|
||||||
"ko_KR": "펌웨어 (폴더) 설치",
|
"ko_KR": "펌웨어(폴더) 설치",
|
||||||
"no_NO": "Installer firmware (mappe)",
|
"no_NO": "Installer firmware (mappe)",
|
||||||
"pl_PL": "Zainstaluj firmware (katalog)",
|
"pl_PL": "Zainstaluj firmware (katalog)",
|
||||||
"pt_BR": "Instalar firmware (diretório)",
|
"pt_BR": "Instalar firmware (diretório)",
|
||||||
@@ -896,25 +896,25 @@
|
|||||||
"ID": "MenuBarFileActionsInstallKeysFromFile",
|
"ID": "MenuBarFileActionsInstallKeysFromFile",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "Schlüssel (.KEYS oder .ZIP) installieren",
|
"de_DE": "Schlüssel (.KEYS) installieren",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Install Keys (.KEYS or .ZIP)",
|
"en_US": "Install Keys (.KEYS)",
|
||||||
"es_ES": "Instalar keys (.KEYS o .ZIP)",
|
"es_ES": "Instalar keys (.KEYS)",
|
||||||
"fr_FR": "Installer des Clés (.KEYS ou .ZIP)",
|
"fr_FR": "Installer des Clés (.KEYS)",
|
||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Installa chiavi (.KEYS o .ZIP)",
|
"it_IT": "Installa chiavi (.KEYS)",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "키 설치 (.KEYS 또는 .ZIP)",
|
"ko_KR": "키 설치(.KEYS)",
|
||||||
"no_NO": "Installer nøkler (.KEYS eller .ZIP)",
|
"no_NO": "Installer nøkler (.KEYS)",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Instalar chaves (.KEYS ou .ZIP",
|
"pt_BR": "Instalar chaves (.KEYS)",
|
||||||
"ru_RU": "Установить ключи (.KEYS или .ZIP)",
|
"ru_RU": "Установить ключи (.KEYS)",
|
||||||
"sv_SE": "Installera nycklar (.KEYS eller .ZIP)",
|
"sv_SE": "Installera nycklar (.KEYS)",
|
||||||
"th_TH": "ติดตั้งคีย์ (.KEYS หรือ .ZIP)",
|
"th_TH": "ติดตั้งคีย์ (.KEYS)",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Встановити ключі (.KEYS або .ZIP)",
|
"uk_UA": "Встановити ключі (.KEYS)",
|
||||||
"zh_CN": "安装密钥(.KEYS 或 .ZIP)",
|
"zh_CN": "安装密钥(.KEYS)",
|
||||||
"zh_TW": "安裝金鑰(.KEYS 或 .ZIP)"
|
"zh_TW": "安裝金鑰(.KEYS)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -929,7 +929,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Installa chiavi (cartella)",
|
"it_IT": "Installa chiavi (cartella)",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "키 (폴더) 설치",
|
"ko_KR": "키(폴더) 설치",
|
||||||
"no_NO": "Installer nøkler (mappe)",
|
"no_NO": "Installer nøkler (mappe)",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Instalar chaves (diretório)",
|
"pt_BR": "Instalar chaves (diretório)",
|
||||||
@@ -1054,7 +1054,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "",
|
"it_IT": "",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "",
|
"ko_KR": "도구",
|
||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "",
|
"pt_BR": "",
|
||||||
@@ -1063,7 +1063,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "",
|
"zh_CN": "工具",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1104,7 +1104,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Dimensione finestra",
|
"it_IT": "Dimensione finestra",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "윈도 창",
|
"ko_KR": "창 크기",
|
||||||
"no_NO": "Vindu størrelse",
|
"no_NO": "Vindu størrelse",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Tamanho da Janela",
|
"pt_BR": "Tamanho da Janela",
|
||||||
@@ -1288,7 +1288,7 @@
|
|||||||
"th_TH": "คำถามที่พบบ่อย & คู่มือ",
|
"th_TH": "คำถามที่พบบ่อย & คู่มือ",
|
||||||
"tr_TR": "SSS & Rehberler",
|
"tr_TR": "SSS & Rehberler",
|
||||||
"uk_UA": "FAQ & посібники",
|
"uk_UA": "FAQ & посібники",
|
||||||
"zh_CN": "问答 & 指南",
|
"zh_CN": "常见问题与指南",
|
||||||
"zh_TW": "常見問題 (FAQ) & 指南"
|
"zh_TW": "常見問題 (FAQ) & 指南"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1304,7 +1304,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "FAQ & Risoluzione dei problemi",
|
"it_IT": "FAQ & Risoluzione dei problemi",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "",
|
"ko_KR": "자주 묻는 질문 및 문제 해결",
|
||||||
"no_NO": "FAQ & Feilsøkinge",
|
"no_NO": "FAQ & Feilsøkinge",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "FAQ & Solução de Problemas",
|
"pt_BR": "FAQ & Solução de Problemas",
|
||||||
@@ -1313,7 +1313,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "FAQ & Усунення несправностей",
|
"uk_UA": "FAQ & Усунення несправностей",
|
||||||
"zh_CN": "",
|
"zh_CN": "常见问题与疑难解答",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1604,7 +1604,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Versione: {0}",
|
"it_IT": "Versione: {0}",
|
||||||
"ja_JP": "バージョン: {0}",
|
"ja_JP": "バージョン: {0}",
|
||||||
"ko_KR": "버전: {0}",
|
"ko_KR": "버전 : {0}",
|
||||||
"no_NO": "Versjon: {0}",
|
"no_NO": "Versjon: {0}",
|
||||||
"pl_PL": "Wersja: {0}",
|
"pl_PL": "Wersja: {0}",
|
||||||
"pt_BR": "Versão: {0}",
|
"pt_BR": "Versão: {0}",
|
||||||
@@ -1629,7 +1629,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Tempo di gioco:",
|
"it_IT": "Tempo di gioco:",
|
||||||
"ja_JP": "プレイ時間:",
|
"ja_JP": "プレイ時間:",
|
||||||
"ko_KR": "플레이 타임:",
|
"ko_KR": "플레이 시간 :",
|
||||||
"no_NO": "Spilletid:",
|
"no_NO": "Spilletid:",
|
||||||
"pl_PL": "Czas w grze:",
|
"pl_PL": "Czas w grze:",
|
||||||
"pt_BR": "Tempo de Jogo:",
|
"pt_BR": "Tempo de Jogo:",
|
||||||
@@ -1654,7 +1654,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Ultima partita:",
|
"it_IT": "Ultima partita:",
|
||||||
"ja_JP": "最終プレイ日時:",
|
"ja_JP": "最終プレイ日時:",
|
||||||
"ko_KR": "마지막 플레이:",
|
"ko_KR": "마지막 플레이 :",
|
||||||
"no_NO": "Sist Spilt:",
|
"no_NO": "Sist Spilt:",
|
||||||
"pl_PL": "Ostatnio grane:",
|
"pl_PL": "Ostatnio grane:",
|
||||||
"pt_BR": "Última vez Jogado:",
|
"pt_BR": "Última vez Jogado:",
|
||||||
@@ -1679,7 +1679,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Estensione: {0}",
|
"it_IT": "Estensione: {0}",
|
||||||
"ja_JP": "ファイル拡張子: {0}",
|
"ja_JP": "ファイル拡張子: {0}",
|
||||||
"ko_KR": "파일 확장자: {0}",
|
"ko_KR": "파일 확장자 : {0}",
|
||||||
"no_NO": "Fil Eks.: {0}",
|
"no_NO": "Fil Eks.: {0}",
|
||||||
"pl_PL": "Rozszerzenie pliku: {0}",
|
"pl_PL": "Rozszerzenie pliku: {0}",
|
||||||
"pt_BR": "Extensão: {0}",
|
"pt_BR": "Extensão: {0}",
|
||||||
@@ -1704,7 +1704,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Dimensione file: {0}",
|
"it_IT": "Dimensione file: {0}",
|
||||||
"ja_JP": "ファイルサイズ: {0}",
|
"ja_JP": "ファイルサイズ: {0}",
|
||||||
"ko_KR": "파일 크기: {0}",
|
"ko_KR": "파일 크기 : {0}",
|
||||||
"no_NO": "Fil Størrelse: {0}",
|
"no_NO": "Fil Størrelse: {0}",
|
||||||
"pl_PL": "Rozmiar pliku: {0}",
|
"pl_PL": "Rozmiar pliku: {0}",
|
||||||
"pt_BR": "Tamanho: {0}",
|
"pt_BR": "Tamanho: {0}",
|
||||||
@@ -1879,7 +1879,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Titolo: A-Z",
|
"it_IT": "Titolo: A-Z",
|
||||||
"ja_JP": "タイトル:A-Z",
|
"ja_JP": "タイトル:A-Z",
|
||||||
"ko_KR": "제목: A-Z",
|
"ko_KR": "타이틀 : A-Z",
|
||||||
"no_NO": "Tittel: A-Z",
|
"no_NO": "Tittel: A-Z",
|
||||||
"pl_PL": "Tytuł: A-Z",
|
"pl_PL": "Tytuł: A-Z",
|
||||||
"pt_BR": "Título: A-Z",
|
"pt_BR": "Título: A-Z",
|
||||||
@@ -1904,7 +1904,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Titolo: Z-A",
|
"it_IT": "Titolo: Z-A",
|
||||||
"ja_JP": "タイトル:Z-A",
|
"ja_JP": "タイトル:Z-A",
|
||||||
"ko_KR": "제목: Z-A",
|
"ko_KR": "타이틀 : Z-A",
|
||||||
"no_NO": "Tittel: Z-A",
|
"no_NO": "Tittel: Z-A",
|
||||||
"pl_PL": "Tytuł: Z-A",
|
"pl_PL": "Tytuł: Z-A",
|
||||||
"pt_BR": "Título: Z-A",
|
"pt_BR": "Título: Z-A",
|
||||||
@@ -1929,7 +1929,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Stato: Disabilitato",
|
"it_IT": "Stato: Disabilitato",
|
||||||
"ja_JP": "ステータス:無効",
|
"ja_JP": "ステータス:無効",
|
||||||
"ko_KR": "상태: 비활성화됨",
|
"ko_KR": "상태 : 비활성화됨",
|
||||||
"no_NO": "Status: Deaktivert",
|
"no_NO": "Status: Deaktivert",
|
||||||
"pl_PL": "Status: Wyłączony",
|
"pl_PL": "Status: Wyłączony",
|
||||||
"pt_BR": "Status: Desativado",
|
"pt_BR": "Status: Desativado",
|
||||||
@@ -1954,7 +1954,7 @@
|
|||||||
"he_IL": "סטטוס: עולה",
|
"he_IL": "סטטוס: עולה",
|
||||||
"it_IT": "Stato: Crescente",
|
"it_IT": "Stato: Crescente",
|
||||||
"ja_JP": "ステータス:昇順",
|
"ja_JP": "ステータス:昇順",
|
||||||
"ko_KR": "상태: 오름차순",
|
"ko_KR": "상태 : 오름차순",
|
||||||
"no_NO": "Status: Stigende",
|
"no_NO": "Status: Stigende",
|
||||||
"pl_PL": "Stan: Rosnący",
|
"pl_PL": "Stan: Rosnący",
|
||||||
"pt_BR": "Status: Crescente",
|
"pt_BR": "Status: Crescente",
|
||||||
@@ -1979,7 +1979,7 @@
|
|||||||
"he_IL": "סטטוס: יורד",
|
"he_IL": "סטטוס: יורד",
|
||||||
"it_IT": "Stato: Decrescente",
|
"it_IT": "Stato: Decrescente",
|
||||||
"ja_JP": "ステータス:降順",
|
"ja_JP": "ステータス:降順",
|
||||||
"ko_KR": "상태: 내림차순",
|
"ko_KR": "상태 : 내림차순",
|
||||||
"no_NO": "Status: Synkende",
|
"no_NO": "Status: Synkende",
|
||||||
"pl_PL": "Stan: Malejący",
|
"pl_PL": "Stan: Malejący",
|
||||||
"pt_BR": "Status: Decrescente",
|
"pt_BR": "Status: Decrescente",
|
||||||
@@ -2354,7 +2354,7 @@
|
|||||||
"he_IL": "ניקוי מטמון הצללות",
|
"he_IL": "ניקוי מטמון הצללות",
|
||||||
"it_IT": "Elimina la cache degli shader",
|
"it_IT": "Elimina la cache degli shader",
|
||||||
"ja_JP": "シェーダーキャッシュを破棄",
|
"ja_JP": "シェーダーキャッシュを破棄",
|
||||||
"ko_KR": "퍼지 셰이더 캐시",
|
"ko_KR": "셰이더 캐시 제거",
|
||||||
"no_NO": "Tøm shader cache",
|
"no_NO": "Tøm shader cache",
|
||||||
"pl_PL": "Wyczyść pamięć podręczną cieni",
|
"pl_PL": "Wyczyść pamięć podręczną cieni",
|
||||||
"pt_BR": "Limpar Cache de Shader",
|
"pt_BR": "Limpar Cache de Shader",
|
||||||
@@ -2554,7 +2554,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "",
|
"it_IT": "",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "",
|
"ko_KR": null,
|
||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "",
|
"pt_BR": "",
|
||||||
@@ -2988,7 +2988,7 @@
|
|||||||
"th_TH": "ตรวจสอบ & ลดขนาดไฟล์ XCI",
|
"th_TH": "ตรวจสอบ & ลดขนาดไฟล์ XCI",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Перевірка & нарізка XCI Файлу",
|
"uk_UA": "Перевірка & нарізка XCI Файлу",
|
||||||
"zh_CN": "检查 & 瘦身 XCI 文件",
|
"zh_CN": "检查并精简 XCI 文件",
|
||||||
"zh_TW": "檢查 & 修剪 XCI 檔案"
|
"zh_TW": "檢查 & 修剪 XCI 檔案"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4804,7 +4804,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "PPTC a basso consumo energetico",
|
"it_IT": "PPTC a basso consumo energetico",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "저전력 PPTC 캐시",
|
"ko_KR": "저전력 PPTC",
|
||||||
"no_NO": "PPTC med lavt strømforbruk",
|
"no_NO": "PPTC med lavt strømforbruk",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "PPTC com Baixo Consumo de Energia",
|
"pt_BR": "PPTC com Baixo Consumo de Energia",
|
||||||
@@ -5904,7 +5904,7 @@
|
|||||||
"he_IL": "רישום",
|
"he_IL": "רישום",
|
||||||
"it_IT": "Log",
|
"it_IT": "Log",
|
||||||
"ja_JP": "ロギング",
|
"ja_JP": "ロギング",
|
||||||
"ko_KR": "로그 기록",
|
"ko_KR": "로그",
|
||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "Dziennik zdarzeń",
|
"pl_PL": "Dziennik zdarzeń",
|
||||||
"pt_BR": "Log",
|
"pt_BR": "Log",
|
||||||
@@ -10054,7 +10054,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Tast. num. Invio",
|
"it_IT": "Tast. num. Invio",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "키패드 입력",
|
"ko_KR": "키패드 엔터",
|
||||||
"no_NO": "Numerisk Enter",
|
"no_NO": "Numerisk Enter",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "",
|
"pt_BR": "",
|
||||||
@@ -12088,7 +12088,7 @@
|
|||||||
"th_TH": "เริ่ม",
|
"th_TH": "เริ่ม",
|
||||||
"tr_TR": "Başlat",
|
"tr_TR": "Başlat",
|
||||||
"uk_UA": "Запустити",
|
"uk_UA": "Запустити",
|
||||||
"zh_CN": "开始",
|
"zh_CN": "启动",
|
||||||
"zh_TW": "開始"
|
"zh_TW": "開始"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -15564,7 +15564,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "显示与此游戏不兼容的 Amiibo",
|
"zh_CN": "显示与此游戏不兼容的 Amiibo",
|
||||||
"zh_TW": ""
|
"zh_TW": "顯示與此遊戲不相容的 Amiibo"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -17854,7 +17854,7 @@
|
|||||||
"he_IL": "שגיאה בניקוי מטמון ההצללות ב-{0}: {1}",
|
"he_IL": "שגיאה בניקוי מטמון ההצללות ב-{0}: {1}",
|
||||||
"it_IT": "Errore nell'eliminazione della cache degli shader a {0}: {1}",
|
"it_IT": "Errore nell'eliminazione della cache degli shader a {0}: {1}",
|
||||||
"ja_JP": "シェーダーキャッシュの破棄エラー {0}: {1}",
|
"ja_JP": "シェーダーキャッシュの破棄エラー {0}: {1}",
|
||||||
"ko_KR": "{0}에서 셰이더 캐시를 삭제하는 중 오류 발생 : {1}",
|
"ko_KR": "{0}에서 셰이더 캐시 제거 중, 오류 발생 : {1}",
|
||||||
"no_NO": "Feil under tømming av shader cache ved {0}: {1}",
|
"no_NO": "Feil under tømming av shader cache ved {0}: {1}",
|
||||||
"pl_PL": "Błąd podczas czyszczenia cache shaderów w {0}: {1}",
|
"pl_PL": "Błąd podczas czyszczenia cache shaderów w {0}: {1}",
|
||||||
"pt_BR": "Erro ao limpar o cache do shader em {0}: {1}",
|
"pt_BR": "Erro ao limpar o cache do shader em {0}: {1}",
|
||||||
@@ -18054,7 +18054,7 @@
|
|||||||
"he_IL": "ריוג'ינקס לא הצליחה למצוא קושחה מותקנת",
|
"he_IL": "ריוג'ינקס לא הצליחה למצוא קושחה מותקנת",
|
||||||
"it_IT": "Ryujinx non è riuscito a trovare alcun firmware installato",
|
"it_IT": "Ryujinx non è riuscito a trovare alcun firmware installato",
|
||||||
"ja_JP": "インストールされたファームウェアが見つかりませんでした",
|
"ja_JP": "インストールされたファームウェアが見つかりませんでした",
|
||||||
"ko_KR": "설치된 펌웨어를 찾을 수 없음",
|
"ko_KR": "Ryujinx가 설치된 펌웨어를 찾을 수 없음",
|
||||||
"no_NO": "Ryujinx kunne ikke finne noen fastvare installert",
|
"no_NO": "Ryujinx kunne ikke finne noen fastvare installert",
|
||||||
"pl_PL": "Ryujinx nie mógł znaleźć żadnego zainstalowanego firmware'u",
|
"pl_PL": "Ryujinx nie mógł znaleźć żadnego zainstalowanego firmware'u",
|
||||||
"pt_BR": "Ryujinx não conseguiu encontrar nenhum Firmware instalado",
|
"pt_BR": "Ryujinx não conseguiu encontrar nenhum Firmware instalado",
|
||||||
@@ -18079,7 +18079,7 @@
|
|||||||
"he_IL": "ריוג'ינקס לא הצליחה לנתח את הקושחה שסופקה. זה נגרם בדרך כלל על ידי מפתחות לא עדכניים.",
|
"he_IL": "ריוג'ינקס לא הצליחה לנתח את הקושחה שסופקה. זה נגרם בדרך כלל על ידי מפתחות לא עדכניים.",
|
||||||
"it_IT": "Ryujinx non è riuscito ad analizzare il firmware. Questo di solito è causato da chiavi non aggiornate.",
|
"it_IT": "Ryujinx non è riuscito ad analizzare il firmware. Questo di solito è causato da chiavi non aggiornate.",
|
||||||
"ja_JP": "ファームウェアをパーズできませんでした.通常,古いキーが原因です.",
|
"ja_JP": "ファームウェアをパーズできませんでした.通常,古いキーが原因です.",
|
||||||
"ko_KR": "Ryujinx가 제공된 펌웨어를 구문 분석하지 못했습니다. 이는 일반적으로 오래된 키로 인해 발생합니다.",
|
"ko_KR": "Ryujinx가 제공된 펌웨어를 구문 분석하지 못했습니다. 일반적으로 오래된 키로 인해 발생합니다.",
|
||||||
"no_NO": "Ryujinx klarte ikke å analysere levert fastvare. Dette er vanligvis forårsaket av utdaterte nøkler.",
|
"no_NO": "Ryujinx klarte ikke å analysere levert fastvare. Dette er vanligvis forårsaket av utdaterte nøkler.",
|
||||||
"pl_PL": "Ryujinx nie był w stanie zparsować dostarczonego firmware'u. Jest to zwykle spowodowane nieaktualnymi kluczami.",
|
"pl_PL": "Ryujinx nie był w stanie zparsować dostarczonego firmware'u. Jest to zwykle spowodowane nieaktualnymi kluczami.",
|
||||||
"pt_BR": "Ryujinx não conseguiu ler o Firmware fornecido. Geralmente isso é causado por chaves desatualizadas.",
|
"pt_BR": "Ryujinx não conseguiu ler o Firmware fornecido. Geralmente isso é causado por chaves desatualizadas.",
|
||||||
@@ -18854,7 +18854,7 @@
|
|||||||
"he_IL": "מצב עגינה מוגדר. כדאי ששליטה ניידת תהיה מושבתת.",
|
"he_IL": "מצב עגינה מוגדר. כדאי ששליטה ניידת תהיה מושבתת.",
|
||||||
"it_IT": "Modalità TV attivata. Gli input della modalità portatile dovrebbero essere disabilitati.",
|
"it_IT": "Modalità TV attivata. Gli input della modalità portatile dovrebbero essere disabilitati.",
|
||||||
"ja_JP": "ドッキングモードが設定されています. 携帯コントロールは無効にする必要があります.",
|
"ja_JP": "ドッキングモードが設定されています. 携帯コントロールは無効にする必要があります.",
|
||||||
"ko_KR": "도킹 모드가 설정되었습니다. 휴대용 제어 기능을 비활성화해야 합니다.",
|
"ko_KR": "도킹 모드가 설정되었습니다. 핸드헬드 제어 기능을 비활성화해야 합니다.",
|
||||||
"no_NO": "Docked modus. Håndholdt kontroll skal være deaktivert.",
|
"no_NO": "Docked modus. Håndholdt kontroll skal være deaktivert.",
|
||||||
"pl_PL": "Ustawiony tryb zadokowany. Sterowanie przenośne powinno być wyłączone.",
|
"pl_PL": "Ustawiony tryb zadokowany. Sterowanie przenośne powinno być wyłączone.",
|
||||||
"pt_BR": "Modo TV definido. O controle portátil deve ser desabilitado.",
|
"pt_BR": "Modo TV definido. O controle portátil deve ser desabilitado.",
|
||||||
@@ -19104,7 +19104,7 @@
|
|||||||
"he_IL": "שגיאת ממשק.",
|
"he_IL": "שגיאת ממשק.",
|
||||||
"it_IT": "Errore dell'API.",
|
"it_IT": "Errore dell'API.",
|
||||||
"ja_JP": "API エラー.",
|
"ja_JP": "API エラー.",
|
||||||
"ko_KR": "API 오류.",
|
"ko_KR": "API 오류입니다.",
|
||||||
"no_NO": "API feil.",
|
"no_NO": "API feil.",
|
||||||
"pl_PL": "Błąd API.",
|
"pl_PL": "Błąd API.",
|
||||||
"pt_BR": "Erro de API.",
|
"pt_BR": "Erro de API.",
|
||||||
@@ -19254,7 +19254,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Scegli un FILE compatibile con Switch da caricare",
|
"it_IT": "Scegli un FILE compatibile con Switch da caricare",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "",
|
"ko_KR": "불러올 Switch 호환 파일을 선택",
|
||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Escolha um ARQUIVO compatível com Switch para carregar",
|
"pt_BR": "Escolha um ARQUIVO compatível com Switch para carregar",
|
||||||
@@ -19263,7 +19263,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Виберіть ФАЙЛ, сумісний із Switch, для завантаження",
|
"uk_UA": "Виберіть ФАЙЛ, сумісний із Switch, для завантаження",
|
||||||
"zh_CN": "",
|
"zh_CN": "请选择要加载的 Switch 兼容文件",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -19279,7 +19279,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Scegli un’applicazione DECOMPRESSA compatibile con Switch da caricare",
|
"it_IT": "Scegli un’applicazione DECOMPRESSA compatibile con Switch da caricare",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "",
|
"ko_KR": "불러올 언팩된 Switch 호환 앱을 선택",
|
||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Escolha um aplicativo DESCOMPACTADO compatível com Switch para carregar",
|
"pt_BR": "Escolha um aplicativo DESCOMPACTADO compatível com Switch para carregar",
|
||||||
@@ -19288,7 +19288,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Виберіть РОЗПАКОВАНИЙ сумісний із Switch додаток для завантаження",
|
"uk_UA": "Виберіть РОЗПАКОВАНИЙ сумісний із Switch додаток для завантаження",
|
||||||
"zh_CN": "",
|
"zh_CN": "请选择要加载的已解包的 Switch 兼容应用程序",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -19304,7 +19304,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Scegli una o più CARTELLE da cui caricare in blocco gli aggiornamenti del titolo",
|
"it_IT": "Scegli una o più CARTELLE da cui caricare in blocco gli aggiornamenti del titolo",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "",
|
"ko_KR": "타이틀 업데이트를 대량으로 불러올 폴더를 하나 이상 선택",
|
||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Escolha uma ou mais PASTAS para carregar atualizações de título em massa",
|
"pt_BR": "Escolha uma ou mais PASTAS para carregar atualizações de título em massa",
|
||||||
@@ -19313,7 +19313,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Виберіть одну або кілька ПАПОК для масового завантаження оновлень титулів",
|
"uk_UA": "Виберіть одну або кілька ПАПОК для масового завантаження оновлень титулів",
|
||||||
"zh_CN": "",
|
"zh_CN": "请选择一个或多个文件夹来批量加载游戏更新",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -19329,7 +19329,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Scegli una o più CARTELLE da cui caricare DLC in blocco",
|
"it_IT": "Scegli una o più CARTELLE da cui caricare DLC in blocco",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "",
|
"ko_KR": "DLC를 대량으로 불러올 폴더를 하나 이상 선택",
|
||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Escolha uma ou mais PASTAS para carregar DLC em massa",
|
"pt_BR": "Escolha uma ou mais PASTAS para carregar DLC em massa",
|
||||||
@@ -19338,7 +19338,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Виберіть одну або кілька ПАПОК для масового завантаження DLC",
|
"uk_UA": "Виберіть одну або кілька ПАПОК для масового завантаження DLC",
|
||||||
"zh_CN": "",
|
"zh_CN": "请选择一个或多个文件夹来批量加载 DLC",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -20279,7 +20279,7 @@
|
|||||||
"he_IL": "נהל הרחבות משחק עבור {0} ({1})",
|
"he_IL": "נהל הרחבות משחק עבור {0} ({1})",
|
||||||
"it_IT": "Gestisci DLC per {0} ({1})",
|
"it_IT": "Gestisci DLC per {0} ({1})",
|
||||||
"ja_JP": "DLC 管理",
|
"ja_JP": "DLC 管理",
|
||||||
"ko_KR": "{0} ({1})의 내려받기 가능한 콘텐츠 관리",
|
"ko_KR": "{0} ({1})의 DLC 관리",
|
||||||
"no_NO": "Behandle nedlastbart innhold for {0} ({1})",
|
"no_NO": "Behandle nedlastbart innhold for {0} ({1})",
|
||||||
"pl_PL": "Menedżer Zawartości do Pobrania",
|
"pl_PL": "Menedżer Zawartości do Pobrania",
|
||||||
"pt_BR": "Gerenciar conteúdo para download para {0} ({1})",
|
"pt_BR": "Gerenciar conteúdo para download para {0} ({1})",
|
||||||
@@ -20904,7 +20904,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "{0} nuovo/i DLC aggiunto/i",
|
"it_IT": "{0} nuovo/i DLC aggiunto/i",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "{0}개의 새로운 내려받기 가능한 콘텐츠가 추가됨",
|
"ko_KR": "{0}개의 새로운 DLC가 추가됨",
|
||||||
"no_NO": "{0} nytt nedlastbart innhold lagt til",
|
"no_NO": "{0} nytt nedlastbart innhold lagt til",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "{0} novo(s) conteúdo(s) para download adicionado(s)",
|
"pt_BR": "{0} novo(s) conteúdo(s) para download adicionado(s)",
|
||||||
@@ -23779,7 +23779,7 @@
|
|||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "",
|
"it_IT": "",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "",
|
"ko_KR": "상태",
|
||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "",
|
"pt_BR": "",
|
||||||
@@ -23789,7 +23789,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "状态",
|
"zh_CN": "状态",
|
||||||
"zh_TW": ""
|
"zh_TW": "統計"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24214,7 +24214,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "调试",
|
"zh_CN": "调试",
|
||||||
"zh_TW": ""
|
"zh_TW": "除錯"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24239,7 +24239,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "调试",
|
"zh_CN": "调试",
|
||||||
"zh_TW": ""
|
"zh_TW": "除錯"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24264,7 +24264,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "警告: 仅限开发者使用,会降低性能",
|
"zh_CN": "警告: 仅限开发者使用,会降低性能",
|
||||||
"zh_TW": ""
|
"zh_TW": "警告:僅供開發者使用,會降低效能"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24289,7 +24289,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "开启 GDB Stub",
|
"zh_CN": "开启 GDB Stub",
|
||||||
"zh_TW": ""
|
"zh_TW": "啟用 GDB Stub"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24314,7 +24314,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "启用 GDB stub 使得可以调试正在运行的应用程序。仅限开发用途!",
|
"zh_CN": "启用 GDB stub 使得可以调试正在运行的应用程序。仅限开发用途!",
|
||||||
"zh_TW": ""
|
"zh_TW": "啟用 GDB stub 可利用 gdb 除錯正在執行的應用程式。僅供開發使用!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24339,7 +24339,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "GDB Stub 端口:",
|
"zh_CN": "GDB Stub 端口:",
|
||||||
"zh_TW": ""
|
"zh_TW": "GDB Stub 通訊埠:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24364,7 +24364,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "启动时暂停应用程序",
|
"zh_CN": "启动时暂停应用程序",
|
||||||
"zh_TW": ""
|
"zh_TW": "執行時暫停應用程式"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24389,7 +24389,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "在执行首条指令前挂起应用程序,这样就可以从最早的点开始调试。",
|
"zh_CN": "在执行首条指令前挂起应用程序,这样就可以从最早的点开始调试。",
|
||||||
"zh_TW": ""
|
"zh_TW": "在執行首項指令前暫停應用程式,以便從最早的點開始除錯。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24414,7 +24414,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "打开 LDN 游戏列表",
|
"zh_CN": "打开 LDN 游戏列表",
|
||||||
"zh_TW": ""
|
"zh_TW": "開啟 LDN 遊戲瀏覽器"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24439,7 +24439,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "LDN 游戏浏览器 - {0} 个游戏",
|
"zh_CN": "LDN 游戏浏览器 - {0} 个游戏",
|
||||||
"zh_TW": ""
|
"zh_TW": "LDN 遊戲瀏覽器 - {0} 個遊戲"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24464,7 +24464,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "搜索到 {0} 个 LDN 游戏...",
|
"zh_CN": "搜索到 {0} 个 LDN 游戏...",
|
||||||
"zh_TW": ""
|
"zh_TW": "搜尋 {0} 個 LDN 遊戲"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24489,7 +24489,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "什么是 LDN",
|
"zh_CN": "什么是 LDN",
|
||||||
"zh_TW": ""
|
"zh_TW": "LDN 是甚麼? (只有英文)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24514,7 +24514,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "在 {0} 时从服务器刷新可用游戏",
|
"zh_CN": "在 {0} 时从服务器刷新可用游戏",
|
||||||
"zh_TW": ""
|
"zh_TW": "從 {0} 伺服器重新整理有效的遊戲"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24539,7 +24539,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "玩家计数 - 关闭",
|
"zh_CN": "玩家计数 - 关闭",
|
||||||
"zh_TW": ""
|
"zh_TW": "玩家數量 - 停用"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24564,7 +24564,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "玩家计数 - 递增",
|
"zh_CN": "玩家计数 - 递增",
|
||||||
"zh_TW": ""
|
"zh_TW": "玩家數量 - 遞增"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24589,7 +24589,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "玩家计数 - 递减",
|
"zh_CN": "玩家计数 - 递减",
|
||||||
"zh_TW": ""
|
"zh_TW": "玩家數量 - 遞減"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24614,7 +24614,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "筛选",
|
"zh_CN": "筛选",
|
||||||
"zh_TW": ""
|
"zh_TW": "篩選"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24639,7 +24639,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "仅显示公开游戏",
|
"zh_CN": "仅显示公开游戏",
|
||||||
"zh_TW": ""
|
"zh_TW": "只列出公開遊戲"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24664,7 +24664,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "仅显示可加入的游戏",
|
"zh_CN": "仅显示可加入的游戏",
|
||||||
"zh_TW": ""
|
"zh_TW": "只列出可加入遊戲"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24689,7 +24689,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "主服务器代理",
|
"zh_CN": "主服务器代理",
|
||||||
"zh_TW": ""
|
"zh_TW": "主代理伺服器"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24714,7 +24714,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": null,
|
"zh_CN": null,
|
||||||
"zh_TW": ""
|
"zh_TW": "對等網路 (P2P)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24739,7 +24739,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "通过 RyuLDN 服务器进行连接 (较慢)。",
|
"zh_CN": "通过 RyuLDN 服务器进行连接 (较慢)。",
|
||||||
"zh_TW": ""
|
"zh_TW": "透過 RyuLDN 伺服器連線 (較慢)。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24764,7 +24764,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "通过 UPnP 进行点对点连接 (较快)。",
|
"zh_CN": "通过 UPnP 进行点对点连接 (较快)。",
|
||||||
"zh_TW": ""
|
"zh_TW": "透過 UPnP 和對等網路 (P2P) 連線 (較快)。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24789,7 +24789,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "已创建: {0} ",
|
"zh_CN": "已创建: {0} ",
|
||||||
"zh_TW": ""
|
"zh_TW": "建立於:{0}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24814,7 +24814,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "玩家 ({0} 之 {1}):",
|
"zh_CN": "玩家 ({0} 之 {1}):",
|
||||||
"zh_TW": ""
|
"zh_TW": "玩家數量 ({1} 之 {0}):"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24839,7 +24839,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "可加入",
|
"zh_CN": "可加入",
|
||||||
"zh_TW": ""
|
"zh_TW": "可加入"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24864,7 +24864,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "如果游戏是公开的或您知道口令则它是可加入的。",
|
"zh_CN": "如果游戏是公开的或您知道口令则它是可加入的。",
|
||||||
"zh_TW": ""
|
"zh_TW": "如果遊戲是公開,或是你已知的網路密碼片語 (passphrase),則為可加入。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24889,7 +24889,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "不可加入",
|
"zh_CN": "不可加入",
|
||||||
"zh_TW": ""
|
"zh_TW": "不可加入"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24914,7 +24914,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "游戏当前正在进行中。",
|
"zh_CN": "游戏当前正在进行中。",
|
||||||
"zh_TW": ""
|
"zh_TW": "遊戲已在進行中。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24939,7 +24939,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "公开",
|
"zh_CN": "公开",
|
||||||
"zh_TW": ""
|
"zh_TW": "公開"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24964,7 +24964,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "任何人都可以加入此游戏。",
|
"zh_CN": "任何人都可以加入此游戏。",
|
||||||
"zh_TW": ""
|
"zh_TW": "任何人都可加入此遊戲。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24989,7 +24989,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "私密",
|
"zh_CN": "私密",
|
||||||
"zh_TW": ""
|
"zh_TW": "私人"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -25014,7 +25014,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "如果您在设置中有某些 LDN 口令则可加入此游戏。",
|
"zh_CN": "如果您在设置中有某些 LDN 口令则可加入此游戏。",
|
||||||
"zh_TW": ""
|
"zh_TW": "你只能加入與 LDN 網路密碼片語 (passphrase) 設定相同的遊戲。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@@ -978,7 +978,7 @@
|
|||||||
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
|
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
|
||||||
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
|
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
|
||||||
01008CB01E52E000,"DOOM + DOOM II",opengl;ldn-untested;LAN,playable,2024-09-12 07:06:01
|
01008CB01E52E000,"DOOM + DOOM II",opengl;ldn-untested;LAN,playable,2024-09-12 07:06:01
|
||||||
010029D00E740000,"DOOM 3",crash,menus,2024-08-03 05:25:47
|
010029D00E740000,"DOOM 3",crash;slow,menus,2024-08-03 05:25:47
|
||||||
01005D700E742000,"DOOM 64",nvdec;vulkan,playable,2020-10-13 23:47:28
|
01005D700E742000,"DOOM 64",nvdec;vulkan,playable,2020-10-13 23:47:28
|
||||||
0100D4F00DD02000,"DOOM II (Classic)",nvdec;online,playable,2021-06-03 20:10:01
|
0100D4F00DD02000,"DOOM II (Classic)",nvdec;online,playable,2021-06-03 20:10:01
|
||||||
0100B1A00D8CE000,"DOOM® Eternal",gpu;slow;nvdec;online-broken,ingame,2024-08-28 15:57:17
|
0100B1A00D8CE000,"DOOM® Eternal",gpu;slow;nvdec;online-broken,ingame,2024-08-28 15:57:17
|
||||||
@@ -1097,7 +1097,7 @@
|
|||||||
0100F9600E746000,"ESP Ra.De. Psi",audio;slow,ingame,2024-03-07 15:05:08
|
0100F9600E746000,"ESP Ra.De. Psi",audio;slow,ingame,2024-03-07 15:05:08
|
||||||
010073000FE18000,"Esports powerful pro yakyuu 2020",gpu;crash;Needs More Attention,ingame,2024-04-29 05:34:14
|
010073000FE18000,"Esports powerful pro yakyuu 2020",gpu;crash;Needs More Attention,ingame,2024-04-29 05:34:14
|
||||||
01004F9012FD8000,"Estranged: The Departure",nvdec;UE4,playable,2022-10-24 10:37:58
|
01004F9012FD8000,"Estranged: The Departure",nvdec;UE4,playable,2022-10-24 10:37:58
|
||||||
010018f01e0a0000,"Eternights",,playable,2025-07-30 12:10:24
|
010018F01E0A0000,"Eternights",,playable,2025-07-30 12:10:24
|
||||||
0100CB900B498000,"Eternum Ex",,playable,2021-01-13 20:28:32
|
0100CB900B498000,"Eternum Ex",,playable,2021-01-13 20:28:32
|
||||||
010092501EB2C000,"Europa (Demo)",gpu;crash;UE4,ingame,2024-04-23 10:47:12
|
010092501EB2C000,"Europa (Demo)",gpu;crash;UE4,ingame,2024-04-23 10:47:12
|
||||||
01007BE0160D6000,"EVE ghost enemies",gpu,ingame,2023-01-14 03:13:30
|
01007BE0160D6000,"EVE ghost enemies",gpu,ingame,2023-01-14 03:13:30
|
||||||
@@ -1243,7 +1243,7 @@
|
|||||||
010003F00BD48000,"Friday the 13th: Killer Puzzle",,playable,2021-01-28 01:33:38
|
010003F00BD48000,"Friday the 13th: Killer Puzzle",,playable,2021-01-28 01:33:38
|
||||||
010092A00C4B6000,"Friday the 13th: The Game Ultimate Slasher Edition",nvdec;online-broken;UE4,playable,2022-09-06 17:33:27
|
010092A00C4B6000,"Friday the 13th: The Game Ultimate Slasher Edition",nvdec;online-broken;UE4,playable,2022-09-06 17:33:27
|
||||||
0100F200178F4000,"FRONT MISSION 1st: Remake",,playable,2023-06-09 07:44:24
|
0100F200178F4000,"FRONT MISSION 1st: Remake",,playable,2023-06-09 07:44:24
|
||||||
0100c4e018a24000,"FRONT MISSION 2: Remake",,playable,2025-07-30 12:11:23
|
0100C4E018A24000,"FRONT MISSION 2: Remake",,playable,2025-07-30 12:11:23
|
||||||
01007E6019872000,"FRONT MISSION 3: Remake",,playable,2025-07-30 12:12:02
|
01007E6019872000,"FRONT MISSION 3: Remake",,playable,2025-07-30 12:12:02
|
||||||
0100861012474000,"Frontline Zed",,playable,2020-10-03 12:55:59
|
0100861012474000,"Frontline Zed",,playable,2020-10-03 12:55:59
|
||||||
0100B5300B49A000,"Frost",,playable,2022-07-27 12:00:36
|
0100B5300B49A000,"Frost",,playable,2022-07-27 12:00:36
|
||||||
@@ -1450,6 +1450,7 @@
|
|||||||
0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35
|
0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35
|
||||||
0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58
|
0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58
|
||||||
0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56
|
0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56
|
||||||
|
010013C00E930000,"Hollow Knight: Silksong",,playable,2025-09-04 17:23:22
|
||||||
0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56
|
0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56
|
||||||
0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56
|
0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56
|
||||||
010071B00C904000,"HoPiKo",,playable,2021-01-13 20:12:38
|
010071B00C904000,"HoPiKo",,playable,2021-01-13 20:12:38
|
||||||
@@ -1888,7 +1889,7 @@
|
|||||||
010097800EA20000,"Monster Energy Supercross - The Official Videogame 3",UE4;audout;nvdec;online,playable,2021-06-14 12:37:54
|
010097800EA20000,"Monster Energy Supercross - The Official Videogame 3",UE4;audout;nvdec;online,playable,2021-06-14 12:37:54
|
||||||
0100E9900ED74000,"Monster Farm",32-bit;nvdec,playable,2021-05-05 19:29:13
|
0100E9900ED74000,"Monster Farm",32-bit;nvdec,playable,2021-05-05 19:29:13
|
||||||
0100770008DD8000,"Monster Hunter Generations Ultimate™",32-bit;online-broken;ldn-works,playable,2024-03-18 14:35:36
|
0100770008DD8000,"Monster Hunter Generations Ultimate™",32-bit;online-broken;ldn-works,playable,2024-03-18 14:35:36
|
||||||
0100B04011742000,"Monster Hunter Rise",gpu;slow;crash;nvdec;online-broken;Needs Update;ldn-works,ingame,2024-08-24 11:04:59
|
0100B04011742000,"MONSTER HUNTER RISE",gpu;slow;crash;nvdec;online-broken;Needs Update;ldn-works,ingame,2024-08-24 11:04:59
|
||||||
010093A01305C000,"Monster Hunter Rise Demo",online-broken;ldn-works;demo,playable,2022-10-18 23:04:17
|
010093A01305C000,"Monster Hunter Rise Demo",online-broken;ldn-works;demo,playable,2022-10-18 23:04:17
|
||||||
0100E21011446000,"Monster Hunter Stories 2: Wings of Ruin",services,ingame,2022-07-10 19:27:30
|
0100E21011446000,"Monster Hunter Stories 2: Wings of Ruin",services,ingame,2022-07-10 19:27:30
|
||||||
010042501329E000,"MONSTER HUNTER STORIES 2: WINGS OF RUIN Trial Version",demo,playable,2022-11-13 22:20:26
|
010042501329E000,"MONSTER HUNTER STORIES 2: WINGS OF RUIN Trial Version",demo,playable,2022-11-13 22:20:26
|
||||||
@@ -2313,7 +2314,7 @@
|
|||||||
010077B00BDD8000,"Professional Farmer: Nintendo Switch™ Edition",slow,playable,2020-12-16 13:38:19
|
010077B00BDD8000,"Professional Farmer: Nintendo Switch™ Edition",slow,playable,2020-12-16 13:38:19
|
||||||
010018300C83A000,"Professor Lupo and his Horrible Pets",,playable,2020-06-12 00:08:45
|
010018300C83A000,"Professor Lupo and his Horrible Pets",,playable,2020-06-12 00:08:45
|
||||||
0100D1F0132F6000,"Professor Lupo: Ocean",,playable,2021-04-14 16:33:33
|
0100D1F0132F6000,"Professor Lupo: Ocean",,playable,2021-04-14 16:33:33
|
||||||
0100c3a017834000,"Prodeus",,playable,2025-07-30 12:07:52
|
0100C3A017834000,"Prodeus",,playable,2025-07-30 12:07:52
|
||||||
0100BBD00976C000,"Project Highrise: Architect's Edition",,playable,2022-08-10 17:19:12
|
0100BBD00976C000,"Project Highrise: Architect's Edition",,playable,2022-08-10 17:19:12
|
||||||
0100ACE00DAB6000,"Project Nimbus: Complete Edition",nvdec;UE4;vulkan-backend-bug,playable,2022-08-10 17:35:43
|
0100ACE00DAB6000,"Project Nimbus: Complete Edition",nvdec;UE4;vulkan-backend-bug,playable,2022-08-10 17:35:43
|
||||||
01002980140F6000,"Project TRIANGLE STRATEGY™ Debut Demo",UE4;demo,playable,2022-10-24 21:40:27
|
01002980140F6000,"Project TRIANGLE STRATEGY™ Debut Demo",UE4;demo,playable,2022-10-24 21:40:27
|
||||||
@@ -2579,6 +2580,7 @@
|
|||||||
0100C610154CA000,"Shadowrun: Hong Kong - Extended Edition",gpu;Needs Update,ingame,2022-10-04 20:53:09
|
0100C610154CA000,"Shadowrun: Hong Kong - Extended Edition",gpu;Needs Update,ingame,2022-10-04 20:53:09
|
||||||
010000000EEF0000,"Shadows 2: Perfidia",,playable,2020-08-07 12:43:46
|
010000000EEF0000,"Shadows 2: Perfidia",,playable,2020-08-07 12:43:46
|
||||||
0100AD700CBBE000,"Shadows of Adam",,playable,2021-01-11 13:35:58
|
0100AD700CBBE000,"Shadows of Adam",,playable,2021-01-11 13:35:58
|
||||||
|
010037A01F96C000,"Shadows of the Damned: Hella Remastered",,playable,2025-09-05 11:34:32
|
||||||
01002A800C064000,"Shadowverse Champions Battle",,playable,2022-10-02 22:59:29
|
01002A800C064000,"Shadowverse Champions Battle",,playable,2022-10-02 22:59:29
|
||||||
01003B90136DA000,"Shadowverse: Champion's Battle",crash,nothing,2023-03-06 00:31:50
|
01003B90136DA000,"Shadowverse: Champion's Battle",crash,nothing,2023-03-06 00:31:50
|
||||||
0100820013612000,"Shady Part of Me",,playable,2022-10-20 11:31:55
|
0100820013612000,"Shady Part of Me",,playable,2022-10-20 11:31:55
|
||||||
@@ -2977,6 +2979,7 @@
|
|||||||
0100EBA01548E000,"The Cruel King and the Great Hero",gpu;services,ingame,2022-12-02 07:02:08
|
0100EBA01548E000,"The Cruel King and the Great Hero",gpu;services,ingame,2022-12-02 07:02:08
|
||||||
010051800E922000,"The Dark Crystal: Age of Resistance Tactics",,playable,2020-08-11 13:43:41
|
010051800E922000,"The Dark Crystal: Age of Resistance Tactics",,playable,2020-08-11 13:43:41
|
||||||
01003DE00918E000,"The Darkside Detective",,playable,2020-06-03 22:16:18
|
01003DE00918E000,"The Darkside Detective",,playable,2020-06-03 22:16:18
|
||||||
|
010032B015D66000,"The DioField Chronicle",,playable,2025-09-05 11:35:50
|
||||||
01000A10041EA000,"The Elder Scrolls V: Skyrim",gpu;crash,ingame,2024-07-14 03:21:31
|
01000A10041EA000,"The Elder Scrolls V: Skyrim",gpu;crash,ingame,2024-07-14 03:21:31
|
||||||
01004A9006B84000,"The End Is Nigh",,playable,2020-06-01 11:26:45
|
01004A9006B84000,"The End Is Nigh",,playable,2020-06-01 11:26:45
|
||||||
0100CA100489C000,"The Escapists 2",nvdec,playable,2020-09-24 12:31:31
|
0100CA100489C000,"The Escapists 2",nvdec,playable,2020-09-24 12:31:31
|
||||||
|
|
@@ -182,6 +182,7 @@ namespace Ryujinx.Common
|
|||||||
"01001cc01b2d4000", // Goat Simulator 3
|
"01001cc01b2d4000", // Goat Simulator 3
|
||||||
"01003620068ea000", // Hand of Fate 2
|
"01003620068ea000", // Hand of Fate 2
|
||||||
"0100f7e00c70e000", // Hogwarts Legacy
|
"0100f7e00c70e000", // Hogwarts Legacy
|
||||||
|
"010013c00e930000", // Hollow Knight: Silksong
|
||||||
"010085500130a000", // Lego City: Undercover
|
"010085500130a000", // Lego City: Undercover
|
||||||
"010073c01af34000", // LEGO Horizon Adventures
|
"010073c01af34000", // LEGO Horizon Adventures
|
||||||
"0100d71004694000", // Minecraft
|
"0100d71004694000", // Minecraft
|
||||||
|
@@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.Device
|
|||||||
uint alignedOffset = index * RegisterSize;
|
uint alignedOffset = index * RegisterSize;
|
||||||
DebugWrite(alignedOffset, data);
|
DebugWrite(alignedOffset, data);
|
||||||
|
|
||||||
GetRefIntAlignedUncheck(index) = data;
|
SetIntAlignedUncheck(index, data);
|
||||||
|
|
||||||
_writeCallbacks[index]?.Invoke(data);
|
_writeCallbacks[index]?.Invoke(data);
|
||||||
}
|
}
|
||||||
@@ -112,9 +112,7 @@ namespace Ryujinx.Graphics.Device
|
|||||||
uint alignedOffset = index * RegisterSize;
|
uint alignedOffset = index * RegisterSize;
|
||||||
DebugWrite(alignedOffset, data);
|
DebugWrite(alignedOffset, data);
|
||||||
|
|
||||||
ref int storage = ref GetRefIntAlignedUncheck(index);
|
changed = SetIntAlignedUncheckChanged(index, data);
|
||||||
changed = storage != data;
|
|
||||||
storage = data;
|
|
||||||
|
|
||||||
_writeCallbacks[index]?.Invoke(data);
|
_writeCallbacks[index]?.Invoke(data);
|
||||||
}
|
}
|
||||||
@@ -154,5 +152,24 @@ namespace Ryujinx.Graphics.Device
|
|||||||
{
|
{
|
||||||
return ref Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (nint)index);
|
return ref Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (nint)index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void SetIntAlignedUncheck(ulong index, int data)
|
||||||
|
{
|
||||||
|
Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (nint)index) = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private bool SetIntAlignedUncheckChanged(ulong index, int data)
|
||||||
|
{
|
||||||
|
ref int val = ref Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (nint)index);
|
||||||
|
if (val == data)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
val = data;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong size,
|
ulong size,
|
||||||
BufferStage stage,
|
BufferStage stage,
|
||||||
bool sparseCompatible,
|
bool sparseCompatible,
|
||||||
List<Buffer> baseBuffers)
|
RangeItem<Buffer>[] baseBuffers)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_physicalMemory = physicalMemory;
|
_physicalMemory = physicalMemory;
|
||||||
@@ -128,18 +128,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
List<IRegionHandle> baseHandles = null;
|
List<IRegionHandle> baseHandles = null;
|
||||||
|
|
||||||
if (baseBuffers.Count != 0)
|
if (baseBuffers.Length != 0)
|
||||||
{
|
{
|
||||||
baseHandles = new List<IRegionHandle>();
|
baseHandles = new List<IRegionHandle>();
|
||||||
foreach (Buffer buffer in baseBuffers)
|
foreach (RangeItem<Buffer> item in baseBuffers)
|
||||||
{
|
{
|
||||||
if (buffer._useGranular)
|
if (item.Value._useGranular)
|
||||||
{
|
{
|
||||||
baseHandles.AddRange((buffer._memoryTrackingGranular.GetHandles()));
|
baseHandles.AddRange((item.Value._memoryTrackingGranular.GetHandles()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
baseHandles.Add(buffer._memoryTracking);
|
baseHandles.Add(item.Value._memoryTracking);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="parent">Parent buffer</param>
|
/// <param name="parent">Parent buffer</param>
|
||||||
/// <param name="stage">Initial buffer stage</param>
|
/// <param name="stage">Initial buffer stage</param>
|
||||||
/// <param name="baseBuffers">Buffers to inherit state from</param>
|
/// <param name="baseBuffers">Buffers to inherit state from</param>
|
||||||
public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, List<Buffer> baseBuffers)
|
public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, RangeItem<Buffer>[] baseBuffers)
|
||||||
{
|
{
|
||||||
_size = (int)parent.Size;
|
_size = (int)parent.Size;
|
||||||
_systemMemoryType = context.Capabilities.MemoryType;
|
_systemMemoryType = context.Capabilities.MemoryType;
|
||||||
@@ -72,7 +73,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
BufferStage storageFlags = stage & BufferStage.StorageMask;
|
BufferStage storageFlags = stage & BufferStage.StorageMask;
|
||||||
|
|
||||||
if (parent.Size > DeviceLocalSizeThreshold && baseBuffers.Count == 0)
|
if (parent.Size > DeviceLocalSizeThreshold && baseBuffers.Length == 0)
|
||||||
{
|
{
|
||||||
_desiredType = BufferBackingType.DeviceMemory;
|
_desiredType = BufferBackingType.DeviceMemory;
|
||||||
}
|
}
|
||||||
@@ -100,11 +101,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
// TODO: Might be nice to force atomic access to be device local for any stage.
|
// TODO: Might be nice to force atomic access to be device local for any stage.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (baseBuffers.Count != 0)
|
if (baseBuffers.Length != 0)
|
||||||
{
|
{
|
||||||
foreach (Buffer buffer in baseBuffers)
|
foreach (RangeItem<Buffer> item in baseBuffers)
|
||||||
{
|
{
|
||||||
CombineState(buffer.BackingState);
|
CombineState(item.Value.BackingState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -81,13 +81,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
MemoryRange subRange = range.GetSubRange(index);
|
MemoryRange subRange = range.GetSubRange(index);
|
||||||
|
|
||||||
_buffers.Lock.EnterReadLock();
|
_buffers.Lock.EnterReadLock();
|
||||||
(RangeItem<Buffer> first, RangeItem<Buffer> last) = _buffers.FindOverlaps(subRange.Address, subRange.Size);
|
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(subRange.Address, subRange.Size);
|
||||||
|
|
||||||
RangeItem<Buffer> current = first;
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
while (last != null && current != last.Next)
|
|
||||||
{
|
{
|
||||||
current.Value.Unmapped(subRange.Address, subRange.Size);
|
overlaps[i].Value.Unmapped(subRange.Address, subRange.Size);
|
||||||
current = current.Next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffers.Lock.ExitReadLock();
|
_buffers.Lock.ExitReadLock();
|
||||||
@@ -489,10 +487,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="stage">The type of usage that created the buffer</param>
|
/// <param name="stage">The type of usage that created the buffer</param>
|
||||||
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage)
|
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage)
|
||||||
{
|
{
|
||||||
|
Buffer newBuffer = null;
|
||||||
|
|
||||||
_buffers.Lock.EnterWriteLock();
|
_buffers.Lock.EnterWriteLock();
|
||||||
(RangeItem<Buffer> first, RangeItem<Buffer> last) = _buffers.FindOverlaps(address, size);
|
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
|
||||||
|
|
||||||
if (first is not null)
|
if (overlaps.Length != 0)
|
||||||
{
|
{
|
||||||
// The buffer already exists. We can just return the existing buffer
|
// The buffer already exists. We can just return the existing buffer
|
||||||
// if the buffer we need is fully contained inside the overlapping buffer.
|
// if the buffer we need is fully contained inside the overlapping buffer.
|
||||||
@@ -502,7 +502,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
|
|
||||||
if (first.Address > address || first.EndAddress < endAddress)
|
if (overlaps[0].Address > address || overlaps[0].EndAddress < endAddress)
|
||||||
{
|
{
|
||||||
bool anySparseCompatible = false;
|
bool anySparseCompatible = false;
|
||||||
|
|
||||||
@@ -515,52 +515,60 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
// sequential memory.
|
// sequential memory.
|
||||||
// Allowing for 2 pages (rather than just one) is necessary to catch cases where the
|
// Allowing for 2 pages (rather than just one) is necessary to catch cases where the
|
||||||
// range crosses a page, and after alignment, ends having a size of 2 pages.
|
// range crosses a page, and after alignment, ends having a size of 2 pages.
|
||||||
if (first == last &&
|
if (overlaps.Length == 1 &&
|
||||||
address >= first.Address &&
|
address >= overlaps[0].Address &&
|
||||||
endAddress - first.EndAddress <= BufferAlignmentSize * 2)
|
endAddress - overlaps[0].EndAddress <= BufferAlignmentSize * 2)
|
||||||
{
|
{
|
||||||
// Try to grow the buffer by 1.5x of its current size.
|
// Try to grow the buffer by 1.5x of its current size.
|
||||||
// This improves performance in the cases where the buffer is resized often by small amounts.
|
// This improves performance in the cases where the buffer is resized often by small amounts.
|
||||||
ulong existingSize = first.Value.Size;
|
ulong existingSize = overlaps[0].Value.Size;
|
||||||
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
|
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
|
||||||
|
|
||||||
size = Math.Max(size, growthSize);
|
size = Math.Max(size, growthSize);
|
||||||
endAddress = address + size;
|
endAddress = address + size;
|
||||||
|
|
||||||
(first, last) = _buffers.FindOverlaps(address, size);
|
overlaps = _buffers.FindOverlapsAsSpan(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
address = Math.Min(address, first.Address);
|
address = Math.Min(address, overlaps[0].Address);
|
||||||
endAddress = Math.Max(endAddress, last.EndAddress);
|
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
|
||||||
|
|
||||||
List<Buffer> overlaps = [];
|
|
||||||
|
|
||||||
RangeItem<Buffer> current = first;
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
while (current != last.Next)
|
|
||||||
{
|
{
|
||||||
anySparseCompatible |= current.Value.SparseCompatible;
|
anySparseCompatible |= overlaps[i].Value.SparseCompatible;
|
||||||
overlaps.Add(current.Value);
|
|
||||||
_buffers.Remove(current.Value);
|
|
||||||
|
|
||||||
current = current.Next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RangeItem<Buffer>[] overlapsArray = overlaps.ToArray();
|
||||||
|
|
||||||
|
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
|
||||||
|
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
|
|
||||||
ulong newSize = endAddress - address;
|
ulong newSize = endAddress - address;
|
||||||
|
|
||||||
Buffer newBuffer = CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlaps);
|
newBuffer = CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlapsArray);
|
||||||
|
}
|
||||||
_buffers.Add(newBuffer);
|
else
|
||||||
|
{
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
|
|
||||||
// No overlap, just create a new buffer.
|
// No overlap, just create a new buffer.
|
||||||
Buffer buffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible: false, []);
|
newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible: false, []);
|
||||||
|
}
|
||||||
_buffers.Add(buffer);
|
|
||||||
|
if (newBuffer is not null)
|
||||||
|
{
|
||||||
|
_buffers.Lock.EnterWriteLock();
|
||||||
|
|
||||||
|
_buffers.Add(newBuffer);
|
||||||
|
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffers.Lock.ExitWriteLock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -575,67 +583,74 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, ulong alignment)
|
private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, ulong alignment)
|
||||||
{
|
{
|
||||||
bool sparseAligned = alignment >= SparseBufferAlignmentSize;
|
bool sparseAligned = alignment >= SparseBufferAlignmentSize;
|
||||||
|
Buffer newBuffer = null;
|
||||||
|
|
||||||
_buffers.Lock.EnterWriteLock();
|
_buffers.Lock.EnterWriteLock();
|
||||||
(RangeItem<Buffer> first, RangeItem<Buffer> last) = _buffers.FindOverlaps(address, size);
|
Span<RangeItem<Buffer>> overlaps = _buffers.FindOverlapsAsSpan(address, size);
|
||||||
|
|
||||||
if (first is not null)
|
if (overlaps.Length != 0)
|
||||||
{
|
{
|
||||||
// If the buffer already exists, make sure if covers the entire range,
|
// If the buffer already exists, make sure if covers the entire range,
|
||||||
// and make sure it is properly aligned, otherwise sparse mapping may fail.
|
// and make sure it is properly aligned, otherwise sparse mapping may fail.
|
||||||
|
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
|
|
||||||
if (first.Address > address ||
|
if (overlaps[0].Address > address ||
|
||||||
first.EndAddress < endAddress ||
|
overlaps[0].EndAddress < endAddress ||
|
||||||
(first.Address & (alignment - 1)) != 0 ||
|
(overlaps[0].Address & (alignment - 1)) != 0 ||
|
||||||
(!first.Value.SparseCompatible && sparseAligned))
|
(!overlaps[0].Value.SparseCompatible && sparseAligned))
|
||||||
{
|
{
|
||||||
// We need to make sure the new buffer is properly aligned.
|
// We need to make sure the new buffer is properly aligned.
|
||||||
// However, after the range is aligned, it is possible that it
|
// However, after the range is aligned, it is possible that it
|
||||||
// overlaps more buffers, so try again after each extension
|
// overlaps more buffers, so try again after each extension
|
||||||
// and ensure we cover all overlaps.
|
// and ensure we cover all overlaps.
|
||||||
|
|
||||||
RangeItem<Buffer> oldFirst;
|
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
|
||||||
endAddress = Math.Max(endAddress, last.EndAddress);
|
int oldOverlapCount;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
address = Math.Min(address, first.Address);
|
address = Math.Min(address, overlaps[0].Address);
|
||||||
|
endAddress = Math.Max(endAddress, overlaps[^1].EndAddress);
|
||||||
|
|
||||||
address &= ~(alignment - 1);
|
address &= ~(alignment - 1);
|
||||||
|
|
||||||
oldFirst = first;
|
oldOverlapCount = overlaps.Length;
|
||||||
(first, last) = _buffers.FindOverlaps(address, endAddress - address);
|
overlaps = _buffers.FindOverlapsAsSpan(address, endAddress - address);
|
||||||
}
|
}
|
||||||
while (oldFirst != first);
|
while (oldOverlapCount != overlaps.Length);
|
||||||
|
|
||||||
ulong newSize = endAddress - address;
|
ulong newSize = endAddress - address;
|
||||||
|
|
||||||
List<Buffer> overlaps = [];
|
RangeItem<Buffer>[] overlapsArray = overlaps.ToArray();
|
||||||
|
|
||||||
RangeItem<Buffer> current = first;
|
_buffers.RemoveRange(overlaps[0], overlaps[^1]);
|
||||||
while (current != last.Next)
|
|
||||||
{
|
|
||||||
overlaps.Add(current.Value);
|
|
||||||
_buffers.Remove(current.Value);
|
|
||||||
|
|
||||||
current = current.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer newBuffer = CreateBufferAligned(address, newSize, stage, sparseAligned, overlaps);
|
_buffers.Lock.ExitWriteLock();
|
||||||
|
|
||||||
_buffers.Add(newBuffer);
|
newBuffer = CreateBufferAligned(address, newSize, stage, sparseAligned, overlapsArray);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
|
|
||||||
// No overlap, just create a new buffer.
|
// No overlap, just create a new buffer.
|
||||||
Buffer buffer = new(_context, _physicalMemory, address, size, stage, sparseAligned, []);
|
newBuffer = new(_context, _physicalMemory, address, size, stage, sparseAligned, []);
|
||||||
|
}
|
||||||
_buffers.Add(buffer);
|
|
||||||
|
if (newBuffer is not null)
|
||||||
|
{
|
||||||
|
_buffers.Lock.EnterWriteLock();
|
||||||
|
|
||||||
|
_buffers.Add(newBuffer);
|
||||||
|
|
||||||
|
_buffers.Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
_buffers.Lock.ExitWriteLock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -648,13 +663,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="stage">The type of usage that created the buffer</param>
|
/// <param name="stage">The type of usage that created the buffer</param>
|
||||||
/// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param>
|
/// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param>
|
||||||
/// <param name="overlaps">Buffers overlapping the range</param>
|
/// <param name="overlaps">Buffers overlapping the range</param>
|
||||||
private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, List<Buffer> overlaps)
|
private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, RangeItem<Buffer>[] overlaps)
|
||||||
{
|
{
|
||||||
Buffer newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible, overlaps);
|
Buffer newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible, overlaps);
|
||||||
|
|
||||||
for (int index = 0; index < overlaps.Count; index++)
|
for (int index = 0; index < overlaps.Length; index++)
|
||||||
{
|
{
|
||||||
Buffer buffer = overlaps[index];
|
Buffer buffer = overlaps[index].Value;
|
||||||
|
|
||||||
int dstOffset = (int)(buffer.Address - newBuffer.Address);
|
int dstOffset = (int)(buffer.Address - newBuffer.Address);
|
||||||
|
|
||||||
@@ -882,7 +897,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
MemoryRange subRange = range.GetSubRange(i);
|
MemoryRange subRange = range.GetSubRange(i);
|
||||||
|
|
||||||
Buffer subBuffer = _buffers.FindOverlapFast(subRange.Address, subRange.Size).Value;
|
Buffer subBuffer = _buffers.FindOverlap(subRange.Address, subRange.Size).Value;
|
||||||
|
|
||||||
subBuffer.SynchronizeMemory(subRange.Address, subRange.Size);
|
subBuffer.SynchronizeMemory(subRange.Address, subRange.Size);
|
||||||
|
|
||||||
@@ -930,7 +945,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
{
|
{
|
||||||
buffer = _buffers.FindOverlapFast(address, size).Value;
|
buffer = _buffers.FindOverlap(address, size).Value;
|
||||||
|
|
||||||
buffer.CopyFromDependantVirtualBuffers();
|
buffer.CopyFromDependantVirtualBuffers();
|
||||||
buffer.SynchronizeMemory(address, size);
|
buffer.SynchronizeMemory(address, size);
|
||||||
@@ -980,7 +995,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
{
|
{
|
||||||
Buffer buffer = _buffers.FindOverlapFast(address, size).Value;
|
Buffer buffer = _buffers.FindOverlap(address, size).Value;
|
||||||
|
|
||||||
if (copyBackVirtual)
|
if (copyBackVirtual)
|
||||||
{
|
{
|
||||||
|
@@ -80,8 +80,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
private BufferMigration _source;
|
private BufferMigration _source;
|
||||||
private BufferModifiedRangeList _migrationTarget;
|
private BufferModifiedRangeList _migrationTarget;
|
||||||
|
|
||||||
private List<RangeItem<BufferModifiedRange>> _overlaps;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the modified range list has any entries or not.
|
/// Whether the modified range list has any entries or not.
|
||||||
@@ -108,7 +106,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
_context = context;
|
_context = context;
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
_flushAction = flushAction;
|
_flushAction = flushAction;
|
||||||
_overlaps = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -122,12 +119,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
// Slices a given region using the modified regions in the list. Calls the action for the new slices.
|
// Slices a given region using the modified regions in the list. Calls the action for the new slices.
|
||||||
Lock.EnterReadLock();
|
Lock.EnterReadLock();
|
||||||
|
|
||||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
|
||||||
|
|
||||||
RangeItem<BufferModifiedRange> current = first;
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
while (last != null && current != last.Next)
|
|
||||||
{
|
{
|
||||||
BufferModifiedRange overlap = current.Value;
|
BufferModifiedRange overlap = overlaps[i].Value;
|
||||||
|
|
||||||
if (overlap.Address > address)
|
if (overlap.Address > address)
|
||||||
{
|
{
|
||||||
@@ -138,7 +134,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
// Remaining region is after this overlap.
|
// Remaining region is after this overlap.
|
||||||
size -= overlap.EndAddress - address;
|
size -= overlap.EndAddress - address;
|
||||||
address = overlap.EndAddress;
|
address = overlap.EndAddress;
|
||||||
current = current.Next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock.ExitReadLock();
|
Lock.ExitReadLock();
|
||||||
@@ -158,12 +153,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size of the modified region in bytes</param>
|
/// <param name="size">Size of the modified region in bytes</param>
|
||||||
public void SignalModified(ulong address, ulong size)
|
public void SignalModified(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
// We may overlap with some existing modified regions. They must be cut into by the new entry.
|
|
||||||
Lock.EnterWriteLock();
|
|
||||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
|
||||||
|
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
ulong syncNumber = _context.SyncNumber;
|
ulong syncNumber = _context.SyncNumber;
|
||||||
|
// We may overlap with some existing modified regions. They must be cut into by the new entry.
|
||||||
|
Lock.EnterWriteLock();
|
||||||
|
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
|
||||||
|
|
||||||
if (first is null)
|
if (first is null)
|
||||||
{
|
{
|
||||||
@@ -172,8 +166,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (first == last)
|
if (first == last)
|
||||||
{
|
{
|
||||||
if (first.Address == address && first.EndAddress == endAddress)
|
if (first.Address == address && first.EndAddress == endAddress)
|
||||||
@@ -260,19 +252,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action<ulong, ulong> rangeAction)
|
public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action<ulong, ulong> rangeAction)
|
||||||
{
|
{
|
||||||
Lock.EnterReadLock();
|
Lock.EnterReadLock();
|
||||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
Span<RangeItem<BufferModifiedRange>> overlaps = FindOverlapsAsSpan(address, size);
|
||||||
|
|
||||||
RangeItem<BufferModifiedRange> current = first;
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
while (last != null && current != last.Next)
|
|
||||||
{
|
{
|
||||||
BufferModifiedRange overlap = current.Value;
|
BufferModifiedRange overlap = overlaps[i].Value;
|
||||||
|
|
||||||
if (overlap.SyncNumber == syncNumber)
|
if (overlap.SyncNumber == syncNumber)
|
||||||
{
|
{
|
||||||
rangeAction(overlap.Address, overlap.Size);
|
rangeAction(overlap.Address, overlap.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
current = current.Next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock.ExitReadLock();
|
Lock.ExitReadLock();
|
||||||
@@ -288,22 +277,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
// We use the non-span method here because keeping the lock will cause a deadlock.
|
// We use the non-span method here because keeping the lock will cause a deadlock.
|
||||||
Lock.EnterReadLock();
|
Lock.EnterReadLock();
|
||||||
|
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
|
||||||
_overlaps.Clear();
|
|
||||||
|
|
||||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
|
||||||
|
|
||||||
RangeItem<BufferModifiedRange> current = first;
|
|
||||||
while (last != null && current != last.Next)
|
|
||||||
{
|
|
||||||
_overlaps.Add(current);
|
|
||||||
current = current.Next;
|
|
||||||
}
|
|
||||||
Lock.ExitReadLock();
|
Lock.ExitReadLock();
|
||||||
|
|
||||||
for (int i = 0; i < _overlaps.Count; i++)
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
{
|
{
|
||||||
BufferModifiedRange overlap = _overlaps[i].Value;
|
BufferModifiedRange overlap = overlaps[i].Value;
|
||||||
rangeAction(overlap.Address, overlap.Size);
|
rangeAction(overlap.Address, overlap.Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -404,8 +383,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
ulong currentSync = _context.SyncNumber;
|
ulong currentSync = _context.SyncNumber;
|
||||||
|
|
||||||
List<RangeItem<BufferModifiedRange>> overlaps = [];
|
|
||||||
|
|
||||||
// Range list must be consistent for this operation
|
// Range list must be consistent for this operation
|
||||||
if (_migrationTarget != null)
|
if (_migrationTarget != null)
|
||||||
{
|
{
|
||||||
@@ -416,16 +393,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
Lock.EnterWriteLock();
|
Lock.EnterWriteLock();
|
||||||
// We use the non-span method here because the array is partially modified by the code, which would invalidate a span.
|
// We use the non-span method here because the array is partially modified by the code, which would invalidate a span.
|
||||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
|
||||||
|
|
||||||
RangeItem<BufferModifiedRange> current = first;
|
|
||||||
while (last != null && current != last.Next)
|
|
||||||
{
|
|
||||||
overlaps.Add(current);
|
|
||||||
current = current.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rangeCount = overlaps.Count;
|
int rangeCount = overlaps.Length;
|
||||||
|
|
||||||
if (rangeCount == 0)
|
if (rangeCount == 0)
|
||||||
{
|
{
|
||||||
@@ -503,6 +473,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ranges._migrationTarget = this;
|
ranges._migrationTarget = this;
|
||||||
|
|
||||||
Lock.EnterWriteLock();
|
Lock.EnterWriteLock();
|
||||||
|
|
||||||
foreach (BufferModifiedRange range in inheritRanges)
|
foreach (BufferModifiedRange range in inheritRanges)
|
||||||
{
|
{
|
||||||
Add(range);
|
Add(range);
|
||||||
@@ -582,7 +553,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
Lock.EnterWriteLock();
|
Lock.EnterWriteLock();
|
||||||
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlaps(address, size);
|
(RangeItem<BufferModifiedRange> first, RangeItem<BufferModifiedRange> last) = FindOverlapsAsNodes(address, size);
|
||||||
|
|
||||||
if (first is null)
|
if (first is null)
|
||||||
{
|
{
|
||||||
|
@@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong originalVa = gpuVa;
|
ulong originalVa = gpuVa;
|
||||||
|
|
||||||
_virtualRanges.Lock.EnterWriteLock();
|
_virtualRanges.Lock.EnterWriteLock();
|
||||||
(RangeItem<VirtualRange> first, RangeItem<VirtualRange> last) = _virtualRanges.FindOverlaps(gpuVa, size);
|
(RangeItem<VirtualRange> first, RangeItem<VirtualRange> last) = _virtualRanges.FindOverlapsAsNodes(gpuVa, size);
|
||||||
|
|
||||||
if (first is not null)
|
if (first is not null)
|
||||||
{
|
{
|
||||||
|
@@ -501,53 +501,13 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
using FileStream file = File.OpenRead(keysSource);
|
using FileStream file = File.OpenRead(keysSource);
|
||||||
|
|
||||||
switch (info.Extension)
|
if (info.Extension is ".keys")
|
||||||
{
|
{
|
||||||
case ".zip":
|
VerifyKeysFile(keysSource);
|
||||||
using (ZipArchive archive = ZipFile.OpenRead(keysSource))
|
File.Copy(keysSource, Path.Combine(installDirectory, info.Name), true);
|
||||||
{
|
}
|
||||||
InstallKeysFromZip(archive, installDirectory);
|
else
|
||||||
}
|
throw new InvalidFirmwarePackageException("Input file is not a valid key package");
|
||||||
|
|
||||||
break;
|
|
||||||
case ".keys":
|
|
||||||
VerifyKeysFile(keysSource);
|
|
||||||
File.Copy(keysSource, Path.Combine(installDirectory, info.Name), true);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new InvalidFirmwarePackageException("Input file is not a valid key package");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void InstallKeysFromZip(ZipArchive archive, string installDirectory)
|
|
||||||
{
|
|
||||||
string temporaryDirectory = Path.Combine(installDirectory, "temp");
|
|
||||||
if (Directory.Exists(temporaryDirectory))
|
|
||||||
{
|
|
||||||
Directory.Delete(temporaryDirectory, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.CreateDirectory(temporaryDirectory);
|
|
||||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
|
||||||
{
|
|
||||||
if (Path.GetExtension(entry.FullName).Equals(".keys", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
string extractDestination = Path.Combine(temporaryDirectory, entry.Name);
|
|
||||||
entry.ExtractToFile(extractDestination, overwrite: true);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
VerifyKeysFile(extractDestination);
|
|
||||||
File.Move(extractDestination, Path.Combine(installDirectory, entry.Name), true);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
Directory.Delete(temporaryDirectory, true);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.Delete(temporaryDirectory, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FinishInstallation(string temporaryDirectory, string registeredDirectory)
|
private void FinishInstallation(string temporaryDirectory, string registeredDirectory)
|
||||||
|
@@ -865,7 +865,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
lock (_threadingLock)
|
lock (_threadingLock)
|
||||||
{
|
{
|
||||||
thread.ProcessListNode = _threads.AddLast(thread);
|
_threads.AddLast(thread.ProcessListNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1227,7 +1227,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
thread.Suspend(ThreadSchedState.ThreadPauseFlag);
|
thread.Suspend(ThreadSchedState.ThreadPauseFlag);
|
||||||
thread.Context.RequestInterrupt();
|
thread.Context.RequestInterrupt();
|
||||||
if (!thread.DebugHalt.WaitOne(TimeSpan.FromMilliseconds(50)))
|
if (!thread.DebugHalt.Wait(TimeSpan.FromMilliseconds(50)))
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Kernel, $"Failed to suspend thread {thread.ThreadUid} in time.");
|
Logger.Warning?.Print(LogClass.Kernel, $"Failed to suspend thread {thread.ThreadUid} in time.");
|
||||||
}
|
}
|
||||||
|
@@ -13,16 +13,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
Monitor.Exit(mutex);
|
Monitor.Exit(mutex);
|
||||||
|
|
||||||
currentThread.Withholder = threadList;
|
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
|
||||||
|
|
||||||
currentThread.WithholderNode = threadList.AddLast(currentThread);
|
|
||||||
|
|
||||||
if (currentThread.TerminationRequested)
|
if (currentThread.TerminationRequested)
|
||||||
{
|
{
|
||||||
threadList.Remove(currentThread.WithholderNode);
|
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Running);
|
currentThread.Reschedule(ThreadSchedState.Running);
|
||||||
|
|
||||||
currentThread.Withholder = null;
|
currentThread.Withholder = null;
|
||||||
@@ -31,6 +23,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
currentThread.Withholder = threadList;
|
||||||
|
|
||||||
|
currentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
|
threadList.AddLast(currentThread.WithholderNode);
|
||||||
|
|
||||||
if (timeout > 0)
|
if (timeout > 0)
|
||||||
{
|
{
|
||||||
context.TimeManager.ScheduleFutureInvocation(currentThread, timeout);
|
context.TimeManager.ScheduleFutureInvocation(currentThread, timeout);
|
||||||
|
@@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
// even if they are not scheduled on guest cores.
|
// even if they are not scheduled on guest cores.
|
||||||
if (currentThread != null && !currentThread.IsSchedulable && currentThread.Context.Running)
|
if (currentThread != null && !currentThread.IsSchedulable && currentThread.Context.Running)
|
||||||
{
|
{
|
||||||
currentThread.SchedulerWaitEvent.WaitOne();
|
currentThread.SchedulerWaitEvent.Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -194,7 +194,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread.SiblingsPerCore[core] = SuggestedQueue(prio, core).AddFirst(thread);
|
SuggestedQueue(prio, core).AddFirst(thread.SiblingsPerCore[core]);
|
||||||
|
|
||||||
_suggestedPrioritiesPerCore[core] |= 1L << prio;
|
_suggestedPrioritiesPerCore[core] |= 1L << prio;
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread.SiblingsPerCore[core] = ScheduledQueue(prio, core).AddLast(thread);
|
ScheduledQueue(prio, core).AddLast(thread.SiblingsPerCore[core]);
|
||||||
|
|
||||||
_scheduledPrioritiesPerCore[core] |= 1L << prio;
|
_scheduledPrioritiesPerCore[core] |= 1L << prio;
|
||||||
}
|
}
|
||||||
@@ -235,7 +235,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread.SiblingsPerCore[core] = ScheduledQueue(prio, core).AddFirst(thread);
|
ScheduledQueue(prio, core).AddFirst(thread.SiblingsPerCore[core]);
|
||||||
|
|
||||||
_scheduledPrioritiesPerCore[core] |= 1L << prio;
|
_scheduledPrioritiesPerCore[core] |= 1L << prio;
|
||||||
}
|
}
|
||||||
@@ -251,7 +251,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
queue.Remove(thread.SiblingsPerCore[core]);
|
queue.Remove(thread.SiblingsPerCore[core]);
|
||||||
|
|
||||||
thread.SiblingsPerCore[core] = queue.AddLast(thread);
|
queue.AddLast(thread.SiblingsPerCore[core]);
|
||||||
|
|
||||||
return queue.First.Value;
|
return queue.First.Value;
|
||||||
}
|
}
|
||||||
|
@@ -318,11 +318,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
if (nextThread == null)
|
if (nextThread == null)
|
||||||
{
|
{
|
||||||
ActivateIdleThread();
|
ActivateIdleThread();
|
||||||
currentThread.SchedulerWaitEvent.WaitOne();
|
currentThread.SchedulerWaitEvent.Wait();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, currentThread.SchedulerWaitEvent);
|
nextThread.SchedulerWaitEvent.Set();
|
||||||
|
currentThread.SchedulerWaitEvent.Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@@ -39,9 +39,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
public const int MaxWaitSyncObjects = 64;
|
public const int MaxWaitSyncObjects = 64;
|
||||||
|
|
||||||
private ManualResetEvent _schedulerWaitEvent;
|
private ManualResetEventSlim _schedulerWaitEvent;
|
||||||
|
|
||||||
public ManualResetEvent SchedulerWaitEvent => _schedulerWaitEvent;
|
public ManualResetEventSlim SchedulerWaitEvent => _schedulerWaitEvent;
|
||||||
|
|
||||||
public Thread HostThread { get; private set; }
|
public Thread HostThread { get; private set; }
|
||||||
|
|
||||||
@@ -93,6 +93,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
private LinkedListNode<KThread> _mutexWaiterNode;
|
private LinkedListNode<KThread> _mutexWaiterNode;
|
||||||
|
|
||||||
private readonly LinkedList<KThread> _pinnedWaiters;
|
private readonly LinkedList<KThread> _pinnedWaiters;
|
||||||
|
private LinkedListNode<KThread> _pinnedWaiterNode;
|
||||||
|
|
||||||
public KThread MutexOwner { get; private set; }
|
public KThread MutexOwner { get; private set; }
|
||||||
|
|
||||||
@@ -135,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
private readonly Lock _activityOperationLock = new();
|
private readonly Lock _activityOperationLock = new();
|
||||||
|
|
||||||
internal readonly ManualResetEvent DebugHalt = new(false);
|
internal readonly ManualResetEventSlim DebugHalt = new(false);
|
||||||
|
|
||||||
public KThread(KernelContext context) : base(context)
|
public KThread(KernelContext context) : base(context)
|
||||||
{
|
{
|
||||||
@@ -144,8 +145,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
|
SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
|
||||||
|
|
||||||
|
for (int i = 0; i < SiblingsPerCore.Length; i++)
|
||||||
|
{
|
||||||
|
SiblingsPerCore[i] = new LinkedListNode<KThread>(this);
|
||||||
|
}
|
||||||
|
|
||||||
_mutexWaiters = [];
|
_mutexWaiters = [];
|
||||||
_pinnedWaiters = [];
|
_pinnedWaiters = [];
|
||||||
|
|
||||||
|
WithholderNode = new LinkedListNode<KThread>(this);
|
||||||
|
ProcessListNode = new LinkedListNode<KThread>(this);
|
||||||
|
_mutexWaiterNode = new LinkedListNode<KThread>(this);
|
||||||
|
_pinnedWaiterNode = new LinkedListNode<KThread>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Initialize(
|
public Result Initialize(
|
||||||
@@ -631,7 +642,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_pinnedWaiters.AddLast(currentThread);
|
_pinnedWaiters.AddLast(_pinnedWaiterNode);
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
currentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
}
|
}
|
||||||
@@ -848,7 +859,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
return KernelResult.ThreadTerminating;
|
return KernelResult.ThreadTerminating;
|
||||||
}
|
}
|
||||||
|
|
||||||
_pinnedWaiters.AddLast(currentThread);
|
_pinnedWaiters.AddLast(_pinnedWaiterNode);
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
currentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
}
|
}
|
||||||
@@ -1262,7 +1273,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
{
|
{
|
||||||
if (_schedulerWaitEvent == null)
|
if (_schedulerWaitEvent == null)
|
||||||
{
|
{
|
||||||
ManualResetEvent schedulerWaitEvent = new(false);
|
ManualResetEventSlim schedulerWaitEvent = new(false);
|
||||||
|
|
||||||
if (Interlocked.Exchange(ref _schedulerWaitEvent, schedulerWaitEvent) == null)
|
if (Interlocked.Exchange(ref _schedulerWaitEvent, schedulerWaitEvent) == null)
|
||||||
{
|
{
|
||||||
@@ -1277,7 +1288,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
private void ThreadStart()
|
private void ThreadStart()
|
||||||
{
|
{
|
||||||
_schedulerWaitEvent.WaitOne();
|
_schedulerWaitEvent.Wait();
|
||||||
DebugHalt.Reset();
|
DebugHalt.Reset();
|
||||||
KernelStatic.SetKernelContext(KernelContext, this);
|
KernelStatic.SetKernelContext(KernelContext, this);
|
||||||
|
|
||||||
|
@@ -31,11 +31,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
private readonly KEvent _friendInvitationStorageChannelEvent;
|
private readonly KEvent _friendInvitationStorageChannelEvent;
|
||||||
private readonly KEvent _notificationStorageChannelEvent;
|
private readonly KEvent _notificationStorageChannelEvent;
|
||||||
private readonly KEvent _healthWarningDisappearedSystemEvent;
|
private readonly KEvent _healthWarningDisappearedSystemEvent;
|
||||||
|
private readonly KEvent _unknownEvent;
|
||||||
|
|
||||||
private int _gpuErrorDetectedSystemEventHandle;
|
private int _gpuErrorDetectedSystemEventHandle;
|
||||||
private int _friendInvitationStorageChannelEventHandle;
|
private int _friendInvitationStorageChannelEventHandle;
|
||||||
private int _notificationStorageChannelEventHandle;
|
private int _notificationStorageChannelEventHandle;
|
||||||
private int _healthWarningDisappearedSystemEventHandle;
|
private int _healthWarningDisappearedSystemEventHandle;
|
||||||
|
private int _unknownEventHandle;
|
||||||
|
|
||||||
private bool _gamePlayRecordingState;
|
private bool _gamePlayRecordingState;
|
||||||
|
|
||||||
@@ -50,6 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
_friendInvitationStorageChannelEvent = new KEvent(system.KernelContext);
|
_friendInvitationStorageChannelEvent = new KEvent(system.KernelContext);
|
||||||
_notificationStorageChannelEvent = new KEvent(system.KernelContext);
|
_notificationStorageChannelEvent = new KEvent(system.KernelContext);
|
||||||
_healthWarningDisappearedSystemEvent = new KEvent(system.KernelContext);
|
_healthWarningDisappearedSystemEvent = new KEvent(system.KernelContext);
|
||||||
|
_unknownEvent = new KEvent(system.KernelContext);
|
||||||
|
|
||||||
_horizon = system.LibHacHorizonManager.AmClient;
|
_horizon = system.LibHacHorizonManager.AmClient;
|
||||||
}
|
}
|
||||||
@@ -647,6 +650,23 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(210)] // 20.0.0+
|
||||||
|
// GetUnknownEvent() -> handle<copy>
|
||||||
|
public ResultCode GetUnknownEvent(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (_unknownEventHandle == 0)
|
||||||
|
{
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(_unknownEvent.ReadableEvent, out _unknownEventHandle) != Result.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Out of handles!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_unknownEventHandle);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(1001)] // 10.0.0+
|
[CommandCmif(1001)] // 10.0.0+
|
||||||
// PrepareForJit()
|
// PrepareForJit()
|
||||||
|
@@ -39,9 +39,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
|||||||
|
|
||||||
public ref AtomicStorage<T> GetCurrentAtomicEntryRef()
|
public ref AtomicStorage<T> GetCurrentAtomicEntryRef()
|
||||||
{
|
{
|
||||||
ulong countAvailaible = Math.Min(Math.Max(0, ReadCurrentCount()), 1);
|
ulong countAvailable = Math.Min(Math.Max(0, ReadCurrentCount()), 1);
|
||||||
|
|
||||||
if (countAvailaible == 0)
|
if (countAvailable == 0)
|
||||||
{
|
{
|
||||||
_storage[0] = default;
|
_storage[0] = default;
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
|||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible) % MaxEntries);
|
int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailable) % MaxEntries);
|
||||||
|
|
||||||
ref AtomicStorage<T> result = ref storageSpan[inputEntryIndex];
|
ref AtomicStorage<T> result = ref storageSpan[inputEntryIndex];
|
||||||
|
|
||||||
@@ -63,9 +63,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
|||||||
|
|
||||||
if (samplingNumber0 != samplingNumber1 && (result.SamplingNumber - result.SamplingNumber) != 1)
|
if (samplingNumber0 != samplingNumber1 && (result.SamplingNumber - result.SamplingNumber) != 1)
|
||||||
{
|
{
|
||||||
ulong tempCount = Math.Min(ReadCurrentCount(), countAvailaible);
|
ulong tempCount = Math.Min(ReadCurrentCount(), countAvailable);
|
||||||
|
|
||||||
countAvailaible = Math.Min(tempCount, 1);
|
countAvailable = Math.Min(tempCount, 1);
|
||||||
index = ReadCurrentIndex();
|
index = ReadCurrentIndex();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
struct NpadCondition
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0414 // Field is assigned but its value is never used
|
||||||
|
private uint _00;
|
||||||
|
private uint _04;
|
||||||
|
private NpadJoyHoldType _holdType;
|
||||||
|
private uint _0C;
|
||||||
|
#pragma warning restore CS0414 // Field is assigned but its value is never used
|
||||||
|
|
||||||
|
public static NpadCondition Create()
|
||||||
|
{
|
||||||
|
return new NpadCondition()
|
||||||
|
{
|
||||||
|
_00 = 0,
|
||||||
|
_04 = 1,
|
||||||
|
_holdType = NpadJoyHoldType.Horizontal,
|
||||||
|
_0C = 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -41,6 +41,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
|||||||
public NpadLarkType LarkTypeRight;
|
public NpadLarkType LarkTypeRight;
|
||||||
public NpadLuciaType LuciaType;
|
public NpadLuciaType LuciaType;
|
||||||
public uint Unknown43EC;
|
public uint Unknown43EC;
|
||||||
|
public ulong SixAxisSensorPropertiesArray;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 123, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Size = 123, Pack = 1)]
|
||||||
private struct Reserved2Struct { }
|
private struct Reserved2Struct { }
|
||||||
|
@@ -52,6 +52,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[FieldOffset(0x3DC00)]
|
[FieldOffset(0x3DC00)]
|
||||||
public RingLifo<DebugMouseState> DebugMouse;
|
public RingLifo<DebugMouseState> DebugMouse;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pad Condition.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(0x3e200)]
|
||||||
|
public NpadCondition Condition;
|
||||||
|
|
||||||
public static SharedMemory Create()
|
public static SharedMemory Create()
|
||||||
{
|
{
|
||||||
@@ -61,6 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory
|
|||||||
TouchScreen = RingLifo<TouchScreenState>.Create(),
|
TouchScreen = RingLifo<TouchScreenState>.Create(),
|
||||||
Mouse = RingLifo<MouseState>.Create(),
|
Mouse = RingLifo<MouseState>.Create(),
|
||||||
Keyboard = RingLifo<KeyboardState>.Create(),
|
Keyboard = RingLifo<KeyboardState>.Create(),
|
||||||
|
Condition = NpadCondition.Create(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Span<NpadState> npadsSpan = result.Npads.AsSpan();
|
Span<NpadState> npadsSpan = result.Npads.AsSpan();
|
||||||
|
@@ -12,9 +12,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <typeparam name="T">Type of the range.</typeparam>
|
/// <typeparam name="T">Type of the range.</typeparam>
|
||||||
public unsafe class NonOverlappingRangeList<T> : RangeListBase<T> where T : class, INonOverlappingRange
|
public unsafe class NonOverlappingRangeList<T> : RangeListBase<T> where T : class, INonOverlappingRange
|
||||||
{
|
{
|
||||||
private readonly Dictionary<ulong, RangeItem<T>> _quickAccess = new(AddressEqualityComparer.Comparer);
|
|
||||||
private readonly Dictionary<ulong, RangeItem<T>> _fastQuickAccess = new(AddressEqualityComparer.Comparer);
|
|
||||||
|
|
||||||
public readonly ReaderWriterLockSlim Lock = new();
|
public readonly ReaderWriterLockSlim Lock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -44,8 +41,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
RangeItem<T> rangeItem = new(item);
|
RangeItem<T> rangeItem = new(item);
|
||||||
|
|
||||||
Insert(index, rangeItem);
|
Insert(index, rangeItem);
|
||||||
|
|
||||||
_quickAccess.Add(item.Address, rangeItem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -71,15 +66,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
Items[index + 1].Previous = rangeItem;
|
Items[index + 1].Previous = rangeItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ulong addr in Items[index].QuickAccessAddresses)
|
|
||||||
{
|
|
||||||
_quickAccess.Remove(addr);
|
|
||||||
_fastQuickAccess.Remove(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Items[index] = rangeItem;
|
Items[index] = rangeItem;
|
||||||
|
|
||||||
_quickAccess[item.Address] = rangeItem;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -108,19 +95,8 @@ namespace Ryujinx.Memory.Range
|
|||||||
Items[index + 1].Previous = rangeItem;
|
Items[index + 1].Previous = rangeItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ulong addr in item.QuickAccessAddresses)
|
|
||||||
{
|
|
||||||
_quickAccess.Remove(addr);
|
|
||||||
_fastQuickAccess.Remove(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Items[index] = rangeItem;
|
Items[index] = rangeItem;
|
||||||
|
|
||||||
if (item.Address != rangeItem.Address)
|
|
||||||
_quickAccess.Remove(item.Address);
|
|
||||||
|
|
||||||
_quickAccess[rangeItem.Address] = rangeItem;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,14 +172,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
|
|
||||||
if (index >= 0 && Items[index].Value.Equals(item))
|
if (index >= 0 && Items[index].Value.Equals(item))
|
||||||
{
|
{
|
||||||
_quickAccess.Remove(item.Address);
|
|
||||||
|
|
||||||
foreach (ulong addr in Items[index].QuickAccessAddresses)
|
|
||||||
{
|
|
||||||
_quickAccess.Remove(addr);
|
|
||||||
_fastQuickAccess.Remove(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoveAt(index);
|
RemoveAt(index);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -232,16 +200,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
|
|
||||||
(int startIndex, int endIndex) = BinarySearchEdges(startItem.Address, endItem.EndAddress);
|
(int startIndex, int endIndex) = BinarySearchEdges(startItem.Address, endItem.EndAddress);
|
||||||
|
|
||||||
for (int i = startIndex; i < endIndex; i++)
|
|
||||||
{
|
|
||||||
_quickAccess.Remove(Items[i].Address);
|
|
||||||
foreach (ulong addr in Items[i].QuickAccessAddresses)
|
|
||||||
{
|
|
||||||
_quickAccess.Remove(addr);
|
|
||||||
_fastQuickAccess.Remove(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endIndex < Count)
|
if (endIndex < Count)
|
||||||
{
|
{
|
||||||
Items[endIndex].Previous = startIndex > 0 ? Items[startIndex - 1] : null;
|
Items[endIndex].Previous = startIndex > 0 ? Items[startIndex - 1] : null;
|
||||||
@@ -279,13 +237,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
|
|
||||||
while (Items[endIndex] is not null && Items[endIndex].Address < address + size)
|
while (Items[endIndex] is not null && Items[endIndex].Address < address + size)
|
||||||
{
|
{
|
||||||
_quickAccess.Remove(Items[endIndex].Address);
|
|
||||||
foreach (ulong addr in Items[endIndex].QuickAccessAddresses)
|
|
||||||
{
|
|
||||||
_quickAccess.Remove(addr);
|
|
||||||
_fastQuickAccess.Remove(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endIndex == Count - 1)
|
if (endIndex == Count - 1)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@@ -321,8 +272,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
{
|
{
|
||||||
Lock.EnterWriteLock();
|
Lock.EnterWriteLock();
|
||||||
Count = 0;
|
Count = 0;
|
||||||
_quickAccess.Clear();
|
|
||||||
_fastQuickAccess.Clear();
|
|
||||||
Lock.ExitWriteLock();
|
Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,7 +293,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
// So we need to return both the split 0-1 and 1-2 ranges.
|
// So we need to return both the split 0-1 and 1-2 ranges.
|
||||||
|
|
||||||
Lock.EnterWriteLock();
|
Lock.EnterWriteLock();
|
||||||
(RangeItem<T> first, RangeItem<T> last) = FindOverlaps(address, size);
|
(RangeItem<T> first, RangeItem<T> last) = FindOverlapsAsNodes(address, size);
|
||||||
list = new List<T>();
|
list = new List<T>();
|
||||||
|
|
||||||
if (first is null)
|
if (first is null)
|
||||||
@@ -436,11 +385,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override RangeItem<T> FindOverlap(ulong address, ulong size)
|
public override RangeItem<T> FindOverlap(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
if (_quickAccess.TryGetValue(address, out RangeItem<T> overlap))
|
|
||||||
{
|
|
||||||
return overlap;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = BinarySearchLeftEdge(address, address + size);
|
int index = BinarySearchLeftEdge(address, address + size);
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
@@ -448,12 +392,6 @@ namespace Ryujinx.Memory.Range
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Items[index].Address < address)
|
|
||||||
{
|
|
||||||
_quickAccess.TryAdd(address, Items[index]);
|
|
||||||
Items[index].QuickAccessAddresses.Add(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Items[index];
|
return Items[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,28 +404,12 @@ namespace Ryujinx.Memory.Range
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override RangeItem<T> FindOverlapFast(ulong address, ulong size)
|
public override RangeItem<T> FindOverlapFast(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
if (_quickAccess.TryGetValue(address, out RangeItem<T> overlap) || _fastQuickAccess.TryGetValue(address, out overlap))
|
|
||||||
{
|
|
||||||
return overlap;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = BinarySearch(address, address + size);
|
int index = BinarySearch(address, address + size);
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Items[index].Address < address)
|
|
||||||
{
|
|
||||||
_quickAccess.TryAdd(address, Items[index]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_fastQuickAccess.TryAdd(address, Items[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Items[index].QuickAccessAddresses.Add(address);
|
|
||||||
|
|
||||||
return Items[index];
|
return Items[index];
|
||||||
}
|
}
|
||||||
@@ -499,18 +421,8 @@ namespace Ryujinx.Memory.Range
|
|||||||
/// <param name="size">Size in bytes of the range</param>
|
/// <param name="size">Size in bytes of the range</param>
|
||||||
/// <returns>The first and last overlapping items, or null if none are found</returns>
|
/// <returns>The first and last overlapping items, or null if none are found</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public (RangeItem<T>, RangeItem<T>) FindOverlaps(ulong address, ulong size)
|
public (RangeItem<T>, RangeItem<T>) FindOverlapsAsNodes(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
if (_quickAccess.TryGetValue(address, out RangeItem<T> overlap))
|
|
||||||
{
|
|
||||||
if (overlap.Next is null || overlap.Next.Address >= address + size)
|
|
||||||
{
|
|
||||||
return (overlap, overlap);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (overlap, Items[BinarySearchRightEdge(address, address + size)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
@@ -518,13 +430,45 @@ namespace Ryujinx.Memory.Range
|
|||||||
return (null, null);
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Items[index].Address < address)
|
return (Items[index], Items[endIndex - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RangeItem<T>[] FindOverlapsAsArray(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
||||||
|
|
||||||
|
RangeItem<T>[] result;
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
{
|
{
|
||||||
_quickAccess.TryAdd(address, Items[index]);
|
result = [];
|
||||||
Items[index].QuickAccessAddresses.Add(address);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = new RangeItem<T>[endIndex - index];
|
||||||
|
|
||||||
|
Array.Copy(Items, index, result, 0, endIndex - index);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (Items[index], Items[endIndex - 1]);
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span<RangeItem<T>> FindOverlapsAsSpan(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
||||||
|
|
||||||
|
Span<RangeItem<T>> result;
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
result = [];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = Items.AsSpan().Slice(index, endIndex - index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerator<T> GetEnumerator()
|
public override IEnumerator<T> GetEnumerator()
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Memory.Tracking
|
namespace Ryujinx.Memory.Tracking
|
||||||
@@ -79,12 +80,10 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
{
|
{
|
||||||
NonOverlappingRangeList<VirtualRegion> regions = type == 0 ? _virtualRegions : _guestVirtualRegions;
|
NonOverlappingRangeList<VirtualRegion> regions = type == 0 ? _virtualRegions : _guestVirtualRegions;
|
||||||
regions.Lock.EnterReadLock();
|
regions.Lock.EnterReadLock();
|
||||||
(RangeItem<VirtualRegion> first, RangeItem<VirtualRegion> last) = regions.FindOverlaps(va, size);
|
Span<RangeItem<VirtualRegion>> overlaps = regions.FindOverlapsAsSpan(va, size);
|
||||||
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
RangeItem<VirtualRegion> current = first;
|
|
||||||
while (last != null && current != last.Next)
|
|
||||||
{
|
{
|
||||||
VirtualRegion region = current.Value;
|
VirtualRegion region = overlaps[i].Value;
|
||||||
|
|
||||||
// If the region has been fully remapped, signal that it has been mapped again.
|
// If the region has been fully remapped, signal that it has been mapped again.
|
||||||
bool remapped = _memoryManager.IsRangeMapped(region.Address, region.Size);
|
bool remapped = _memoryManager.IsRangeMapped(region.Address, region.Size);
|
||||||
@@ -94,7 +93,6 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
}
|
}
|
||||||
|
|
||||||
region.UpdateProtection();
|
region.UpdateProtection();
|
||||||
current = current.Next;
|
|
||||||
}
|
}
|
||||||
regions.Lock.ExitReadLock();
|
regions.Lock.ExitReadLock();
|
||||||
}
|
}
|
||||||
@@ -118,15 +116,11 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
{
|
{
|
||||||
NonOverlappingRangeList<VirtualRegion> regions = type == 0 ? _virtualRegions : _guestVirtualRegions;
|
NonOverlappingRangeList<VirtualRegion> regions = type == 0 ? _virtualRegions : _guestVirtualRegions;
|
||||||
regions.Lock.EnterReadLock();
|
regions.Lock.EnterReadLock();
|
||||||
(RangeItem<VirtualRegion> first, RangeItem<VirtualRegion> last) = regions.FindOverlaps(va, size);
|
Span<RangeItem<VirtualRegion>> overlaps = regions.FindOverlapsAsSpan(va, size);
|
||||||
|
|
||||||
RangeItem<VirtualRegion> current = first;
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
while (last != null && current != last.Next)
|
|
||||||
{
|
{
|
||||||
VirtualRegion region = current.Value;
|
overlaps[i].Value.SignalMappingChanged(false);
|
||||||
|
|
||||||
region.SignalMappingChanged(false);
|
|
||||||
current = current.Next;
|
|
||||||
}
|
}
|
||||||
regions.Lock.ExitReadLock();
|
regions.Lock.ExitReadLock();
|
||||||
}
|
}
|
||||||
@@ -303,21 +297,13 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
lock (TrackingLock)
|
lock (TrackingLock)
|
||||||
{
|
{
|
||||||
NonOverlappingRangeList<VirtualRegion> regions = guest ? _guestVirtualRegions : _virtualRegions;
|
NonOverlappingRangeList<VirtualRegion> regions = guest ? _guestVirtualRegions : _virtualRegions;
|
||||||
List<RangeItem<VirtualRegion>> overlaps = [];
|
|
||||||
|
|
||||||
// We use the non-span method here because keeping the lock will cause a deadlock.
|
// We use the non-span method here because keeping the lock will cause a deadlock.
|
||||||
regions.Lock.EnterReadLock();
|
regions.Lock.EnterReadLock();
|
||||||
(RangeItem<VirtualRegion> first, RangeItem<VirtualRegion> last) = regions.FindOverlaps(address, size);
|
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size);
|
||||||
|
|
||||||
RangeItem<VirtualRegion> current = first;
|
|
||||||
while (last != null && current != last.Next)
|
|
||||||
{
|
|
||||||
overlaps.Add(current);
|
|
||||||
current = current.Next;
|
|
||||||
}
|
|
||||||
regions.Lock.ExitReadLock();
|
regions.Lock.ExitReadLock();
|
||||||
|
|
||||||
if (first is null && !precise)
|
if (overlaps.Length == 0 && !precise)
|
||||||
{
|
{
|
||||||
if (_memoryManager.IsRangeMapped(address, size))
|
if (_memoryManager.IsRangeMapped(address, size))
|
||||||
{
|
{
|
||||||
@@ -338,7 +324,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
size += (ulong)_pageSize;
|
size += (ulong)_pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < overlaps.Count; i++)
|
for (int i = 0; i < overlaps.Length; i++)
|
||||||
{
|
{
|
||||||
VirtualRegion region = overlaps[i].Value;
|
VirtualRegion region = overlaps[i].Value;
|
||||||
|
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Gommon;
|
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.Account;
|
using LibHac.Account;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
@@ -411,7 +410,7 @@ namespace Ryujinx.Ava.Common
|
|||||||
|
|
||||||
public static async Task ExtractAoc(IStorageProvider storageProvider, string updateFilePath, string updateName)
|
public static async Task ExtractAoc(IStorageProvider storageProvider, string updateFilePath, string updateName)
|
||||||
{
|
{
|
||||||
Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
|
Gommon.Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
|
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
|
||||||
});
|
});
|
||||||
@@ -424,7 +423,7 @@ namespace Ryujinx.Ava.Common
|
|||||||
|
|
||||||
public static async Task ExtractSection(IStorageProvider storageProvider, NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0)
|
public static async Task ExtractSection(IStorageProvider storageProvider, NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0)
|
||||||
{
|
{
|
||||||
Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
|
Gommon.Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
|
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
|
||||||
});
|
});
|
||||||
|
@@ -1045,7 +1045,7 @@ namespace Ryujinx.Ava.Systems
|
|||||||
_viewModel.Window.TitleBar.ExtendsContentIntoTitleBar = true;
|
_viewModel.Window.TitleBar.ExtendsContentIntoTitleBar = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_viewModel.WindowState is WindowState.FullScreen || _viewModel.StartGamesWithoutUI)
|
if (_viewModel.WindowState is WindowState.FullScreen || _viewModel.StartGamesWithoutUi)
|
||||||
{
|
{
|
||||||
_viewModel.ShowMenuAndStatusBar = false;
|
_viewModel.ShowMenuAndStatusBar = false;
|
||||||
}
|
}
|
||||||
|
@@ -117,7 +117,7 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
|||||||
|
|
||||||
using UniqueRef<IFile> npdmFile = new();
|
using UniqueRef<IFile> npdmFile = new();
|
||||||
|
|
||||||
Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
|
LibHac.Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
|
||||||
|
|
||||||
if (ResultFs.PathNotFound.Includes(result))
|
if (ResultFs.PathNotFound.Includes(result))
|
||||||
{
|
{
|
||||||
|
@@ -97,8 +97,10 @@ namespace Ryujinx.Ava.Systems.PlayReport
|
|||||||
//TODO DLC Locations
|
//TODO DLC Locations
|
||||||
_ => FormattedValue.ForceReset
|
_ => FormattedValue.ForceReset
|
||||||
};
|
};
|
||||||
|
|
||||||
return $"{playStatus} in {locations}";
|
return locations.Reset
|
||||||
|
? FormattedValue.ForceReset
|
||||||
|
: $"{playStatus} in {locations}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FormattedValue SuperSmashBrosUltimate_Mode(SparseMultiValue values)
|
private static FormattedValue SuperSmashBrosUltimate_Mode(SparseMultiValue values)
|
||||||
|
@@ -7,6 +7,7 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Systems.Update.Client;
|
using Ryujinx.Systems.Update.Client;
|
||||||
using Ryujinx.Systems.Update.Common;
|
using Ryujinx.Systems.Update.Common;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.Systems
|
namespace Ryujinx.Ava.Systems
|
||||||
@@ -14,16 +15,38 @@ namespace Ryujinx.Ava.Systems
|
|||||||
internal static partial class Updater
|
internal static partial class Updater
|
||||||
{
|
{
|
||||||
private static VersionResponse _versionResponse;
|
private static VersionResponse _versionResponse;
|
||||||
|
private static UpdateClient _updateClient;
|
||||||
|
|
||||||
private static UpdateClient CreateUpdateClient()
|
private static async Task<Return<VersionResponse>> QueryLatestVersionAsync()
|
||||||
=> UpdateClient.Builder()
|
{
|
||||||
|
_updateClient ??= UpdateClient.Builder()
|
||||||
.WithServerEndpoint("https://update.ryujinx.app") // This is the default, and doesn't need to be provided; it's here for transparency.
|
.WithServerEndpoint("https://update.ryujinx.app") // This is the default, and doesn't need to be provided; it's here for transparency.
|
||||||
.WithLogger((format, args, caller) =>
|
.WithLogger((format, args, caller) =>
|
||||||
Logger.Info?.Print(
|
Logger.Info?.Print(
|
||||||
LogClass.Application,
|
LogClass.Application,
|
||||||
args.Length is 0 ? format : format.Format(args),
|
args.Length is 0 ? format : format.Format(args),
|
||||||
caller: caller)
|
caller: caller)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _updateClient.QueryLatestAsync(ReleaseInformation.IsCanaryBuild
|
||||||
|
? ReleaseChannel.Canary
|
||||||
|
: ReleaseChannel.Stable);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException hre)
|
||||||
|
when (hre.HttpRequestError is HttpRequestError.ConnectionError)
|
||||||
|
{
|
||||||
|
return Return<VersionResponse>.Failure(
|
||||||
|
new MessageError("Connection error occurred. Is your internet down?"));
|
||||||
|
}
|
||||||
|
catch (HttpRequestException hre)
|
||||||
|
when (hre.HttpRequestError is HttpRequestError.NameResolutionError)
|
||||||
|
{
|
||||||
|
return Return<VersionResponse>.Failure(
|
||||||
|
new MessageError("DNS resolution error occurred. Is your internet down?"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task<Optional<(Version Current, Version Incoming)>> CheckVersionAsync(bool showVersionUpToDate = false)
|
public static async Task<Optional<(Version Current, Version Incoming)>> CheckVersionAsync(bool showVersionUpToDate = false)
|
||||||
{
|
{
|
||||||
@@ -41,22 +64,18 @@ namespace Ryujinx.Ava.Systems
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
using UpdateClient updateClient = CreateUpdateClient();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_versionResponse = await updateClient.QueryLatestAsync(ReleaseInformation.IsCanaryBuild
|
_versionResponse = await QueryLatestVersionAsync().Then(x => x.Unwrap());
|
||||||
? ReleaseChannel.Canary
|
|
||||||
: ReleaseChannel.Stable);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, $"An error occurred when requesting for updates ({e.GetType().AsFullNamePrettyString()}): {e.Message}");
|
Logger.Error?.Print(LogClass.Application, $"{e.GetType().AsPrettyString()} thrown when requesting updates: {e.Message}");
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_versionResponse == null)
|
if (_versionResponse == null)
|
||||||
{
|
{
|
||||||
// logging is done via the UpdateClient library
|
// logging is done via the UpdateClient library
|
||||||
|
@@ -562,7 +562,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool StartGamesWithoutUI
|
public bool StartGamesWithoutUi
|
||||||
{
|
{
|
||||||
get => ConfigurationState.Instance.UI.StartNoUI;
|
get => ConfigurationState.Instance.UI.StartNoUI;
|
||||||
set
|
set
|
||||||
@@ -974,9 +974,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
string dialogTitle = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallTitle);
|
string dialogTitle = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallTitle);
|
||||||
string dialogMessage = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallMessage);
|
string dialogMessage = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallMessage);
|
||||||
|
|
||||||
bool alreadyKesyInstalled = ContentManager.AreKeysAlredyPresent(systemDirectory);
|
if (ContentManager.AreKeysAlredyPresent(systemDirectory))
|
||||||
if (alreadyKesyInstalled)
|
|
||||||
{
|
{
|
||||||
dialogMessage += LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallSubMessage);
|
dialogMessage += LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallSubMessage);
|
||||||
}
|
}
|
||||||
@@ -994,7 +993,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (result == UserResult.Yes)
|
if (result == UserResult.Yes)
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Application, $"Installing Keys");
|
Logger.Info?.Print(LogClass.Application, $"Installing keys from {filename}");
|
||||||
|
|
||||||
Thread thread = new(() =>
|
Thread thread = new(() =>
|
||||||
{
|
{
|
||||||
@@ -1206,15 +1205,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
private async Task LoadContentFromFolder(LocaleKeys localeMessageAddedKey, LocaleKeys localeMessageRemovedKey, LoadContentFromFolderDelegate onDirsSelected, LocaleKeys dirSelectDialogTitle)
|
private async Task LoadContentFromFolder(LocaleKeys localeMessageAddedKey, LocaleKeys localeMessageRemovedKey, LoadContentFromFolderDelegate onDirsSelected, LocaleKeys dirSelectDialogTitle)
|
||||||
{
|
{
|
||||||
IReadOnlyList<IStorageFolder> result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
Optional<IReadOnlyList<IStorageFolder>> result = await StorageProvider.OpenMultiFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[dirSelectDialogTitle],
|
Title = LocaleManager.Instance[dirSelectDialogTitle]
|
||||||
AllowMultiple = true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.TryGet(out IReadOnlyList<IStorageFolder> foldersToLoad))
|
||||||
{
|
{
|
||||||
List<string> dirs = result.Select(it => it.Path.LocalPath).ToList();
|
List<string> dirs = foldersToLoad.Select(it => it.Path.LocalPath).ToList();
|
||||||
int numAdded = onDirsSelected(dirs, out int numRemoved);
|
int numAdded = onDirsSelected(dirs, out int numRemoved);
|
||||||
|
|
||||||
string msg = string.Join("\n",
|
string msg = string.Join("\n",
|
||||||
@@ -1270,51 +1268,26 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TakeScreenshot()
|
public void TakeScreenshot() => AppHost.ScreenshotRequested = true;
|
||||||
{
|
|
||||||
AppHost.ScreenshotRequested = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void HideUi()
|
public void HideUi() => ShowMenuAndStatusBar = false;
|
||||||
{
|
|
||||||
ShowMenuAndStatusBar = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToggleStartGamesInFullscreen()
|
public void ToggleStartGamesInFullscreen() => StartGamesInFullscreen = !StartGamesInFullscreen;
|
||||||
{
|
|
||||||
StartGamesInFullscreen = !StartGamesInFullscreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToggleStartGamesWithoutUI()
|
public void ToggleStartGamesWithoutUi() => StartGamesWithoutUi = !StartGamesWithoutUi;
|
||||||
{
|
|
||||||
StartGamesWithoutUI = !StartGamesWithoutUI;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToggleShowConsole()
|
public void ToggleShowConsole() => ShowConsole = !ShowConsole;
|
||||||
{
|
|
||||||
ShowConsole = !ShowConsole;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetListMode()
|
public void SetListMode() => Glyph = Glyph.List;
|
||||||
{
|
|
||||||
Glyph = Glyph.List;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetGridMode()
|
public void SetGridMode() => Glyph = Glyph.Grid;
|
||||||
{
|
|
||||||
Glyph = Glyph.Grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetAspectRatio(AspectRatio aspectRatio)
|
public void SetAspectRatio(AspectRatio aspectRatio) => ConfigurationState.Instance.Graphics.AspectRatio.Value = aspectRatio;
|
||||||
{
|
|
||||||
ConfigurationState.Instance.Graphics.AspectRatio.Value = aspectRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task InstallFirmwareFromFile()
|
public async Task InstallFirmwareFromFile()
|
||||||
{
|
{
|
||||||
IReadOnlyList<IStorageFile> result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
|
||||||
{
|
{
|
||||||
AllowMultiple = false,
|
|
||||||
FileTypeFilter = new List<FilePickerFileType>
|
FileTypeFilter = new List<FilePickerFileType>
|
||||||
{
|
{
|
||||||
new(LocaleManager.Instance[LocaleKeys.FileDialogAllTypes])
|
new(LocaleManager.Instance[LocaleKeys.FileDialogAllTypes])
|
||||||
@@ -1338,69 +1311,50 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.HasValue)
|
||||||
{
|
{
|
||||||
await HandleFirmwareInstallation(result[0].Path.LocalPath);
|
await HandleFirmwareInstallation(result.Value.Path.LocalPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InstallFirmwareFromFolder()
|
public async Task InstallFirmwareFromFolder()
|
||||||
{
|
{
|
||||||
IReadOnlyList<IStorageFolder> result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync();
|
||||||
{
|
|
||||||
AllowMultiple = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.HasValue)
|
||||||
{
|
{
|
||||||
await HandleFirmwareInstallation(result[0].Path.LocalPath);
|
await HandleFirmwareInstallation(result.Value.Path.LocalPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InstallKeysFromFile()
|
public async Task InstallKeysFromFile()
|
||||||
{
|
{
|
||||||
IReadOnlyList<IStorageFile> result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
|
||||||
{
|
{
|
||||||
AllowMultiple = false,
|
|
||||||
FileTypeFilter = new List<FilePickerFileType>
|
FileTypeFilter = new List<FilePickerFileType>
|
||||||
{
|
{
|
||||||
new(LocaleManager.Instance[LocaleKeys.FileDialogAllTypes])
|
|
||||||
{
|
|
||||||
Patterns = ["*.keys", "*.zip"],
|
|
||||||
AppleUniformTypeIdentifiers = ["com.ryujinx.xci", "public.zip-archive"],
|
|
||||||
MimeTypes = ["application/keys", "application/zip"],
|
|
||||||
},
|
|
||||||
new("KEYS")
|
new("KEYS")
|
||||||
{
|
{
|
||||||
Patterns = ["*.keys"],
|
Patterns = ["*.keys"],
|
||||||
AppleUniformTypeIdentifiers = ["com.ryujinx.xci"],
|
AppleUniformTypeIdentifiers = ["com.ryujinx.xci"],
|
||||||
MimeTypes = ["application/keys"],
|
MimeTypes = ["application/keys"],
|
||||||
},
|
},
|
||||||
new("ZIP")
|
|
||||||
{
|
|
||||||
Patterns = ["*.zip"],
|
|
||||||
AppleUniformTypeIdentifiers = ["public.zip-archive"],
|
|
||||||
MimeTypes = ["application/zip"],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.HasValue)
|
||||||
{
|
{
|
||||||
await HandleKeysInstallation(result[0].Path.LocalPath);
|
await HandleKeysInstallation(result.Value.Path.LocalPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InstallKeysFromFolder()
|
public async Task InstallKeysFromFolder()
|
||||||
{
|
{
|
||||||
IReadOnlyList<IStorageFolder> result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync();
|
||||||
{
|
|
||||||
AllowMultiple = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.HasValue)
|
||||||
{
|
{
|
||||||
await HandleKeysInstallation(result[0].Path.LocalPath);
|
await HandleKeysInstallation(result.Value.Path.LocalPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1503,10 +1457,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public async Task OpenFile()
|
public async Task OpenFile()
|
||||||
{
|
{
|
||||||
IReadOnlyList<IStorageFile> result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.LoadApplicationFromFileDialogTitle],
|
Title = LocaleManager.Instance[LocaleKeys.LoadApplicationFromFileDialogTitle],
|
||||||
AllowMultiple = false,
|
|
||||||
FileTypeFilter = new List<FilePickerFileType>
|
FileTypeFilter = new List<FilePickerFileType>
|
||||||
{
|
{
|
||||||
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
|
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
|
||||||
@@ -1562,9 +1515,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.HasValue)
|
||||||
{
|
{
|
||||||
if (ApplicationLibrary.TryGetApplicationsFromFile(result[0].Path.LocalPath,
|
if (ApplicationLibrary.TryGetApplicationsFromFile(result.Value.Path.LocalPath,
|
||||||
out List<ApplicationData> applications))
|
out List<ApplicationData> applications))
|
||||||
{
|
{
|
||||||
await LoadApplication(applications[0]);
|
await LoadApplication(applications[0]);
|
||||||
@@ -1596,18 +1549,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public async Task OpenFolder()
|
public async Task OpenFolder()
|
||||||
{
|
{
|
||||||
IReadOnlyList<IStorageFolder> result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.LoadUnpackedGameFromFolderDialogTitle],
|
Title = LocaleManager.Instance[LocaleKeys.LoadUnpackedGameFromFolderDialogTitle]
|
||||||
AllowMultiple = false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.TryGet(out IStorageFolder value))
|
||||||
{
|
{
|
||||||
ApplicationData applicationData = new()
|
ApplicationData applicationData = new()
|
||||||
{
|
{
|
||||||
Name = Path.GetFileNameWithoutExtension(result[0].Path.LocalPath),
|
Name = Path.GetFileNameWithoutExtension(value.Path.LocalPath),
|
||||||
Path = result[0].Path.LocalPath,
|
Path = value.Path.LocalPath,
|
||||||
};
|
};
|
||||||
|
|
||||||
await LoadApplication(applicationData);
|
await LoadApplication(applicationData);
|
||||||
@@ -1812,10 +1764,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
if (AppHost.Device.System.SearchingForAmiibo(out _) && IsGameRunning)
|
if (AppHost.Device.System.SearchingForAmiibo(out _) && IsGameRunning)
|
||||||
{
|
{
|
||||||
IReadOnlyList<IStorageFile> result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle],
|
Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle],
|
||||||
AllowMultiple = false,
|
|
||||||
FileTypeFilter = new List<FilePickerFileType>
|
FileTypeFilter = new List<FilePickerFileType>
|
||||||
{
|
{
|
||||||
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
|
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
|
||||||
@@ -1824,9 +1775,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (result.Count > 0)
|
|
||||||
|
if (result.HasValue)
|
||||||
{
|
{
|
||||||
AppHost.Device.System.ScanAmiiboFromBin(result[0].Path.LocalPath);
|
AppHost.Device.System.ScanAmiiboFromBin(result.Value.Path.LocalPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -92,14 +92,14 @@
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Padding="0"
|
Padding="0"
|
||||||
Command="{Binding ToggleStartGamesWithoutUI}"
|
Command="{Binding ToggleStartGamesWithoutUi}"
|
||||||
Header="{ext:Locale MenuBarOptionsStartGamesWithoutUI}"
|
Header="{ext:Locale MenuBarOptionsStartGamesWithoutUI}"
|
||||||
Classes="withCheckbox">
|
Classes="withCheckbox">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<CheckBox
|
<CheckBox
|
||||||
MinWidth="{DynamicResource CheckBoxSize}"
|
MinWidth="{DynamicResource CheckBoxSize}"
|
||||||
MinHeight="{DynamicResource CheckBoxSize}"
|
MinHeight="{DynamicResource CheckBoxSize}"
|
||||||
IsChecked="{Binding StartGamesWithoutUI, Mode=TwoWay}"
|
IsChecked="{Binding StartGamesWithoutUi, Mode=TwoWay}"
|
||||||
Padding="0" />
|
Padding="0" />
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
Reference in New Issue
Block a user