This commit is contained in:
恍兮惚兮 2025-01-06 20:44:12 +08:00
parent e8accb3ca2
commit a7f98301a1
20 changed files with 257 additions and 148 deletions

View File

@ -23,16 +23,15 @@ namespace
} }
void embed_fun(hook_context *s, TextBuffer buffer) void embed_fun(hook_context *s, TextBuffer buffer)
{ {
static std::string ts; auto ts = allocateString(buffer.viewA());
ts = buffer.viewA();
if (_type == 1) if (_type == 1)
{ {
s->stack[1] = (DWORD)ts.c_str(); s->stack[1] = (DWORD)ts;
} }
else else
{ {
s->ecx = (DWORD)ts.c_str(); s->ecx = (DWORD)ts;
} }
} }
bool InsertDebonosuScenarioHook() bool InsertDebonosuScenarioHook()

View File

@ -211,7 +211,6 @@ namespace
char nameText[1]; // +4*10+4*3, could be bad address though char nameText[1]; // +4*10+4*3, could be bad address though
}; };
std::string data_;
TextArgument *scenarioArg_, TextArgument *scenarioArg_,
*nameArg_; *nameArg_;
LPCSTR scenarioText_; LPCSTR scenarioText_;
@ -265,10 +264,9 @@ namespace
auto text = arg->scenarioText; auto text = arg->scenarioText;
if (!Engine::isAddressReadable(text)) if (!Engine::isAddressReadable(text))
return; return;
data_ = newData;
scenarioArg_ = arg; scenarioArg_ = arg;
scenarioText_ = arg->scenarioText; scenarioText_ = arg->scenarioText;
arg->scenarioText = (LPCSTR)data_.c_str(); arg->scenarioText = (LPCSTR)allocateString(newData);
} }
else if (arg->nameFlag == 0) else if (arg->nameFlag == 0)
{ {

View File

@ -218,12 +218,11 @@ namespace
} }
void embed_fun(hook_context *s, TextBuffer buffer) void embed_fun(hook_context *s, TextBuffer buffer)
{ {
static std::string data_; auto data_ = buffer.strA();
data_ = buffer.strA();
auto arg = (HookArgument *)s->stack[1]; auto arg = (HookArgument *)s->stack[1];
if (trimmedText != arg->text) if (trimmedText != arg->text)
data_.insert(0, std::string(arg->text, trimmedText - arg->text)); data_.insert(0, std::string(arg->text, trimmedText - arg->text));
arg->text = data_.c_str(); arg->text = allocateString(data_);
} }
} // unnamed namespace } // unnamed namespace
bool InsertEscudeHook() bool InsertEscudeHook()

View File

@ -176,10 +176,9 @@ namespace
} }
void hookafter1(hook_context *s, TextBuffer buffer) void hookafter1(hook_context *s, TextBuffer buffer)
{ {
static std::string newData; auto newData = buffer.strA();
newData = buffer.strA(); cache_.put(newData);
newData = cache_.put(newData).first; s->stack[1] = (ULONG)allocateString(newData); // arg1
s->stack[1] = (ULONG)newData.c_str(); // arg1
} }
} // namespace Private } // namespace Private

View File

@ -1113,7 +1113,6 @@ namespace
// I need a cache retainer here to make sure same text result in same result // I need a cache retainer here to make sure same text result in same result
void hookafter(hook_context *s, TextBuffer buffer) void hookafter(hook_context *s, TextBuffer buffer)
{ {
static std::string data_;
static std::unordered_set<uint64_t> hashes_; static std::unordered_set<uint64_t> hashes_;
auto text = (LPCWSTR)s->stack[1]; auto text = (LPCWSTR)s->stack[1];
if (!text || !*text || !(text[0] == 0x7 && text[1] == 0x8) && all_ascii(text)) if (!text || !*text || !(text[0] == 0x7 && text[1] == 0x8) && all_ascii(text))
@ -1171,10 +1170,8 @@ namespace
data.push_back(0); data.push_back(0);
data.push_back(0); data.push_back(0);
data.push_back(0); data.push_back(0);
data_ = data;
text = (LPCWSTR)data_.c_str();
s->stack[1] = (ULONG)text; s->stack[1] = (ULONG)allocateString(data);
} }
} }
void hookBefore(hook_context *s, HookParam *hp, TextBuffer *buffer, uintptr_t *role) void hookBefore(hook_context *s, HookParam *hp, TextBuffer *buffer, uintptr_t *role)

View File

@ -162,13 +162,17 @@ bool InsertMinkHook()
0x83, 0x60, 0x70, 0xfd, // 00451654 8360 70 fd and dword ptr ds:[eax+0x70],0xfffffffd 0x83, 0x60, 0x70, 0xfd, // 00451654 8360 70 fd and dword ptr ds:[eax+0x70],0xfffffffd
0x8b, 0x45, 0x08 // 00451658 8b45 08 mov eax,dword ptr ss:[ebp+0x8] 0x8b, 0x45, 0x08 // 00451658 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
}; };
enum { addr_offset = 2 }; enum
{
addr_offset = 2
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
// ULONG addr = 0x45164a; // ULONG addr = 0x45164a;
// ULONG addr = 0x451648; // ULONG addr = 0x451648;
// ULONG addr = 0x4521a8; // ULONG addr = 0x4521a8;
// GROWL_DWORD(addr); // GROWL_DWORD(addr);
if (!addr) { if (!addr)
{
ConsoleOutput("Mink: pattern not found"); ConsoleOutput("Mink: pattern not found");
return false; return false;
} }
@ -186,7 +190,8 @@ bool InsertMinkHook()
// //
} }
bool Mink2::attach_function() { bool Mink2::attach_function()
{
const BYTE pattern[] = { const BYTE pattern[] = {
// 破談屋 // 破談屋
// https://vndb.org/v2719 // https://vndb.org/v2719
@ -195,13 +200,13 @@ bool Mink2::attach_function() {
0xC1, 0xE9, 0x02, 0xC1, 0xE9, 0x02,
0x83, 0xE2, 0x03, 0x83, 0xE2, 0x03,
0x83, 0xF9, 0x08, 0x83, 0xF9, 0x08,
0x72,XX 0x72, XX};
};
bool found = false; bool found = false;
for (auto addr : Util::SearchMemory(pattern, sizeof(pattern), PAGE_EXECUTE, processStartAddress, processStopAddress)) for (auto addr : Util::SearchMemory(pattern, sizeof(pattern), PAGE_EXECUTE, processStartAddress, processStopAddress))
{ {
addr = MemDbg::findEnclosingAlignedFunction(addr, 0x100); addr = MemDbg::findEnclosingAlignedFunction(addr, 0x100);
if (addr == 0)return false; if (addr == 0)
return false;
HookParam hp; HookParam hp;
hp.address = addr; hp.address = addr;
hp.offset = stackoffset(2); hp.offset = stackoffset(2);
@ -211,7 +216,48 @@ bool Mink2::attach_function() {
} }
return found; return found;
} }
bool Mink::attach_function() { bool Mink::attach_function()
{
return InsertMinkHook(); return InsertMinkHook();
} }
bool Mink3::attach_function()
{
const BYTE pattern[] = {
// 夜勤病棟 復刻版+
0xff, 0x15, XX4,
0x33, 0xdb,
0x89, 0x44, 0x24, XX,
0x85, 0xc0,
0x7e, XX,
0x8a, 0x07,
0x8d, 0x4c, 0x24, 0x10,
0x50,
0xe8, XX4,
0x83, 0xf8, 0x02,
0x75, 0x08,
0x03, 0xd8,
0x03, 0xf8,
0x03, 0xf0,
0xeb, XX,
0x57,
0x8b, 0xcd,
0xe8, XX4,
0x25, 0xff, 0x00, 0x00, 0x00,
0x83, 0xe8, 0x00};
auto addr = MemDbg::findBytes(pattern, sizeof(pattern), processStartAddress, processStopAddress);
if (!addr)
return false;
addr = MemDbg::findEnclosingAlignedFunction(addr, 0x100);
if (!addr)
return false;
HookParam hp;
hp.address = addr;
hp.offset = stackoffset(1);
hp.type = USING_STRING | EMBED_ABLE | EMBED_AFTER_OVERWRITE | EMBED_DYNA_SJIS;
hp.embed_hook_font = F_TextOutA;
hp.lineSeparator = L"\\n";
PcHooks::hookGDIFunctions(TextOutA);
return NewHook(hp, "Mink");
}

View File

@ -1,8 +1,10 @@
class Mink:public ENGINE{ class Mink : public ENGINE
{
public: public:
Mink(){ Mink()
{
check_by = CHECK_BY::FILE; check_by = CHECK_BY::FILE;
check_by_target = L"*.at2"; // Mink, sample files: voice.at2, voice.det, voice.nme check_by_target = L"*.at2"; // Mink, sample files: voice.at2, voice.det, voice.nme
@ -10,9 +12,11 @@ class Mink:public ENGINE{
bool attach_function(); bool attach_function();
}; };
class Mink2:public ENGINE{ class Mink2 : public ENGINE
{
public: public:
Mink2(){ Mink2()
{
check_by = CHECK_BY::FILE; check_by = CHECK_BY::FILE;
check_by_target = L"Scr\\*.sc"; check_by_target = L"Scr\\*.sc";
@ -20,3 +24,15 @@ class Mink2:public ENGINE{
}; };
bool attach_function(); bool attach_function();
}; };
class Mink3 : public ENGINE
{
public:
Mink3()
{
// 夜勤病棟 復刻版+
check_by = CHECK_BY::FILE_ALL;
check_by_target = check_by_list{L"voice*.dat", L"tpd.dat", L"tms.dat", L"thm.dat", L"se.dat", L"scr.dat", L"rec.dat", L"bgm.dat", L"cgm.dat", L"gpd.dat", L"gpdtp.dat", L"mov.dat", L"msk.dat", L"msktp.dat", L"read.dat"};
is_engine_certain = false;
};
bool attach_function();
};

View File

@ -746,10 +746,8 @@ namespace
newData = newData + "[n]"; newData = newData + "[n]";
else if (endtype == 2) else if (endtype == 2)
newData = newData + "[c]"; newData = newData + "[c]";
static std::string data_; s->edx = (ULONG)allocateString(newData); // reset arg1
data_ = newData; *(DWORD *)(s->edx - 4) = newData.size();
s->edx = (ULONG)data_.c_str(); // reset arg1
*(DWORD *)(s->edx - 4) = data_.size();
// arg->size = data_.size(); // no idea why this will crash ... // arg->size = data_.size(); // no idea why this will crash ...
//*(DWORD *)(s->edx - 4) = newData.size() + trimmedText - text; //*(DWORD *)(s->edx - 4) = newData.size() + trimmedText - text;

View File

@ -1764,11 +1764,7 @@ namespace
void hookafter(hook_context *s, TextBuffer buffer) void hookafter(hook_context *s, TextBuffer buffer)
{ {
auto arg = (TextUnionW *)(type_ == Type1 ? s->ecx : s->stack[1]); auto arg = (TextUnionW *)(type_ == Type1 ? s->ecx : s->stack[1]);
auto argValue = *arg;
arg->setText(buffer.viewW()); arg->setText(buffer.viewW());
// Restoring is indispensible, and as a result, the default hook does not work
//*arg = argValue;
} }
} }
bool attach(ULONG startAddress, ULONG stopAddress) // attach scenario bool attach(ULONG startAddress, ULONG stopAddress) // attach scenario
@ -1794,8 +1790,6 @@ namespace OtherHook
namespace Private namespace Private
{ {
TextUnionW *arg_,
argValue_;
void hookBefore(hook_context *s, HookParam *hp, TextBuffer *buffer, uintptr_t *role) void hookBefore(hook_context *s, HookParam *hp, TextBuffer *buffer, uintptr_t *role)
{ {
static std::wstring text_; static std::wstring text_;
@ -1833,8 +1827,6 @@ namespace OtherHook
void hookafter2(hook_context *s, TextBuffer buffer) void hookafter2(hook_context *s, TextBuffer buffer)
{ {
auto arg = (TextUnionW *)s->stack[0]; auto arg = (TextUnionW *)s->stack[0];
arg_ = arg;
argValue_ = *arg;
arg->setText(buffer.viewW()); arg->setText(buffer.viewW());
} }

View File

@ -211,13 +211,11 @@ namespace
} }
void hookafter(hook_context *s, TextBuffer buffer) void hookafter(hook_context *s, TextBuffer buffer)
{ {
static std::string data_;
std::string newData = buffer.strA(); std::string newData = buffer.strA();
data_ = newData;
int capacity = s->stack[1]; // arg 2, should always be 0x1000 int capacity = s->stack[1]; // arg 2, should always be 0x1000
if (data_.size() >= capacity) if (newData.size() >= capacity)
data_ = data_.substr(0, capacity - 1); newData = newData.substr(0, capacity - 1);
s->stack[2] = (ULONG)data_.c_str(); // arg 3 s->stack[2] = (ULONG)allocateString(newData); // arg 3
} }
} // namespace Private } // namespace Private

View File

@ -575,9 +575,8 @@ namespace
void hookafter(hook_context *s, TextBuffer buffer) void hookafter(hook_context *s, TextBuffer buffer)
{ {
static std::string data_; auto data_ = buffer.strA();
data_ = buffer.strA(); s->stack[1] = (ULONG)allocateString(data_);
s->stack[1] = (ULONG)data_.c_str();
s->stack[2] = data_.size(); s->stack[2] = data_.size();
} }
} // namespace Private } // namespace Private

View File

@ -834,7 +834,7 @@ namespace
if (suffixSize) if (suffixSize)
newText.append(std::wstring(trimmedText + trimmedSize, suffixSize)); newText.append(std::wstring(trimmedText + trimmedSize, suffixSize));
info->text_ = newText; info->text_ = newText;
s->stack[info->stackIndex_] = (ULONG)info->text_.c_str(); s->stack[info->stackIndex_] = (ULONG)allocateString(info->text_);
} }
// explicit TextHookW(int hookStackIndex, int role = Engine::UnknownRole) : stackIndex_(hookStackIndex), role_(role) {} // explicit TextHookW(int hookStackIndex, int role = Engine::UnknownRole) : stackIndex_(hookStackIndex), role_(role) {}
template <int _type> template <int _type>

View File

@ -428,6 +428,7 @@ std::vector<ENGINE *> check_engines()
new DAC, new DAC,
new AbogadoPowers, new AbogadoPowers,
new e_Erekiteru, new e_Erekiteru,
new H_do_C new H_do_C,
new Mink3,
}; };
} }

View File

@ -1,7 +1,7 @@
set(VERSION_MAJOR 6) set(VERSION_MAJOR 6)
set(VERSION_MINOR 17) set(VERSION_MINOR 17)
set(VERSION_PATCH 4) set(VERSION_PATCH 5)
set(VERSION_REVISION 0) set(VERSION_REVISION 0)
set(LUNA_VERSION "{${VERSION_MAJOR},${VERSION_MINOR},${VERSION_PATCH},${VERSION_REVISION}}") set(LUNA_VERSION "{${VERSION_MAJOR},${VERSION_MINOR},${VERSION_PATCH},${VERSION_REVISION}}")
add_library(VERSION_DEF ${CMAKE_CURRENT_LIST_DIR}/version_def.cpp) add_library(VERSION_DEF ${CMAKE_CURRENT_LIST_DIR}/version_def.cpp)

View File

@ -245,4 +245,8 @@ The following commands remove the OCR pack for "en-US":
https://learn.microsoft.com/en-us/windows/powertoys/text-extractor#supported-languages https://learn.microsoft.com/en-us/windows/powertoys/text-extractor#supported-languages
#### **Tesseract5**
https://github.com/tesseract-ocr/tesseract/releases
<!-- tabs:end --> <!-- tabs:end -->

View File

@ -246,4 +246,8 @@ State : NotPresent
https://learn.microsoft.com/ja-jp/windows/powertoys/text-extractor#supported-languages https://learn.microsoft.com/ja-jp/windows/powertoys/text-extractor#supported-languages
#### **Tesseract5**
https://github.com/tesseract-ocr/tesseract/releases
<!-- tabs:end --> <!-- tabs:end -->

View File

@ -281,5 +281,9 @@ State : NotPresent
https://learn.microsoft.com/zh-cn/windows/powertoys/text-extractor#supported-languages https://learn.microsoft.com/zh-cn/windows/powertoys/text-extractor#supported-languages
#### **Tesseract5**
https://github.com/tesseract-ocr/tesseract/releases
<!-- tabs:end --> <!-- tabs:end -->

View File

@ -294,7 +294,10 @@ def delayloadlinks(key, lay):
else: else:
for link in source["links"]: for link in source["links"]:
__grid.append( __grid.append(
[link["name"], (makehtml(link["link"]), 2, "link")] [
link["name"],
(makehtml(link["link"], link.get("vis", None)), 2, "link"),
]
+ ([link.get("about")] if link.get("about") else []) + ([link.get("about")] if link.get("about") else [])
) )
grid.append( grid.append(

View File

@ -1,37 +1,110 @@
import os, uuid, gobject import os, uuid, gobject, winreg
from myutils.config import _TR, ocrsetting from myutils.config import _TR, globalconfig
from ocrengines.baseocrclass import baseocr from ocrengines.baseocrclass import baseocr
from myutils.subproc import subproc_w from myutils.subproc import subproc_w
from language import Languages
def list_langs():
path = ocrsetting["tesseract5"]["args"]["路径"]
if os.path.exists(path) == False:
return []
res = subproc_w(
'"{}" --list-langs'.format(path), needstdio=True, run=True, encoding="utf8"
).stdout
return res.split("\n")[1:-1]
class OCR(baseocr): class OCR(baseocr):
def findts__(self):
k = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE,
r"SOFTWARE\Tesseract-OCR",
0,
winreg.KEY_QUERY_VALUE,
)
base = winreg.QueryValueEx(k, "Path")[0]
winreg.CloseKey(k)
return base
def findts(self):
try:
_ = self.findts__()
_ = os.path.join(_, "tesseract.exe")
return _
except:
return
def list_langs(self):
if not (self.path and os.path.exists(self.path)):
raise Exception(_TR("路径不存在"))
res = subproc_w(
'"{}" --list-langs'.format(self.path),
needstdio=True,
run=True,
encoding="utf8",
).stdout
return res.split("\n")[1:-1]
def langmap(self):
# https://github.com/tesseract-ocr/tessdoc/blob/main/tess3/Data-Files.md
return {
Languages.Chinese: "chi_sim",
Languages.TradChinese: "chi_tra",
Languages.Japanese: "jpn",
Languages.English: "eng",
Languages.Russian: "rus",
Languages.Korean: "kor",
Languages.Arabic: "ara",
Languages.Italian: "ita",
Languages.Polish: "pol",
Languages.Spanish: "spa",
Languages.Swedish: "swe",
Languages.Ukrainian: "ukr",
Languages.Vietnamese: "vie",
Languages.French: "fra",
Languages.Turkish: "tur",
Languages.German: "deu",
Languages.Dutch: "nld",
Languages.Portuguese: "por",
Languages.Czech: "ces",
Languages.Hungarian: "hun",
Languages.Thai: "tha",
Languages.Latin: "lat",
}
def initocr(self): def initocr(self):
self.langs = list_langs() self.path = self.findts()
self.langs = self.list_langs()
print(self.langs)
def ocr(self, imagebinary): def ocr(self, imagebinary):
self.checkempty(["路径"]) if not (self.path and os.path.exists(self.path)):
path = self.config["路径"] raise Exception(_TR("not installed"))
if os.path.exists(path) == False: self.raise_cant_be_auto_lang()
raise Exception(_TR("路径不存在")) lang = self.srclang
psm = 6
imgfile = None
if globalconfig["verticalocr"] == 0:
pass
elif globalconfig["verticalocr"] == 1:
lang += "_vert"
psm = 5
elif globalconfig["verticalocr"] == 2:
fname = gobject.gettempdir(str(uuid.uuid4()) + ".png") fname = gobject.gettempdir(str(uuid.uuid4()) + ".png")
with open(fname, "wb") as ff: with open(fname, "wb") as ff:
ff.write(imagebinary) ff.write(imagebinary)
imgfile = os.path.abspath(fname) imgfile = os.path.abspath(fname)
_ = subproc_w( _ = subproc_w(
'"{}" "{}" - -l {} {}'.format( '"{}" "{}" stdout -l osd --psm 0'.format(self.path, imgfile),
path, imgfile, self.langs[self.config["语言"]], self.config["附加参数"] needstdio=True,
), encoding="utf8",
run=True,
)
err = _.stderr
if len(err):
pass
elif "Orientation in degrees: 0" not in _.stdout:
lang += "_vert"
psm = 5
if not imgfile:
fname = gobject.gettempdir(str(uuid.uuid4()) + ".png")
with open(fname, "wb") as ff:
ff.write(imagebinary)
imgfile = os.path.abspath(fname)
_ = subproc_w(
'"{}" "{}" - -l {} --psm {}'.format(self.path, imgfile, lang, psm),
needstdio=True, needstdio=True,
encoding="utf8", encoding="utf8",
run=True, run=True,

View File

@ -150,27 +150,6 @@
"Secret Access Key": "" "Secret Access Key": ""
} }
}, },
"tesseract5": {
"args": {
"路径": "",
"语言": 0,
"附加参数": "--psm 6"
},
"argstype": {
"路径": {
"type": "file",
"dir": false,
"filter": "tesseract.exe"
},
"语言": {
"type": "combo",
"list_function": [
"ocrengines.tesseract5",
"list_langs"
]
}
}
},
"googlecloudvision": { "googlecloudvision": {
"args": { "args": {
"key": "" "key": ""