do some more cleanup with rcheevos stuff, also rework rcheevos read map. having a dict full of delegates ends up allocating >26GiB of memory for New 3DS (i.e. 256MiB of memory), so instead have a map of _memFunction indexes corresponding to address, thereby making memory usage equal to the memory mapping size (so for New 3DS this will only take 256MiB of memory). it comes with some limitations, none of which matter for now
This commit is contained in:
parent
6b0b6f2106
commit
3bcc6ee977
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<byte>.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<byte>.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)
|
||||
|
|
|
@ -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<uint, (ReadMemoryFunc Func, uint Start)> _readMap = new();
|
||||
private byte[] _readMap = Array.Empty<byte>();
|
||||
|
||||
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<byte>();
|
||||
|
||||
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<Cheevo>() : _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)
|
||||
|
|
|
@ -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; }
|
||||
|
|
Loading…
Reference in New Issue