From 2bb07a43640c3822a48f2bb27f9cf96790c5956f Mon Sep 17 00:00:00 2001 From: avan Date: Sat, 8 Aug 2026 00:53:53 +0800 Subject: [PATCH] Fix crash on duplicate Build IDs in ModLoader.LoadCheats Multiple executables may report the same Build ID, causing ToDictionary to throw an ArgumentException while creating the executable lookup. Build the lookup incrementally and keep the first code address when duplicate Build IDs are encountered. Log a warning if a duplicate Build ID is associated with a different code address. (cherry picked from commit 93b4c53c8ac3b61d57b090cdbb3d7d97442a8f53) --- src/Ryujinx.HLE/HOS/ModLoader.cs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index b6cfb6578..f9bef49a0 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -749,8 +749,28 @@ namespace Ryujinx.HLE.HOS } List cheats = mods.Cheats; - Dictionary processExes = tamperInfo.BuildIds.Zip(tamperInfo.CodeAddresses, (k, v) => new { k, v }) - .ToDictionary(x => x.k[..Math.Min(Cheat.CheatIdSize, x.k.Length)], x => x.v); + Dictionary processExes = new(); + + foreach ((string buildId, ulong codeAddress) in tamperInfo.BuildIds.Zip(tamperInfo.CodeAddresses)) + { + string normalizedBuildId = buildId[..Math.Min(Cheat.CheatIdSize, buildId.Length)]; + + if (processExes.TryGetValue(normalizedBuildId, out ulong existingAddress)) + { + if (existingAddress != codeAddress) + { + Logger.Warning?.Print( + LogClass.ModLoader, + $"Duplicate BuildId prefix '{normalizedBuildId}' has different code addresses. " + + $"Existing: 0x{existingAddress:X}, duplicate: 0x{codeAddress:X}. " + + $"Keeping the first one."); + } + + continue; + } + + processExes.Add(normalizedBuildId, codeAddress); + } foreach (Cheat cheat in cheats) {