diff --git a/Source/Sonic Frontiers/Libraries/Converse.hmm b/Source/Sonic Frontiers/Libraries/Converse.hmm index 91a91f9b..1446cdc9 100644 --- a/Source/Sonic Frontiers/Libraries/Converse.hmm +++ b/Source/Sonic Frontiers/Libraries/Converse.hmm @@ -1,4 +1,4 @@ -Library "Converse" by "Hyper & M&M" +Library "Converse" by "Hyper & RagdollClash" { #include "Helpers" noemit @@ -81,95 +81,73 @@ Library "Converse" by "Hyper & M&M" UNMANAGED_FUNCTION(long, RedirectString, long in_pCnvrsKey, long in_pCnvrsText, int in_cnvrsTextLength) { - if (in_pCnvrsKey == 0) - return in_pCnvrsText; + var result = string.Empty; + var cnvrsKey = string.Empty; + var cnvrsText = string.Empty; - string cnvrsKey = Marshal.PtrToStringAnsi((IntPtr)in_pCnvrsKey); - string cnvrsText = in_pCnvrsText == 0 ? string.Empty : Marshal.PtrToStringUni((IntPtr)in_pCnvrsText, in_cnvrsTextLength); - nint pData = 0; + if (in_pCnvrsKey != 0) + cnvrsKey = Marshal.PtrToStringAnsi((nint)in_pCnvrsKey); - void MarshalString(string in_str) + if (in_pCnvrsText != 0 && in_cnvrsTextLength > 0) + result = cnvrsText = Marshal.PtrToStringUni((nint)in_pCnvrsText, in_cnvrsTextLength); + + foreach (var entry in _redirectedStrings) { - int length = sizeof(int) + ((in_str.Length + 1) * sizeof(short)); - - pData = Marshal.AllocHGlobal(length); - - var wideTextStart = pData + sizeof(int); - var wideText = Encoding.Unicode.GetBytes(in_str); - var wideTextEnd = wideTextStart + wideText.Length; - - Marshal.Copy(wideText, 0, wideTextStart, wideText.Length); + if (entry.Key != cnvrsKey) + continue; - // Null terminator. - Marshal.WriteInt16(wideTextEnd, 0); - - in_pCnvrsText = (long)pData; - in_cnvrsTextLength = length; + result = entry.IsRawAttributes + ? entry.Value + : WriteAttributes(entry.Value); } - string redirectResult = cnvrsText; + foreach (var entry in _converseNotifyActions) { - foreach (var entry in _redirectedStrings) - { - if (entry.Key == cnvrsKey) - { - redirectResult = entry.IsRawAttributes - ? entry.Value - : WriteAttributes(entry.Value); - } - } + result = entry.Key(cnvrsKey, result, ReadAttributes(result)); - if (redirectResult != cnvrsText) - MarshalString(redirectResult); + if (!entry.Value) + result = WriteAttributes(result); } - string notifyResult = redirectResult; + foreach (var entry in _replacementStrings) { - foreach (var entry in _converseNotifyActions) - { - notifyResult = entry.Key(cnvrsKey, notifyResult, ReadAttributes(notifyResult)); - - if (!entry.Value) - notifyResult = WriteAttributes(notifyResult); - } + var text = entry.IsRawAttributes + ? result + : ReadAttributes(result); + + if (string.IsNullOrEmpty(text)) + break; + + text = entry.IsRegex + ? new Regex(entry.Pattern).Replace(text, entry.Replacement) + : text.Replace(entry.Pattern, entry.Replacement); - if (notifyResult != cnvrsText) - MarshalString(notifyResult); + result = entry.IsRawAttributes + ? text + : WriteAttributes(text); } - foreach (var entry in _replacementStrings) - { - string replacementResult = entry.IsRawAttributes - ? notifyResult - : ReadAttributes(notifyResult); + // Fallback for null strings. + if (string.IsNullOrEmpty(result)) + result = " "; - // Perform replacement operation. - replacementResult = entry.IsRegex - ? new Regex(entry.Pattern).Replace(replacementResult, entry.Replacement) - : replacementResult.Replace(entry.Pattern, entry.Replacement); + if (result != cnvrsText) + { + var length = result.Length; + var buffer = Marshal.AllocHGlobal(sizeof(int) + (length * sizeof(char))); - replacementResult = entry.IsRawAttributes - ? replacementResult - : WriteAttributes(replacementResult); + Marshal.WriteInt32(buffer, length); + Marshal.Copy(result.ToCharArray(), 0, buffer + sizeof(int), length); - if (replacementResult != cnvrsText) - MarshalString(replacementResult); + return (long)buffer; } - if (pData != 0) - { - // Wide string length. - Marshal.WriteInt32(pData, in_cnvrsTextLength); - Marshal.FreeHGlobal(pData); - } - else if (in_pCnvrsText != 0) - { - /* No need to reallocate all strings, - return -1 and reload original pointer. */ - in_pCnvrsText = -1; - } + return -1L; + } - return in_pCnvrsText; + UNMANAGED_FUNCTION(void, FreeString, long in_pCnvrsText) + { + Marshal.FreeHGlobal((nint)in_pCnvrsText); } [LibraryInitializer] @@ -177,34 +155,54 @@ Library "Converse" by "Hyper & M&M" { if (_isInitialised) return; - + void ConverseHook(long in_addr) { WriteAsmHook ( $@" - mov r8d, dword ptr [rsp + 0x1C8 + 0xFFFFFFFFFFFFFE60] - mov rcx, rsi + mov r8d, dword ptr [rsp + 0x28] ; Converse Text Length + mov rdx, qword ptr [rsp + 0x20] ; Converse Text + mov rcx, rsi ; Converse Key mov rax, {GET_UNMANAGED_FUNCTION_PTR(RedirectString)} call rax - cmp rax, 0 - jne notNull - mov rdx, {TO_WSTRING_PTR(" ")} - mov r8d, 1 - jmp exit - notNull: - cmp rax, -1 - je notCustom - lea rdx, qword ptr [rax + 4] - mov r8d, dword ptr [rax] - jmp exit - notCustom: - mov rdx, qword ptr [rsp + 0x1C8 + 0xFFFFFFFFFFFFFE58] - mov r8d, dword ptr [rsp + 0x1C8 + 0xFFFFFFFFFFFFFE60] - exit: - mov rcx, qword ptr [rdi + 8] + mov rbx, rax ; Preserve result in RBX. + + ; Check if result is -1 or null, use original string if so. + cmp rbx, -1 + je original + test rbx, rbx + jz original + + mov r8d, dword ptr [rbx] ; Redirected Text Length + lea rdx, qword ptr [rbx + 0x04] ; Redirected Text + jmp cnvrs + + original: + mov r8d, dword ptr [rsp + 0x28] ; Converse Text Length + mov rdx, qword ptr [rsp + 0x20] ; Converse Text + + cnvrs: + mov rcx, qword ptr [rdi + 0x08] ; Allocator + push rbx mov rax, {Memory.ReadCall(in_addr + 0x09)} call rax + pop rbx + + ; Check if redirect result is -1, skip free if so. + cmp rbx, -1 + je exit + + ; Free redirected string. + mov rcx, rbx + mov rbx, rax + sub rsp, 32 + mov rax, {GET_UNMANAGED_FUNCTION_PTR(FreeString)} + call rax + add rsp, 32 + mov rax, rbx ; Restore original result. + + exit: ", in_addr, @@ -215,10 +213,10 @@ Library "Converse" by "Hyper & M&M" WriteNop ( - /* v1.41: 0x14DA978CD */ + /* v1.42: 0x14D89049D */ ScanSignature ( - "\x0F\x84\xE8\x00\x00\x00\x48\x8D\x8F\xC8\x00\x00\x00", + "\x0F\x84\xF7\x00\x00\x00\x48\x8D\x8F\xC8\x00\x00\x00", "xxxxxxxxxxxxx" ), @@ -227,7 +225,7 @@ Library "Converse" by "Hyper & M&M" ConverseHook ( - /* 0x14DC858D3 */ + /* v1.42: 0x14D8904D1 */ ScanSignature ( "\x44\x8B\x44\x24\x28\x48\x8B\x4F\x08\xE8\xCC\xCC\xCC\xCC\x48\x89\xC2\x48\x8B\x8F\xF0\x00\x00\x00", @@ -249,7 +247,7 @@ Library "Converse" by "Hyper & M&M" ConverseHook ( - /* 0x14DC8593A */ + /* v1.42: 0x14D89053C */ ScanSignature ( "\x44\x8B\x44\x24\x28\x48\x8B\x4F\x08\xE8\xCC\xCC\xCC\xCC\x48\x89\xC2\x48\x89\xF9", @@ -259,7 +257,7 @@ Library "Converse" by "Hyper & M&M" fixed (sbyte* p_language = &_language) { - /* 0x140AFA200 */ + /* v1.42: 0x146E3A1CC */ var sig = Memory.ReadCall ( ScanSignature @@ -368,12 +366,14 @@ Library "Converse" by "Hyper & M&M" /// The input string where known binary Converse attributes are replaced with XML keys. public string ReadAttributes(string in_text) { + if (string.IsNullOrEmpty(in_text)) + return in_text; + var result = new StringBuilder(); - var chars = Encoding.Unicode.GetChars(Encoding.Unicode.GetBytes(in_text)); - for (int i = 0; i < chars.Length; i++) + for (int i = 0; i < in_text.Length; i++) { - var c = chars[i]; + var c = in_text[i]; switch ((AttributeType)(c & 0xF00F)) { @@ -385,15 +385,15 @@ Library "Converse" by "Hyper & M&M" result.Append(""); break; } - + i++; var nameLength = GetNameLength(c) - 2; - var argb = (uint)(chars[i] << 16 | (chars[i + 1])); + var argb = (uint)(in_text[i] << 16 | (in_text[i + 1])); i += 2; - var name = new string(chars, i, nameLength); + var name = in_text.Substring(i, nameLength); result.Append($""); @@ -401,13 +401,13 @@ Library "Converse" by "Hyper & M&M" break; } - + case AttributeType.Variable: { i++; var nameLength = GetNameLength(c); - var name = new string(chars, i, nameLength); + var name = in_text.Substring(i, nameLength); result.Append($""); @@ -415,13 +415,13 @@ Library "Converse" by "Hyper & M&M" break; } - + case AttributeType.Image: { i++; var nameLength = GetNameLength(c); - var name = new string(chars, i, nameLength); + var name = in_text.Substring(i, nameLength); result.Append($""); @@ -447,6 +447,9 @@ Library "Converse" by "Hyper & M&M" /// The input string where XML Converse attributes are replaced with known binary attributes. public string WriteAttributes(string in_text, XElement in_element = null) { + if (string.IsNullOrEmpty(in_text)) + return in_text; + var result = new StringBuilder(); var root = in_element ?? XElement.Parse($"{in_text}"); @@ -531,7 +534,7 @@ Library "Converse" by "Hyper & M&M" public string OmitAttributes(string in_text) { var result = new StringBuilder(); - var chars = Encoding.Unicode.GetChars(Encoding.Unicode.GetBytes(in_text)); + var chars = in_text.ToCharArray(); for (int i = 0; i < chars.Length; i++) { @@ -601,6 +604,7 @@ Library "Converse" by "Hyper & M&M" if (first != null) { first.Value = in_text; + first.IsRawAttributes = in_isRawAttributes; return; } @@ -624,6 +628,8 @@ Library "Converse" by "Hyper & M&M" if (first != null) { first.Replacement = in_replacement; + first.IsRegex = in_isRegex; + first.IsRawAttributes = in_isRawAttributes; return; }