mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-11-27 07:44:02 +08:00
rgss300
This commit is contained in:
parent
621fd866e6
commit
406438bb42
@ -1,16 +1,20 @@
|
||||
#include"RPGMakerRGSS3.h"
|
||||
namespace { // unnamed
|
||||
#include "RPGMakerRGSS3.h"
|
||||
namespace
|
||||
{ // unnamed
|
||||
|
||||
namespace RGSS3 {
|
||||
namespace RGSS3
|
||||
{
|
||||
|
||||
namespace Private {
|
||||
std::vector<std::wstring> glob(const std::wstring& relpath)
|
||||
namespace Private
|
||||
{
|
||||
std::vector<std::wstring> glob(const std::wstring &relpath)
|
||||
{
|
||||
std::wstring path = std::wstring(MAX_PATH, 0);
|
||||
GetModuleFileNameW(nullptr, &path[0], MAX_PATH);
|
||||
|
||||
size_t i = relpath.rfind(L'/');
|
||||
if (i != std::wstring::npos) {
|
||||
if (i != std::wstring::npos)
|
||||
{
|
||||
std::wstring dir_path = path + L"/" + relpath.substr(0, i);
|
||||
WIN32_FIND_DATAW find_data;
|
||||
HANDLE hFind = FindFirstFileW((dir_path + L"/*").c_str(), &find_data);
|
||||
@ -18,9 +22,11 @@ namespace Private {
|
||||
return {};
|
||||
|
||||
std::vector<std::wstring> results;
|
||||
do {
|
||||
do
|
||||
{
|
||||
if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
|
||||
PathMatchSpecW(find_data.cFileName, relpath.substr(i + 1).c_str())) {
|
||||
PathMatchSpecW(find_data.cFileName, relpath.substr(i + 1).c_str()))
|
||||
{
|
||||
results.push_back(dir_path + L"/" + find_data.cFileName);
|
||||
}
|
||||
} while (FindNextFileW(hFind, &find_data));
|
||||
@ -28,14 +34,16 @@ namespace Private {
|
||||
|
||||
return results;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
WIN32_FIND_DATAW find_data;
|
||||
HANDLE hFind = FindFirstFileW(relpath.c_str(), &find_data);
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
return {};
|
||||
|
||||
std::vector<std::wstring> results;
|
||||
do {
|
||||
do
|
||||
{
|
||||
if (!(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
results.push_back(find_data.cFileName);
|
||||
} while (FindNextFileW(hFind, &find_data));
|
||||
@ -46,27 +54,29 @@ namespace Private {
|
||||
}
|
||||
std::wstring getDllModuleName()
|
||||
{
|
||||
for (const auto &dll: glob(L"System/RGSS3*.dll"))
|
||||
for (const auto &dll : glob(L"System/RGSS3*.dll"))
|
||||
if (::GetModuleHandleW((LPCWSTR)dll.c_str()))
|
||||
return dll;
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Private
|
||||
} // namespace Private
|
||||
|
||||
bool getMemoryRange(ULONG *startAddress, ULONG *stopAddress)
|
||||
{
|
||||
bool getMemoryRange(ULONG *startAddress, ULONG *stopAddress)
|
||||
{
|
||||
std::wstring module = Private::getDllModuleName();
|
||||
if (module.empty())
|
||||
return false;
|
||||
auto [_1,_2]=Util::QueryModuleLimits(GetModuleHandle(module.c_str()));
|
||||
*startAddress=_1;*stopAddress=_2;
|
||||
auto [_1, _2] = Util::QueryModuleLimits(GetModuleHandle(module.c_str()));
|
||||
*startAddress = _1;
|
||||
*stopAddress = _2;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ScenarioHook {
|
||||
namespace ScenarioHook
|
||||
{
|
||||
|
||||
/**
|
||||
/**
|
||||
* Sample game:
|
||||
* - Mogeko Castle with RGSS 3.01
|
||||
* - 魔鎧の少女騎士エルトリンデ with RGSS 3.02
|
||||
@ -504,11 +514,12 @@ namespace ScenarioHook {
|
||||
* 10180BAA CC INT3
|
||||
* 10180BAB CC INT3
|
||||
*/
|
||||
namespace Private {
|
||||
namespace Private
|
||||
{
|
||||
|
||||
//enum { MaxTextSize = 0x1000 };
|
||||
//char oldText_[MaxTextSize + 1]; // 1 extra 0 that is always 0
|
||||
//size_t oldSize_;
|
||||
// enum { MaxTextSize = 0x1000 };
|
||||
// char oldText_[MaxTextSize + 1]; // 1 extra 0 that is always 0
|
||||
// size_t oldSize_;
|
||||
|
||||
struct HookArgument
|
||||
{
|
||||
@ -519,44 +530,47 @@ namespace Private {
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return Engine::isAddressReadable(type) && *type
|
||||
&& size && size < 1500
|
||||
&& Engine::isAddressWritable(text, size + 1) && *text
|
||||
&& text[size] == 0 && ::strlen(text) == size // validate size
|
||||
return Engine::isAddressReadable(type) && *type && size && size < 1500 && Engine::isAddressWritable(text, size + 1) && *text && text[size] == 0 && ::strlen(text) == size // validate size
|
||||
//&& !::strchr(text, '/')
|
||||
&& !all_ascii(text);
|
||||
}
|
||||
|
||||
//int size() const { return (*type >> 0xe) & 0x1f; }
|
||||
// int size() const { return (*type >> 0xe) & 0x1f; }
|
||||
};
|
||||
|
||||
inline bool _trims(const wchar_t &ch)
|
||||
{ return ch <= 127 ||std::isspace(ch,std::locale("ja_JP.SJIS")); }
|
||||
{
|
||||
return ch <= 127 || std::isspace(ch, std::locale("ja_JP.SJIS"));
|
||||
}
|
||||
|
||||
std::wstring trim(const std::wstring& text, std::wstring* prefix = nullptr, std::wstring* suffix = nullptr)
|
||||
std::wstring trim(const std::wstring &text, std::wstring *prefix = nullptr, std::wstring *suffix = nullptr)
|
||||
{
|
||||
if (text.empty() ||
|
||||
!_trims(text[0]) && !_trims(text[text.size() - 1]))
|
||||
return text;
|
||||
std::wstring ret = text;
|
||||
if (_trims(ret[0])) {
|
||||
if (_trims(ret[0]))
|
||||
{
|
||||
int pos = 1;
|
||||
for (; pos < ret.size() && _trims(ret[pos]); pos++);
|
||||
for (; pos < ret.size() && _trims(ret[pos]); pos++)
|
||||
;
|
||||
if (prefix)
|
||||
*prefix = ret.substr(0,pos);
|
||||
*prefix = ret.substr(0, pos);
|
||||
ret = ret.substr(pos);
|
||||
}
|
||||
if (!ret.empty() && _trims(ret[ret.size() - 1])) {
|
||||
if (!ret.empty() && _trims(ret[ret.size() - 1]))
|
||||
{
|
||||
int pos = ret.size() - 2;
|
||||
for (; pos >= 0 && _trims(ret[pos]); pos--);
|
||||
for (; pos >= 0 && _trims(ret[pos]); pos--)
|
||||
;
|
||||
if (suffix)
|
||||
*suffix = ret.substr(pos + 1);
|
||||
ret = ret.substr(0,pos + 1);
|
||||
ret = ret.substr(0, pos + 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//bool textsContains(const QSet<QString> &texts, const QString &text)
|
||||
// bool textsContains(const QSet<QString> &texts, const QString &text)
|
||||
//{
|
||||
// if (texts.contains(text))
|
||||
// return true;
|
||||
@ -565,19 +579,21 @@ namespace Private {
|
||||
// if (texts.contains(it))
|
||||
// return true;
|
||||
// return false;
|
||||
//}
|
||||
// }
|
||||
|
||||
int guessTextRole(const std::wstring &text)
|
||||
{
|
||||
enum { MaxNameSize = 100 };
|
||||
enum : wchar_t {
|
||||
w_square_open = 0x3010 /* 【 */
|
||||
, w_square_close = 0x3011 /* 】 */
|
||||
enum
|
||||
{
|
||||
MaxNameSize = 100
|
||||
};
|
||||
if (text.size() > 2
|
||||
&& text.size() < MaxNameSize
|
||||
&& text[0] == w_square_open
|
||||
&& text[text.size() - 1] == w_square_close)
|
||||
enum : wchar_t
|
||||
{
|
||||
w_square_open = 0x3010 /* 【 */
|
||||
,
|
||||
w_square_close = 0x3011 /* 】 */
|
||||
};
|
||||
if (text.size() > 2 && text.size() < MaxNameSize && text[0] == w_square_open && text[text.size() - 1] == w_square_close)
|
||||
return Engine::NameRole;
|
||||
return Engine::ScenarioRole;
|
||||
}
|
||||
@ -587,36 +603,43 @@ namespace Private {
|
||||
LPCSTR oldText_;
|
||||
size_t oldSize_;
|
||||
std::unordered_set<std::wstring> texts_;
|
||||
void hookafter2(hook_stack*s,void* data1, size_t len){
|
||||
void hookafter2(hook_stack *s, void *data1, size_t len)
|
||||
{
|
||||
|
||||
enum { RecentTextCapacity = 4 };
|
||||
enum
|
||||
{
|
||||
RecentTextCapacity = 4
|
||||
};
|
||||
static std::vector<std::wstring> recentTexts_; // used to eliminate recent duplicates
|
||||
|
||||
auto arg = (HookArgument *)s->stack[0]; // arg1
|
||||
if (arg && arg->isValid()) { // && (quint8)arg->text[0] > 127) { // skip translate text beginning with ascii character
|
||||
std::wstring oldText =StringToWideString(std::string(arg->text, arg->size),CP_UTF8).value(),// QString::fromUtf8(arg->text, arg->size),
|
||||
if (arg && arg->isValid())
|
||||
{ // && (quint8)arg->text[0] > 127) { // skip translate text beginning with ascii character
|
||||
std::wstring oldText = StringToWideString(std::string(arg->text, arg->size), CP_UTF8).value(), // QString::fromUtf8(arg->text, arg->size),
|
||||
prefix,
|
||||
suffix,
|
||||
trimmedText = trim(oldText, &prefix, &suffix);
|
||||
|
||||
if (!trimmedText.empty() && (texts_.find(trimmedText)==texts_.end())) { // skip text beginning with ascii character
|
||||
if (!trimmedText.empty() && (texts_.find(trimmedText) == texts_.end()))
|
||||
{ // skip text beginning with ascii character
|
||||
|
||||
//ULONG split = arg->unknown2[0]; // always 2
|
||||
//ULONG split = s->stack[0]; // return address
|
||||
std::wstring newText =std::wstring((wchar_t*)data1,len/2);
|
||||
// ULONG split = arg->unknown2[0]; // always 2
|
||||
// ULONG split = s->stack[0]; // return address
|
||||
std::wstring newText = std::wstring((wchar_t *)data1, len / 2);
|
||||
|
||||
if (newText != trimmedText) {
|
||||
if (newText != trimmedText)
|
||||
{
|
||||
texts_.insert(newText);
|
||||
texts_.insert(trim(newText)); // in case there are leading/trailing English letters in the translation
|
||||
|
||||
if (!prefix.empty())
|
||||
newText.insert(0,prefix);
|
||||
newText.insert(0, prefix);
|
||||
if (!suffix.empty())
|
||||
newText.append(suffix);
|
||||
|
||||
//texts_.insert(newText);
|
||||
// texts_.insert(newText);
|
||||
|
||||
data_ = WideStringToString(newText, CP_UTF8);// newText.toUtf8();
|
||||
data_ = WideStringToString(newText, CP_UTF8); // newText.toUtf8();
|
||||
|
||||
arg_ = arg;
|
||||
oldSize_ = arg->size;
|
||||
@ -629,46 +652,53 @@ namespace Private {
|
||||
}
|
||||
}
|
||||
}
|
||||
bool hookBefore(hook_stack*s,void* data1, size_t* len1,uintptr_t*role)
|
||||
bool hookBefore(hook_stack *s, void *data1, size_t *len1, uintptr_t *role)
|
||||
{
|
||||
|
||||
enum { RecentTextCapacity = 4 };
|
||||
enum
|
||||
{
|
||||
RecentTextCapacity = 4
|
||||
};
|
||||
static std::vector<std::wstring> recentTexts_; // used to eliminate recent duplicates
|
||||
|
||||
auto arg = (HookArgument *)s->stack[0]; // arg1
|
||||
if (arg && arg->isValid()) { // && (quint8)arg->text[0] > 127) { // skip translate text beginning with ascii character
|
||||
std::wstring oldText =StringToWideString(std::string(arg->text, arg->size),CP_UTF8).value(),// QString::fromUtf8(arg->text, arg->size),
|
||||
if (arg && arg->isValid())
|
||||
{ // && (quint8)arg->text[0] > 127) { // skip translate text beginning with ascii character
|
||||
std::wstring oldText = StringToWideString(std::string(arg->text, arg->size), CP_UTF8).value(), // QString::fromUtf8(arg->text, arg->size),
|
||||
prefix,
|
||||
suffix,
|
||||
trimmedText = trim(oldText, &prefix, &suffix);
|
||||
|
||||
if (!trimmedText.empty() && (texts_.find(trimmedText)==texts_.end())) { // skip text beginning with ascii character
|
||||
if (!trimmedText.empty() && (texts_.find(trimmedText) == texts_.end()))
|
||||
{ // skip text beginning with ascii character
|
||||
|
||||
const bool sendAllowed = (std::find(recentTexts_.begin(),recentTexts_.end(),oldText)==recentTexts_.end());
|
||||
if (sendAllowed) {
|
||||
const bool sendAllowed = (std::find(recentTexts_.begin(), recentTexts_.end(), oldText) == recentTexts_.end());
|
||||
if (sendAllowed)
|
||||
{
|
||||
recentTexts_.push_back(oldText);
|
||||
if (recentTexts_.size() > RecentTextCapacity)
|
||||
recentTexts_.erase(recentTexts_.begin());
|
||||
}
|
||||
|
||||
//ULONG split = arg->unknown2[0]; // always 2
|
||||
//ULONG split = s->stack[0]; // return address
|
||||
// ULONG split = arg->unknown2[0]; // always 2
|
||||
// ULONG split = s->stack[0]; // return address
|
||||
std::wstring newText;
|
||||
write_string_overwrite(data1,len1,trimmedText);
|
||||
write_string_overwrite(data1, len1, trimmedText);
|
||||
return 1;
|
||||
|
||||
if (newText != trimmedText) {
|
||||
if (newText != trimmedText)
|
||||
{
|
||||
texts_.insert(newText);
|
||||
texts_.insert(trim(newText)); // in case there are leading/trailing English letters in the translation
|
||||
|
||||
if (!prefix.empty())
|
||||
newText.insert(0,prefix);
|
||||
newText.insert(0, prefix);
|
||||
if (!suffix.empty())
|
||||
newText.append(suffix);
|
||||
|
||||
//texts_.insert(newText);
|
||||
// texts_.insert(newText);
|
||||
|
||||
data_ = WideStringToString(newText, CP_UTF8);// newText.toUtf8();
|
||||
data_ = WideStringToString(newText, CP_UTF8); // newText.toUtf8();
|
||||
|
||||
arg_ = arg;
|
||||
oldSize_ = arg->size;
|
||||
@ -683,9 +713,10 @@ namespace Private {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool hookAfter(hook_stack*s,void* data1, size_t* len1,uintptr_t*role)
|
||||
bool hookAfter(hook_stack *s, void *data1, size_t *len1, uintptr_t *role)
|
||||
{
|
||||
if (arg_)
|
||||
{
|
||||
if (arg_) {
|
||||
arg_->size = oldSize_;
|
||||
arg_->text = oldText_;
|
||||
//::strcpy(arg_->text, oldText_);
|
||||
@ -693,16 +724,16 @@ namespace Private {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace Private
|
||||
} // namespace Private
|
||||
|
||||
bool attach(ULONG startAddress, ULONG stopAddress) // attach scenario
|
||||
{
|
||||
bool attach(ULONG startAddress, ULONG stopAddress) // attach scenario
|
||||
{
|
||||
const uint8_t bytes[] = {
|
||||
0x8b,0x54,0x24, 0x24, // 1004155c 8b5424 24 mov edx,dword ptr ss:[esp+0x24]
|
||||
0x8b,0x02, // 10041560 8b02 mov eax,dword ptr ds:[edx]
|
||||
0x8b,0xc8, // 10041562 8bc8 mov ecx,eax
|
||||
0x83,0xc4, 0x0c, // 10041564 83c4 0c add esp,0xc
|
||||
0x81,0xe1, 0x00,0x20,0x00,0x00 // 10041567 81e1 00200000 and ecx,0x2000
|
||||
0x8b, 0x54, 0x24, 0x24, // 1004155c 8b5424 24 mov edx,dword ptr ss:[esp+0x24]
|
||||
0x8b, 0x02, // 10041560 8b02 mov eax,dword ptr ds:[edx]
|
||||
0x8b, 0xc8, // 10041562 8bc8 mov ecx,eax
|
||||
0x83, 0xc4, 0x0c, // 10041564 83c4 0c add esp,0xc
|
||||
0x81, 0xe1, 0x00, 0x20, 0x00, 0x00 // 10041567 81e1 00200000 and ecx,0x2000
|
||||
};
|
||||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
|
||||
if (!addr)
|
||||
@ -710,33 +741,35 @@ bool attach(ULONG startAddress, ULONG stopAddress) // attach scenario
|
||||
addr = MemDbg::findEnclosingAlignedFunction(addr);
|
||||
if (!addr)
|
||||
return false;
|
||||
//addr = MemDbg::findPushAddress(addr, startAddress, stopAddress);
|
||||
//addr = 0x10041557;
|
||||
//addr = 0x100414a0;
|
||||
//addr = 0x10056BC0;
|
||||
//addr = 0x1002e5e1;
|
||||
// addr = MemDbg::findPushAddress(addr, startAddress, stopAddress);
|
||||
// addr = 0x10041557;
|
||||
// addr = 0x100414a0;
|
||||
// addr = 0x10056BC0;
|
||||
// addr = 0x1002e5e1;
|
||||
addr = MemDbg::findNearCallAddress(addr, startAddress, stopAddress);
|
||||
if (!addr)
|
||||
return false;
|
||||
//return winhook::hook_both(addr, Private::hookBefore, Private::hookAfter);
|
||||
// return winhook::hook_both(addr, Private::hookBefore, Private::hookAfter);
|
||||
HookParam hp;
|
||||
hp.address=addr;
|
||||
hp.hook_before=Private::hookBefore;
|
||||
hp.hook_after=Private::hookafter2;
|
||||
hp.type=USING_STRING|CODEC_UTF16|EMBED_ABLE;
|
||||
hp.hook_font=F_GetGlyphOutlineW;
|
||||
auto succ=NewHook(hp,"EmbedRGSS3");
|
||||
hp.address=addr+5;
|
||||
hp.hook_before=Private::hookAfter;
|
||||
hp.type=HOOK_EMPTY|EMBED_ABLE;
|
||||
succ|=NewHook(hp,"EmbedRGSS3");
|
||||
hp.address = addr;
|
||||
hp.hook_before = Private::hookBefore;
|
||||
hp.hook_after = Private::hookafter2;
|
||||
hp.type = USING_STRING | CODEC_UTF16 | EMBED_ABLE;
|
||||
hp.hook_font = F_GetGlyphOutlineW;
|
||||
auto succ = NewHook(hp, "EmbedRGSS3");
|
||||
hp.address = addr + 5;
|
||||
hp.hook_before = Private::hookAfter;
|
||||
hp.type = HOOK_EMPTY | EMBED_ABLE;
|
||||
succ |= NewHook(hp, "EmbedRGSS3");
|
||||
return succ;
|
||||
}
|
||||
} // namespace ScenarioHook
|
||||
}
|
||||
} // namespace ScenarioHook
|
||||
|
||||
namespace ChoiceHook {
|
||||
namespace ChoiceHook
|
||||
{
|
||||
|
||||
namespace Private {
|
||||
namespace Private
|
||||
{
|
||||
|
||||
struct HookArgument
|
||||
{
|
||||
@ -747,22 +780,21 @@ namespace Private {
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return text
|
||||
&& Engine::isAddressReadable(text) && *text
|
||||
&& Engine::isAddressWritable(text, ::strlen(text));
|
||||
return text && Engine::isAddressReadable(text) && *text && Engine::isAddressWritable(text, ::strlen(text));
|
||||
}
|
||||
|
||||
//int size() const { return (*type >> 0xe) & 0x1f; }
|
||||
// int size() const { return (*type >> 0xe) & 0x1f; }
|
||||
};
|
||||
|
||||
bool hookBefore(hook_stack*s,void* data1, size_t* len1,uintptr_t*role)
|
||||
bool hookBefore(hook_stack *s, void *data1, size_t *len1, uintptr_t *role)
|
||||
{
|
||||
* role = Engine::OtherRole ;
|
||||
*role = Engine::OtherRole;
|
||||
auto arg = (HookArgument *)s->stack[2]; // arg2
|
||||
if (arg->isValid()) {
|
||||
auto oldText =StringToWideString(std::string(arg->text),CP_UTF8).value();
|
||||
if (arg->isValid())
|
||||
{
|
||||
auto oldText = StringToWideString(std::string(arg->text), CP_UTF8).value();
|
||||
auto split = s->stack[0]; // return address
|
||||
write_string_overwrite(data1,len1,oldText);
|
||||
write_string_overwrite(data1, len1, oldText);
|
||||
return 1;
|
||||
// std::wstring newText = EngineController::instance()->dispatchTextWSTD(oldText, role, sig);
|
||||
// if (newText != oldText) {
|
||||
@ -773,25 +805,28 @@ namespace Private {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void hookafter2(hook_stack*s,void* data1, size_t len){
|
||||
void hookafter2(hook_stack *s, void *data1, size_t len)
|
||||
{
|
||||
{
|
||||
auto arg = (HookArgument *)s->stack[2]; // arg2
|
||||
if (arg->isValid()) {
|
||||
auto oldText =StringToWideString(std::string(arg->text),CP_UTF8).value();
|
||||
if (arg->isValid())
|
||||
{
|
||||
auto oldText = StringToWideString(std::string(arg->text), CP_UTF8).value();
|
||||
auto split = s->stack[0]; // return address
|
||||
std::wstring old=oldText;
|
||||
std::wstring old = oldText;
|
||||
|
||||
std::wstring newText =std::wstring((wchar_t*)data1,len/2);
|
||||
if (newText != oldText) {
|
||||
std::wstring newText = std::wstring((wchar_t *)data1, len / 2);
|
||||
if (newText != oldText)
|
||||
{
|
||||
if (newText.size() < oldText.size())
|
||||
::memset(arg->text, 0, ::strlen(arg->text));
|
||||
::strcpy(arg->text, WideStringToString(newText, CP_UTF8).c_str());// newText.toUtf8());
|
||||
::strcpy(arg->text, WideStringToString(newText, CP_UTF8).c_str()); // newText.toUtf8());
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Private
|
||||
} // namespace Private
|
||||
|
||||
/**
|
||||
/**
|
||||
* Sample game: Mogeko Castle
|
||||
*
|
||||
* One of the caller of the three GetGlyphOutlineW
|
||||
@ -870,77 +905,87 @@ namespace Private {
|
||||
* 100075ED C3 RETN
|
||||
* 100075EE CC INT3
|
||||
*/
|
||||
ULONG functionAddress; // the function address being hooked
|
||||
bool attach(ULONG startAddress, ULONG stopAddress) // attach other text
|
||||
{
|
||||
ULONG functionAddress; // the function address being hooked
|
||||
bool attach(ULONG startAddress, ULONG stopAddress) // attach other text
|
||||
{
|
||||
const uint8_t bytes[] = {
|
||||
0x89,0x45, 0xfc, // 100075b5 8945 fc mov dword ptr ss:[ebp-0x4],eax
|
||||
0x8b,0x4d, 0xfc, // 100075b8 8b4d fc mov ecx,dword ptr ss:[ebp-0x4]
|
||||
0x8b,0x51, 0x10, // 100075bb 8b51 10 mov edx,dword ptr ds:[ecx+0x10]
|
||||
0x89,0x55, 0xe8, // 100075be 8955 e8 mov dword ptr ss:[ebp-0x18],edx
|
||||
0x8b,0x45, 0xe8 // 100075c1 8b45 e8 mov eax,dword ptr ss:[ebp-0x18]
|
||||
0x89, 0x45, 0xfc, // 100075b5 8945 fc mov dword ptr ss:[ebp-0x4],eax
|
||||
0x8b, 0x4d, 0xfc, // 100075b8 8b4d fc mov ecx,dword ptr ss:[ebp-0x4]
|
||||
0x8b, 0x51, 0x10, // 100075bb 8b51 10 mov edx,dword ptr ds:[ecx+0x10]
|
||||
0x89, 0x55, 0xe8, // 100075be 8955 e8 mov dword ptr ss:[ebp-0x18],edx
|
||||
0x8b, 0x45, 0xe8 // 100075c1 8b45 e8 mov eax,dword ptr ss:[ebp-0x18]
|
||||
};
|
||||
if (ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress))
|
||||
if (addr = MemDbg::findEnclosingAlignedFunction(addr)){
|
||||
if (addr = MemDbg::findEnclosingAlignedFunction(addr))
|
||||
{
|
||||
HookParam hp;
|
||||
hp.address=addr;
|
||||
hp.hook_before=Private::hookBefore;
|
||||
hp.hook_after=Private::hookafter2;
|
||||
hp.type=USING_STRING|CODEC_UTF16|EMBED_ABLE;
|
||||
hp.hook_font=F_GetGlyphOutlineW;
|
||||
hp.address = addr;
|
||||
hp.hook_before = Private::hookBefore;
|
||||
hp.hook_after = Private::hookafter2;
|
||||
hp.type = USING_STRING | CODEC_UTF16 | EMBED_ABLE;
|
||||
hp.hook_font = F_GetGlyphOutlineW;
|
||||
|
||||
functionAddress = addr;
|
||||
return NewHook(hp,"EmbedRGSS3Choice");
|
||||
return NewHook(hp, "EmbedRGSS3Choice");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ChoiceHook
|
||||
} // namespace ChoiceHook
|
||||
|
||||
}
|
||||
namespace OtherHook {
|
||||
|
||||
namespace Private {
|
||||
|
||||
bool hookBefore(hook_stack*s,void* data1, size_t* len1,uintptr_t*role)
|
||||
}
|
||||
namespace OtherHook
|
||||
{
|
||||
{* role = Engine::OtherRole ;};
|
||||
|
||||
namespace Private
|
||||
{
|
||||
|
||||
bool hookBefore(hook_stack *s, void *data1, size_t *len1, uintptr_t *role)
|
||||
{
|
||||
{
|
||||
*role = Engine::OtherRole;
|
||||
};
|
||||
auto retaddr = s->stack[0];
|
||||
if (retaddr > ChoiceHook::Private::functionAddress && retaddr - ChoiceHook::Private::functionAddress < 0xff)
|
||||
return 0; // skip translate already-hooked function
|
||||
|
||||
auto text = (LPWSTR)s->stack[1]; // arg1
|
||||
if (text && *text) {
|
||||
if (text && *text)
|
||||
{
|
||||
std::wstring oldText(text);
|
||||
if (oldText.size() > 1) {
|
||||
write_string_overwrite(data1,len1,oldText);
|
||||
if (oldText.size() > 1)
|
||||
{
|
||||
write_string_overwrite(data1, len1, oldText);
|
||||
return 1;
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void hookafter2(hook_stack*s,void* data1, size_t len){
|
||||
void hookafter2(hook_stack *s, void *data1, size_t len)
|
||||
{
|
||||
{
|
||||
auto retaddr = s->stack[0];
|
||||
if (retaddr > ChoiceHook::Private::functionAddress && retaddr - ChoiceHook::Private::functionAddress < 0xff)
|
||||
return ; // skip translate already-hooked function
|
||||
return; // skip translate already-hooked function
|
||||
|
||||
auto text = (LPWSTR)s->stack[1]; // arg1
|
||||
if (text && *text) {
|
||||
if (text && *text)
|
||||
{
|
||||
std::wstring oldText(text);
|
||||
if (oldText.size() > 1) {
|
||||
if (oldText.size() > 1)
|
||||
{
|
||||
|
||||
std::wstring newText =std::wstring((wchar_t*)data1,len/2); ;
|
||||
std::wstring newText = std::wstring((wchar_t *)data1, len / 2);
|
||||
;
|
||||
if (newText != oldText)
|
||||
::wcscpy(text, (LPCWSTR)newText.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Private
|
||||
} // namespace Private
|
||||
|
||||
/**
|
||||
/**
|
||||
* Sample game: Mogeko Castle
|
||||
*
|
||||
* There are three GetGlyphIndicesW.
|
||||
@ -1328,24 +1373,25 @@ namespace Private {
|
||||
* 1000C69C CC INT3
|
||||
* 1000C69D CC INT3
|
||||
*/
|
||||
ULONG functionAddress; // the beginning of the function being hooked
|
||||
bool attach(ULONG startAddress, ULONG stopAddress) // attach other text
|
||||
{
|
||||
ULONG functionAddress; // the beginning of the function being hooked
|
||||
bool attach(ULONG startAddress, ULONG stopAddress) // attach other text
|
||||
{
|
||||
ULONG addr = MemDbg::findCallerAddressAfterInt3((ULONG)::GetGlyphOutlineW, startAddress, stopAddress);
|
||||
if(addr==0)return 0;
|
||||
if (addr == 0)
|
||||
return 0;
|
||||
HookParam hp;
|
||||
hp.address=addr;
|
||||
hp.hook_before=Private::hookBefore;
|
||||
hp.hook_after=Private::hookafter2;
|
||||
hp.type=USING_STRING|CODEC_UTF16|EMBED_ABLE;
|
||||
hp.hook_font=F_GetGlyphOutlineW;
|
||||
hp.address = addr;
|
||||
hp.hook_before = Private::hookBefore;
|
||||
hp.hook_after = Private::hookafter2;
|
||||
hp.type = USING_STRING | CODEC_UTF16 | EMBED_ABLE;
|
||||
hp.hook_font = F_GetGlyphOutlineW;
|
||||
|
||||
return NewHook(hp,"EmbedRGSS3Other");
|
||||
}
|
||||
}
|
||||
} // namespace OtherHook
|
||||
return NewHook(hp, "EmbedRGSS3Other");
|
||||
}
|
||||
}
|
||||
} // namespace OtherHook
|
||||
|
||||
} // namespace RGSS3Hook
|
||||
} // namespace RGSS3Hook
|
||||
|
||||
#if 0
|
||||
|
||||
@ -1391,10 +1437,10 @@ bool attach()
|
||||
|
||||
#endif // 0
|
||||
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
bool RPGMakerRGSS3::attach_function() {
|
||||
bool RPGMakerRGSS3::attach_function()
|
||||
{
|
||||
ULONG startAddress, stopAddress;
|
||||
if (!RGSS3::getMemoryRange(&startAddress, &stopAddress))
|
||||
return false;
|
||||
@ -1406,3 +1452,23 @@ bool RPGMakerRGSS3::attach_function() {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RPGMakerRGSS300::attach_function()
|
||||
{
|
||||
trigger_fun = [](LPVOID addr1, hook_stack *stack)
|
||||
{
|
||||
if (addr1 != GetGlyphOutlineW)
|
||||
return false;
|
||||
auto addr = stack->retaddr;
|
||||
addr = MemDbg::findEnclosingAlignedFunction(addr);
|
||||
if (addr == 0)
|
||||
return false;
|
||||
HookParam hp;
|
||||
hp.address = addr;
|
||||
hp.type = USING_STRING | CODEC_UTF16;
|
||||
hp.offset = get_stack(1);
|
||||
NewHook(hp, "RGSS300.dll");
|
||||
return true;
|
||||
};
|
||||
return GetModuleHandle(L"RGSS300.dll");
|
||||
}
|
@ -1,12 +1,26 @@
|
||||
|
||||
|
||||
class RPGMakerRGSS3:public ENGINE{
|
||||
public:
|
||||
RPGMakerRGSS3(){
|
||||
class RPGMakerRGSS3 : public ENGINE
|
||||
{
|
||||
public:
|
||||
RPGMakerRGSS3()
|
||||
{
|
||||
|
||||
check_by=CHECK_BY::FILE_ALL;
|
||||
check_by_target=check_by_list{L"*.rgss3a",L"System/RGSS3*.dll"};
|
||||
is_engine_certain=false;
|
||||
check_by = CHECK_BY::FILE_ALL;
|
||||
check_by_target = check_by_list{L"*.rgss3a", L"System/RGSS3*.dll"};
|
||||
is_engine_certain = false;
|
||||
};
|
||||
bool attach_function();
|
||||
};
|
||||
|
||||
class RPGMakerRGSS300 : public ENGINE
|
||||
{
|
||||
public:
|
||||
RPGMakerRGSS300()
|
||||
{
|
||||
check_by = CHECK_BY::FILE_ALL;
|
||||
check_by_target = check_by_list{L"System/RGSS300.dll"};
|
||||
is_engine_certain = false;
|
||||
};
|
||||
bool attach_function();
|
||||
};
|
@ -392,5 +392,6 @@ std::vector<ENGINE *> check_engines()
|
||||
//
|
||||
//
|
||||
new DISCOVERY,
|
||||
new RPGMakerRGSS300,
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user