From 93b4c53c8ac3b61d57b090cdbb3d7d97442a8f53 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. --- 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 c50edbaa8..2a8576ce0 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -783,8 +783,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) {