Skip to content

Commit

Permalink
Support for Modded Softpal Engine
Browse files Browse the repository at this point in the history
Leyline was released with a manual text hook that applies the wordwrap in the text, but since o SRL do wordwrap too, the leyline patch, who do the wordwrap after the SRL, didn't check if the line is already with wordwrap and tries again, breaking everything, this update fix that by detecting the wordwrap patch and hooking after the game wordwrap is done.
  • Loading branch information
marcussacana committed Mar 30, 2023
1 parent 69a42dd commit 99b3923
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 39 deletions.
130 changes: 104 additions & 26 deletions StringReloads/AutoInstall/SoftPalMethodA.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using StringReloads.Engine.String;
using StringReloads.AutoInstall.Patcher;
using StringReloads.Engine.Interface;
using Iced.Intel;

namespace StringReloads.AutoInstall
{
Expand All @@ -18,7 +19,8 @@ unsafe class SoftPalMethodA : IAutoInstall

bool SetupMode = false;
CallerTracer Tracer;
Interceptor Intercepter;
Intercept Intercepter;
SoftPal_PalSpriteCreateText FontDrawTextHook;

Config Config => Config.Default;
Dictionary<string, string> SoftPalConfig;
Expand All @@ -39,9 +41,13 @@ public void Install()
}

StackOffset = SoftPalConfig["stackoffset"].ToUInt32();
FromEBP = SoftPalConfig["fromebp"].ToBoolean();
void* hFunc = (void*)((int)Config.GameBaseAddress + SoftPalConfig["hookoffset"].ToInt32());
Intercepter = new Interceptor(hFunc, new InterceptDelegate(DrawTextHook));
Intercepter = new ManagedInterceptor(hFunc, new ManagedInterceptDelegate(DrawTextHook));
Intercepter.Install();

FontDrawTextHook = new SoftPal_PalSpriteCreateText();
FontDrawTextHook.Install();
}

public bool IsCompatible()
Expand Down Expand Up @@ -96,10 +102,15 @@ public bool IsCompatible()
public void Uninstall()
{
Intercepter.Uninstall();
FontDrawTextHook.Uninstall();
}

void DrawTextHook(void* Stack) {
uint* Address = ((uint*)Stack) + StackOffset;
void DrawTextHook(ref ulong ESP, ref ulong EAX, ref ulong ECX, ref ulong EDX, ref ulong EBX, ref ulong EBP, ref ulong ESI, ref ulong EDI)
{
uint* BasePtr = (FromEBP ? ((uint*)EBP) : ((uint*)ESP));

uint* Address = BasePtr + StackOffset;

*Address = (uint)EntryPoint.Process((void*)*Address);
}

Expand All @@ -121,22 +132,66 @@ void SetupStepA(void* Caller) {

Log.Debug($"PalFontDrawText Referenced by 0x{(uint)hFunc:X8}");


var MemReader = new MemoryCodeReader(hFunc);
var Diassembler = Decoder.Create(32, MemReader);
for (int i = 0, x = 0; i < 30; i++)
{
var CurrentOffset = Diassembler.IP;
var Instruction = Diassembler.Decode();
switch (x)
{
case 0:
if (Instruction.IsCallNear && Instruction.IPRelativeMemoryAddress == Diassembler.IP)
x++;
break;
case 1:
if (Instruction.Code == Code.Pop_r32)
x++;
else
x = 0;
break;
case 2:
if (Instruction.Code == Code.Add_EAX_imm32)
x++;
else
x = 0;
break;
case 3:
if (Instruction.IsJmpNearIndirect)
x++;
else
x = 0;
break;
case 4:
if (Instruction.Code == Code.Nopd)
break;

Log.Debug("SoftPAL Wordwrap Patched Engine Detected");
hFunc = (byte*)hFunc + CurrentOffset;
x++;
break;
}
}

SoftPalConfig = new Dictionary<string, string>();
SoftPalConfig["HookOffset"] = ((uint)hFunc - (uint)Config.GameBaseAddress).ToString();
SoftPalConfig["EngineSize"] = new FileInfo(Config.GameExePath).Length.ToString();

Tracer.Uninstall();


Intercepter = new Interceptor(hFunc, new InterceptDelegate(SetupStepB));
Intercepter = new ManagedInterceptor(hFunc, new ManagedInterceptDelegate(SetupStepB));
Intercepter.Install();
ShowMessageBox($"Very well, looks like SRL can perform Auto-Install in this game!\nSRL now needs to gather more intricate information from the game.\nPlease press OK and continue to the next in-game dialogue.", "StringReloads Setup Wizard", MBButtons.Ok, MBIcon.Information);
}

bool FromEBP;
int LastOffset = 0;
bool WaitingConfirmation = false;
bool FirstTry = true;
void SetupStepB(void* ESP) {
void SetupStepB(ref ulong ESP, ref ulong EAX, ref ulong ECX, ref ulong EDX, ref ulong EBX, ref ulong EBP, ref ulong ESI, ref ulong EDI)
{
if (FirstTry)
ShowMessageBox("SRL will now need your help to confirm if the dialogue has been found.\nThe program will display the game dialogue; when the correct dialogue is shown, press YES. If nothing is shown or if you see corrupted text, press NO.\nTake note that if you don't set the proper game encoding inside SRL.ini, the correct dialogue will most likely never be shown.\nPress OK if you have read and understood the above.", "StringReloads Setup Wizard", MBButtons.Ok, MBIcon.Information);

Expand All @@ -152,36 +207,33 @@ void SetupStepB(void* ESP) {
}

uint* Stack = (uint*)ESP;
uint* BaseStack = (uint*)EBP;
int StackOffset = LastOffset;
while (StackOffset < 50) {
while (StackOffset < 80)
{
var RStack = (Stack + StackOffset);
if (IsBadCodePtr((void*)*RStack)) {
Log.Debug($"Bad Pointer: 0x{*RStack:X8}");
StackOffset++;
continue;
var BStack = (BaseStack + StackOffset);

if (GuessOffset(RStack, StackOffset))
{
FromEBP = false;
break;
}

Log.Debug($"Guessing Offset: 0x{(uint)RStack:X8} (+{StackOffset}) (0x{*RStack:X8})");
CString Str = (byte*)*RStack;
if (Str.Count() > 0 && Str.Count() < 500)
if (GuessOffset(BStack, StackOffset))
{
var Reply = ShowMessageBox(Str, "Is this the dialogue?", MBButtons.YesNo, MBIcon.Question);
if (Reply == MBResult.Yes)
{
Str = "Looks Like everything is working,<br>Continue to the next game dialogue!";
*RStack = (uint)(void*)Str;
WaitingConfirmation = true;
break;
}
FromEBP = true;
break;
}

StackOffset++;
continue;
}


LastOffset = StackOffset;

if (StackOffset == 50)
if (StackOffset == 60)
SetupFailed();


Expand All @@ -191,20 +243,46 @@ void SetupStepB(void* ESP) {
}
}

private bool GuessOffset(uint* Address, int Offset)
{
if (IsBadCodePtr((void*)*Address))
{
return false;
}

Log.Debug($"Guessing Offset: 0x{(uint)Address:X8} (+{StackOffset}) (0x{*Address:X8})");
CString Str = (byte*)*Address;
if (Str.Count() > 0 && Str.Count() < 500)
{
var Reply = ShowMessageBox(Str, "Is this the dialogue?", MBButtons.YesNo, MBIcon.Question);
if (Reply == MBResult.Yes)
{
Str = "Looks Like everything is working,<br>Continue to the next game dialogue!";
*Address = (uint)(void*)Str;
WaitingConfirmation = true;
return true;
}
}

return false;
}

private void FinishSetup() {
SoftPalConfig["StackOffset"] = LastOffset.ToString();

SoftPalConfig["FromEBP"] = FromEBP ? "true" : "false";

if (Config.BreakLine != "<br>") {
var Rst = ShowMessageBox("Looks like you aren't using the tag <br> as breakline rigth now, You want use it?", "StringReloader Setup Wizard", MBButtons.YesNo, MBIcon.Question);
if (Rst == MBResult.Yes)
Config.SetValue("BreakLine", "<br>");
}

if (!Config.Overwrite)
if (!Config.SafeOverwrite)
{
var Rst = ShowMessageBox("It looks like you are not in memory overwrite mode, which is probably needed for this game, do you want to enable memory overwrite mode?", "StringReloader Setup Wizard", MBButtons.YesNo, MBIcon.Question);
var Rst = ShowMessageBox("It looks like you are not in safe memory overwrite mode, which is probably needed for this game, do you want to enable safe memory overwrite mode?", "StringReloader Setup Wizard", MBButtons.YesNo, MBIcon.Question);
if (Rst == MBResult.Yes)
Config.SetValue("Overwrite", "true");
Config.SetValue("SafeOverwrite", "true");
}

Config.SetValues("SoftPal", SoftPalConfig);
Expand Down
5 changes: 4 additions & 1 deletion StringReloads/Engine/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ public string[] IniLines {
public bool HeapAlloc => ((bool?)(_HeapAlloc ??= GetValue("HeapAlloc").ToBoolean())).Value;

bool? _Overwrite = null;
public bool Overwrite => ((bool?)(_Overwrite ??= GetValue("Overwrite").ToBoolean())).Value;
public bool Overwrite => ((bool?)(_Overwrite ??= GetValue("Overwrite").ToBoolean())).Value && !SafeOverwrite;

bool? _SafeOverwrite = null;
public bool SafeOverwrite => ((bool?)(_SafeOverwrite ??= GetValue("SafeOverwrite").ToBoolean())).Value;

bool? _ReloadRegexCaptures = null;
public bool ReloadRegexCaptures => ((bool?)(_ReloadRegexCaptures ??= GetValue("ReloadRegexCaptures").ToBoolean())).Value;
Expand Down
10 changes: 8 additions & 2 deletions StringReloads/Engine/SRL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ public Database CurrentDatabase {
}

if (Settings.Overwrite && Output != null && ((string)Output) != null)
return (CString)Alloc.Overwrite(Output.ToArray(), pString, pString.Count());
return Alloc.Overwrite(Output, pString);

if (Settings.SafeOverwrite && Output != null && ((string)Output) != null)
return Alloc.SafeOverwrite(Output, pString);

if (Settings.HeapAlloc && Output != null && ((string)Output) != null)
return (CString)Alloc.CreateHeap(Output.ToArray());
Expand Down Expand Up @@ -177,7 +180,10 @@ public Database CurrentDatabase {
}

if (Settings.Overwrite && Output != null && ((string)Output) != null)
return (WCString)Alloc.Overwrite(Output.ToArray(), pString, pString.Count());
return Alloc.Overwrite(Output, pString);

if (Settings.SafeOverwrite && Output != null && ((string)Output) != null)
return Alloc.SafeOverwrite(Output, pString);

if (Settings.HeapAlloc && Output != null && ((string)Output) != null)
return (WCString)Alloc.CreateHeap(Output.ToArray());
Expand Down
85 changes: 81 additions & 4 deletions StringReloads/Engine/Unmanaged/Alloc.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
using Antlr.Runtime.Tree;
using StringReloads.Engine.String;
using System;
using System.Linq;
using System.Runtime.InteropServices;

namespace StringReloads.Engine.Unmanaged
Expand All @@ -13,13 +16,87 @@ unsafe static class Alloc
return hAlloc;
}

public static void* Overwrite(byte[] Data, void* Address, int OriginalSize) {
public static CString Overwrite(CString String, CString OriString)
{
var Data = String.ToArray();

Array.Resize(ref Data, Data.Length + 1);//Null Termination

var OriginalSize = OriString.Count();

return OveriteInternal(Data, OriString, OriginalSize);
}

public static WCString Overwrite(WCString String, WCString OriString)
{
var Data = String.ToArray();

Array.Resize(ref Data, Data.Length + 2);//Null Termination

var OriginalSize = OriString.Count();

return OveriteInternal(Data, OriString, OriginalSize);
}

private static void* OveriteInternal(byte[] Data, void* OriString, int OriginalSize)
{
if (Data.Length < OriginalSize)
{
Array.Resize(ref Data, OriginalSize);
}

Marshal.Copy(Data, 0, new IntPtr(OriString), Data.Length);
return OriString;
}

public static CString SafeOverwrite(CString String, CString OriString)
{
var Data = String.ToArray();

Array.Resize(ref Data, Data.Length + 1);//Null Termination

var OriginalSize = OriString.Count();

return SafeOveriteInternal(Data, String, OriString, OriginalSize);
}

public static WCString SafeOverwrite(WCString String, WCString OriString)
{
var Data = String.ToArray();

Array.Resize(ref Data, Data.Length + 2);//Null Termination

var OriginalSize = OriString.Count();

return SafeOveriteInternal(Data, String, OriString, OriginalSize);
}

private static void* SafeOveriteInternal(byte[] Data, void* String, void* OriString, int OriginalSize)
{
if (Data.Length < OriginalSize)
{
Array.Resize(ref Data, OriginalSize);
}
Marshal.Copy(Data, 0, new IntPtr(Address), Data.Length);
return Address;

if (Data.Length > OriginalSize)
{
byte* pOriEnd = (byte*)OriString + OriginalSize;

int EmptyBufferSize = 0;
while (*pOriEnd == 0)
{
EmptyBufferSize++;
pOriEnd++;
}

if (EmptyBufferSize + OriginalSize < Data.Length)
{
return String;
}
}

Marshal.Copy(Data, 0, new IntPtr(OriString), Data.Length);
return OriString;
}

[DllImport("kernel32.dll", SetLastError = true)]
Expand Down
Loading

0 comments on commit 99b3923

Please sign in to comment.