Compare commits

..

2 Commits

Author SHA1 Message Date
ParantezTech c082c23552 [libc] use C locale for printf 2026-07-14 16:55:32 +03:00
ParantezTech 14fe861ef0 [agc] add resource registration 2026-07-14 16:55:25 +03:00
11 changed files with 23 additions and 1001 deletions
@@ -881,7 +881,7 @@ public sealed partial class DirectExecutionBackend
"Q2V+iqvjgC0" or // vsnprintf
"j4ViWNHEgww" or // strlen
"5jNubw4vlAA" or // strnlen
"LHMrG7e8G78" or // wcsmisc
"LHMrG7e8G78" or // wcslen
"WkkeywLJcgU" or // wcslen
"Ovb2dSJOAuE" or // strcmp
"aesyjrHVWy4" or // strncmp
+15 -39
View File
@@ -45,13 +45,7 @@ public static class Ps5ParamJsonReader
try
{
ReadOnlyMemory<byte> json = data;
if (json.Span.StartsWith("\uFEFF"u8))
{
json = json[3..];
}
using var doc = JsonDocument.Parse(json);
using var doc = JsonDocument.Parse(data);
return TryReadPs5Param(doc.RootElement);
}
catch (JsonException)
@@ -62,15 +56,12 @@ public static class Ps5ParamJsonReader
private static (string? Title, string? TitleId, string? Version) TryReadPs5Param(JsonElement root)
{
if (root.ValueKind != JsonValueKind.Object)
return (null, null, null);
var titleId = GetString(root, "titleId");
string? titleId = root.TryGetProperty("titleId", out var eTid) ? eTid.GetString() : null;
string? ver =
GetString(root, "contentVersion")
?? GetString(root, "masterVersion")
?? GetString(root, "targetContentVersion");
(root.TryGetProperty("contentVersion", out var cv) ? cv.GetString() : null)
?? (root.TryGetProperty("masterVersion", out var mv) ? mv.GetString() : null)
?? (root.TryGetProperty("targetContentVersion", out var tv) ? tv.GetString() : null);
string? title = ExtractTitleName(root);
@@ -79,49 +70,34 @@ public static class Ps5ParamJsonReader
private static string? ExtractTitleName(JsonElement root)
{
if ((!root.TryGetProperty("localizedParameters", out var lp) || lp.ValueKind != JsonValueKind.Object) &&
root.TryGetProperty("disc", out var disc) && disc.ValueKind == JsonValueKind.Object)
if (!root.TryGetProperty("localizedParameters", out var lp))
{
disc.TryGetProperty("localizedParameters", out lp);
if (root.TryGetProperty("disc", out var disc) && disc.ValueKind == JsonValueKind.Object)
{
disc.TryGetProperty("localizedParameters", out lp);
}
}
if (lp.ValueKind != JsonValueKind.Object)
return null;
var defLang = GetString(lp, "defaultLanguage");
string? defLang = lp.TryGetProperty("defaultLanguage", out var dl) ? dl.GetString() : null;
if (!string.IsNullOrEmpty(defLang))
{
if (lp.TryGetProperty(defLang, out var langObj) && langObj.ValueKind == JsonValueKind.Object)
{
var title = GetString(langObj, "titleName");
if (!string.IsNullOrWhiteSpace(title))
return title;
if (langObj.TryGetProperty("titleName", out var tn))
return tn.GetString();
}
}
if (lp.TryGetProperty("en-US", out var en) && en.ValueKind == JsonValueKind.Object)
{
var title = GetString(en, "titleName");
if (!string.IsNullOrWhiteSpace(title))
return title;
}
foreach (var property in lp.EnumerateObject())
{
if (property.Value.ValueKind == JsonValueKind.Object)
{
var title = GetString(property.Value, "titleName");
if (!string.IsNullOrWhiteSpace(title))
return title;
}
if (en.TryGetProperty("titleName", out var tn2))
return tn2.GetString();
}
return null;
}
private static string? GetString(JsonElement parent, string propertyName) =>
parent.TryGetProperty(propertyName, out var value) && value.ValueKind == JsonValueKind.String
? value.GetString()
: null;
}
-129
View File
@@ -1,129 +0,0 @@
{
"_languageName": "العربية",
"Page.Library": "المكتبة",
"Page.Options": "الخيارات",
"Page.GameCount.One": "لعبة واحدة",
"Page.GameCount.Other": "{0} لعبة",
"Library.SearchWatermark": "ابحث في المكتبة...",
"Library.AddFolder": "+ إضافة مجلد",
"Library.Rescan": "⟳ إعادة الفحص",
"Library.OpenFile": "فتح ملف...",
"Library.Context.Launch": "تشغيل",
"Library.Context.OpenFolder": "فتح مجلد اللعبة",
"Library.Context.CopyPath": "نسخ المسار",
"Library.Context.CopyTitleId": "نسخ معرف العنوان",
"Library.Context.Remove": "إزالة من المكتبة",
"Library.Empty.Title": "مكتبتك فارغة",
"Library.Empty.Hint": "أضف مجلداً يحتوي على ألعابك للبدء.",
"Library.Empty.SearchTitle": "لا توجد ألعاب تطابق بحثك",
"Library.Empty.SearchHint": "لا شيء في المكتبة يطابق “{0}”.",
"Library.Empty.AddFolder": "+ إضافة مجلد ألعاب",
"Library.Loading": "جارٍ تحميل المكتبة...",
"Options.General": "عام",
"Options.Section.Emulation": "المحاكاة",
"Options.Section.Logging": "التسجيل",
"Options.Section.Launcher": "المُشغِّل",
"Options.CpuEngine.Label": "محرك المعالج",
"Options.CpuEngine.Desc": "محرك التنفيذ المستخدم لتشغيل كود اللعبة.",
"Options.CpuEngine.Native": "أصلي",
"Options.Strict.Label": "ربط صارم للمكتبات الديناميكية (dynlib)",
"Options.Strict.Desc": "إفشال التشغيل عندما يتعذر التعرف على رمز مستورد.",
"Options.LogLevel.Label": "مستوى التسجيل",
"Options.LogLevel.Desc": "مدى تفصيل مخرجات نافذة سجلات المحاكي.",
"Options.LogLevel.Trace": "تتبع",
"Options.LogLevel.Debug": "تصحيح الأخطاء",
"Options.LogLevel.Info": "معلومات",
"Options.LogLevel.Warning": "تحذير",
"Options.LogLevel.Error": "خطأ",
"Options.LogLevel.Critical": "حرج",
"Options.TraceImports.Label": "حد تتبع الاستيراد",
"Options.TraceImports.Desc": "تتبع أول N استيراد لكل وحدة (0 = إيقاف).",
"Options.LogToFile.Label": "التسجيل في ملف",
"Options.LogToFile.Desc": "نسخ مخرجات المحاكي إلى ملف سجل.",
"Options.LogFilePath.Label": "مسار ملف السجل",
"Options.LogFilePath.Default": "لا يوجد مسار مخصص — تُحفظ السجلات في user/logs بجوار المحاكي.",
"Options.LogFilePath.Select": "تحديد...",
"Options.OverrideLogFile.Label": "الكتابة فوق ملف السجل",
"Options.OverrideLogFile.Desc": "استخدام مسار الملف الدقيق بدلاً من إلحاق معرف العنوان والطابع الزمني.",
"Options.TitleMusic.Label": "موسيقى اللعبة",
"Options.TitleMusic.Desc": "تكرار موسيقى المعاينة للعبة المحددة في المكتبة.",
"Options.Discord.Label": "حالة دسكورد",
"Options.Discord.Desc": "إظهار اللعبة قيد التشغيل في ملفك الشخصي على دسكورد.",
"Options.Language.Label": "لغة المحاكي",
"Options.Language.Desc": "اللغة المستخدمة في جميع أنحاء المشغل. تُطبق فوراً.",
"Common.On": "تشغيل",
"Common.Off": "إيقاف",
"Console.Title": "نافذة السجلات",
"Console.SearchWatermark": "بحث...",
"Console.AutoScroll": "تمرير تلقائي",
"Console.Split": "تقسيم",
"Console.Copy": "نسخ",
"Console.Clear": "مسح",
"Console.WindowTitle": "نافذة سجلات SharpEmu",
"Launch.NoGameSelected": "لم تُحدد أي لعبة",
"Launch.NoGameHint": "اختر لعبة من المكتبة، أو افتح ملف eboot.bin مباشرة.",
"Launch.Idle": "خامل",
"Launch.Console": "≡ نافذة السجلات",
"Launch.Launch": "▶ تشغيل",
"Launch.Stop": "■ إيقاف",
"Launch.Running": "قيد التشغيل — {0}",
"Launch.Stopping": "جارٍ الإيقاف...",
"Launch.Exited": "انتهى برمز {0} ({1})",
"Launch.ExeNotFound": "لم يُعثر على الملف التنفيذي SharpEmu. ابنِ مشروع SharpEmu.CLI أولاً (dotnet build).",
"Launch.LogFile": "ملف السجل: {0}",
"Launch.Command": "$ SharpEmu {0}",
"Launch.StartFailed": "فشل بدء تشغيل المحاكي: {0}",
"Launch.ProcessExited": "انتهت العملية برمز {0} ({1}).",
"Exit.Ok": "موافق",
"Exit.InvalidArguments": "معطيات غير صالحة",
"Exit.EbootNotFound": "لم يُعثر على eboot",
"Exit.RuntimeException": "استثناء وقت التشغيل",
"Exit.EmulationError": "خطأ في المحاكاة",
"Exit.Unknown": "غير معروف",
"Status.EmulatorLocating": "المحاكي: جارٍ تحديد الموقع...",
"Status.EmulatorPath": "المحاكي: {0}",
"Status.EmulatorNotFound": "المحاكي: لم يُعثر على الملف التنفيذي SharpEmu — ابنِ SharpEmu.CLI أولاً.",
"Status.ScanningLibrary": "جارٍ فحص المكتبة...",
"Status.AddFolderPrompt": "أضف مجلد ألعاب لملء المكتبة.",
"Status.LibraryScanned": "فُحصت المكتبة: {0} لعبة في {1} مجلد.",
"Status.CouldNotOpenFolder": "تعذر فتح المجلد: {0}",
"Status.CopiedToClipboard": "نُسخ {0} إلى الحافظة.",
"Status.RemovedFromLibrary": "أُزيل “{0}” من المكتبة. أعد إضافة مجلده لاستعادته.",
"Status.Running": "جارٍ تشغيل {0}",
"Status.Stopping": "جارٍ الإيقاف...",
"Status.Idle": "خامل",
"Clipboard.Path": "المسار",
"Clipboard.TitleId": "معرف العنوان",
"Discord.Playing": "يلعب {0}",
"Discord.Browsing": "يتصفح المكتبة",
"Dialog.ChooseGameFolder": "اختر مجلداً يحتوي على ألعاب",
"Dialog.OpenExecutable": "افتح ملفاً تنفيذياً لتشغيله",
"Dialog.PsExecutables": "ملفات PS التنفيذية",
"Dialog.SaveLogFile": "حدد مكان حفظ ملف السجل",
"Dialog.PlainTextFiles": "ملفات نصية عادية",
"Dialog.LogFiles": "ملفات السجل"
}
-129
View File
@@ -1,129 +0,0 @@
{
"_languageName": "Deutsch",
"Page.Library": "Bibliothek",
"Page.Options": "Optionen",
"Page.GameCount.One": "1 Spiel",
"Page.GameCount.Other": "{0} Spiele",
"Library.SearchWatermark": "Bibliothek durchsuchen…",
"Library.AddFolder": " Spielordner hinzufügen",
"Library.Rescan": "⟳ Neu scannen",
"Library.OpenFile": "Datei öffnen…",
"Library.Context.Launch": "Starten",
"Library.Context.OpenFolder": "Spielordner öffnen",
"Library.Context.CopyPath": "Pfad kopieren",
"Library.Context.CopyTitleId": "Title ID kopieren",
"Library.Context.Remove": "Aus Bibliothek entfernen",
"Library.Empty.Title": "Deine Bibliothek ist leer",
"Library.Empty.Hint": "Füge einen Ordner mit deinen Spielen hinzu, um zu beginnen.",
"Library.Empty.SearchTitle": "Keine Spiele gefunden",
"Library.Empty.SearchHint": "Nichts in der Bibliothek entspricht "{0}".",
"Library.Empty.AddFolder": " Spielordner hinzufügen",
"Library.Loading": "Bibliothek wird geladen…",
"Options.General": "Allgemein",
"Options.Section.Emulation": "EMULATION",
"Options.Section.Logging": "PROTOKOLLIERUNG",
"Options.Section.Launcher": "LAUNCHER",
"Options.CpuEngine.Label": "CPU-Engine",
"Options.CpuEngine.Desc": "Ausführungs-Engine, die zum Ausführen des Spiel-Codes verwendet wird.",
"Options.CpuEngine.Native": "Nativ",
"Options.Strict.Label": "Strikte dynlib-Auflösung",
"Options.Strict.Desc": "Starten abbrechen, wenn ein importiertes Symbol nicht aufgelöst werden kann.",
"Options.LogLevel.Label": "Protokollstufe",
"Options.LogLevel.Desc": "Ausführlichkeit der Emulator-Konsolenausgabe.",
"Options.LogLevel.Trace": "Trace",
"Options.LogLevel.Debug": "Debug",
"Options.LogLevel.Info": "Info",
"Options.LogLevel.Warning": "Warnung",
"Options.LogLevel.Error": "Fehler",
"Options.LogLevel.Critical": "Kritisch",
"Options.TraceImports.Label": "Import-Trace-Limit",
"Options.TraceImports.Desc": "Die ersten N Imports pro Modul verfolgen (0 = aus).",
"Options.LogToFile.Label": "In Datei protokollieren",
"Options.LogToFile.Desc": "Emulator-Ausgabe zusätzlich in eine Log-Datei schreiben.",
"Options.LogFilePath.Label": "Protokolldatei-Pfad",
"Options.LogFilePath.Default": "Kein benutzerdefinierter Pfad Logs werden im Ordner user/logs neben dem Emulator gespeichert.",
"Options.LogFilePath.Select": "Auswählen…",
"Options.OverrideLogFile.Label": "Protokolldatei überschreiben",
"Options.OverrideLogFile.Desc": "Genauen Dateipfad verwenden, statt Title-ID und Zeitstempel anzuhängen.",
"Options.TitleMusic.Label": "Titel-Musik",
"Options.TitleMusic.Desc": "Die Vorschau-Musik des ausgewählten Spiels in der Bibliothek loopend abspielen.",
"Options.Discord.Label": "Discord-Präsenz",
"Options.Discord.Desc": "Zeigt das aktuell gespielte Spiel in deinem Discord-Profil an.",
"Options.Language.Label": "Emulator-Sprache",
"Options.Language.Desc": "Sprache der Benutzeroberfläche. Wird sofort angewendet.",
"Common.On": "An",
"Common.Off": "Aus",
"Console.Title": "KONSOLE",
"Console.SearchWatermark": "Suchen...",
"Console.AutoScroll": "Auto-Scroll",
"Console.Split": "Teilen",
"Console.Copy": "Kopieren",
"Console.Clear": "Leeren",
"Console.WindowTitle": "SharpEmu Konsole",
"Launch.NoGameSelected": "Kein Spiel ausgewählt",
"Launch.NoGameHint": "Wähle ein Spiel aus der Bibliothek aus oder öffne eine eboot.bin direkt.",
"Launch.Idle": "Bereit",
"Launch.Console": "≡ Konsole",
"Launch.Launch": "▶ Starten",
"Launch.Stop": "■ Stoppen",
"Launch.Running": "Läuft -- {0}",
"Launch.Stopping": "Wird beendet…",
"Launch.Exited": "Beendet mit Code {0} ({1})",
"Launch.ExeNotFound": "SharpEmu-Executable wurde nicht gefunden. Baue zuerst das SharpEmu.CLI-Projekt (dotnet build).",
"Launch.LogFile": "Log-Datei: {0}",
"Launch.Command": "$ SharpEmu {0}",
"Launch.StartFailed": "Emulator konnte nicht gestartet werden: {0}",
"Launch.ProcessExited": "Prozess wurde mit Code {0} ({1}) beendet.",
"Exit.Ok": "OK",
"Exit.InvalidArguments": "Ungültige Argumente",
"Exit.EbootNotFound": "eboot nicht gefunden",
"Exit.RuntimeException": "Laufzeitfehler",
"Exit.EmulationError": "Emulationsfehler",
"Exit.Unknown": "unbekannt",
"Status.EmulatorLocating": "Emulator: wird gesucht…",
"Status.EmulatorPath": "Emulator: {0}",
"Status.EmulatorNotFound": "Emulator: SharpEmu-Executable nicht gefunden -- baue zuerst SharpEmu.CLI.",
"Status.ScanningLibrary": "Bibliothek wird gescannt…",
"Status.AddFolderPrompt": "Füge einen Spielordner hinzu, um die Bibliothek zu füllen.",
"Status.LibraryScanned": "Bibliothek gescannt: {0} Spiel(e) in {1} Ordner(n).",
"Status.CouldNotOpenFolder": "Ordner konnte nicht geöffnet werden: {0}",
"Status.CopiedToClipboard": "{0} in die Zwischenablage kopiert.",
"Status.RemovedFromLibrary": ""{0}" wurde aus der Bibliothek entfernt. Füge den Ordner erneut hinzu, um es wiederherzustellen.",
"Status.Running": "Läuft {0}",
"Status.Stopping": "Wird gestoppt…",
"Status.Idle": "Bereit",
"Clipboard.Path": "Pfad",
"Clipboard.TitleId": "Title ID",
"Discord.Playing": "Spielt {0}",
"Discord.Browsing": "Durchsucht die Bibliothek",
"Dialog.ChooseGameFolder": "Spielordner auswählen",
"Dialog.OpenExecutable": "Ausführbare Datei zum Starten öffnen",
"Dialog.PsExecutables": "PS-Ausführbare Dateien",
"Dialog.SaveLogFile": "Protokolldatei speichern unter",
"Dialog.PlainTextFiles": "Textdateien",
"Dialog.LogFiles": "Protokolldateien"
}
-129
View File
@@ -1,129 +0,0 @@
{
"_languageName": "Dansk",
"Page.Library": "Bibliotek",
"Page.Options": "Indstillinger",
"Page.GameCount.One": "1 spil",
"Page.GameCount.Other": "{0} spil",
"Library.SearchWatermark": "Søg i biblioteket…",
"Library.AddFolder": " Tilføj mappe",
"Library.Rescan": "⟳ Genindlæs",
"Library.OpenFile": "Åbn fil…",
"Library.Context.Launch": "Start",
"Library.Context.OpenFolder": "Åbn spilmappe",
"Library.Context.CopyPath": "Kopiér sti",
"Library.Context.CopyTitleId": "Kopiér titel-ID",
"Library.Context.Remove": "Fjern fra bibliotek",
"Library.Empty.Title": "Dit bibliotek er tomt",
"Library.Empty.Hint": "Tilføj en mappe med dine spil for at komme i gang.",
"Library.Empty.SearchTitle": "Ingen spil matcher din søgning",
"Library.Empty.SearchHint": "Intet i biblioteket matcher “{0}”.",
"Library.Empty.AddFolder": " Tilføj spilmappe",
"Library.Loading": "Indlæser bibliotek…",
"Options.General": "Generelt",
"Options.Section.Emulation": "EMULERING",
"Options.Section.Logging": "LOGGING",
"Options.Section.Launcher": "LAUNCHER",
"Options.CpuEngine.Label": "CPU-engine",
"Options.CpuEngine.Desc": "Den eksekveringsengine der bruges til at køre spilkode.",
"Options.CpuEngine.Native": "Native",
"Options.Strict.Label": "Streng dynlib-opløsning",
"Options.Strict.Desc": "Afbryd opstarten, når et importeret symbol ikke kan findes.",
"Options.LogLevel.Label": "Logniveau",
"Options.LogLevel.Desc": "Detaljeringsgrad for emulatorens konsoloutput.",
"Options.LogLevel.Trace": "Trace",
"Options.LogLevel.Debug": "Debug",
"Options.LogLevel.Info": "Info",
"Options.LogLevel.Warning": "Advarsel",
"Options.LogLevel.Error": "Fejl",
"Options.LogLevel.Critical": "Kritisk",
"Options.TraceImports.Label": "Grænse for import-trace",
"Options.TraceImports.Desc": "Spor de første N imports pr. modul (0 = fra).",
"Options.LogToFile.Label": "Log til fil",
"Options.LogToFile.Desc": "Spejl emulatorens output til en logfil.",
"Options.LogFilePath.Label": "Sti til logfil",
"Options.LogFilePath.Default": "Ingen brugerdefineret sti — logs gemmes i user/logs ved siden af emulatoren.",
"Options.LogFilePath.Select": "Vælg…",
"Options.OverrideLogFile.Label": "Tilsidesæt logfil",
"Options.OverrideLogFile.Desc": "Brug den præcise filsti i stedet for at tilføje titel-ID og tidsstempel.",
"Options.TitleMusic.Label": "Titelmusik",
"Options.TitleMusic.Desc": "Gentag det valgte spils forhåndsvisningsmusik i biblioteket.",
"Options.Discord.Label": "Discord-tilstedeværelse",
"Options.Discord.Desc": "Vis det kørende spil på din Discord-profil.",
"Options.Language.Label": "Emulatorsprog",
"Options.Language.Desc": "Sprog der bruges i hele launcheren. Anvendes med det samme.",
"Common.On": "Til",
"Common.Off": "Fra",
"Console.Title": "KONSOL",
"Console.SearchWatermark": "Søg...",
"Console.AutoScroll": "Auto-scroll",
"Console.Split": "Opdel",
"Console.Copy": "Kopiér",
"Console.Clear": "Ryd",
"Console.WindowTitle": "SharpEmu-konsol",
"Launch.NoGameSelected": "Intet spil valgt",
"Launch.NoGameHint": "Vælg et spil fra biblioteket, eller åbn en eboot.bin direkte.",
"Launch.Idle": "Inaktiv",
"Launch.Console": "≡ Konsol",
"Launch.Launch": "▶ Start",
"Launch.Stop": "■ Stop",
"Launch.Running": "Kører — {0}",
"Launch.Stopping": "Stopper…",
"Launch.Exited": "Afsluttet med kode {0} ({1})",
"Launch.ExeNotFound": "SharpEmu-programmet blev ikke fundet. Byg SharpEmu.CLI-projektet først (dotnet build).",
"Launch.LogFile": "Logfil: {0}",
"Launch.Command": "$ SharpEmu {0}",
"Launch.StartFailed": "Kunne ikke starte emulatoren: {0}",
"Launch.ProcessExited": "Processen afsluttede med kode {0} ({1}).",
"Exit.Ok": "OK",
"Exit.InvalidArguments": "ugyldige argumenter",
"Exit.EbootNotFound": "eboot ikke fundet",
"Exit.RuntimeException": "runtime-fejl",
"Exit.EmulationError": "emuleringsfejl",
"Exit.Unknown": "ukendt",
"Status.EmulatorLocating": "Emulator: lokaliserer…",
"Status.EmulatorPath": "Emulator: {0}",
"Status.EmulatorNotFound": "Emulator: SharpEmu-programmet blev ikke fundet — byg SharpEmu.CLI først.",
"Status.ScanningLibrary": "Skanner bibliotek…",
"Status.AddFolderPrompt": "Tilføj en spilmappe for at udfylde biblioteket.",
"Status.LibraryScanned": "Bibliotek skannet: {0} spil i {1} mappe(r).",
"Status.CouldNotOpenFolder": "Kunne ikke åbne mappe: {0}",
"Status.CopiedToClipboard": "{0} kopieret til udklipsholderen.",
"Status.RemovedFromLibrary": "“{0}” fjernet fra biblioteket. Tilføj mappen igen for at gendanne det.",
"Status.Running": "Kører {0}",
"Status.Stopping": "Stopper…",
"Status.Idle": "Inaktiv",
"Clipboard.Path": "Sti",
"Clipboard.TitleId": "Titel-ID",
"Discord.Playing": "Spiller {0}",
"Discord.Browsing": "Gennemser biblioteket",
"Dialog.ChooseGameFolder": "Vælg en mappe der indeholder spil",
"Dialog.OpenExecutable": "Åbn et program der skal startes",
"Dialog.PsExecutables": "PS-programmer",
"Dialog.SaveLogFile": "Vælg hvor logfilen skal gemmes",
"Dialog.PlainTextFiles": "Almindelige tekstfiler",
"Dialog.LogFiles": "Logfiler"
}
@@ -396,14 +396,6 @@ internal static partial class Gen5SpirvTranslator
_module.Constant64(_ulongType, 32)));
break;
}
case "VBcntU32B32":
result = IAdd(
_module.AddInstruction(
SpirvOp.BitCount,
_uintType,
GetRawSource(instruction, 0)),
GetRawSource(instruction, 1));
break;
case "VMadU32U24":
{
var left = BitwiseAnd(
@@ -601,18 +593,6 @@ internal static partial class Gen5SpirvTranslator
Ext(38, _uintType, high, right));
break;
}
case "VSadU32":
{
var left = GetRawSource(instruction, 0);
var right = GetRawSource(instruction, 1);
var difference = _module.AddInstruction(
SpirvOp.ISub,
_uintType,
Ext(41, _uintType, left, right),
Ext(38, _uintType, left, right));
result = IAdd(difference, GetRawSource(instruction, 2));
break;
}
case "VMed3I32":
{
var left = Bitcast(_intType, GetRawSource(instruction, 0));
@@ -722,14 +702,6 @@ internal static partial class Gen5SpirvTranslator
result = Ext(58, _uintType, vector);
break;
}
case "VCvtPkU16U32":
case "VCvtPkI16I32":
result = BitwiseOr(
BitwiseAnd(GetRawSource(instruction, 0), UInt(0xFFFF)),
ShiftLeftLogical(
BitwiseAnd(GetRawSource(instruction, 1), UInt(0xFFFF)),
UInt(16)));
break;
default:
error = $"unsupported vector opcode {instruction.Opcode}";
return false;
@@ -1331,43 +1303,6 @@ internal static partial class Gen5SpirvTranslator
Store(_scc, IsNotZero(result));
break;
}
case "SAbsdiffI32":
{
var wideLeft = _module.AddInstruction(
SpirvOp.SConvert,
_longType,
Bitcast(_intType, left));
var wideRight = _module.AddInstruction(
SpirvOp.SConvert,
_longType,
Bitcast(_intType, right));
var difference = _module.AddInstruction(
SpirvOp.ISub,
_longType,
wideLeft,
wideRight);
result = _module.AddInstruction(
SpirvOp.UConvert,
_uintType,
Ext(5, _longType, difference));
Store(_scc, IsNotZero(result));
break;
}
case "SPackLlB32B16":
result = BitwiseOr(
BitwiseAnd(left, UInt(0xFFFF)),
ShiftLeftLogical(right, UInt(16)));
break;
case "SPackLhB32B16":
result = BitwiseOr(
BitwiseAnd(left, UInt(0xFFFF)),
BitwiseAnd(right, UInt(0xFFFF0000)));
break;
case "SPackHhB32B16":
result = BitwiseOr(
ShiftRightLogical(left, UInt(16)),
BitwiseAnd(right, UInt(0xFFFF0000)));
break;
case "SCselectB32":
result = _module.AddInstruction(
SpirvOp.Select,
+4 -4
View File
@@ -80,7 +80,7 @@ public static class JsonExports
[SysAbiExport(
Nid = "WSOuge5IsCg",
ExportName = "_ZN3sce4Json14InitParameter2C1Ev",
ExportName = "_ZN3sce4Json15InitParameter2C2Ev",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceJson2")]
public static int InitParameter2Constructor(CpuContext ctx)
@@ -93,7 +93,7 @@ public static class JsonExports
[SysAbiExport(
Nid = "I2QC8PYhJWY",
ExportName = "_ZN3sce4Json14InitParameter212setAllocatorEPNS0_12MemAllocatorEPv",
ExportName = "_ZN3sce4Json15InitParameter212setAllocatorERNS0_12MemAllocatorE",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceJson2")]
public static int InitParameter2SetAllocator(CpuContext ctx)
@@ -106,7 +106,7 @@ public static class JsonExports
[SysAbiExport(
Nid = "Eu95jmqn5Rw",
ExportName = "_ZN3sce4Json14InitParameter217setFileBufferSizeEm",
ExportName = "_ZN3sce4Json15InitParameter217setFileBufferSizeEm",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceJson2")]
public static int InitParameter2SetFileBufferSize(CpuContext ctx)
@@ -119,7 +119,7 @@ public static class JsonExports
[SysAbiExport(
Nid = "IXW-z8pggfg",
ExportName = "_ZN3sce4Json11Initializer10initializeEPKNS0_14InitParameter2E",
ExportName = "_ZN3sce4Json12Initializer2C1Ev",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceJson2")]
public static int Initializer2Constructor(CpuContext ctx)
@@ -478,7 +478,7 @@ public static class KernelMemoryCompatExports
[SysAbiExport(
Nid = "LHMrG7e8G78",
ExportName = "wcsmisc",
ExportName = "wcslen",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libc")]
public static int Wcslen(CpuContext ctx)
@@ -1488,214 +1488,6 @@ public static class KernelMemoryCompatExports
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "ob5xAW4ln-0",
ExportName = "strchr",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libc")]
public static int Strchr(CpuContext ctx)
{
var address = ctx[CpuRegister.Rdi];
var needle = unchecked((byte)ctx[CpuRegister.Rsi]);
if (address == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
// The terminator counts as part of the scanned range, so strchr(s, '\0')
// returns a pointer to the string's null byte just like a native libc.
Span<byte> current = stackalloc byte[1];
for (ulong index = 0; index < 1_048_576; index++)
{
if (!TryReadCompat(ctx, address + index, current))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
if (current[0] == needle)
{
ctx[CpuRegister.Rax] = address + index;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
if (current[0] == 0)
{
break;
}
}
ctx[CpuRegister.Rax] = 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "9yDWMxEFdJU",
ExportName = "strrchr",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libc")]
public static int Strrchr(CpuContext ctx)
{
var address = ctx[CpuRegister.Rdi];
var needle = unchecked((byte)ctx[CpuRegister.Rsi]);
if (address == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
ulong match = 0;
var found = false;
Span<byte> current = stackalloc byte[1];
for (ulong index = 0; index < 1_048_576; index++)
{
if (!TryReadCompat(ctx, address + index, current))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
if (current[0] == needle)
{
match = address + index;
found = true;
}
if (current[0] == 0)
{
break;
}
}
ctx[CpuRegister.Rax] = found ? match : 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "8u8lPzUEq+U",
ExportName = "memchr",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libc")]
public static int Memchr(CpuContext ctx)
{
var address = ctx[CpuRegister.Rdi];
var needle = unchecked((byte)ctx[CpuRegister.Rsi]);
var count = ctx[CpuRegister.Rdx];
Span<byte> current = stackalloc byte[1];
for (ulong index = 0; index < count; index++)
{
if (!TryReadCompat(ctx, address + index, current))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
if (current[0] == needle)
{
ctx[CpuRegister.Rax] = address + index;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
}
ctx[CpuRegister.Rax] = 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "Ls4tzzhimqQ",
ExportName = "strcat",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libc")]
public static int Strcat(CpuContext ctx)
{
var destination = ctx[CpuRegister.Rdi];
var source = ctx[CpuRegister.Rsi];
if (!TryReadCString(ctx, source, 1_048_576, out var sourceBytes))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
if (!TryReadCString(ctx, destination, 1_048_576, out var destinationBytes))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
// Overwrite the destination terminator and re-terminate after the copied bytes.
var appendAddress = destination + (ulong)destinationBytes.Length;
var payload = new byte[sourceBytes.Length + 1];
sourceBytes.CopyTo(payload.AsSpan());
if (!TryWriteCompat(ctx, appendAddress, payload))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
ctx[CpuRegister.Rax] = destination;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "kHg45qPC6f0",
ExportName = "strncat",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libc")]
public static int Strncat(CpuContext ctx)
{
var destination = ctx[CpuRegister.Rdi];
var source = ctx[CpuRegister.Rsi];
var limit = ctx[CpuRegister.Rdx];
// Bounding the source read by the count yields strncat's "at most n bytes"
// semantics while still stopping early at the source terminator.
if (!TryReadCString(ctx, source, limit, out var sourceBytes))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
if (!TryReadCString(ctx, destination, 1_048_576, out var destinationBytes))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
var appendAddress = destination + (ulong)destinationBytes.Length;
var payload = new byte[sourceBytes.Length + 1];
sourceBytes.CopyTo(payload.AsSpan());
if (!TryWriteCompat(ctx, appendAddress, payload))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
ctx[CpuRegister.Rax] = destination;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "viiwFMaNamA",
ExportName = "strstr",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libc")]
public static int Strstr(CpuContext ctx)
{
var haystack = ctx[CpuRegister.Rdi];
var needle = ctx[CpuRegister.Rsi];
if (!TryReadCString(ctx, haystack, 1_048_576, out var haystackBytes))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
if (!TryReadCString(ctx, needle, 1_048_576, out var needleBytes))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
// An empty needle matches at the start of the haystack.
if (needleBytes.Length == 0)
{
ctx[CpuRegister.Rax] = haystack;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
var matchIndex = haystackBytes.AsSpan().IndexOf(needleBytes.AsSpan());
ctx[CpuRegister.Rax] = matchIndex >= 0 ? haystack + (ulong)matchIndex : 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "QrZZdJ8XsX0",
ExportName = "fputs",
@@ -3245,7 +3037,7 @@ public static class KernelMemoryCompatExports
[SysAbiExport(
Nid = "4h6F1LLbTiw",
ExportName = "sceKernelMapNamedFlexibleMemoryInternal",
ExportName = "sceKernelMapFlexibleMemoryInternal",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int KernelMapFlexibleMemoryInternal(CpuContext ctx)
+1 -1
View File
@@ -19,7 +19,7 @@ public static class LibcInternalExports
[SysAbiExport(
Nid = "NWtTN10cJzE",
ExportName = "sceLibcHeapGetTraceInfo",
ExportName = "LibcHeapGetTraceInfo",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "LibcInternalExt")]
public static int LibcHeapGetTraceInfo(CpuContext ctx)
-275
View File
@@ -1,275 +0,0 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Synthetic-shader conformance dumper.
//
// Feeds hand-assembled Gen5 (gfx10) instruction words through the real
// decode -> SPIR-V pipeline (Gen5ShaderTranslator / Gen5SpirvTranslator, via
// reflection so no emulator source changes are required) and writes the
// resulting vertex and compute SPIR-V blobs to disk. The blobs can then be
// checked with spirv-val / spirv-dis.
//
// Programs that contain buffer_store_dword automatically get a single
// global-memory binding covering every store, which the emitter exposes as
// guestBuffers[0] (descriptor set 0, binding 0).
//
// Each program carries an expectation: ExpectTranslate=true programs must
// decode and emit both stages; ExpectTranslate=false programs pin a decode
// failure that must stay loud. Any unexpected outcome makes the tool exit
// non-zero, so it can gate scripts/CI.
//
// Usage: SharpEmu.Tools.ShaderDump [output-directory]
using System.Buffers.Binary;
using System.Reflection;
using SharpEmu.HLE;
using SharpEmu.Libs.CxxAbi;
const ulong ProgramAddress = 0x100000;
(string Name, bool ExpectTranslate, uint[] Words)[] testPrograms =
[
("fmac", true, [
0x560A0501, // v_fmac_f32 v5, v1, v2
0x580A0501, 0x42280000, // v_fmamk_f32 v5, v1, 42.0, v2
0x5A0A0501, 0x42280000, // v_fmaak_f32 v5, v1, v2, 42.0
0xD52B0005, 0x00020501, // v_fmac_f32_e64 v5, v1, v2
0xBF810000, // s_endpgm
]),
("muls", true, [
0xD5690005, 0x00020501, // v_mul_lo_u32 v5, v1, v2
0xD56A0005, 0x00020501, // v_mul_hi_u32 v5, v1, v2
0xD56B0005, 0x00020501, // v_mul_lo_i32 v5, v1, v2
0xD56C0005, 0x00020501, // v_mul_hi_i32 v5, v1, v2
0xBF810000, // s_endpgm
]),
("sopp-hints", true, [
0xBFA10001, // s_clause 0x1
0xBFA30000, // s_waitcnt_depctr 0x0
0xBF810000, // s_endpgm
]),
// s_round_mode / s_denorm_mode write the FP MODE state and must keep
// failing decode loudly until their semantics are modeled (see #108);
// this program pins that behavior.
("sopp-mode", false, [
0xBFA40000, // s_round_mode 0x0
0xBFA50000, // s_denorm_mode 0x0
0xBF810000, // s_endpgm
]),
// Executable end-to-end test: compute with real ALU instructions, then
// buffer_store_dword results to guestBuffers[0] at offsets 0/4/8, prove
// that a store with EXEC=0 does not land (offset 12 stays sentinel), and
// that stores work again after EXEC is restored (offset 16).
("exec", true, [
0xBFA10001, // s_clause 0x1 (hint no-op in an executed program, needs #108)
0x7E0002FF, 0x3FC00000, // v_mov_b32 v0, 1.5f
0x7E0202FF, 0x40100000, // v_mov_b32 v1, 2.25f
0x7E0402FF, 0x41200000, // v_mov_b32 v2, 10.0f
0x56040300, // v_fmac_f32 v2, v0, v1 -> v2 = fma(1.5, 2.25, 10.0)
0x7E0602FF, 0x7FFFFFFF, // v_mov_b32 v3, 0x7FFFFFFF
0x7E0802FF, 0x00010003, // v_mov_b32 v4, 0x00010003
0xD56C0005, 0x00020903, // v_mul_hi_i32 v5, v3, v4
0xD56B0006, 0x00020903, // v_mul_lo_i32 v6, v3, v4
0xE0700000, 0x80020200, // buffer_store_dword v2, off, s[8:11], 0
0xE0700004, 0x80020500, // buffer_store_dword v5, off, s[8:11], 0 offset:4
0xE0700008, 0x80020600, // buffer_store_dword v6, off, s[8:11], 0 offset:8
0xBEFE0380, // s_mov_b32 exec_lo, 0 -> lane inactive
0xE070000C, 0x80020200, // buffer_store_dword v2, off, s[8:11], 0 offset:12 (masked, must not land)
0xBEFE03C1, // s_mov_b32 exec_lo, -1 -> lane active again
0xE0700010, 0x80020000, // buffer_store_dword v0, off, s[8:11], 0 offset:16
0xBF810000, // s_endpgm
]),
];
var assembly = typeof(CxaGuardExports).Assembly;
var shaderTranslator = assembly.GetType("SharpEmu.Libs.Agc.Gen5ShaderTranslator")
?? throw new InvalidOperationException("Gen5ShaderTranslator not found");
var spirvTranslator = assembly.GetType("SharpEmu.Libs.Agc.Gen5SpirvTranslator")
?? throw new InvalidOperationException("Gen5SpirvTranslator not found");
var describe = shaderTranslator.GetMethod(
"Describe",
BindingFlags.Public | BindingFlags.Static)
?? throw new InvalidOperationException("Gen5ShaderTranslator.Describe not found");
var tryDecode = shaderTranslator.GetMethod(
"TryDecodeProgram",
BindingFlags.NonPublic | BindingFlags.Static)
?? throw new InvalidOperationException("Gen5ShaderTranslator.TryDecodeProgram not found");
var stateType = assembly.GetType("SharpEmu.Libs.Agc.Gen5ShaderState")
?? throw new InvalidOperationException("Gen5ShaderState not found");
var evaluationType = assembly.GetType("SharpEmu.Libs.Agc.Gen5ShaderEvaluation")
?? throw new InvalidOperationException("Gen5ShaderEvaluation not found");
var imageBindingType = assembly.GetType("SharpEmu.Libs.Agc.Gen5ImageBinding")
?? throw new InvalidOperationException("Gen5ImageBinding not found");
var globalBindingType = assembly.GetType("SharpEmu.Libs.Agc.Gen5GlobalMemoryBinding")
?? throw new InvalidOperationException("Gen5GlobalMemoryBinding not found");
var tryCompile = spirvTranslator.GetMethod(
"TryCompileVertexShader",
BindingFlags.Public | BindingFlags.Static)
?? throw new InvalidOperationException("Gen5SpirvTranslator.TryCompileVertexShader not found");
var tryCompileCompute = spirvTranslator.GetMethod(
"TryCompileComputeShader",
BindingFlags.Public | BindingFlags.Static)
?? throw new InvalidOperationException("Gen5SpirvTranslator.TryCompileComputeShader not found");
var outputDirectory = args.Length > 0
? args[0]
: Path.Combine(AppContext.BaseDirectory, "spv");
Directory.CreateDirectory(outputDirectory);
var failures = 0;
foreach (var (name, expectTranslate, words) in testPrograms)
{
var memory = new FakeMemory();
memory.AddRegion(ProgramAddress, words);
var ctx = new CpuContext(memory, Generation.Gen5);
Console.WriteLine(
$"[{name}] decode: " +
(string)describe.Invoke(null, [ctx, ProgramAddress, ProgramAddress])!);
object?[] decodeArgs = [ctx, ProgramAddress, null, null];
if (!(bool)tryDecode.Invoke(null, decodeArgs)!)
{
if (expectTranslate)
{
failures++;
Console.WriteLine($"[{name}] FAILED: decode error ({decodeArgs[3]})");
}
else
{
Console.WriteLine($"[{name}] decode failed as expected ({decodeArgs[3]})");
}
continue;
}
if (!expectTranslate)
{
failures++;
Console.WriteLine(
$"[{name}] FAILED: decoded successfully but is pinned as a decode failure — " +
"if the new decode support is intentional, its semantics need verifying here first");
continue;
}
// Buffer stores need a global-memory binding; the emitter resolves them by
// instruction PC, so collect store PCs from the decoded program itself.
var programObj = decodeArgs[2]!;
var instructions = (System.Collections.IEnumerable)programObj
.GetType().GetProperty("Instructions")!.GetValue(programObj)!;
var storePcs = new List<uint>();
foreach (var instruction in instructions)
{
var op = (string)instruction.GetType().GetProperty("Opcode")!.GetValue(instruction)!;
if (op.StartsWith("BufferStore", StringComparison.Ordinal))
{
storePcs.Add((uint)instruction.GetType().GetProperty("Pc")!.GetValue(instruction)!);
}
}
// The binding's scalar base (8 -> s[8:11]) must match the srsrc field of
// the hand-assembled buffer_store words, and the 64-byte backing store
// must cover every hand-assembled store offset.
var globalBindings = Array.CreateInstance(globalBindingType, storePcs.Count > 0 ? 1 : 0);
if (storePcs.Count > 0)
{
globalBindings.SetValue(
Activator.CreateInstance(
globalBindingType,
8u,
0UL,
(IReadOnlyList<uint>)storePcs,
new byte[64]),
0);
}
var state = Activator.CreateInstance(
stateType,
programObj,
new uint[16],
null,
null,
0u)!;
var evaluation = Activator.CreateInstance(
evaluationType,
new uint[256],
new uint[256],
new Dictionary<uint, IReadOnlyList<uint>>(),
Array.CreateInstance(imageBindingType, 0),
globalBindings,
null,
null,
null)!;
object?[] compileArgs = [state, evaluation, null, null, 0, -1, 0];
if ((bool)tryCompile.Invoke(null, compileArgs)!)
{
var shader = compileArgs[2]!;
var spirv = (byte[])shader.GetType().GetProperty("Spirv")!.GetValue(shader)!;
var path = Path.Combine(outputDirectory, $"{name}.spv");
File.WriteAllBytes(path, spirv);
Console.WriteLine($"[{name}] emit: success, {spirv.Length} bytes -> {path}");
}
else
{
failures++;
Console.WriteLine($"[{name}] emit: FAILED ({compileArgs[3]})");
}
object?[] computeArgs = [state, evaluation, 1u, 1u, 1u, null, null];
if ((bool)tryCompileCompute.Invoke(null, computeArgs)!)
{
var shader = computeArgs[5]!;
var spirv = (byte[])shader.GetType().GetProperty("Spirv")!.GetValue(shader)!;
var path = Path.Combine(outputDirectory, $"{name}-cs.spv");
File.WriteAllBytes(path, spirv);
Console.WriteLine($"[{name}] compute emit: success, {spirv.Length} bytes -> {path}");
}
else
{
failures++;
Console.WriteLine($"[{name}] compute emit: FAILED ({computeArgs[6]})");
}
}
Console.WriteLine(failures == 0
? "RESULT: all programs behaved as expected"
: $"RESULT: {failures} unexpected outcome(s)");
Environment.ExitCode = failures == 0 ? 0 : 1;
internal sealed class FakeMemory : ICpuMemory
{
private readonly List<(ulong Base, byte[] Data)> _regions = [];
public void AddRegion(ulong baseAddress, uint[] words)
{
var bytes = new byte[words.Length * sizeof(uint)];
for (var index = 0; index < words.Length; index++)
{
BinaryPrimitives.WriteUInt32LittleEndian(
bytes.AsSpan(index * sizeof(uint)),
words[index]);
}
_regions.Add((baseAddress, bytes));
}
public bool TryRead(ulong virtualAddress, Span<byte> destination)
{
foreach (var (baseAddress, data) in _regions)
{
if (virtualAddress >= baseAddress &&
virtualAddress + (ulong)destination.Length <= baseAddress + (ulong)data.Length)
{
data.AsSpan(
(int)(virtualAddress - baseAddress),
destination.Length).CopyTo(destination);
return true;
}
}
return false;
}
public bool TryWrite(ulong virtualAddress, ReadOnlySpan<byte> source) => false;
}
@@ -1,19 +0,0 @@
<!--
Copyright (C) 2026 SharpEmu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later
-->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<!-- Standalone dev tool: opt out of the repo-wide lock-file requirement
so no packages.lock.json is generated or committed for it. -->
<RestorePackagesWithLockFile>false</RestorePackagesWithLockFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\SharpEmu.Libs\SharpEmu.Libs.csproj" />
</ItemGroup>
</Project>