diff --git a/README.MD b/README.MD
index 0b2cebd..88ed5d2 100644
--- a/README.MD
+++ b/README.MD
@@ -92,6 +92,7 @@ First, dowload the SRL [HERE](https://github.com/marcussacana/StringReloads/rele
- CMVS32 and CMVS64
- ExHIBIT
- EntisGLS
+- Favorite (FVP)
Some SoftPal games needs a manual setup with the help of the Auto-Installer, click below to see the example:
[![SRL SoftPal Auto-Install Feature](http://img.youtube.com/vi/RAgZQBWqiJQ/0.jpg)](http://www.youtube.com/watch?v=RAgZQBWqiJQ "SRL SoftPal Auto-Install Feature")
diff --git a/SRLNative/SRLNative.vcxproj b/SRLNative/SRLNative.vcxproj
index 8876b7e..876b2b6 100644
--- a/SRLNative/SRLNative.vcxproj
+++ b/SRLNative/SRLNative.vcxproj
@@ -29,26 +29,26 @@
DynamicLibrary
true
- v142
+ v143
Unicode
DynamicLibrary
false
- v142
+ v143
false
Unicode
DynamicLibrary
true
- v142
+ v143
Unicode
DynamicLibrary
false
- v142
+ v143
true
Unicode
diff --git a/StringReloads/AutoInstall/CMVS.cs b/StringReloads/AutoInstall/CMVS.cs
index 72a6c97..a5f818e 100644
--- a/StringReloads/AutoInstall/CMVS.cs
+++ b/StringReloads/AutoInstall/CMVS.cs
@@ -2,12 +2,14 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net;
using Iced.Intel;
using StringReloads.Engine;
using StringReloads.Engine.Interface;
using StringReloads.Engine.Unmanaged;
using StringReloads.Hook.Base;
using StringReloads.Hook.Others;
+using StringReloads.Tools;
namespace StringReloads.AutoInstall
{
@@ -95,51 +97,35 @@ private void EnsureMultiArch()
if (!AltSRLAvailable)
Log.Warning($"Failed to Find the {AltSRL}.");
else
- Patcher.Tools.ThirdPartyApplyPatch(AltExe, AltSRL);
+ Patcher.ExeTools.ThirdPartyApplyPatch(AltExe, AltSRL);
- Patcher.Tools.ApplyWrapperPatch(CurrentSRL);
+ Patcher.ExeTools.ApplyWrapperPatch(CurrentSRL);
}
void SearchOffset()
{
- var Imports = ModuleInfo.GetModuleImports((byte*)Config.GameBaseAddress);
- var ShellExec = Imports.Where(x => x.Function == "ShellExecuteA").Single();
-
- var Bitness = Environment.Is64BitProcess ? 64 : 32;
-
ulong? Address = null;
- byte?[] Pattern = new byte?[] { 0xFF, 0x15 };
- foreach (var lAddress in Scan(Pattern))
+ var Bitness = Environment.Is64BitProcess ? 64 : 32;
+ foreach (var lAddress in Scanner.SearchExportCall("ShellExecuteA"))
{
- Decoder Dissassembler = Decoder.Create(Bitness, new MemoryCodeReader((byte*)lAddress));
- Dissassembler.IP = (ulong)lAddress;
- var Call = Dissassembler.PeekDecode();
- var MemAddress = Call.IPRelativeMemoryAddress;
- //if (Environment.Is64BitProcess)
- // MemAddress = Call.IPRelativeMemoryAddress + (ulong)lAddress + 6ul;
-
- if (MemAddress == (ulong)ShellExec.ImportAddress)
+ var Pattern = new byte?[] { 0xE8 };
+ foreach (var lgetTextAddress in Scanner.Scan(Pattern, (ulong)lAddress, true))
{
- Log.Debug($"Call dword ptr ds: [&ShellExecuteA] - Found at 0x{lAddress:X16}");
- Pattern = new byte?[] { 0xE8 };
- foreach (var lgetTextAddress in Scan(Pattern, (ulong)lAddress, true))
+ var Dissassembler = Decoder.Create(Bitness, new MemoryCodeReader((byte*)lgetTextAddress));
+ Dissassembler.IP = (ulong)lgetTextAddress;
+ var Call = Dissassembler.PeekDecode();
+
+ var Immediate = Environment.Is64BitProcess ? Call.MemoryDisplacement64 : Call.MemoryDisplacement32;
+
+ if (Address != Immediate)
{
- Dissassembler = Decoder.Create(Bitness, new MemoryCodeReader((byte*)lgetTextAddress));
- Dissassembler.IP = (ulong)lgetTextAddress;
- Call = Dissassembler.PeekDecode();
-
- var Immediate = Environment.Is64BitProcess ? Call.MemoryDisplacement64 : Call.MemoryDisplacement32;
-
- if (Address != Immediate)
- {
- Address = Immediate;
- continue;
- }
- else
- break;
+ Address = Immediate;
+ continue;
}
- break;
+ else
+ break;
}
+ break;
}
if (Address == null)
@@ -152,82 +138,6 @@ void SearchOffset()
Log.Debug($"CMVS Injection Offset Found: 0x{Offset:X16}");
}
- private IEnumerable Scan(byte?[] Pattern, ulong? BeginAddress = null, bool Up = false)
- {
- ulong? Match = BeginAddress;
- do
- {
- if (Match != null) {
-
- if (Up)
- Match--;
- else
- Match++;
-
- }
-
- Match = Up ? ScanUp(Pattern, Match ?? ulong.MaxValue) : ScanDown(Pattern, Match ?? 0ul);
-
- if (Match != null)
- yield return (long)Match;
-
- } while (Match != null);
- }
-
- private unsafe ulong? ScanUp(byte?[] Pattern, ulong BeginAddress = ulong.MaxValue)
- {
- var Info = ModuleInfo.GetCodeInfo((byte*)Config.GameBaseAddress);
-
- if (BeginAddress == ulong.MaxValue)
- BeginAddress = (ulong)Info.CodeAddress + Info.CodeSize;
-
- ulong CodeAdd = (ulong)Info.CodeAddress;
- for (ulong i = BeginAddress - CodeAdd; i >= 0; i--)
- {
- byte* pAddress = (byte*)(CodeAdd + i);
- if (!CheckPattern(pAddress, Pattern))
- continue;
-
- return (ulong)pAddress;
- }
-
- return null;
- }
-
- private unsafe ulong? ScanDown(byte?[] Pattern, ulong BeginAddress = 0)
- {
- var Info = ModuleInfo.GetCodeInfo((byte*)Config.GameBaseAddress);
-
- ulong CodeAdd = (ulong)Info.CodeAddress;
- ulong CodeLen = Info.CodeSize;
-
- if (BeginAddress != 0)
- BeginAddress = BeginAddress - CodeAdd;
-
- for (ulong i = BeginAddress; i < CodeLen; i++)
- {
- byte* pAddress = (byte*)(CodeAdd + i);
- if (!CheckPattern(pAddress, Pattern))
- continue;
-
- return (ulong)pAddress;
- }
-
- return null;
- }
-
- private bool CheckPattern(byte* Buffer, byte?[] Pattern)
- {
- for (int i = 0; i < Pattern.Length; i++)
- {
- if (Pattern[i] == null)
- continue;
- byte bPattern = Pattern[i].Value;
- if (bPattern != Buffer[i])
- return false;
- }
- return true;
- }
public bool IsCompatible() => Config.Default.GameExePath.GetFilenameNoExt().ToLowerInvariant().StartsWith("cmvs");
diff --git a/StringReloads/AutoInstall/Favorite.cs b/StringReloads/AutoInstall/Favorite.cs
new file mode 100644
index 0000000..47aef84
--- /dev/null
+++ b/StringReloads/AutoInstall/Favorite.cs
@@ -0,0 +1,117 @@
+using Iced.Intel;
+using StringReloads.Engine;
+using StringReloads.Engine.Interface;
+using StringReloads.Engine.String;
+using StringReloads.Engine.Unmanaged;
+using StringReloads.Hook;
+using StringReloads.Hook.Base;
+using StringReloads.Tools;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace StringReloads.AutoInstall
+{
+ internal class Favorite : IAutoInstall
+ {
+ long Address = 0;
+ Register Register = Register.None;
+ Intercept Interceptor = null;
+
+ public string Name => "FavoriteViewPoint";
+
+ public void Install()
+ {
+ DetectHookConfig();
+ Interceptor.Install();
+ }
+ unsafe void DetectHookConfig()
+ {
+ var Settings = Config.Default.GetValues("Favorite");
+
+ if (Settings == null)
+ {
+ foreach (var Import in Scanner.SearchExportCall("lstrlenA"))
+ {
+ for (var i = 0; i < 10; i++)
+ {
+ var Addr = Import - i;
+ var Dissassembler = Decoder.Create(32, new MemoryCodeReader((byte*)Addr));
+ Dissassembler.IP = (ulong)Addr;
+ var Instruction = Dissassembler.PeekDecode();
+
+ switch (Instruction.Code)
+ {
+ case Code.Push_r32:
+ Register = Instruction.Op0Register;
+ break;
+ default:
+ continue;
+ }
+
+ Address = Import;
+ break;
+ }
+ }
+
+ Settings = new Dictionary();
+ Settings["Address"] = Address.ToString();
+ Settings["Register"] = ((int)Register).ToString();
+ Config.Default.SetValues("Favorite", Settings);
+ Config.Default.SaveSettings();
+ }
+ else
+ {
+ Address = long.Parse(Settings["address"]);
+ Register = (Register)int.Parse(Settings["register"]);
+ }
+
+ Interceptor = new ManagedInterceptor((void*)Address, onIntercepted);
+ Log.Information($"Favorite Engine Intercepted at 0x{Address:X8}");
+ }
+
+ private unsafe void onIntercepted(ref ulong ESP, ref ulong EAX, ref ulong ECX, ref ulong EDX, ref ulong EBX, ref ulong EBP, ref ulong ESI, ref ulong EDI)
+ {
+ switch (Register)
+ {
+ case Register.EBX:
+ EBX = (ulong)EntryPoint.Process((void*)EBX);
+ EAX = (ulong)((CString)EBX).Count();
+ break;
+ case Register.ECX:
+ ECX = (ulong)EntryPoint.Process((void*)ECX);
+ EAX = (ulong)((CString)ECX).Count();
+ break;
+ case Register.EDX:
+ EDX = (ulong)EntryPoint.Process((void*)EDX);
+ EAX = (ulong)((CString)EDX).Count();
+ break;
+ case Register.EBP:
+ EBP = (ulong)EntryPoint.Process((void*)EBP);
+ EAX = (ulong)((CString)EBP).Count();
+ break;
+ case Register.ESI:
+ ESI = (ulong)EntryPoint.Process((void*)ESI);
+ EAX = (ulong)((CString)ESI).Count();
+ break;
+ case Register.EDI:
+ EDI = (ulong)EntryPoint.Process((void*)EDI);
+ EAX = (ulong)((CString)EDI).Count();
+ break;
+ }
+ }
+
+ public bool IsCompatible()
+ {
+ return Directory.GetFiles(".\\", "*.hcb").Any();
+ }
+
+ public void Uninstall()
+ {
+ Interceptor.Uninstall();
+ }
+ }
+}
diff --git a/StringReloads/AutoInstall/Patcher/Tools.cs b/StringReloads/AutoInstall/Patcher/ExeTools.cs
similarity index 99%
rename from StringReloads/AutoInstall/Patcher/Tools.cs
rename to StringReloads/AutoInstall/Patcher/ExeTools.cs
index dba1252..75e47b6 100644
--- a/StringReloads/AutoInstall/Patcher/Tools.cs
+++ b/StringReloads/AutoInstall/Patcher/ExeTools.cs
@@ -8,7 +8,7 @@
namespace StringReloads.AutoInstall.Patcher
{
- static class Tools
+ static class ExeTools
{
static readonly string[] SupportedWrappers = new string[] {
"version.dll", "d3d9.dll", "d3d10.dll", "d3d11.dll",
diff --git a/StringReloads/AutoInstall/SoftPalMethodA.cs b/StringReloads/AutoInstall/SoftPalMethodA.cs
index 9da6a6e..153b151 100644
--- a/StringReloads/AutoInstall/SoftPalMethodA.cs
+++ b/StringReloads/AutoInstall/SoftPalMethodA.cs
@@ -92,7 +92,7 @@ public bool IsCompatible()
}
}
- Tools.ApplyWrapperPatch();
+ ExeTools.ApplyWrapperPatch();
SetupMode = true;
}
@@ -289,11 +289,11 @@ private void FinishSetup() {
Config.SaveSettings();
ShowMessageBox($"Perfect! SRL is now ready to use! Enjoy!", "StringReloads", MBButtons.Ok, MBIcon.Information);
- Tools.Restart();
+ ExeTools.Restart();
}
private void SetupFailed() {
ShowMessageBox("Hmm, looks like SRL can't perform Auto-Install in this game at the moment. Please report an issue in the GitHub repository.", "StringReloads Setup Wizard", MBButtons.Ok, MBIcon.Error);
- Tools.Restart();
+ ExeTools.Restart();
}
}
}
diff --git a/StringReloads/AutoInstall/SoftPalMethodB.cs b/StringReloads/AutoInstall/SoftPalMethodB.cs
index 46f936e..2163383 100644
--- a/StringReloads/AutoInstall/SoftPalMethodB.cs
+++ b/StringReloads/AutoInstall/SoftPalMethodB.cs
@@ -35,7 +35,7 @@ public bool IsCompatible()
SoftPal_DrawText.AltName = true;
}
- Tools.ApplyWrapperPatch();
+ ExeTools.ApplyWrapperPatch();
Hook = new SoftPal_DrawText();
return true;
}
diff --git a/StringReloads/Engine/SRL.cs b/StringReloads/Engine/SRL.cs
index d4ebbc0..6a9af38 100644
--- a/StringReloads/Engine/SRL.cs
+++ b/StringReloads/Engine/SRL.cs
@@ -92,7 +92,8 @@ where typeof(IPlugin).IsAssignableFrom(Typ) && !Typ.IsInterface
new SoftPalMethodA(),
new SoftPalMethodB(),
new CMVS(),
- new EntisGLS()
+ new EntisGLS(),
+ new Favorite()
};
internal IEncoding[] Encodings = new IEncoding[0];
diff --git a/StringReloads/Engine/String/CString.cs b/StringReloads/Engine/String/CString.cs
index 1981512..e8dfbe2 100644
--- a/StringReloads/Engine/String/CString.cs
+++ b/StringReloads/Engine/String/CString.cs
@@ -15,6 +15,8 @@ private CString(byte* Ptr) : base(Ptr) { }
public Encoding WriteEncoding = Config.Default.WriteEncoding;
public static implicit operator CString(byte* Ptr) => new CString(Ptr);
+ public static implicit operator CString(long Ptr) => new CString((byte*)Ptr);
+ public static implicit operator CString(ulong Ptr) => new CString((byte*)Ptr);
public static implicit operator CString(void* Ptr) => new CString((byte*)Ptr);
public static implicit operator CString(IntPtr Ptr) => new CString((byte*)Ptr.ToPointer());
diff --git a/StringReloads/Engine/String/WCString.cs b/StringReloads/Engine/String/WCString.cs
index e891d38..b76069d 100644
--- a/StringReloads/Engine/String/WCString.cs
+++ b/StringReloads/Engine/String/WCString.cs
@@ -12,6 +12,8 @@ public unsafe class WCString : StringBufferW
private WCString(byte* Ptr) : base(Ptr) { }
public static implicit operator WCString(byte* Ptr) => new WCString(Ptr);
+ public static implicit operator WCString(long Ptr) => new WCString((byte*)Ptr);
+ public static implicit operator WCString(ulong Ptr) => new WCString((byte*)Ptr);
public static implicit operator WCString(void* Ptr) => new WCString((byte*)Ptr);
public static implicit operator WCString(IntPtr Ptr) => new WCString((byte*)Ptr.ToPointer());
diff --git a/StringReloads/Engine/Unmanaged/ModuleInfo.cs b/StringReloads/Engine/Unmanaged/ModuleInfo.cs
index e6b5dcd..b0ad423 100644
--- a/StringReloads/Engine/Unmanaged/ModuleInfo.cs
+++ b/StringReloads/Engine/Unmanaged/ModuleInfo.cs
@@ -23,6 +23,7 @@ public unsafe static CodeInfo GetCodeInfo(byte* hModule)
};
}
+ public unsafe static ImportEntry[] GetMainModuleImports() => GetModuleImports((byte*)Config.GameBaseAddress);
public unsafe static ImportEntry[] GetModuleImports(byte* Module)
{
if (Module == null)
@@ -149,6 +150,7 @@ public unsafe struct ImportEntry
/// The Address of the Imported Function
///
public void* FunctionAddress;
+
}
}
\ No newline at end of file
diff --git a/StringReloads/Hook/Base/MemoryCodeReader.cs b/StringReloads/Hook/Base/MemoryCodeReader.cs
index 4030e1d..cd82508 100644
--- a/StringReloads/Hook/Base/MemoryCodeReader.cs
+++ b/StringReloads/Hook/Base/MemoryCodeReader.cs
@@ -12,6 +12,7 @@ public unsafe class MemoryCodeReader : CodeReader, IDisposable
uint Length = 0;
+ public MemoryCodeReader(long Address) : this((byte*)Address) { }
public MemoryCodeReader(void* Address) : this((byte*)Address) { }
public MemoryCodeReader(byte* Address) : this(Address, 0) { }
public MemoryCodeReader(void* Address, uint Length) : this((byte*)Address, Length) { }
diff --git a/StringReloads/Tools/Scanner.cs b/StringReloads/Tools/Scanner.cs
new file mode 100644
index 0000000..5501f0d
--- /dev/null
+++ b/StringReloads/Tools/Scanner.cs
@@ -0,0 +1,115 @@
+using StringReloads.Engine.Unmanaged;
+using StringReloads.Engine;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using StringReloads.Hook.Base;
+using Iced.Intel;
+
+namespace StringReloads.Tools
+{
+ internal unsafe class Scanner
+ {
+ public static IEnumerable SearchExportCall(string Function)
+ {
+ var Imports = ModuleInfo.GetMainModuleImports();
+ var Func = Imports.Where(x => x.Function == Function).Single();
+
+ var Bitness = Environment.Is64BitProcess ? 64 : 32;
+
+ byte?[] Pattern = new byte?[] { 0xFF, 0x15 };
+ foreach (var lAddress in Scan(Pattern))
+ {
+ Decoder Dissassembler = Decoder.Create(Bitness, new MemoryCodeReader(lAddress));
+ Dissassembler.IP = (ulong)lAddress;
+ var Call = Dissassembler.PeekDecode();
+ var MemAddress = Call.IPRelativeMemoryAddress;
+
+ if (MemAddress == GetAddress(Func))
+ {
+ Log.Debug($"Call dword ptr ds: [&{Function}] - Found at 0x{lAddress:X16}");
+ yield return lAddress;
+ }
+ }
+ }
+ private unsafe static ulong GetAddress(ImportEntry Entry) => (ulong)Entry.ImportAddress;
+
+ internal static IEnumerable Scan(byte?[] Pattern, ulong? BeginAddress = null, bool Up = false)
+ {
+ ulong? Match = BeginAddress;
+ do
+ {
+ if (Match != null)
+ {
+
+ if (Up)
+ Match--;
+ else
+ Match++;
+
+ }
+
+ Match = Up ? ScanUp(Pattern, Match ?? ulong.MaxValue) : ScanDown(Pattern, Match ?? 0ul);
+
+ if (Match != null)
+ yield return (long)Match;
+
+ } while (Match != null);
+ }
+
+ private static ulong? ScanUp(byte?[] Pattern, ulong BeginAddress = ulong.MaxValue)
+ {
+ var Info = ModuleInfo.GetCodeInfo((byte*)Config.GameBaseAddress);
+
+ if (BeginAddress == ulong.MaxValue)
+ BeginAddress = (ulong)Info.CodeAddress + Info.CodeSize;
+
+ ulong CodeAdd = (ulong)Info.CodeAddress;
+ for (ulong i = BeginAddress - CodeAdd; i >= 0; i--)
+ {
+ byte* pAddress = (byte*)(CodeAdd + i);
+ if (!CheckPattern(pAddress, Pattern))
+ continue;
+
+ return (ulong)pAddress;
+ }
+
+ return null;
+ }
+
+ private static ulong? ScanDown(byte?[] Pattern, ulong BeginAddress = 0)
+ {
+ var Info = ModuleInfo.GetCodeInfo((byte*)Config.GameBaseAddress);
+
+ ulong CodeAdd = (ulong)Info.CodeAddress;
+ ulong CodeLen = Info.CodeSize;
+
+ if (BeginAddress != 0)
+ BeginAddress = BeginAddress - CodeAdd;
+
+ for (ulong i = BeginAddress; i < CodeLen; i++)
+ {
+ byte* pAddress = (byte*)(CodeAdd + i);
+ if (!CheckPattern(pAddress, Pattern))
+ continue;
+
+ return (ulong)pAddress;
+ }
+
+ return null;
+ }
+
+ private static bool CheckPattern(byte* Buffer, byte?[] Pattern)
+ {
+ for (int i = 0; i < Pattern.Length; i++)
+ {
+ if (Pattern[i] == null)
+ continue;
+ byte bPattern = Pattern[i].Value;
+ if (bPattern != Buffer[i])
+ return false;
+ }
+ return true;
+ }
+ }
+}