diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegration.cs b/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegration.cs index 4028d69b1e..e292f253fa 100644 --- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegration.cs +++ b/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegration.cs @@ -15,16 +15,17 @@ namespace BizHawk.Client.EmuHawk public partial class RAIntegration : RetroAchievements { private readonly RAInterface RA; - + static RAIntegration() { + if (OSTailoredCode.IsUnixHost) + { + // RAIntegration is Windows only + return; + } + try { - if (OSTailoredCode.IsUnixHost) - { - throw new NotSupportedException("RAIntegration is Windows only!"); - } - AttachDll(); } catch @@ -56,25 +57,24 @@ namespace BizHawk.Client.EmuHawk private void RebuildMenu() { var numItems = RA.GetPopupMenuItems(_menuItems); - var tsmiddi = _raDropDownItems; - tsmiddi.Clear(); + _raDropDownItems.Clear(); { var tsi = new ToolStripMenuItem("Shutdown RetroAchievements"); tsi.Click += (_, _) => _shutdownRACallback(); - tsmiddi.Add(tsi); + _raDropDownItems.Add(tsi); - tsi = new ToolStripMenuItem("Autostart RetroAchievements") + tsi = new("Autostart RetroAchievements") { Checked = _getConfig().RAAutostart, CheckOnClick = true, }; tsi.CheckedChanged += (_, _) => _getConfig().RAAutostart ^= true; - tsmiddi.Add(tsi); + _raDropDownItems.Add(tsi); var tss = new ToolStripSeparator(); - tsmiddi.Add(tss); + _raDropDownItems.Add(tss); } - for (int i = 0; i < numItems; i++) + for (var i = 0; i < numItems; i++) { if (_menuItems[i].Label != IntPtr.Zero) { @@ -88,12 +88,12 @@ namespace BizHawk.Client.EmuHawk RA.InvokeDialog(id); _mainForm.UpdateWindowTitle(); }; - tsmiddi.Add(tsi); + _raDropDownItems.Add(tsi); } else { var tss = new ToolStripSeparator(); - tsmiddi.Add(tss); + _raDropDownItems.Add(tss); } } } diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Debug.cs b/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Debug.cs index 671b9ce5fe..a1054211ea 100644 --- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Debug.cs +++ b/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.Debug.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.IO; using System.Runtime.InteropServices; using System.Text; @@ -55,30 +56,37 @@ namespace BizHawk.Client.EmuHawk return file.GetStream().Position; } - private static UIntPtr ReadFileCallback(IntPtr file_handle, IntPtr buffer, UIntPtr requested_bytes) + private static nuint ReadFileCallback(IntPtr file_handle, IntPtr buffer, nuint requested_bytes) { var handle = GCHandle.FromIntPtr(file_handle); var file = (HawkFile)handle.Target; + var stream = file.GetStream(); + // this is poop without spans const int TMP_BUFFER_LEN = 65536; - var tmp = new byte[TMP_BUFFER_LEN]; - var stream = file.GetStream(); - var remainingBytes = (ulong)requested_bytes; - - while (remainingBytes != 0) + var tmp = ArrayPool.Shared.Rent(TMP_BUFFER_LEN); + try { - var numRead = stream.Read(tmp, 0, (int)Math.Min(remainingBytes, TMP_BUFFER_LEN)); - if (numRead == 0) // reached end of stream + var remainingBytes = requested_bytes; + while (remainingBytes != 0) { - break; + var numRead = stream.Read(tmp, 0, (int)Math.Min(remainingBytes, TMP_BUFFER_LEN)); + if (numRead == 0) // reached end of stream + { + break; + } + + Marshal.Copy(tmp, 0, buffer, numRead); + buffer += numRead; + remainingBytes -= (uint)numRead; } - Marshal.Copy(tmp, 0, buffer, numRead); - buffer += numRead; - remainingBytes -= (ulong)numRead; + return requested_bytes - remainingBytes; + } + finally + { + ArrayPool.Shared.Return(tmp); } - - return new((ulong)requested_bytes - remainingBytes); } private static void CloseFileCallback(IntPtr file_handle) @@ -189,7 +197,7 @@ namespace BizHawk.Client.EmuHawk _buf2448 = new byte[2448]; } - public int ReadSector(int lba, IntPtr buffer, ulong requestedBytes) + public int ReadSector(int lba, IntPtr buffer, nuint requestedBytes) { if (lba < _track.LBA || lba >= _track.NextTrack.LBA) { @@ -221,11 +229,11 @@ namespace BizHawk.Client.EmuHawk return GCHandle.ToIntPtr(handle); } - private static UIntPtr ReadSectorCallback(IntPtr track_handle, uint sector, IntPtr buffer, UIntPtr requested_bytes) + private static nuint ReadSectorCallback(IntPtr track_handle, uint sector, IntPtr buffer, nuint requested_bytes) { var handle = GCHandle.FromIntPtr(track_handle); var track = (RCTrack)handle.Target; - return new((uint)track.ReadSector((int)sector, buffer, (ulong)requested_bytes)); + return (uint)track.ReadSector((int)sector, buffer, requested_bytes); } private static void CloseTrackCallback(IntPtr track_handle) @@ -302,7 +310,7 @@ namespace BizHawk.Client.EmuHawk { static string ResolvePath(string path) { - if (Disc.IsValidExtension(Path.GetExtension(path))) + if (path.IndexOf('|') == -1 && Disc.IsValidExtension(Path.GetExtension(path))) { return path; // nothing to do in this case } @@ -334,7 +342,7 @@ namespace BizHawk.Client.EmuHawk static ConsoleID IdentifyConsole(string path) { - if (Disc.IsValidExtension(Path.GetExtension(path))) + if (path.IndexOf('|') == -1 && Disc.IsValidExtension(Path.GetExtension(path))) { using var disc = DiscExtensions.CreateAnyType(path, Console.WriteLine); if (disc is null) diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.cs b/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.cs index f96a5bfa04..433797f29a 100644 --- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.cs +++ b/src/BizHawk.Client.EmuHawk/RetroAchievements/RCheevos.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; @@ -42,8 +41,9 @@ namespace BizHawk.Client.EmuHawk private readonly LibRCheevos.rc_runtime_event_handler_t _eventcb; private readonly LibRCheevos.rc_runtime_peek_t _peekcb; + private readonly LibRCheevos.rc_runtime_validate_address_t _validatecb; - private readonly Dictionary _readMap = new(); + private byte[] _readMap = Array.Empty(); private ToolStripMenuItem _hardcoreModeMenuItem; private bool _hardcoreMode; @@ -231,6 +231,7 @@ namespace BizHawk.Client.EmuHawk _eventcb = EventHandlerCallback; _peekcb = PeekCallback; + _validatecb = ValidateCallback; var config = _getConfig(); CheevosActive = config.RACheevosActive; @@ -327,6 +328,9 @@ namespace BizHawk.Client.EmuHawk config.RAAllowUnofficialCheevos = AllowUnofficialCheevos; } + private bool ValidateCallback(uint address) + => address < _readMap.Length && _readMap[address] != 0xFF; + public override void Restart() { if (_firstRestart) @@ -363,24 +367,32 @@ namespace BizHawk.Client.EmuHawk _consoleId = SystemIdToConsoleId(); // init the read map - _readMap.Clear(); + _readMap = Array.Empty(); if (Emu.HasMemoryDomains()) { _memFunctions = CreateMemoryBanks(_consoleId, Domains, Emu.CanDebug() ? Emu.AsDebuggable() : null); + if (_memFunctions.Count > 255) + { + throw new InvalidOperationException("_memFunctions must have less than 256 memory banks"); + } + + // this is kind of poop, it would prevent having >2GiB total banksize + // but no system needs that right now, the largest is just New 3DS at 256MiB + _readMap = new byte[_memFunctions.Sum(mfun => mfun.BankSize)]; uint addr = 0; - foreach (var memFunctions in _memFunctions) + for (var i = 0; i < _memFunctions.Count; i++) { - if (memFunctions.ReadFunc is not null) + _memFunctions[i].StartAddress = addr; + + var mapValue = _memFunctions[i].ReadFunc is not null ? i : 0xFF; + for (var j = 0; j < _memFunctions[i].BankSize; j++) { - for (uint i = 0; i < memFunctions.BankSize; i++) - { - _readMap.Add(addr + i, (memFunctions.ReadFunc, addr)); - } + _readMap[addr + j] = (byte)mapValue; } - addr = checked(addr + memFunctions.BankSize); + addr = checked(addr + _memFunctions[i].BankSize); } } @@ -426,11 +438,7 @@ namespace BizHawk.Client.EmuHawk } // validate addresses now that we have cheevos init - // ReSharper disable once ConvertToLocalFunction -#pragma warning disable IDE0039 - LibRCheevos.rc_runtime_validate_address_t peekcb = address => _readMap.ContainsKey(address); -#pragma warning restore IDE0039 - _lib.rc_runtime_validate_addresses(_runtime, _eventcb, peekcb); + _lib.rc_runtime_validate_addresses(_runtime, _eventcb, _validatecb); _gameInfoForm.Restart(_gameData.Title, _gameData.TotalCheevoPoints(HardcoreMode), CurrentRichPresence ?? "N/A"); _cheevoListForm.Restart(_gameData.GameID == 0 ? Array.Empty() : _gameData.CheevoEnumerable, GetCheevoProgress); @@ -615,20 +623,25 @@ namespace BizHawk.Client.EmuHawk } } - private uint PeekCallback(uint address, uint num_bytes, IntPtr ud) + private uint Peek(uint address) { - uint Peek(uint addr) - => _readMap.TryGetValue(addr, out var reader) ? reader.Func(addr - reader.Start) : 0u; - - return num_bytes switch + if (address < _readMap.Length && _readMap[address] != 0xFF) { - 1 => Peek(address), - 2 => Peek(address) | (Peek(address + 1) << 8), - 4 => Peek(address) | (Peek(address + 1) << 8) | (Peek(address + 2) << 16) | (Peek(address + 3) << 24), - _ => throw new InvalidOperationException($"Requested {num_bytes} in {nameof(PeekCallback)}"), - }; + var memFuncs = _memFunctions[_readMap[address]]; + return memFuncs.ReadFunc(address - memFuncs.StartAddress); + } + + return 0; } + private uint PeekCallback(uint address, uint num_bytes, IntPtr ud) => num_bytes switch + { + 1 => Peek(address), + 2 => Peek(address) | (Peek(address + 1) << 8), + 4 => Peek(address) | (Peek(address + 1) << 8) | (Peek(address + 2) << 16) | (Peek(address + 3) << 24), + _ => throw new InvalidOperationException($"Requested {num_bytes} in {nameof(PeekCallback)}"), + }; + public override void OnFrameAdvance() { if (!LoggedIn || !_activeModeUnlocksRequest.IsCompleted) diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RetroAchievements.Memory.cs b/src/BizHawk.Client.EmuHawk/RetroAchievements/RetroAchievements.Memory.cs index 22feaab408..68189a76ea 100644 --- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RetroAchievements.Memory.cs +++ b/src/BizHawk.Client.EmuHawk/RetroAchievements/RetroAchievements.Memory.cs @@ -112,6 +112,7 @@ namespace BizHawk.Client.EmuHawk public WriteMemoryFunc WriteFunc { get; protected init; } public ReadMemoryBlockFunc ReadBlockFunc { get; protected init; } + public uint StartAddress; // this is set for our rcheevos impl public readonly uint BankSize; public RAMemGuard MemGuard { get; set; }