恍兮惚兮 dc7eaafcc7 .
2024-12-16 15:48:47 +08:00

678 lines
22 KiB
C++

#include <CommCtrl.h>
#include <TlHelp32.h>
#include <stdio.h>
#include <thread>
#include <fstream>
#include "host.h"
#include "textthread.h"
#include "LunaHost.h"
#include "http.hpp"
extern std::vector<std::tuple<SUPPORT_LANG, std::wstring, std::vector<std::string>>> lang_map;
bool sendclipboarddata_i(const std::wstring &text, HWND hwnd)
{
if (!OpenClipboard((HWND)hwnd))
return false;
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, (text.size() + 1) * sizeof(wchar_t));
memcpy(GlobalLock(hMem), text.c_str(), (text.size() + 1) * sizeof(wchar_t));
EmptyClipboard();
SetClipboardData(CF_UNICODETEXT, hMem);
GlobalUnlock(hMem);
CloseClipboard();
return true;
}
bool sendclipboarddata(const std::wstring &text, HWND hwnd)
{
for (int loop = 0; loop < 10; loop++)
{
auto succ = sendclipboarddata_i(text, hwnd);
if (succ)
return true;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
return false;
}
void LunaHost::on_close()
{
hasstoped = true;
savesettings();
delete configs;
auto _attachedprocess = attachedprocess;
for (auto pid : _attachedprocess)
{
Host::DetachProcess(pid);
}
if (_attachedprocess.size())
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
void LunaHost::savesettings()
{
configs->set("ToClipboard", check_toclipboard);
configs->set("ToClipboardSelection", check_toclipboard_selection);
configs->set("AutoAttach", autoattach);
configs->set("AutoAttach_SavedOnly", autoattach_savedonly);
configs->set("flushDelay", TextThread::flushDelay);
configs->set("filterRepetition", TextThread::filterRepetition);
configs->set("maxBufferSize", TextThread::maxBufferSize);
configs->set("maxHistorySize", TextThread::maxHistorySize);
configs->set("defaultCodepage", Host::defaultCodepage);
configs->set("autoattachexes", autoattachexes);
configs->set("savedhookcontext", savedhookcontext);
configs->set("DefaultFont2", WideStringToString(uifont.fontfamily));
configs->set("fontsize", uifont.fontsize);
configs->set("font_italic", uifont.italic);
configs->set("font_bold", uifont.bold);
configs->set("Language", map_from_support_lang(curr_lang));
}
std::string getdefaultlang()
{
LANGID langid = GetUserDefaultUILanguage();
CHAR szLang[100];
std::string lang;
GetLocaleInfoA(MAKELCID(langid, SORT_DEFAULT), LOCALE_SISO639LANGNAME, szLang, 100);
lang += szLang;
GetLocaleInfoA(MAKELCID(langid, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, szLang, 100);
lang += "-";
lang += szLang;
return lang;
}
void LunaHost::loadsettings()
{
curr_lang = map_to_support_lang(configs->get("Language", getdefaultlang()).c_str());
uifont.italic = configs->get("font_italic", false);
uifont.bold = configs->get("font_bold", false);
uifont.fontsize = configs->get("fontsize", 14);
uifont.fontfamily = StringToWideString(configs->get("DefaultFont2", WideStringToString(TR[DefaultFont])));
check_toclipboard_selection = configs->get("ToClipboardSelection", false);
check_toclipboard = configs->get("ToClipboard", false);
autoattach = configs->get("AutoAttach", false);
autoattach_savedonly = configs->get("AutoAttach_SavedOnly", true);
TextThread::flushDelay = configs->get("flushDelay", TextThread::flushDelay);
TextThread::filterRepetition = configs->get("filterRepetition", TextThread::filterRepetition);
TextThread::maxBufferSize = configs->get("maxBufferSize", TextThread::maxBufferSize);
TextThread::maxHistorySize = configs->get("maxHistorySize", TextThread::maxHistorySize);
Host::defaultCodepage = configs->get("defaultCodepage", Host::defaultCodepage);
autoattachexes = configs->get("autoattachexes", std::set<std::string>{});
savedhookcontext = configs->get("savedhookcontext", decltype(savedhookcontext){});
}
std::unordered_map<std::wstring, std::vector<int>> getprocesslist();
void LunaHost::doautoattach()
{
if (autoattach == false && autoattach_savedonly == false)
return;
if (autoattachexes.empty())
return;
for (auto [pexe, pids] : getprocesslist())
{
auto &&u8procname = WideStringToString(pexe);
if (autoattachexes.find(u8procname) == autoattachexes.end())
continue;
if (autoattach_savedonly && savedhookcontext.find(u8procname) == savedhookcontext.end())
continue;
for (auto pid : pids)
{
if (userdetachedpids.find(pid) != userdetachedpids.end())
continue;
if (attachedprocess.find(pid) == attachedprocess.end())
Host::InjectProcess(pid);
}
break;
}
}
void LunaHost::on_proc_disconnect(DWORD pid)
{
attachedprocess.erase(pid);
}
void LunaHost::on_proc_connect(DWORD pid)
{
attachedprocess.insert(pid);
if (auto pexe = getModuleFilename(pid))
{
autoattachexes.insert(WideStringToString(pexe.value()));
auto u8procname = WideStringToString(pexe.value());
if (savedhookcontext.find(u8procname) != savedhookcontext.end())
{
std::string name = safequeryjson(savedhookcontext[u8procname], "name", std::string());
if (startWith(name, "UserHook"))
{
if (auto hp = HookCode::Parse(StringToWideString(std::string_view(savedhookcontext[u8procname]["hookcode"]))))
Host::InsertHook(pid, hp.value());
}
}
}
}
LunaHost::LunaHost()
{
configs = new confighelper;
loadsettings();
setfont(uifont);
btnshowsettionwindow = new button(this, TR[TSetting]);
g_selectprocessbutton = new button(this, TR[WndSelectProcess]);
// btnsavehook=new button(this,BtnSaveHook,10,10,10,10);
// btnsavehook->onclick=std::bind(&LunaHost::btnsavehookscallback,this);
btndetachall = new button(this, TR[BtnDetach]);
btndetachall->onclick = [&]()
{
for (auto pid : attachedprocess)
{
Host::DetachProcess(pid);
userdetachedpids.insert(pid);
}
};
g_hEdit_userhook = new lineedit(this);
btnplugin = new button(this, TR[TPlugins]);
plugins = new Pluginmanager(this);
btnplugin->onclick = [&]()
{
if (pluginwindow == 0)
pluginwindow = new Pluginwindow(this, plugins);
pluginwindow->show();
};
g_hButton_insert = new button(this, TR[BtnInsertUserHook]);
btnshowsettionwindow->onclick = [&]()
{
if (settingwindow == 0)
settingwindow = new Settingwindow(this);
settingwindow->show();
};
g_selectprocessbutton->onclick = [&]()
{
if (_processlistwindow == 0)
_processlistwindow = new processlistwindow(this);
_processlistwindow->show();
};
g_hButton_insert->onclick = [&]()
{
auto hp = HookCode::Parse(std::move(g_hEdit_userhook->text()));
if (hp)
{
for (auto _ : attachedprocess)
{
Host::InsertHook(_, hp.value());
}
}
else
{
showtext(TR[NotifyInvalidHookCode], false);
}
};
g_hListBox_listtext = new listview(this, false, false);
g_hListBox_listtext->setheader({TR[LIST_HOOK], TR[HS_TEXT]});
g_hListBox_listtext->oncurrentchange = [&](int idx)
{
auto thread_p = g_hListBox_listtext->getdata(idx);
std::wstring get;
currentselect = thread_p;
std::wstring copy = ((TextThread *)thread_p)->storage->c_str();
strReplace(copy, L"\n", L"\r\n");
showtext(copy, true);
};
g_hListBox_listtext->on_menu = [&]() -> maybehavemenu
{
auto tt = (TextThread *)g_hListBox_listtext->getdata(g_hListBox_listtext->currentidx());
Menu menu;
menu.add(TR[MenuCopyHookCode], [&, tt]()
{ sendclipboarddata(tt->hp.hookcode, winId); });
menu.add_sep();
menu.add(TR[MenuRemoveHook], [&, tt]()
{ Host::RemoveHook(tt->tp.processId, tt->tp.addr); });
menu.add(TR[MenuDetachProcess], [&, tt]()
{
Host::DetachProcess(tt->tp.processId);
userdetachedpids.insert(tt->tp.processId); });
menu.add_sep();
menu.add(TR[MenuRemeberSelect], [&, tt]()
{
if(auto pexe=getModuleFilename(tt->tp.processId))
savedhookcontext[WideStringToString(pexe.value())]={
{"hookcode",WideStringToString(tt->hp.hookcode)},
{"ctx1",tt->tp.ctx},
{"ctx2",tt->tp.ctx2},
{"name",WideStringToString(tt->name)}
}; });
menu.add(TR[MenuForgetSelect], [&, tt]()
{
if(auto pexe=getModuleFilename(tt->tp.processId))
savedhookcontext.erase(WideStringToString(pexe.value())); });
return menu;
};
g_showtexts = new multilineedit(this);
g_showtexts->setreadonly(true);
Host::StartEx(
std::bind(&LunaHost::on_proc_connect, this, std::placeholders::_1),
std::bind(&LunaHost::on_proc_disconnect, this, std::placeholders::_1),
std::bind(&LunaHost::on_thread_create, this, std::placeholders::_1),
std::bind(&LunaHost::on_thread_delete, this, std::placeholders::_1),
std::bind(&LunaHost::on_text_recv, this, std::placeholders::_1, std::placeholders::_2),
true,
[=](HOSTINFO type, const std::wstring &output)
{ on_info(type, output); },
{},
{});
mainlayout = new gridlayout();
mainlayout->addcontrol(g_selectprocessbutton, 0, 0);
mainlayout->addcontrol(btndetachall, 0, 1);
mainlayout->addcontrol(btnshowsettionwindow, 0, 2);
mainlayout->addcontrol(btnplugin, 0, 3);
mainlayout->addcontrol(g_hEdit_userhook, 1, 0, 1, 3);
mainlayout->addcontrol(g_hButton_insert, 1, 3);
mainlayout->addcontrol(g_hListBox_listtext, 2, 0, 1, 4);
mainlayout->addcontrol(g_showtexts, 3, 0, 1, 4);
mainlayout->setfixedheigth(0, 30);
mainlayout->setfixedheigth(1, 30);
setlayout(mainlayout);
setcentral(1200, 800);
std::wstring title = TR[WndLunaHostGui];
settext(title);
std::thread([&]()
{
std::wstring sel;
while(1)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if(check_toclipboard_selection)
{
auto _sel=g_showtexts->getsel();
if(_sel!=sel){
sel=_sel;
sendclipboarddata(sel,winId);
}
}
} })
.detach();
std::thread([&]
{
while(1){
doautoattach();
std::this_thread::sleep_for(std::chrono::seconds(2));
} })
.detach();
WCHAR vs[32];
wsprintf(vs, L" | %s v%d.%d.%d", TR[VersionCurrent], LUNA_VERSION[0], LUNA_VERSION[1], LUNA_VERSION[2]);
title += vs;
settext(title);
std::thread([&]()
{
if (HttpRequest httpRequest{
L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
L"lunatranslator.org",
L"GET",
L"/version"
}){
try{
auto resp=nlohmann::json::parse(WideStringToString(httpRequest.response));
std::string ver=resp["version"];
settext(text()+L" | "+TR[VersionLatest]+L" "+ StringToWideString(ver));
}
catch(std::exception&e){}
} })
.detach();
}
void LunaHost::on_text_recv_checkissaved(TextThread &thread)
{
if (auto exe = getModuleFilename(thread.tp.processId))
{
auto exea = WideStringToString(exe.value());
if (savedhookcontext.find(exea) == savedhookcontext.end())
return;
std::string hc = savedhookcontext[exea]["hookcode"];
uint64_t ctx1 = savedhookcontext[exea]["ctx1"];
uint64_t ctx2 = savedhookcontext[exea]["ctx2"];
if (((ctx1 & 0xffff) == (thread.tp.ctx & 0xffff)) && (ctx2 == thread.tp.ctx2) && (hc == WideStringToString(thread.hp.hookcode)))
{
for (int i = 0; i < g_hListBox_listtext->count(); i++)
{
auto handle = g_hListBox_listtext->getdata(i);
if (handle == (LONG_PTR)&thread)
{
g_hListBox_listtext->setcurrent(i);
break;
}
}
}
}
}
std::wstring sanitize(const std::wstring &s1)
{
std::wstring s = s1;
s.erase(std::remove_if(s.begin(), s.end(), [](wchar_t ch)
{ return (ch >= 0xD800 && ch <= 0xDBFF) || (ch >= 0xDC00 && ch <= 0xDFFF); }),
s.end());
return s;
}
void LunaHost::showtext(const std::wstring &text, bool clear)
{
auto output = sanitize(text);
strReplace(output, L"\n", L"\r\n");
if (clear)
{
g_showtexts->settext(output);
g_showtexts->scrolltoend();
}
else
{
g_showtexts->scrolltoend();
g_showtexts->appendtext(output);
}
}
void LunaHost::updatelisttext(const std::wstring &text, LONG_PTR data)
{
auto idx = g_hListBox_listtext->querydataidx(data);
if (idx >= 0)
{
auto __output = sanitize(text);
strReplace(__output, L"\n", L" ");
if (__output.size() > 0x40)
{
__output = __output.substr(0, 0x40) + L"...";
}
g_hListBox_listtext->settext(idx, 1, __output);
}
}
bool LunaHost::on_text_recv(TextThread &thread, std::wstring &output)
{
if (hasstoped)
return true;
if (!plugins->dispatch(thread, output))
return false;
updatelisttext(output, (LONG_PTR)&thread);
if (currentselect == (LONG_PTR)&thread)
{
showtext(output, false);
}
return true;
}
void LunaHost::on_info(HOSTINFO type, const std::wstring &warning)
{
switch (type)
{
case HOSTINFO::Warning:
MessageBoxW(winId, warning.c_str(), TR[T_WARNING], 0);
break;
}
}
void LunaHost::on_thread_create(TextThread &thread)
{
wchar_t buff[65535];
swprintf_s(buff, L"%I64X:%s:%s:%I32X:%I64X:%I64X",
thread.handle,
thread.name.c_str(),
thread.hp.hookcode,
thread.tp.processId,
thread.tp.ctx,
thread.tp.ctx2);
int index = g_hListBox_listtext->additem(buff, NULL);
g_hListBox_listtext->setdata(index, (LONG_PTR)&thread);
on_text_recv_checkissaved(thread);
}
void LunaHost::on_thread_delete(TextThread &thread)
{
if (currentselect == (LONG_PTR)&thread)
currentselect = 0;
int count = g_hListBox_listtext->count();
for (int i = 0; i < count; i++)
{
auto thread_p = g_hListBox_listtext->getdata(i);
if (thread_p == (LONG_PTR)&thread)
{
g_hListBox_listtext->deleteitem(i);
break;
}
}
}
Settingwindow::Settingwindow(LunaHost *host) : mainwindow(host)
{
int height = 30;
int curry = 10;
int space = 10;
int labelwidth = 300;
int spinwidth = 200;
g_timeout = new spinbox(this, TextThread::flushDelay);
g_codepage = new spinbox(this, Host::defaultCodepage);
spinmaxbuffsize = new spinbox(this, TextThread::maxBufferSize);
;
curry += height + space;
spinmaxbuffsize->onvaluechange = [=](int v)
{
TextThread::maxBufferSize = v;
};
spinmaxhistsize = new spinbox(this, TextThread::maxHistorySize);
;
curry += height + space;
spinmaxhistsize->onvaluechange = [=](int v)
{
TextThread::maxHistorySize = v;
};
ckbfilterrepeat = new checkbox(this, TR[LblFilterRepeat]);
ckbfilterrepeat->onclick = [=]()
{
TextThread::filterRepetition = ckbfilterrepeat->ischecked();
};
ckbfilterrepeat->setcheck(TextThread::filterRepetition);
g_check_clipboard = new checkbox(this, TR[BtnToClipboard]);
g_check_clipboard->onclick = [=]()
{
host->check_toclipboard = g_check_clipboard->ischecked();
};
g_check_clipboard->setcheck(host->check_toclipboard);
// copyselect=new checkbox(this,COPYSELECTION);
// copyselect->onclick=[=](){
// host->check_toclipboard_selection=copyselect->ischecked();
// };
// copyselect->setcheck(host->check_toclipboard_selection);
autoattach = new checkbox(this, TR[LblAutoAttach]);
autoattach->onclick = [=]()
{
host->autoattach = autoattach->ischecked();
};
autoattach->setcheck(host->autoattach);
autoattach_so = new checkbox(this, TR[LblAutoAttach_savedonly]);
autoattach_so->onclick = [=]()
{
host->autoattach_savedonly = autoattach_so->ischecked();
};
autoattach_so->setcheck(host->autoattach_savedonly);
readonlycheck = new checkbox(this, TR[BtnReadOnly]);
readonlycheck->onclick = [=]()
{
host->g_showtexts->setreadonly(readonlycheck->ischecked());
};
readonlycheck->setcheck(true);
g_timeout->onvaluechange = [=](int v)
{
TextThread::flushDelay = v;
};
g_codepage->onvaluechange = [=](int v)
{
if (IsValidCodePage(v))
{
Host::defaultCodepage = v;
}
};
g_codepage->setminmax(0, CP_UTF8);
showfont = new lineedit(this);
showfont->settext(host->uifont.fontfamily);
showfont->setreadonly(true);
selectfont = new button(this, TR[FONTSELECT]);
selectfont->onclick = [=]()
{
FontSelector(winId, host->uifont, [=](const Font &f)
{
host->uifont=f;
showfont->settext(f.fontfamily);
host->setfont(f); });
};
language = new combobox(this);
for (auto &&[_, l, __] : lang_map)
{
language->additem(l);
}
language->setcurrent(curr_lang);
language->oncurrentchange = [](int idx)
{
curr_lang = (decltype(curr_lang))idx;
};
mainlayout = new gridlayout();
mainlayout->addcontrol(new label(this, TR[LblLanguage]), 0, 0);
mainlayout->addcontrol(language, 0, 1);
mainlayout->addcontrol(new label(this, TR[LblFlushDelay]), 1, 0);
mainlayout->addcontrol(g_timeout, 1, 1);
mainlayout->addcontrol(new label(this, TR[LblCodePage]), 2, 0);
mainlayout->addcontrol(g_codepage, 2, 1);
mainlayout->addcontrol(new label(this, TR[LblMaxBuff]), 3, 0);
mainlayout->addcontrol(spinmaxbuffsize, 3, 1);
mainlayout->addcontrol(new label(this, TR[LblMaxHist]), 4, 0);
mainlayout->addcontrol(spinmaxhistsize, 4, 1);
mainlayout->addcontrol(ckbfilterrepeat, 5, 0, 1, 2);
mainlayout->addcontrol(g_check_clipboard, 6, 0, 1, 2);
mainlayout->addcontrol(autoattach, 7, 0, 1, 2);
mainlayout->addcontrol(autoattach_so, 8, 0, 1, 2);
mainlayout->addcontrol(readonlycheck, 9, 0, 1, 2);
mainlayout->addcontrol(showfont, 10, 1);
mainlayout->addcontrol(selectfont, 10, 0);
setlayout(mainlayout);
setcentral(600, 500);
settext(TR[TSetting]);
}
void Pluginwindow::on_size(int w, int h)
{
listplugins->setgeo(10, 10, w - 20, h - 20);
}
void Pluginwindow::pluginrankmove(int moveoffset)
{
auto idx = listplugins->currentidx();
if (idx == -1)
return;
auto idx2 = idx + moveoffset;
auto a = min(idx, idx2), b = max(idx, idx2);
if (a < 0 || b >= listplugins->count())
return;
pluginmanager->swaprank(a, b);
auto pa = ((LPCWSTR)listplugins->getdata(a));
auto pb = ((LPCWSTR)listplugins->getdata(b));
listplugins->deleteitem(a);
listplugins->insertitem(b, std::filesystem::path(pa).stem());
listplugins->setdata(b, (LONG_PTR)pa);
}
Pluginwindow::Pluginwindow(mainwindow *p, Pluginmanager *pl) : mainwindow(p), pluginmanager(pl)
{
static auto listadd = [&](const std::wstring &s)
{
auto idx = listplugins->additem(std::filesystem::path(s).stem());
auto _s = new wchar_t[s.size() + 1];
wcscpy(_s, s.c_str());
listplugins->setdata(idx, (LONG_PTR)_s);
};
listplugins = new listbox(this);
listplugins->on_menu = [&]()
{
Menu menu;
menu.add(TR[MenuAddPlugin], [&]()
{
if(auto f=pluginmanager->selectpluginfile())
switch (auto res=pluginmanager->addplugin(f.value()))
{
case addpluginresult::success:
listadd(f.value());
break;
default:
std::map<addpluginresult,LPCWSTR> errorlog={
{addpluginresult::isnotaplugins,TR[InvalidPlugin]},
{addpluginresult::invaliddll,TR[InvalidDll]},
{addpluginresult::dumplicate,TR[InvalidDump]},
};
MessageBoxW(winId,errorlog[res],TR[MsgError],0);
} });
auto idx = listplugins->currentidx();
if (idx != -1)
{
menu.add(TR[MenuRemovePlugin], [&, idx]()
{
pluginmanager->remove((LPCWSTR)listplugins->getdata(idx));
listplugins->deleteitem(idx); });
menu.add_sep();
menu.add(TR[MenuPluginRankUp], std::bind(&Pluginwindow::pluginrankmove, this, -1));
menu.add(TR[MenuPluginRankDown], std::bind(&Pluginwindow::pluginrankmove, this, 1));
menu.add_sep();
menu.add_checkable(TR[MenuPluginEnable], pluginmanager->getenable(idx), [&, idx](bool check)
{
pluginmanager->setenable(idx,check);
if(check)
pluginmanager->load((LPCWSTR)listplugins->getdata(idx));
else
pluginmanager->unload((LPCWSTR)listplugins->getdata(idx)); });
if (pluginmanager->getvisible_setable(idx))
menu.add_checkable(TR[MenuPluginVisSetting], pluginmanager->getvisible(idx), [&, idx](bool check)
{ pluginmanager->setvisible(idx, check); });
}
return menu;
};
for (int i = 0; i < pluginmanager->count(); i++)
{
listadd(pluginmanager->getname(i));
}
settext(TR[TPlugins]);
setcentral(500, 400);
}