fix possible deadlocks with RAIntegration due to dumb threading bs, cleanup some of this code
also make sure to reboot core when starting up RetroAchievements with RAIntegration active (RAIntegration doesn't do this itself it seems)
This commit is contained in:
parent
95001d0baa
commit
f8a5adecb5
|
@ -19,7 +19,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
_resolver = new("RA_Integration-x64.dll", hasLimitedLifetime: true);
|
||||
RA = BizInvoker.GetInvoker<RAInterface>(_resolver, CallingConventionAdapters.Native);
|
||||
_version = new(Marshal.PtrToStringAnsi(RA.IntegrationVersion()));
|
||||
_version = new(Marshal.PtrToStringAnsi(RA.IntegrationVersion())!);
|
||||
Console.WriteLine($"Loaded RetroAchievements v{_version}");
|
||||
}
|
||||
|
||||
|
@ -58,58 +58,42 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
if (_version < minVer)
|
||||
{
|
||||
if (mainForm.ShowMessageBox2(
|
||||
owner: null,
|
||||
text: "An update is required to use RetroAchievements. Do you want to download the update now?",
|
||||
caption: "Update",
|
||||
icon: EMsgBoxIcon.Question,
|
||||
useOKCancel: false))
|
||||
{
|
||||
DetachDll();
|
||||
var ret = DownloadDll((string)info["LatestVersionUrlX64"]);
|
||||
AttachDll();
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!mainForm.ShowMessageBox2(
|
||||
owner: null,
|
||||
text:
|
||||
"An update is required to use RetroAchievements. Do you want to download the update now?",
|
||||
caption: "Update",
|
||||
icon: EMsgBoxIcon.Question,
|
||||
useOKCancel: false)) return false;
|
||||
DetachDll();
|
||||
var ret = DownloadDll((string)info["LatestVersionUrlX64"]);
|
||||
AttachDll();
|
||||
return ret;
|
||||
}
|
||||
else if (_version < lastestVer)
|
||||
{
|
||||
if (mainForm.ShowMessageBox2(
|
||||
owner: null,
|
||||
text: "An optional update is available for RetroAchievements. Do you want to download the update now?",
|
||||
caption: "Update",
|
||||
icon: EMsgBoxIcon.Question,
|
||||
useOKCancel: false))
|
||||
{
|
||||
DetachDll();
|
||||
DownloadDll((string)info["LatestVersionUrlX64"]);
|
||||
AttachDll();
|
||||
return true; // even if this fails, should be OK to use the old dll
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't have to update in this case
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mainForm.ShowMessageBox(
|
||||
owner: null,
|
||||
text: "Failed to fetch update information, cannot start RetroAchievements.",
|
||||
caption: "Error",
|
||||
icon: EMsgBoxIcon.Error);
|
||||
|
||||
return false;
|
||||
if (_version >= lastestVer) return true;
|
||||
|
||||
if (!mainForm.ShowMessageBox2(
|
||||
owner: null,
|
||||
text:
|
||||
"An optional update is available for RetroAchievements. Do you want to download the update now?",
|
||||
caption: "Update",
|
||||
icon: EMsgBoxIcon.Question,
|
||||
useOKCancel: false)) return true;
|
||||
|
||||
DetachDll();
|
||||
DownloadDll((string)info["LatestVersionUrlX64"]);
|
||||
AttachDll();
|
||||
return true; // even if this fails, should be OK to use the old dll
|
||||
}
|
||||
|
||||
mainForm.ShowMessageBox(
|
||||
owner: null,
|
||||
text: "Failed to fetch update information, cannot start RetroAchievements.",
|
||||
caption: "Error",
|
||||
icon: EMsgBoxIcon.Error);
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -43,12 +43,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private readonly RAInterface.MenuItem[] _menuItems = new RAInterface.MenuItem[40];
|
||||
|
||||
// Memory may be accessed by another thread (for rich presence)
|
||||
// Memory may be accessed by another thread (mainly rich presence, some other things too)
|
||||
// and peeks for us are not thread safe, so we need to guard it
|
||||
private readonly AutoResetEvent _memAccessReady = new(false);
|
||||
private readonly AutoResetEvent _memAccessGo = new(false);
|
||||
private readonly AutoResetEvent _memAccessDone = new(false);
|
||||
private readonly RAMemGuard _memGuard;
|
||||
private readonly RAMemGuard _memGuard = new();
|
||||
|
||||
private bool _firstRestart = true;
|
||||
|
||||
private void RebuildMenu()
|
||||
{
|
||||
|
@ -111,8 +110,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
Func<Config> getConfig, ToolStripItemCollection raDropDownItems, Action shutdownRACallback)
|
||||
: base(mainForm, inputManager, tools, getConfig, raDropDownItems, shutdownRACallback)
|
||||
{
|
||||
_memGuard = new(_memAccessReady, _memAccessGo, _memAccessDone);
|
||||
|
||||
RA.InitClient(_mainForm.Handle, "BizHawk", VersionInfo.GetEmuVersion());
|
||||
|
||||
_isActive = () => !Emu.IsNull();
|
||||
|
@ -125,7 +122,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
Marshal.Copy(name, 0, buffer, Math.Min(name.Length, 256));
|
||||
};
|
||||
_resetEmulator = () => _mainForm.RebootCore();
|
||||
_loadROM = path => _ = _mainForm.LoadRom(path, new LoadRomArgs { OpenAdvanced = OpenAdvancedSerializer.ParseWithLegacy(path) });
|
||||
_loadROM = path => _ = _mainForm.LoadRom(path, new() { OpenAdvanced = OpenAdvancedSerializer.ParseWithLegacy(path) });
|
||||
|
||||
RA.InstallSharedFunctionsExt(_isActive, _unpause, _pause, _rebuildMenu, _estimateTitle, _resetEmulator, _loadROM);
|
||||
|
||||
|
@ -159,6 +156,25 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
public override void Restart()
|
||||
{
|
||||
if (_firstRestart)
|
||||
{
|
||||
_firstRestart = false;
|
||||
if (RA.HardcoreModeIsActive())
|
||||
{
|
||||
if (!_mainForm.RebootCore())
|
||||
{
|
||||
// unset hardcore mode if we fail to reboot core somehow
|
||||
HandleHardcoreModeDisable("Failed to reboot core.");
|
||||
}
|
||||
if (RA.HardcoreModeIsActive() && _mainForm.CurrentlyOpenRomArgs is not null)
|
||||
{
|
||||
// if we aren't hardcore anymore, we failed to reboot the core (and didn't call Restart probably)
|
||||
// if CurrentlyOpenRomArgs is null, then Restart won't be called (as RebootCore returns true immediately), so
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var consoleId = SystemIdToConsoleId();
|
||||
RA.SetConsoleID(consoleId);
|
||||
|
||||
|
@ -168,7 +184,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
_memFunctions = CreateMemoryBanks(consoleId, Domains, Emu.CanDebug() ? Emu.AsDebuggable() : null);
|
||||
|
||||
for (int i = 0; i < _memFunctions.Count; i++)
|
||||
for (var i = 0; i < _memFunctions.Count; i++)
|
||||
{
|
||||
_memFunctions[i].MemGuard = _memGuard;
|
||||
RA.InstallMemoryBank(i, _memFunctions[i].ReadFunc, _memFunctions[i].WriteFunc, _memFunctions[i].BankSize);
|
||||
|
@ -209,6 +225,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
public override void Update()
|
||||
{
|
||||
using var access = _memGuard.GetAccess();
|
||||
|
||||
if (RA.HardcoreModeIsActive())
|
||||
{
|
||||
CheckHardcoreModeConditions();
|
||||
|
@ -219,41 +237,32 @@ namespace BizHawk.Client.EmuHawk
|
|||
RA.SetPaused(true);
|
||||
}
|
||||
|
||||
if (RA.IsOverlayFullyVisible())
|
||||
if (!RA.IsOverlayFullyVisible()) return;
|
||||
|
||||
var ci = new RAInterface.ControllerInput
|
||||
{
|
||||
var ci = new RAInterface.ControllerInput
|
||||
{
|
||||
UpPressed = _inputManager.ClientControls["RA Up"],
|
||||
DownPressed = _inputManager.ClientControls["RA Down"],
|
||||
LeftPressed = _inputManager.ClientControls["RA Left"],
|
||||
RightPressed = _inputManager.ClientControls["RA Right"],
|
||||
ConfirmPressed = _inputManager.ClientControls["RA Confirm"],
|
||||
CancelPressed = _inputManager.ClientControls["RA Cancel"],
|
||||
QuitPressed = _inputManager.ClientControls["RA Quit"],
|
||||
};
|
||||
UpPressed = _inputManager.ClientControls["RA Up"],
|
||||
DownPressed = _inputManager.ClientControls["RA Down"],
|
||||
LeftPressed = _inputManager.ClientControls["RA Left"],
|
||||
RightPressed = _inputManager.ClientControls["RA Right"],
|
||||
ConfirmPressed = _inputManager.ClientControls["RA Confirm"],
|
||||
CancelPressed = _inputManager.ClientControls["RA Cancel"],
|
||||
QuitPressed = _inputManager.ClientControls["RA Quit"],
|
||||
};
|
||||
|
||||
RA.NavigateOverlay(ref ci);
|
||||
RA.NavigateOverlay(ref ci);
|
||||
|
||||
// todo: suppress user inputs with overlay active?
|
||||
}
|
||||
|
||||
if (_memAccessReady.WaitOne(0))
|
||||
{
|
||||
_memAccessGo.Set();
|
||||
_memAccessDone.WaitOne();
|
||||
}
|
||||
// todo: suppress user inputs with overlay active?
|
||||
}
|
||||
|
||||
public override void OnFrameAdvance()
|
||||
{
|
||||
using var access = _memGuard.GetAccess();
|
||||
|
||||
var input = _inputManager.ControllerOutput;
|
||||
foreach (var resetButton in input.Definition.BoolButtons.Where(b => b.Contains("Power") || b.Contains("Reset")))
|
||||
if (input.Definition.BoolButtons.Any(b => (b.Contains("Power") || b.Contains("Reset")) && input.IsPressed(b)))
|
||||
{
|
||||
if (input.IsPressed(resetButton))
|
||||
{
|
||||
RA.OnReset();
|
||||
break;
|
||||
}
|
||||
RA.OnReset();
|
||||
}
|
||||
|
||||
if (Emu.HasMemoryDomains())
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private readonly byte[] _cheevoFormatBuffer = new byte[1024];
|
||||
|
||||
public string GetCheevoProgress(int id)
|
||||
private string GetCheevoProgress(int id)
|
||||
{
|
||||
var len = _lib.rc_runtime_format_achievement_measured(ref _runtime, id, _cheevoFormatBuffer, _cheevoFormatBuffer.Length);
|
||||
return Encoding.ASCII.GetString(_cheevoFormatBuffer, 0, len);
|
||||
|
|
|
@ -53,9 +53,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
unsafe
|
||||
{
|
||||
var unlocks = (int*)resp.achievement_ids;
|
||||
for (int i = 0; i < resp.num_achievement_ids; i++)
|
||||
for (var i = 0; i < resp.num_achievement_ids; i++)
|
||||
{
|
||||
if (_cheevos.TryGetValue(unlocks[i], out var cheevo))
|
||||
if (_cheevos.TryGetValue(unlocks![i], out var cheevo))
|
||||
{
|
||||
cheevo.SetUnlocked(hardcore, true);
|
||||
}
|
||||
|
@ -104,18 +104,18 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
var cheevos = new Dictionary<int, Cheevo>();
|
||||
var cptr = (LibRCheevos.rc_api_achievement_definition_t*)resp.achievements;
|
||||
for (int i = 0; i < resp.num_achievements; i++)
|
||||
for (var i = 0; i < resp.num_achievements; i++)
|
||||
{
|
||||
cheevos.Add(cptr[i].id, new(in cptr[i], allowUnofficialCheevos));
|
||||
cheevos.Add(cptr![i].id, new(in cptr[i], allowUnofficialCheevos));
|
||||
}
|
||||
|
||||
_cheevos = cheevos;
|
||||
|
||||
var lboards = new Dictionary<int, LBoard>();
|
||||
var lptr = (LibRCheevos.rc_api_leaderboard_definition_t*)resp.leaderboards;
|
||||
for (int i = 0; i < resp.num_leaderboards; i++)
|
||||
for (var i = 0; i < resp.num_leaderboards; i++)
|
||||
{
|
||||
lboards.Add(lptr[i].id, new(in lptr[i]));
|
||||
lboards.Add(lptr![i].id, new(in lptr[i]));
|
||||
}
|
||||
|
||||
_lboards = lboards;
|
||||
|
@ -133,21 +133,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
GameBadge = null;
|
||||
RichPresenseScript = gameData.RichPresenseScript;
|
||||
|
||||
var cheevos = new Dictionary<int, Cheevo>();
|
||||
foreach (var cheevo in gameData.CheevoEnumerable)
|
||||
{
|
||||
cheevos.Add(cheevo.ID, new(in cheevo, allowUnofficialCheevos));
|
||||
}
|
||||
|
||||
_cheevos = cheevos;
|
||||
|
||||
var lboards = new Dictionary<int, LBoard>();
|
||||
foreach (var lboard in gameData.LBoardEnumerable)
|
||||
{
|
||||
lboards.Add(lboard.ID, new(in lboard));
|
||||
}
|
||||
|
||||
_lboards = lboards;
|
||||
_cheevos = gameData.CheevoEnumerable.ToDictionary<Cheevo, int, Cheevo>(cheevo => cheevo.ID, cheevo => new(in cheevo, allowUnofficialCheevos));
|
||||
_lboards = gameData.LBoardEnumerable.ToDictionary<LBoard, int, LBoard>(lboard => lboard.ID, lboard => new(in lboard));
|
||||
|
||||
SoftcoreInitUnlocksReady = new(false);
|
||||
HardcoreInitUnlocksReady = new(false);
|
||||
|
@ -159,7 +146,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
private async Task<int> SendHashAsync(string hash)
|
||||
private static async Task<int> SendHashAsync(string hash)
|
||||
{
|
||||
var api_params = new LibRCheevos.rc_api_resolve_hash_request_t(null, null, hash);
|
||||
var ret = 0;
|
||||
|
@ -244,7 +231,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
try
|
||||
{
|
||||
var serv_resp = await SendAPIRequest(in api_req).ConfigureAwait(false);
|
||||
ret = new Bitmap(new MemoryStream(serv_resp));
|
||||
ret = new(new MemoryStream(serv_resp));
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
@ -7,7 +7,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
public partial class RCheevos
|
||||
{
|
||||
private bool RichPresenceActive { get; set; }
|
||||
|
||||
private string CurrentRichPresence { get; set; }
|
||||
|
||||
private static async Task<bool> StartGameSessionAsync(string username, string api_token, int id)
|
||||
|
@ -63,11 +62,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
|
||||
var now = DateTime.Now;
|
||||
if ((now - _lastPingTime) >= _pingCooldown)
|
||||
{
|
||||
SendPing(Username, ApiToken, _gameData.GameID, CurrentRichPresence);
|
||||
_lastPingTime = now;
|
||||
}
|
||||
if (now - _lastPingTime < _pingCooldown) return;
|
||||
SendPing(Username, ApiToken, _gameData.GameID, CurrentRichPresence);
|
||||
_lastPingTime = now;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -246,12 +246,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
_lib.rc_runtime_reset(ref _runtime);
|
||||
|
||||
if (File.Exists(path + ".rap"))
|
||||
{
|
||||
using var file = File.OpenRead(path + ".rap");
|
||||
var buffer = file.ReadAllBytes();
|
||||
_lib.rc_runtime_deserialize_progress(ref _runtime, buffer, IntPtr.Zero);
|
||||
}
|
||||
if (!File.Exists(path + ".rap")) return;
|
||||
|
||||
using var file = File.OpenRead(path + ".rap");
|
||||
var buffer = file.ReadAllBytes();
|
||||
_lib.rc_runtime_deserialize_progress(ref _runtime, buffer, IntPtr.Zero);
|
||||
}
|
||||
|
||||
// not sure if we really need to do anything here...
|
||||
|
@ -333,14 +332,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
if (gameId != 0)
|
||||
{
|
||||
if (_cachedGameDatas.TryGetValue(gameId, out var cachedGameData))
|
||||
{
|
||||
_gameData = new GameData(cachedGameData, () => AllowUnofficialCheevos);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gameData = GetGameData(Username, ApiToken, gameId, () => AllowUnofficialCheevos);
|
||||
}
|
||||
_gameData = _cachedGameDatas.TryGetValue(gameId, out var cachedGameData)
|
||||
? new(cachedGameData, () => AllowUnofficialCheevos)
|
||||
: GetGameData(Username, ApiToken, gameId, () => AllowUnofficialCheevos);
|
||||
|
||||
StartGameSession(Username, ApiToken, gameId);
|
||||
|
||||
|
@ -373,12 +367,12 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
else
|
||||
{
|
||||
_gameData = new GameData();
|
||||
_gameData = new();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_gameData = new GameData();
|
||||
_gameData = new();
|
||||
}
|
||||
|
||||
// validate addresses now that we have cheevos init
|
||||
|
@ -593,13 +587,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
|
||||
var input = _inputManager.ControllerOutput;
|
||||
foreach (var resetButton in input.Definition.BoolButtons.Where(b => b.Contains("Power") || b.Contains("Reset")))
|
||||
if (input.Definition.BoolButtons.Any(b => (b.Contains("Power") || b.Contains("Reset")) && input.IsPressed(b)))
|
||||
{
|
||||
if (input.IsPressed(resetButton))
|
||||
{
|
||||
_lib.rc_runtime_reset(ref _runtime);
|
||||
break;
|
||||
}
|
||||
_lib.rc_runtime_reset(ref _runtime);
|
||||
}
|
||||
|
||||
if (Emu.HasMemoryDomains())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using BizHawk.Common;
|
||||
|
@ -48,7 +49,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
buffer.AddRange(new ArraySegment<byte>(buf2048, 0, 128));
|
||||
var bootSector = (buf2048[35] << 24) | (buf2048[34] << 16) | (buf2048[33] << 8) | buf2048[32];
|
||||
var numSectors = (buf2048[39] << 24) | (buf2048[38] << 16) | (buf2048[37] << 8) | buf2048[36];
|
||||
for (int i = 0; i < numSectors; i++)
|
||||
for (var i = 0; i < numSectors; i++)
|
||||
{
|
||||
dsr.ReadLBA_2048(bootSector + i, buf2048, 0);
|
||||
buffer.AddRange(buf2048);
|
||||
|
@ -82,7 +83,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
return -1;
|
||||
}
|
||||
|
||||
string exePath = "PSX.EXE";
|
||||
var exePath = "PSX.EXE";
|
||||
|
||||
// find SYSTEM.CNF sector
|
||||
var sector = GetFileSector("SYSTEM.CNF", out _);
|
||||
|
@ -154,36 +155,30 @@ namespace BizHawk.Client.EmuHawk
|
|||
var numLbas = disc.Session1.FirstInformationTrack.NextTrack.LBA - disc.Session1.FirstInformationTrack.LBA;
|
||||
int bootLen = 0, bootLba = 0, bootOff = 0;
|
||||
bool byteswapped = false, foundHeader = false;
|
||||
for (int i = 0; i < numLbas; i++)
|
||||
for (var i = 0; i < numLbas; i++)
|
||||
{
|
||||
dsr.ReadLBA_2352(startLba + i, buf2352, 0);
|
||||
|
||||
for (int j = 0; j < (2352 - 32 - 4 - 4); j++)
|
||||
for (var j = 0; j < 2352 - 32 - 4 - 4; j++)
|
||||
{
|
||||
if (buf2352[j] == _jaguarHeader[0])
|
||||
{
|
||||
if (_jaguarHeader == Encoding.ASCII.GetString(buf2352, j, 32))
|
||||
{
|
||||
bootLen = (buf2352[j + 36] << 24) | (buf2352[j + 37] << 16) | (buf2352[j + 38] << 8) | buf2352[j + 39];
|
||||
bootLba = startLba + i;
|
||||
bootOff = j + 32 + 4 + 4;
|
||||
byteswapped = false;
|
||||
foundHeader = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (buf2352[j] == _jaguarBSHeader[0])
|
||||
{
|
||||
if (_jaguarBSHeader == Encoding.ASCII.GetString(buf2352, j, 32))
|
||||
{
|
||||
bootLen = (buf2352[j + 37] << 24) | (buf2352[j + 36] << 16) | (buf2352[j + 39] << 8) | buf2352[j + 38];
|
||||
bootLba = startLba + i;
|
||||
bootOff = j + 32 + 4 + 4;
|
||||
byteswapped = true;
|
||||
foundHeader = true;
|
||||
break;
|
||||
}
|
||||
if (_jaguarHeader != Encoding.ASCII.GetString(buf2352, j, 32)) continue;
|
||||
bootLen = (buf2352[j + 36] << 24) | (buf2352[j + 37] << 16) | (buf2352[j + 38] << 8) | buf2352[j + 39];
|
||||
bootLba = startLba + i;
|
||||
bootOff = j + 32 + 4 + 4;
|
||||
byteswapped = false;
|
||||
foundHeader = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (buf2352[j] != _jaguarBSHeader[0] || _jaguarBSHeader != Encoding.ASCII.GetString(buf2352, j, 32)) continue;
|
||||
bootLen = (buf2352[j + 37] << 24) | (buf2352[j + 36] << 16) | (buf2352[j + 39] << 8) | buf2352[j + 38];
|
||||
bootLba = startLba + i;
|
||||
bootOff = j + 32 + 4 + 4;
|
||||
byteswapped = true;
|
||||
foundHeader = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (foundHeader)
|
||||
|
@ -256,14 +251,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
using var sr = new StreamReader(file.GetStream());
|
||||
var m3u = M3U_File.Read(sr);
|
||||
m3u.Rebase(Path.GetDirectoryName(ioa.SimplePath));
|
||||
foreach (var entry in m3u.Entries)
|
||||
{
|
||||
var id = HashDisc(entry.Path, consoleID, ++discCount);
|
||||
if (id.HasValue)
|
||||
{
|
||||
ret.Add(id.Value);
|
||||
}
|
||||
}
|
||||
ret.AddRange(m3u.Entries.Select(entry => HashDisc(entry.Path, consoleID, ++discCount))
|
||||
.Where(id => id.HasValue)
|
||||
.Select(id => id.Value));
|
||||
}
|
||||
else if (ext == ".xml")
|
||||
{
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
// To keep changes outside this file minimal, we'll simply check if any problematic condition arises and disable hardcore mode
|
||||
// (with the exception of frame advance and rewind, which we can just suppress)
|
||||
|
||||
private static readonly Type[] HardcoreProhibitedTools = new[]
|
||||
private static readonly Type[] HardcoreProhibitedTools =
|
||||
{
|
||||
typeof(LuaConsole), typeof(RamWatch), typeof(RamSearch),
|
||||
typeof(GameShark), typeof(SNESGraphicsDebugger), typeof(PceBgViewer),
|
||||
|
@ -93,11 +93,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
foreach (var t in HardcoreProhibitedTools)
|
||||
{
|
||||
if (_tools.IsLoaded(t))
|
||||
{
|
||||
HandleHardcoreModeDisable($"Using {t.Name} in hardcore mode is not allowed.");
|
||||
return;
|
||||
}
|
||||
if (!_tools.IsLoaded(t)) continue;
|
||||
HandleHardcoreModeDisable($"Using {t.Name} in hardcore mode is not allowed.");
|
||||
return;
|
||||
}
|
||||
|
||||
// can't know what external tools are doing, so just don't allow them here
|
||||
|
@ -107,58 +105,53 @@ namespace BizHawk.Client.EmuHawk
|
|||
return;
|
||||
}
|
||||
|
||||
if (Emu is SubNESHawk or SubBsnesCore or SubGBHawk)
|
||||
switch (Emu)
|
||||
{
|
||||
// this is mostly due to wonkiness with subframes which can be used as pseudo slowdown
|
||||
HandleHardcoreModeDisable($"Using subframes in hardcore mode is not allowed.");
|
||||
return;
|
||||
}
|
||||
else if (Emu is NymaCore nyma)
|
||||
{
|
||||
if (nyma.GetSettings().DisabledLayers.Any())
|
||||
{
|
||||
HandleHardcoreModeDisable($"Disabling {Emu.GetType().Name}'s graphics layers in hardcore mode is not allowed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Emu is GambatteLink gl)
|
||||
{
|
||||
foreach (var ss in gl.GetSyncSettings()._linkedSyncSettings)
|
||||
{
|
||||
if (!ss.DisplayBG || !ss.DisplayOBJ || !ss.DisplayWindow)
|
||||
case SubNESHawk or SubBsnesCore or SubGBHawk:
|
||||
// this is mostly due to wonkiness with subframes which can be used as pseudo slowdown
|
||||
HandleHardcoreModeDisable($"Using subframes in hardcore mode is not allowed.");
|
||||
break;
|
||||
case NymaCore nyma:
|
||||
if (nyma.GetSettings().DisabledLayers.Any())
|
||||
{
|
||||
HandleHardcoreModeDisable($"Disabling {Emu.GetType().Name}'s graphics layers in hardcore mode is not allowed.");
|
||||
}
|
||||
break;
|
||||
case GambatteLink gl:
|
||||
if (gl.GetSyncSettings()._linkedSyncSettings.Any(ss => !ss.DisplayBG || !ss.DisplayOBJ || !ss.DisplayWindow))
|
||||
{
|
||||
HandleHardcoreModeDisable($"Disabling GambatteLink's graphics layers in hardcore mode is not allowed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Emu is Gameboy gb)
|
||||
{
|
||||
var ss = gb.GetSyncSettings();
|
||||
if (!ss.DisplayBG || !ss.DisplayOBJ || !ss.DisplayWindow)
|
||||
break;
|
||||
case Gameboy gb:
|
||||
{
|
||||
HandleHardcoreModeDisable($"Disabling Gambatte's graphics layers in hardcore mode is not allowed.");
|
||||
return;
|
||||
}
|
||||
if (ss.FrameLength is Gameboy.GambatteSyncSettings.FrameLengthType.UserDefinedFrames)
|
||||
{
|
||||
HandleHardcoreModeDisable($"Using subframes in hardcore mode is not allowed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (CoreGraphicsLayers.TryGetValue(Emu.GetType(), out var layers))
|
||||
{
|
||||
var s = _mainForm.GetSettingsAdapterForLoadedCoreUntyped().GetSettings();
|
||||
var t = s.GetType();
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
// annoyingly NES has fields instead of properties for layers
|
||||
if (!(bool)(t.GetProperty(layer)?.GetValue(s) ?? t.GetField(layer).GetValue(s)))
|
||||
var ss = gb.GetSyncSettings();
|
||||
if (!ss.DisplayBG || !ss.DisplayOBJ || !ss.DisplayWindow)
|
||||
{
|
||||
HandleHardcoreModeDisable($"Disabling {Emu.GetType().Name}'s {layer} in hardcore mode is not allowed.");
|
||||
return;
|
||||
HandleHardcoreModeDisable($"Disabling Gambatte's graphics layers in hardcore mode is not allowed.");
|
||||
}
|
||||
else if (ss.FrameLength is Gameboy.GambatteSyncSettings.FrameLengthType.UserDefinedFrames)
|
||||
{
|
||||
HandleHardcoreModeDisable($"Using subframes in hardcore mode is not allowed.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (CoreGraphicsLayers.TryGetValue(Emu.GetType(), out var layers))
|
||||
{
|
||||
var s = _mainForm.GetSettingsAdapterForLoadedCoreUntyped().GetSettings();
|
||||
var t = s.GetType();
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
// annoyingly NES has fields instead of properties for layers
|
||||
if ((bool)(t.GetProperty(layer)
|
||||
?.GetValue(s) ?? t.GetField(layer)
|
||||
.GetValue(s))) continue;
|
||||
HandleHardcoreModeDisable($"Disabling {Emu.GetType().Name}'s {layer} in hardcore mode is not allowed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
|
@ -10,45 +11,55 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
public abstract partial class RetroAchievements
|
||||
{
|
||||
public struct RAMemGuard : IMonitor, IDisposable
|
||||
public class RAMemGuard : IMonitor, IDisposable
|
||||
{
|
||||
private readonly AutoResetEvent _start, _go, _end;
|
||||
private readonly ThreadLocal<bool> _isMainThread;
|
||||
|
||||
private bool IsNotMainThread => !_isMainThread.Value;
|
||||
|
||||
public RAMemGuard(AutoResetEvent start, AutoResetEvent go, AutoResetEvent end)
|
||||
{
|
||||
_start = start;
|
||||
_go = go;
|
||||
_end = end;
|
||||
_isMainThread = new() { Value = true };
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_start.Dispose();
|
||||
_go.Dispose();
|
||||
_end.Dispose();
|
||||
_isMainThread.Dispose();
|
||||
}
|
||||
private readonly ManualResetEventSlim MemLock = new(false);
|
||||
private readonly SemaphoreSlim MemSema = new(1);
|
||||
private readonly object MemSync = new();
|
||||
|
||||
public void Enter()
|
||||
{
|
||||
if (IsNotMainThread)
|
||||
lock (MemSync)
|
||||
{
|
||||
_start.Set();
|
||||
_go.WaitOne();
|
||||
MemSema.Wait();
|
||||
MemLock.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
if (IsNotMainThread)
|
||||
MemSema.Release();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
MemLock.Dispose();
|
||||
MemSema.Dispose();
|
||||
}
|
||||
|
||||
public readonly ref struct AccessWrapper
|
||||
{
|
||||
private readonly RAMemGuard _guard;
|
||||
|
||||
internal AccessWrapper(RAMemGuard guard)
|
||||
{
|
||||
_end.Set();
|
||||
_guard = guard;
|
||||
_guard.MemLock.Set();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_guard.MemSync)
|
||||
{
|
||||
_guard.MemLock.Reset();
|
||||
_guard.MemSema.Wait();
|
||||
_guard.MemSema.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AccessWrapper GetAccess()
|
||||
=> new(this);
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
|
@ -60,7 +71,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int ReadMemoryBlockFunc(int address, IntPtr buffer, int bytes);
|
||||
|
||||
public class MemFunctions
|
||||
protected class MemFunctions
|
||||
{
|
||||
protected readonly MemoryDomain _domain;
|
||||
private readonly int _domainAddrStart; // addr of _domain where bank begins
|
||||
|
@ -72,7 +83,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
public readonly int BankSize;
|
||||
|
||||
public RAMemGuard? MemGuard { get; set; }
|
||||
public RAMemGuard MemGuard { get; set; }
|
||||
|
||||
protected virtual int FixAddr(int addr)
|
||||
=> _domainAddrStart + addr;
|
||||
|
@ -119,7 +130,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
for (var i = addr; i < end; i++)
|
||||
{
|
||||
((byte*)buffer)[i - addr] = _domain.PeekByte(i ^ _addressMangler);
|
||||
((byte*)buffer)![i - addr] = _domain.PeekByte(i ^ _addressMangler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,11 +213,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
if ((i & 2) != 0)
|
||||
{
|
||||
((byte*)buffer)[i - addr] = 0;
|
||||
((byte*)buffer)![i - addr] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
((byte*)buffer)[i - addr] = _domain.PeekByte(FixAddr(i));
|
||||
((byte*)buffer)![i - addr] = _domain.PeekByte(FixAddr(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +290,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
var regs = _debuggable.GetCpuFlagsAndRegisters();
|
||||
var end = Math.Min(addr + bytes, BankSize);
|
||||
for (int i = addr; i < end; i++)
|
||||
for (var i = addr; i < end; i++)
|
||||
{
|
||||
byte val;
|
||||
if (i < 0x40)
|
||||
|
@ -293,7 +304,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
unsafe
|
||||
{
|
||||
((byte*)buffer)[i - addr] = val;
|
||||
((byte*)buffer)![i - addr] = val;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,13 +321,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
|
||||
// these consoles will use the entire system bus
|
||||
private static readonly ConsoleID[] UseFullSysBus = new[]
|
||||
private static readonly ConsoleID[] UseFullSysBus =
|
||||
{
|
||||
ConsoleID.NES, ConsoleID.C64, ConsoleID.AmstradCPC, ConsoleID.Atari7800,
|
||||
};
|
||||
|
||||
// these consoles will use the entire main memory domain
|
||||
private static readonly ConsoleID[] UseFullMainMem = new[]
|
||||
private static readonly ConsoleID[] UseFullMainMem =
|
||||
{
|
||||
ConsoleID.PlayStation, ConsoleID.Lynx, ConsoleID.Lynx, ConsoleID.NeoGeoPocket,
|
||||
ConsoleID.Jaguar, ConsoleID.JaguarCD, ConsoleID.DS, ConsoleID.AppleII,
|
||||
|
@ -370,10 +381,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
else if (UsePartialSysBus.TryGetValue(consoleId, out var pairs))
|
||||
{
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
mfs.Add(new(domains.SystemBus, pair.Start, pair.Size));
|
||||
}
|
||||
mfs.AddRange(pairs.Select(pair => new MemFunctions(domains.SystemBus, pair.Start, pair.Size)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -502,13 +510,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
mfs.Add(new(domains.MainMemory, 0, domains.MainMemory.Size, 3));
|
||||
break;
|
||||
case ConsoleID.Arcade:
|
||||
foreach (var domain in domains)
|
||||
{
|
||||
if (domain.Name.Contains("ram"))
|
||||
{
|
||||
mfs.Add(new(domain, 0, domain.Size));
|
||||
}
|
||||
}
|
||||
mfs.AddRange(domains.Where(domain => domain.Name.Contains("ram"))
|
||||
.Select(domain => new MemFunctions(domain, 0, domain.Size)));
|
||||
break;
|
||||
case ConsoleID.UnknownConsoleID:
|
||||
case ConsoleID.ZXSpectrum: // this doesn't actually have anything standardized, so...
|
||||
|
|
Loading…
Reference in New Issue