diff --git a/Ryo.Reloaded/CRI/CriAtomEx/CriAtomEx.cs b/Ryo.Reloaded/CRI/CriAtomEx/CriAtomEx.cs index c4da6fe..57c9758 100644 --- a/Ryo.Reloaded/CRI/CriAtomEx/CriAtomEx.cs +++ b/Ryo.Reloaded/CRI/CriAtomEx/CriAtomEx.cs @@ -5,6 +5,7 @@ using Ryo.Definitions.Classes; using static Ryo.Definitions.Functions.CriAtomExFunctions; using Ryo.Definitions.Enums; +using Ryo.Reloaded.Common; using SharedScans.Interfaces; namespace Ryo.Reloaded.CRI.CriAtomEx; @@ -46,9 +47,7 @@ internal unsafe class CriAtomEx : ICriAtomEx private readonly WrapperContainer getCategoryVolume; private readonly WrapperContainer getVolumeById; private readonly WrapperContainer setVolumeByIndex; - private criAtomExPlayer_SetFormat? setFormat; - private readonly object setFormatLock = new(); - private int setFormatSignaturesScanned; + private readonly MultiSignature setFormat; private readonly WrapperContainer setSamplingRate; private readonly WrapperContainer setNumChannels; private readonly WrapperContainer setVolume; @@ -138,44 +137,8 @@ public CriAtomEx(string game, ISharedScans scans, IReloadedHooks reloadedHooks) scans.AddScan(this.patterns.criAtomExPlayer_GetNumPlayedSamples); this.getNumPlayedSamples = scans.CreateWrapper(Mod.NAME); - - foreach (var (Index, Candidate) in this.patterns.criAtomExPlayer_SetFormat.Select((x, i) => (i, x))) - { - Project.Scans.AddScanHook($"criAtomExPlayer_SetFormat[{Index}]", Candidate, (result, hooks) => - { - lock (setFormatLock) - { - setFormatSignaturesScanned++; - if (this.setFormat == null) - { - scans.Broadcast(result); - } - this.setFormat ??= hooks.CreateWrapper(result, out _); - } - }, () => - { - lock (setFormatLock) - { - setFormatSignaturesScanned++; - if (setFormatSignaturesScanned == this.patterns.criAtomExPlayer_SetFormat.Length) - { - Log.Error($"Failed to find a pattern for criAtomExPlayer_SetFormat."); - } - else - { - Log.Debug($"No matching pattern for criAtomExPlayer_SetFormat[{Index}]."); - } - } - }); - /* - var ListenerId = $"criAtomExPlayer_SetFormat_{Index}"; - scans.AddScan(ListenerId, Candidate); - scans.CreateListener(ListenerId, x => - { - this.setFormat[Index] = this.reloadedHooks.CreateWrapper(x, out _); - }); - */ - } + + this.setFormat = new(scans, this.patterns.criAtomExPlayer_SetFormat); scans.AddScan(this.patterns.criAtomExPlayer_SetSamplingRate); this.setSamplingRate = scans.CreateWrapper(Mod.NAME); @@ -267,7 +230,7 @@ public nint Acb_LoadAcbData(nint acbData, int acbDataSize, nint awbBinder, nint public void Player_SetFile(nint playerHn, nint criBinderHn, byte* path) => this.setFile.Wrapper(playerHn, criBinderHn, path); - public void Player_SetFormat(nint playerHn, CriAtomFormat format) => this.setFormat!(playerHn, format); + public void Player_SetFormat(nint playerHn, CriAtomFormat format) => this.setFormat.Function.Wrapper(playerHn, format); public void Player_SetNumChannels(nint playerHn, int numChannels) => this.setNumChannels.Wrapper(playerHn, numChannels); diff --git a/Ryo.Reloaded/Common/MultiSignature.cs b/Ryo.Reloaded/Common/MultiSignature.cs new file mode 100644 index 0000000..21fc67b --- /dev/null +++ b/Ryo.Reloaded/Common/MultiSignature.cs @@ -0,0 +1,56 @@ +using SharedScans.Interfaces; + +namespace Ryo.Reloaded.Common; + +public class MultiSignature +{ + public readonly WrapperContainer Function; + // public TFunction? Wrapper; + private readonly object Lock = new(); + private string[] Signatures; + private int ScansCompleted; + + // Add support for multiple signature candidates to allow support for multiple game versions instead of *just* the + // latest version. The selected function will be the first candidate to successfully match their signature to + // some offset in the executable. + // Note that if you are using both hardcoded signatures and Scan INI, the mod may complain that it can't find a + // signature from the hardcoded list but does from Scan INI. + public MultiSignature(ISharedScans scans, string[] Signatures) + { + this.Signatures = Signatures; + // Check for a single pattern defined inside a Scan INI + scans.AddScan(typeof(TFunction).Name, null); + Function = scans.CreateWrapper(Mod.NAME); + + // Check for multiple signature candidates defined in code + foreach (var (Index, Candidate) in this.Signatures.Select((x, i) => (i, x))) + { + Project.Scans.AddScanHook($"{typeof(TFunction).Name}[{Index}]", Candidate, (result, hooks) => + { + lock (Lock) + { + ScansCompleted++; + if (Function.Wrapper == null) + { + scans.Broadcast(result); + } + Function.Wrapper ??= hooks.CreateWrapper(result, out _); + } + }, () => + { + lock (Lock) + { + ScansCompleted++; + if (Function.Wrapper == null && ScansCompleted == this.Signatures.Length) + { + Log.Error($"Failed to find a pattern for {typeof(TFunction).Name}."); + } + else + { + Log.Debug($"No matching pattern for {typeof(TFunction).Name}[{Index}]."); + } + } + }); + } + } +} \ No newline at end of file diff --git a/Ryo.Reloaded/ModConfig.json b/Ryo.Reloaded/ModConfig.json index 734b415..733b1b8 100644 --- a/Ryo.Reloaded/ModConfig.json +++ b/Ryo.Reloaded/ModConfig.json @@ -2,7 +2,7 @@ "ModId": "Ryo.Reloaded", "ModName": "Ryo Framework", "ModAuthor": "RyoTune", - "ModVersion": "2.11.4", + "ModVersion": "2.11.5", "ModDescription": "Ryo Framework adds the ability to add or replace game audio and movies with zero file editing.", "ModDll": "Ryo.Reloaded.dll", "ModIcon": "Preview.png",