This commit is contained in:
恍兮惚兮 2024-06-30 20:08:02 +08:00
parent 0c40cdc6dd
commit 38be8e7ff6
39 changed files with 599 additions and 414 deletions

View File

@ -7,7 +7,7 @@ from myutils.config import (
_TR,
savehook_new_list,
uid2gamepath,
gamepath2uid,
findgameuidofpath,
savehook_new_data,
setlanguage,
static_data,
@ -20,7 +20,6 @@ from myutils.utils import (
kanjitrans,
checkifnewgame,
checkpostusing,
getpostfile,
stringfyerror,
)
from myutils.wrapper import threader
@ -43,7 +42,7 @@ from gui.attachprocessdialog import AttachProcessDialog
import windows
import gobject
import winsharedutils
from winsharedutils import pid_running
from winsharedutils import collect_running_pids
from myutils.post import POSTSOLVE
from myutils.utils import nowisdark
@ -105,9 +104,11 @@ class MAINUI:
for item in static_data["transoptimi"]:
name = item["name"]
try:
mm = getpostfile(name)
if not mm:
checkpath = "./LunaTranslator/transoptimi/" + name + ".py"
if os.path.exists(checkpath) == False:
continue
mm = "transoptimi." + name
Process = importlib.import_module(mm).Process
def __(kls, _name):
@ -475,12 +476,17 @@ class MAINUI:
use, self.settin_ui.voicelistsignal, self.settin_ui.mp3playsignal
)
def selectprocess(self, selectedp):
def selectprocess(self, selectedp, title):
self.textsource = None
pids, pexe, hwnd = selectedp
checkifnewgame(savehook_new_list, pexe, windows.GetWindowText(hwnd))
if len(collect_running_pids(pids)) == 0:
return
if not title:
title = windows.GetWindowText(hwnd)
checkifnewgame(savehook_new_list, pexe, title)
if globalconfig["sourcestatus2"]["texthook"]["use"]:
self.textsource = texthook(pids, hwnd, pexe, gamepath2uid[pexe])
gameuid = findgameuidofpath(pexe, savehook_new_list)
self.textsource = texthook(pids, hwnd, pexe, gameuid)
self.textsource.start()
def starttextsource(self, use=None, checked=True):
@ -563,16 +569,9 @@ class MAINUI:
def fanyiinitmethod(self, classname):
try:
if classname == "selfbuild":
if not os.path.exists("./userconfig/selfbuild.py"):
return None
aclass = importlib.import_module("selfbuild").TS
else:
if not os.path.exists(
"./LunaTranslator/translator/" + classname + ".py"
):
return None
aclass = importlib.import_module("translator." + classname).TS
if not os.path.exists("./LunaTranslator/translator/" + classname + ".py"):
return None
aclass = importlib.import_module("translator." + classname).TS
except Exception as e:
print_exc()
self.textgetmethod(
@ -676,109 +675,101 @@ class MAINUI:
except:
print_exc()
def onwindowloadautohook(self):
textsourceusing = globalconfig["sourcestatus2"]["texthook"]["use"]
if not (globalconfig["autostarthook"] and textsourceusing):
return
elif self.AttachProcessDialog and self.AttachProcessDialog.isVisible():
return
else:
try:
if self.textsource is None:
hwnd = windows.GetForegroundWindow()
pid = windows.GetWindowThreadProcessId(hwnd)
name_ = getpidexe(pid)
if (
name_
and name_ in gamepath2uid
and gamepath2uid[name_] in savehook_new_list
):
lps = ListProcess(False)
for pids, _exe in lps:
if _exe == name_:
# if any(map(testprivilege,pids)):
self.textsource = None
if globalconfig["sourcestatus2"]["texthook"]["use"]:
if globalconfig["startgamenototop"] == False:
idx = savehook_new_list.index(
gamepath2uid[name_]
)
savehook_new_list.insert(
0, savehook_new_list.pop(idx)
)
needinserthookcode = savehook_new_data[
gamepath2uid[name_]
]["needinserthookcode"]
self.textsource = texthook(
pids,
hwnd,
name_,
gamepath2uid[name_],
autostarthookcode=savehook_new_data[
gamepath2uid[name_]
]["hook"],
needinserthookcode=needinserthookcode,
)
self.textsource.start()
else:
pids = self.textsource.pids
if sum([int(pid_running(pid)) for pid in pids]) == 0:
self.textsource = None
self.translation_ui.thistimenotsetop = False
if globalconfig["keepontop"]:
self.translation_ui.settop()
except:
print_exc()
def autohookmonitorthread(self):
def onwindowloadautohook():
textsourceusing = globalconfig["sourcestatus2"]["texthook"]["use"]
if not (globalconfig["autostarthook"] and textsourceusing):
return
elif self.AttachProcessDialog and self.AttachProcessDialog.isVisible():
return
if self.textsource is None:
hwnd = windows.GetForegroundWindow()
pid = windows.GetWindowThreadProcessId(hwnd)
name_ = getpidexe(pid)
if not name_:
return
uid = findgameuidofpath(name_, savehook_new_list)
if not uid:
return
lps = ListProcess(False)
for pids, _exe in lps:
if _exe != name_:
continue
if self.textsource is not None:
return
if not globalconfig["sourcestatus2"]["texthook"]["use"]:
return
if globalconfig["startgamenototop"] == False:
idx = savehook_new_list.index(uid)
savehook_new_list.insert(0, savehook_new_list.pop(idx))
needinserthookcode = savehook_new_data[uid]["needinserthookcode"]
self.textsource = texthook(
pids,
hwnd,
name_,
uid,
autostarthookcode=savehook_new_data[uid]["hook"],
needinserthookcode=needinserthookcode,
)
self.textsource.start()
else:
pids = self.textsource.pids
if len(collect_running_pids(pids)) != 0:
return
self.textsource = None
self.translation_ui.thistimenotsetop = False
if globalconfig["keepontop"]:
self.translation_ui.settop()
while self.isrunning:
self.onwindowloadautohook()
time.sleep(
0.5
) # 太短了的话,中间存在一瞬间,后台进程比前台窗口内存占用要大。。。
try:
onwindowloadautohook()
except:
print_exc()
time.sleep(0.5)
# 太短了的话,中间存在一瞬间,后台进程比前台窗口内存占用要大。。。
def autocheckhwndexists(self):
def setandrefresh(bool):
if self.translation_ui.isbindedwindow != bool:
self.translation_ui.isbindedwindow = bool
def setandrefresh(b):
if self.translation_ui.isbindedwindow != b:
self.translation_ui.isbindedwindow = b
self.translation_ui.refreshtooliconsignal.emit()
while self.isrunning:
if self.textsource:
hwnd = self.textsource.hwnd
if hwnd == 0:
if globalconfig["sourcestatus2"]["texthook"]["use"]:
fhwnd = windows.GetForegroundWindow()
pids = self.textsource.pids
if (
hwnd == 0
and windows.GetWindowThreadProcessId(fhwnd) in pids
):
if "once" not in dir(self.textsource):
self.textsource.once = True
self.textsource.hwnd = fhwnd
setandrefresh(True)
else:
setandrefresh(False)
else:
if windows.GetWindowThreadProcessId(hwnd) == 0:
self.textsource.hwnd = 0
setandrefresh(False)
elif "once" not in dir(self.textsource):
self.textsource.once = True
setandrefresh(True)
if len(self.textsource.pids):
_mute = winsharedutils.GetProcessMute(self.textsource.pids[0])
if self.translation_ui.processismuteed != _mute:
self.translation_ui.processismuteed = _mute
self.translation_ui.refreshtooliconsignal.emit()
else:
def __do():
if not self.textsource:
setandrefresh(False)
return
hwnd = self.textsource.hwnd
if hwnd == 0:
if not globalconfig["sourcestatus2"]["texthook"]["use"]:
setandrefresh(False)
else:
fhwnd = windows.GetForegroundWindow()
pids = self.textsource.pids
notdone = "once" not in dir(self.textsource)
isgoodproc = windows.GetWindowThreadProcessId(fhwnd) in pids
if isgoodproc and notdone:
self.textsource.once = True
self.textsource.hwnd = fhwnd
setandrefresh(True)
else:
if windows.GetWindowThreadProcessId(hwnd) == 0:
self.textsource.hwnd = 0
setandrefresh(False)
elif "once" not in dir(self.textsource):
self.textsource.once = True
setandrefresh(True)
if len(self.textsource.pids):
_mute = winsharedutils.GetProcessMute(self.textsource.pids[0])
if self.translation_ui.processismuteed != _mute:
self.translation_ui.processismuteed = _mute
self.translation_ui.refreshtooliconsignal.emit()
while self.isrunning:
__do()
time.sleep(0.5)
@ -925,29 +916,28 @@ class MAINUI:
True,
)
_hwnd = windows.GetForegroundWindow()
_pid = windows.GetWindowThreadProcessId(_hwnd)
try:
_hwnd = windows.GetForegroundWindow()
_pid = windows.GetWindowThreadProcessId(_hwnd)
if len(self.textsource.pids) == 0:
raise Exception()
if _pid in self.textsource.pids or _pid == os.getpid():
isok(self.textsource.gameuid)
else:
self.__currentexe = None
except:
name_ = getpidexe(_pid)
if not name_:
return
uids = findgameuidofpath(name_, findall=True)
try:
if len(self.textsource.pids) == 0:
raise Exception()
if _pid in self.textsource.pids or _pid == os.getpid():
isok(self.textsource.gameuid)
if len(uids):
for uid in uids:
isok(uid)
else:
self.__currentexe = None
except:
name_ = getpidexe(_pid)
if (
name_
and name_ in gamepath2uid
and gamepath2uid[name_] in savehook_new_list
):
isok(gamepath2uid[name_])
else:
self.__currentexe = None
except:
print_exc()
print_exc()
@threader
def clickwordcallback(self, word, append):

View File

@ -34,8 +34,13 @@ def gettranslationrecorddir(name):
return getcachedir(name, "translation_record")
def gettempdir_1():
tgt = getcachedir("temp")
return tgt
def gettempdir(filename):
tgt = getcachedir(os.path.join(f"temp{os.getpid()}", filename))
tgt = getcachedir(os.path.join(f"temp/{os.getpid()}", filename))
return tgt

View File

@ -32,8 +32,10 @@ class AttachProcessDialog(saveposwindow):
_pids = [pid]
self.processEdit.setText(name)
self.processIdEdit.setText(",".join([str(pid) for pid in _pids]))
[_.show() for _ in self.windowtextlayoutwidgets]
self.windowtext.setText(windows.GetWindowText(hwnd))
self.processEdit.setCursorPosition(0)
self.processIdEdit.setCursorPosition(0)
self.windowtext.setCursorPosition(0)
self.selectedp = (_pids, name, hwnd)
def closeEvent(self, e):
@ -80,10 +82,8 @@ class AttachProcessDialog(saveposwindow):
self.layout3.addWidget(self.processEdit)
self.windowtext = QLineEdit()
self.windowtextlayoutwidgets = [QLabel(_TR("窗口名")), self.windowtext]
[_.hide() for _ in self.windowtextlayoutwidgets]
self.layout2.addWidget(self.windowtextlayoutwidgets[0])
self.layout2.addWidget(self.windowtextlayoutwidgets[1])
self.layout2.addWidget(QLabel(_TR("标题")))
self.layout2.addWidget(self.windowtext)
self.processList = QListView()
self.buttonBox = QDialogButtonBox()
self.buttonBox.setStandardButtons(
@ -109,15 +109,25 @@ class AttachProcessDialog(saveposwindow):
self.processList.clicked.connect(self.selectedfunc)
self.processIdEdit.textEdited.connect(self.editpid)
self.processEdit.setReadOnly(True)
self.windowtext.setReadOnly(True)
# self.processEdit.setReadOnly(True)
self.processEdit.textEdited.connect(self.filterproc)
def filterproc(self):
self.processIdEdit.clear()
self.windowtext.clear()
text = self.processEdit.text()
if len(text) == 0:
self.refreshfunction()
return
for row in range(self.model.rowCount()):
hide = not (text in self.model.item(row, 0).text())
self.processList.setRowHidden(row, hide)
def refreshfunction(self):
self.windowtext.clear()
self.processEdit.clear()
self.processIdEdit.clear()
[_.hide() for _ in self.windowtextlayoutwidgets]
self.selectedp = None
###########################
@ -151,15 +161,21 @@ class AttachProcessDialog(saveposwindow):
def editpid(self, process):
pids = self.safesplit(process)
self.selectedp = (pids, getpidexe(pids[0]), self.guesshwnd(pids))
self.windowtext.setText(windows.GetWindowText(self.selectedp[-1]))
self.processEdit.setText(self.selectedp[1])
[_.hide() for _ in self.windowtextlayoutwidgets]
self.windowtext.setCursorPosition(0)
self.processEdit.setCursorPosition(0)
def selectedfunc(self, index):
pids, pexe = self.processlist[index.row()]
self.processEdit.setText(pexe)
self.processIdEdit.setText(",".join([str(pid) for pid in pids]))
[_.hide() for _ in self.windowtextlayoutwidgets]
self.selectedp = pids, pexe, self.guesshwnd(pids)
self.windowtext.setText(windows.GetWindowText(self.selectedp[-1]))
self.processEdit.setCursorPosition(0)
self.processIdEdit.setCursorPosition(0)
self.windowtext.setCursorPosition(0)
def guesshwnd(self, pids):
for pid in pids:
@ -175,10 +191,5 @@ class AttachProcessDialog(saveposwindow):
if self.selectedp[1] is None:
getQMessageBox(self, "错误", "权限不足,请以管理员权限运行!")
return
# for pid in self.selectedp[0]:
# if(not testprivilege(pid)):
# getQMessageBox(self,"错误","权限不足,请使用管理员权限运行本程序!")
# return
self.close()
self.callback(self.selectedp)
self.callback(self.selectedp, self.windowtext.text())

View File

@ -6,7 +6,6 @@ import windows, gobject, winsharedutils
from myutils.config import (
savehook_new_list,
savehook_new_data,
gamepath2uid,
savegametaged,
uid2gamepath,
_TR,
@ -614,15 +613,14 @@ def maybehavebutton(self, gameuid, post):
class dialog_setting_game_internal(QWidget):
def selectexe(self):
f = QFileDialog.getOpenFileName(directory=uid2gamepath[self.gameuid])
originpath = uid2gamepath[self.gameuid]
f = QFileDialog.getOpenFileName(directory=originpath)
res = f[0]
if res == "":
return
# 修改路径允许路径重复
# 添加路径实际上也允许重复,只不过会去重。
res = os.path.normpath(res)
if res in gamepath2uid:
return
originpath = uid2gamepath[self.gameuid]
gamepath2uid[res] = gamepath2uid.pop(originpath)
uid2gamepath[self.gameuid] = res
gobject.baseobject.resetgameinternal(originpath, res)
_icon = getExeIcon(res, cache=True)
@ -715,13 +713,16 @@ class dialog_setting_game_internal(QWidget):
internallist=list(globalconfig["metadata"].keys()),
),
)
formLayout.addRow(None, QLabel())
for key in globalconfig["metadata"]:
try:
idname = globalconfig["metadata"][key]["target"]
vndbid = QLineEdit(str(savehook_new_data[gameuid][idname]))
if globalconfig["metadata"][key].get("idtype", 1) == 0:
vndbid.setValidator(QIntValidator())
vndbid.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
vndbid.setSizePolicy(
QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed
)
vndbid.textEdited.connect(
functools.partial(idtypecheck, key, idname, gameuid)
@ -730,6 +731,7 @@ class dialog_setting_game_internal(QWidget):
functools.partial(gamdidchangedtask, key, idname, gameuid)
)
_vbox_internal = [
getsimpleswitch(globalconfig["metadata"][key], "auto"),
vndbid,
getIconButton(
functools.partial(self.openrefmainpage, key, idname, gameuid),
@ -746,7 +748,7 @@ class dialog_setting_game_internal(QWidget):
try:
__settting = targetmod[key].querysettingwindow
_vbox_internal.insert(
1,
2,
getIconButton(
functools.partial(__settting, self, gameuid), icon="fa.gear"
),
@ -1974,7 +1976,7 @@ class dialog_savedgame_new(QWidget):
menu = QMenu(self)
editname = QAction(_TR("修改列表名称"))
addlist = QAction(_TR("添加列表"))
addlist = QAction(_TR("创建列表"))
dellist = QAction(_TR("删除列表"))
startgame = QAction(_TR("开始游戏"))
@ -2028,7 +2030,7 @@ class dialog_savedgame_new(QWidget):
elif action == editname or action == addlist:
_dia = Prompt_dialog(
self,
_TR("修改列表名称" if action == editname else "添加列表"),
_TR("修改列表名称" if action == editname else "创建列表"),
"",
[
[
@ -2160,7 +2162,7 @@ class dialog_savedgame_new(QWidget):
self.simplebutton("删除游戏", True, self.clicked2, False)
self.simplebutton("打开目录", True, self.clicked4, True)
self.simplebutton("添加到列表", False, self.addtolist, 1)
self.simplebutton("添加到列表", True, self.addtolist, False)
if globalconfig["startgamenototop"]:
self.simplebutton("左移", True, functools.partial(self.moverank, -1), False)
self.simplebutton("右移", True, functools.partial(self.moverank, 1), False)
@ -2770,9 +2772,9 @@ class dialog_savedgame_v3(QWidget):
self.stack.directshow()
def stack_showmenu(self, ispixmenu, p):
if not self.currentfocusuid:
return
menu = QMenu(self)
addlist = QAction(_TR("创建列表"))
startgame = QAction(_TR("开始游戏"))
delgame = QAction(_TR("删除游戏"))
opendir = QAction(_TR("打开目录"))
@ -2780,24 +2782,57 @@ class dialog_savedgame_v3(QWidget):
setimage = QAction(_TR("设为封面"))
deleteimage = QAction(_TR("删除图片"))
hualang = QAction(_TR("画廊"))
exists = os.path.exists(uid2gamepath[self.currentfocusuid])
if exists:
menu.addAction(startgame)
menu.addAction(delgame)
if exists:
menu.addAction(opendir)
if not self.currentfocusuid:
menu.addSeparator()
menu.addAction(addtolist)
menu.addAction(addlist)
else:
exists = os.path.exists(uid2gamepath[self.currentfocusuid])
if exists:
menu.addAction(startgame)
menu.addAction(delgame)
if exists:
menu.addAction(opendir)
if ispixmenu:
menu.addSeparator()
menu.addAction(setimage)
menu.addAction(deleteimage)
menu.addAction(hualang)
menu.addAction(addtolist)
if ispixmenu:
menu.addSeparator()
menu.addAction(setimage)
menu.addAction(deleteimage)
menu.addAction(hualang)
action = menu.exec(QCursor.pos())
if action == startgame:
startgamecheck(self, self.currentfocusuid)
elif addlist == action:
_dia = Prompt_dialog(
self,
_TR("创建列表"),
"",
[
[
_TR("名称"),
(""),
],
],
)
if _dia.exec():
title = _dia.text[0].text()
if title != "":
i = calculatetagidx(None)
if action == addlist:
tag = {
"title": title,
"games": [],
"uid": str(uuid.uuid4()),
"opened": True,
}
savegametaged.insert(i, tag)
group0 = self.createtaglist(self.stack, title, tag["uid"], True)
self.stack.insertw(i, group0)
elif action == delgame:
self.shanchuyouxi()
elif action == hualang:
@ -2885,7 +2920,7 @@ class dialog_savedgame_v3(QWidget):
)
self.simplebutton("删除游戏", True, self.shanchuyouxi, False)
self.simplebutton("打开目录", True, self.clicked4, True)
self.simplebutton("添加到列表", False, self.addtolist, 1)
self.simplebutton("添加到列表", True, self.addtolist, False)
if globalconfig["startgamenototop"]:
self.simplebutton("上移", True, functools.partial(self.moverank, -1), False)
self.simplebutton("下移", True, functools.partial(self.moverank, 1), False)
@ -2945,7 +2980,7 @@ class dialog_savedgame_v3(QWidget):
self.reftagid = tagid
menu = QMenu(self)
editname = QAction(_TR("修改列表名称"))
addlist = QAction(_TR("添加列表"))
addlist = QAction(_TR("创建列表"))
dellist = QAction(_TR("删除列表"))
Upaction = QAction(_TR("上移"))
Downaction = QAction(_TR("下移"))
@ -2975,7 +3010,7 @@ class dialog_savedgame_v3(QWidget):
elif action == editname or action == addlist:
_dia = Prompt_dialog(
self,
_TR("修改列表名称" if action == editname else "添加列表"),
_TR("修改列表名称" if action == editname else "创建列表"),
"",
[
[

View File

@ -22,7 +22,7 @@ def getall(l, item="fanyi", name=None):
continue
if name:
_f = name % fanyi
if fanyi != "selfbuild" and os.path.exists(_f) == False:
if not os.path.exists(_f):
continue
i += 1

View File

@ -34,14 +34,18 @@ from gui.usefulwidget import (
def __create(self):
self.selectbutton = getIconButton(
gobject.baseobject.createattachprocess, icon="fa.gear"
gobject.baseobject.createattachprocess,
icon="fa.gear",
enable=globalconfig["sourcestatus2"]["texthook"]["use"],
)
return self.selectbutton
def __create2(self):
self.selecthookbutton = getIconButton(
lambda: gobject.baseobject.hookselectdialog.showsignal.emit(), icon="fa.gear"
lambda: gobject.baseobject.hookselectdialog.showsignal.emit(),
icon="fa.gear",
enable=globalconfig["sourcestatus2"]["texthook"]["use"],
)
return self.selecthookbutton

View File

@ -38,7 +38,7 @@ def initsome11(self, l, label=None):
continue
_f = "./Lunatranslator/translator/{}.py".format(fanyi)
if fanyi != "selfbuild" and os.path.exists(_f) == False:
if not os.path.exists(_f):
continue
i += 1
@ -128,12 +128,9 @@ def checkconnected(self):
for dev in develop:
if not globalconfig["fanyi"][dev]["use"]:
continue
if dev == "selfbuild":
if not os.path.exists("./userconfig/selfbuild.py"):
continue
else:
if not os.path.exists("./LunaTranslator/translator/" + dev + ".py"):
continue
if not os.path.exists("./LunaTranslator/translator/" + dev + ".py"):
continue
needstart = True
break
try:
@ -218,7 +215,7 @@ def setTabTwo_lazy(self, basel):
"",
"模糊匹配_相似度_%",
D_getspinbox(0, 100, globalconfig, "premtsimi2"),
""
"",
],
[
(functools.partial(createbtnexport, self), 0),
@ -229,7 +226,7 @@ def setTabTwo_lazy(self, basel):
"group",
)
],
[]
[],
]
_items = [
{

View File

@ -489,6 +489,11 @@ class AnkiWindow(QWidget):
cropbutton = QPushButton(qtawesome.icon("fa.crop"), "")
cropbutton.clicked.connect(self.crop)
grabwindowbtn = QPushButton(qtawesome.icon("fa.camera"), "")
grabwindowbtn.clicked.connect(
lambda: grabwindow(getimageformat(), self.editpath.setText)
)
self.audiopath = QLineEdit()
self.audiopath.setReadOnly(True)
self.audiopath_sentence = QLineEdit()
@ -566,6 +571,7 @@ class AnkiWindow(QWidget):
QLabel(_TR("截图")),
self.editpath,
cropbutton,
grabwindowbtn,
getIconButton(
functools.partial(
self.selecfile, self.editpath

View File

@ -3,7 +3,14 @@ import time, functools, threading, os, sys, importlib, shutil
from traceback import print_exc
import windows, qtawesome, gobject, winsharedutils
from myutils.wrapper import threader, trypass
from myutils.config import globalconfig, saveallconfig, _TR, static_data, gamepath2uid
from myutils.config import (
globalconfig,
saveallconfig,
_TR,
static_data,
findgameuidofpath,
savehook_new_list,
)
from myutils.subproc import endsubprocs
from myutils.ocrutil import ocr_run, imageCut
from myutils.utils import loadpostsettingwindowmethod, str2rgba
@ -12,13 +19,14 @@ from gui.setting_about import doupdate
from gui.dialog_memory import dialog_memory
from gui.textbrowser import Textbrowser
from gui.rangeselect import rangeselct_function
from gui.usefulwidget import resizableframeless, isinrect
from gui.usefulwidget import resizableframeless, isinrect, getQMessageBox
from gui.edittext import edittrans
from gui.dialog_savedgame import browserdialog, dialog_savedgame_integrated
class QUnFrameWindow(resizableframeless):
displayglobaltooltip = pyqtSignal(str)
displaymessagebox = pyqtSignal(str, str)
displayres = pyqtSignal(dict)
displayraw1 = pyqtSignal(dict)
displaystatus = pyqtSignal(str, str, bool, bool)
@ -522,9 +530,13 @@ class QUnFrameWindow(resizableframeless):
def displayglobaltooltip_f(self, string):
QToolTip.showText(QCursor.pos(), string, self)
def displaymessagebox_f(self, string1, string2):
getQMessageBox(self, string1, string2)
def initsignals(self):
self.hidesignal.connect(self.hide_)
self.displayglobaltooltip.connect(self.displayglobaltooltip_f)
self.displaymessagebox.connect(self.displaymessagebox_f)
self.ocr_once_signal.connect(self.ocr_once_function)
self.displaystatus.connect(self.showstatus)
self.showhideuisignal.connect(self.showhideui)
@ -797,7 +809,7 @@ class QUnFrameWindow(resizableframeless):
gobject.baseobject.textsource.hwnd = hwnd if pid != _pid else None
if not globalconfig["sourcestatus2"]["texthook"]["use"]:
gobject.baseobject.textsource.pids = [pid] if pid != _pid else None
gameuid = gamepath2uid.get(getpidexe(pid), None)
gameuid = findgameuidofpath(getpidexe(pid), savehook_new_list)
if gameuid:
gobject.baseobject.textsource.gameuid = gameuid
self.isbindedwindow = pid != _pid
@ -1008,13 +1020,31 @@ class QUnFrameWindow(resizableframeless):
self.buttons[name] = button
def tryremoveuseless(self):
try:
allisremoved = True
tmpbase = gobject.gettempdir_1()
for f in os.listdir(tmpbase):
try:
pid = int(f)
except:
continue
if (pid != os.getpid()) and (winsharedutils.pid_running(pid)):
allisremoved = False
continue
try:
shutil.rmtree(gobject.gettempdir(''))
except:
pass
try:
os.remove("./cache/Updater.exe")
try:
shutil.rmtree(os.path.join(tmpbase, f))
except:
pass
if allisremoved:
try:
shutil.rmtree(tmpbase)
except:
pass
try:
os.remove("./cache/Updater.exe")
except:
pass
except:
pass

View File

@ -71,7 +71,7 @@ class bgmsettings(QDialog):
if vid in collect:
gameuid = collect[vid]
else:
gameuid = initanewitem(f"bgm_{vid}_{time.time()}", title)
gameuid = initanewitem(title)
savehook_new_data[gameuid][self._ref.idname] = vid
gamdidchangedtask(self._ref.typename, self._ref.idname, gameuid)
reflist.insert(0, gameuid)
@ -203,9 +203,8 @@ class searcher(common):
vndbtags = [_["name"] for _ in response["tags"]]
developers = []
for _ in response["infobox"]:
if _["key"] == "游戏开发商":
developers = [_["value"]]
break
if _["key"] in ["游戏开发商", "开发", "发行"]:
developers += [_["value"]]
return {
# "namemap": namemap,
"title": response["name"],

View File

@ -72,7 +72,7 @@ class steamsettings(QDialog):
if vid in collect:
gameuid = collect[vid]
else:
gameuid = initanewitem(f"steam_{vid}_{time.time()}", title)
gameuid = initanewitem(title)
savehook_new_data[gameuid][self._ref.idname] = vid
gamdidchangedtask(self._ref.typename, self._ref.idname, gameuid)
reflist.insert(0, gameuid)

View File

@ -232,7 +232,7 @@ class vndbsettings(QDialog):
if vid in collect:
gameuid = collect[vid]
else:
gameuid = initanewitem(f"vndb_{vid}_{time.time()}", title)
gameuid = initanewitem(title)
savehook_new_data[gameuid][self._ref.idname] = vid
gamdidchangedtask(self._ref.typename, self._ref.idname, gameuid)
reflist.insert(0, gameuid)

View File

@ -52,11 +52,11 @@ if _savehook:
savehook_new_list = _savehook[0]
savehook_new_data = _savehook[1]
savegametaged = _savehook[2]
gamepath2uid = _savehook[3]
# gamepath2uid = _savehook[3] 不再使用允许重复的path
else:
_savehook = tryreadconfig("savehook_new_1.39.4.json", default=[[], {}])
# savehook_new_list: [gamepath,...]
# savehook_new_data:{gamepath:dict,...}
# savegametaged: 可能没有该项 [ None, {'games':[gamepath,...],'title':str,'opened':bool,'uid':str},...]
@ -73,23 +73,23 @@ else:
savegametaged = [None]
# 将savehook_new_data转换为新的格式
gamepath2uid = {}
__gamepath2uid = {}
__savehook_new_data = {}
for k in savehook_new_data:
uid = f"{time.time()}_{uuid.uuid4()}"
__savehook_new_data[uid] = savehook_new_data[k]
__savehook_new_data[uid].update(gamepath=k)
gamepath2uid[k] = uid
__gamepath2uid[k] = uid
savehook_new_data = __savehook_new_data
# 将global游戏表和自定义子列表都转换成新格式
def parselist(ls):
for i in range(len(ls)):
ori = ls[i]
if ori not in gamepath2uid:
if ori not in __gamepath2uid:
continue
ls[i] = gamepath2uid[ori]
ls[i] = __gamepath2uid[ori]
parselist(savehook_new_list)
for sub in savegametaged:
@ -100,9 +100,9 @@ translatorsetting = tryreadconfig("translatorsetting.json")
ocrsetting = tryreadconfig("ocrsetting.json")
def getdefaultsavehook(gamepath, title=None):
def getdefaultsavehook(title=None):
default = {
# "gamepath": gamepath,
"gamepath": "", # 不要直接访问要通过uid2gamepath来间接访问
"hooksetting_follow_default": True,
"hooksetting_private": {}, # 显示时再加载缺省用global中的键
"textproc_follow_default": True,
@ -167,12 +167,6 @@ def getdefaultsavehook(gamepath, title=None):
}
if title and len(title):
default["title"] = title # metadata
else:
default["title"] = (
os.path.basename(os.path.dirname(gamepath))
+ "/"
+ os.path.basename(gamepath)
)
return default
@ -205,6 +199,34 @@ class __uid2gamepath:
uid2gamepath = __uid2gamepath()
def findgameuidofpath(gamepath, targetlist=None, findall=False):
# 一般只在save_game_list里查找用于从getpidexe获取uid
# 因为有可能有过去的不再使用的uid发生碰撞。
# 只在添加游戏时,全面查找。
if not gamepath:
if findall:
return []
else:
return None
# 遍历的速度非常快1w条的速度也就0.001x秒
# 但1w条数据时load/dump的速度就有点慢了能2秒多
checkin = targetlist
if checkin is None:
checkin = savehook_new_data.keys()
collect = []
for uid in checkin:
if savehook_new_data[uid]["gamepath"] == gamepath:
if findall:
collect.append(uid)
else:
return uid
if findall:
return collect
else:
return None
def syncconfig(config1, default, drop=False, deep=0, skipdict=False):
for key in default:
@ -404,11 +426,15 @@ def _TRL(kk):
return x
def safesave(fname, js):
def safesave(fname, js, beatiful=True):
# 有时保存时意外退出会导致config文件被清空
os.makedirs("./userconfig", exist_ok=True)
with open(fname + ".tmp", "w", encoding="utf-8") as ff:
ff.write(json.dumps(js, ensure_ascii=False, sort_keys=False, indent=4))
if beatiful:
ff.write(json.dumps(js, ensure_ascii=False, sort_keys=False, indent=4))
else:
# savegamedata 1w条时indent=4要2秒不indent 0.37秒不ensure_ascii 0.27秒,用不着数据库了
ff.write(json.dumps(js, sort_keys=False))
if os.path.exists(fname):
os.remove(fname)
os.rename(fname + ".tmp", fname)
@ -426,7 +452,8 @@ def saveallconfig():
safesave("./userconfig/ocrsetting.json", ocrsetting)
safesave(
"./userconfig/savegamedata_5.3.1.json",
[savehook_new_list, savehook_new_data, savegametaged, gamepath2uid],
[savehook_new_list, savehook_new_data, savegametaged, None],
beatiful=False,
)
safesave(
"./files/lang/{}.json".format(getlanguse()),

View File

@ -84,25 +84,33 @@ def getprocesslist():
def getpidexe(pid):
hwnd1 = windows.AutoHandle(
windows.OpenProcess(windows.PROCESS_ALL_ACCESS, False, (pid))
windows.OpenProcess(windows.PROCESS_ALL_ACCESS, False, pid)
)
if hwnd1 == 0:
if not hwnd1:
hwnd1 = windows.OpenProcess(
windows.PROCESS_QUERY_LIMITED_INFORMATION, False, (pid)
windows.PROCESS_QUERY_LIMITED_INFORMATION, False, pid
)
if hwnd1 == 0:
if not hwnd1:
name_ = None
else:
name_ = windows.GetProcessFileName(hwnd1)
return name_
def testprivilege(pid):
hwnd1 = windows.AutoHandle(
windows.OpenProcess(windows.PROCESS_INJECT_ACCESS, False, (pid))
def test_injectable_1(pid):
return bool(
windows.AutoHandle(
windows.OpenProcess(windows.PROCESS_INJECT_ACCESS, False, pid)
)
)
return hwnd1 != 0
def test_injectable(pids):
for pid in pids:
if not test_injectable_1(pid):
return False
return True
def ListProcess(filt=True):
@ -128,18 +136,13 @@ def ListProcess(filt=True):
pass
kv = {}
for pid, exe in ret:
if exe in kv:
kv[exe]["pid"].append(pid)
else:
kv[exe] = {"pid": [pid]}
# for exe in kv:
# if len(kv[exe]['pid'])>1:
# mems=[getprocessmem(_) for _ in kv[exe]['pid']]
# _i=argsort(mems)
# kv[exe]['pid']=[kv[exe]['pid'][_i[-1]]]
if exe not in kv:
kv[exe] = []
kv[exe].append(pid)
xxx = []
for exe in kv:
xxx.append([kv[exe]["pid"], exe])
xxx.append([kv[exe], exe])
return xxx
@ -184,28 +187,26 @@ def getExeIcon(name, icon=True, cache=False):
def injectdll(injectpids, injecter, dll):
pid = " ".join([str(_) for _ in injectpids])
if any(map(testprivilege, injectpids)) == False:
windows.ShellExecute(
0,
"runas",
injecter,
'dllinject {} "{}"'.format(pid, dll),
None,
windows.SW_HIDE,
)
else:
for _ in (0,):
if not test_injectable(injectpids):
break
ret = subprocess.run(
'"{}" dllinject {} "{}"'.format(injecter, pid, dll)
).returncode
if ret == 0:
windows.ShellExecute(
0,
"runas",
injecter,
'dllinject {} "{}"'.format(pid, dll),
None,
windows.SW_HIDE,
)
if ret:
return
pids = winsharedutils.collect_running_pids(injectpids)
pid = " ".join([str(_) for _ in pids])
windows.ShellExecute(
0,
"runas",
injecter,
'dllinject {} "{}"'.format(pid, dll),
None,
windows.SW_HIDE,
)
def mouseselectwindow(callback):

View File

@ -1,8 +1,8 @@
import re, codecs, inspect
from traceback import print_exc
from collections import Counter
import importlib, gobject
from myutils.utils import getfilemd5, LRUCache, getlangsrc
import gobject
from myutils.utils import checkchaos, checkmd5reloadmodule, LRUCache, getlangsrc
from myutils.config import (
postprocessconfig,
globalconfig,
@ -322,9 +322,6 @@ def lines_threshold(line, args):
return line
from myutils.utils import checkchaos
def _remove_chaos(line):
newline = ""
for c in line:
@ -334,12 +331,16 @@ def _remove_chaos(line):
return newline
_selfdefpost = None
_selfdefpostmd5 = None
def _mypostloader(line, file, module):
isnew, _ = checkmd5reloadmodule(file, module)
# 这个是单独函数的模块不需要用isnew来判断是否需要重新初始化
if not _:
return line
return _.POSTSOLVE(line)
def POSTSOLVE(line):
global _selfdefpostmd5, _selfdefpost
if line == "":
return ""
functions = {
@ -367,6 +368,7 @@ def POSTSOLVE(line):
"dedump": dedump,
"length_threshold": length_threshold,
"lines_threshold": lines_threshold,
"_11": _mypostloader,
}
useranklist = globalconfig["postprocess_rank"]
usedpostprocessconfig = postprocessconfig
@ -394,22 +396,6 @@ def POSTSOLVE(line):
)
except:
print_exc()
try:
md5 = getfilemd5(usemypostpath)
if md5 != _selfdefpostmd5:
_ = importlib.import_module(usemodule)
_ = importlib.reload(_)
_selfdefpostmd5 = md5
_selfdefpost = _
else:
_ = _selfdefpost
functions.update({"_11": _.POSTSOLVE})
except ModuleNotFoundError:
pass
except:
print_exc()
pass
for postitem in useranklist:
if postitem not in functions:
continue
@ -418,20 +404,20 @@ def POSTSOLVE(line):
if usedpostprocessconfig[postitem]["use"]:
try:
_f = functions[postitem]
sig = inspect.signature(_f)
np = len(sig.parameters)
if np == 1:
line = functions[postitem](line)
elif np == 2:
line = functions[postitem](
line, usedpostprocessconfig[postitem].get("args", {})
)
else:
raise Exception("unsupported parameters num")
except Exception as e:
print_exc()
if postitem == "_11":
raise e
line = functions[postitem](line, usemypostpath, usemodule)
else:
sig = inspect.signature(_f)
np = len(sig.parameters)
if np == 1:
line = functions[postitem](line)
elif np == 2:
line = functions[postitem](
line, usedpostprocessconfig[postitem].get("args", {})
)
else:
raise Exception("unsupported parameters num")
except:
print_exc()
return line

View File

@ -13,9 +13,9 @@ from myutils.config import (
globalconfig,
static_data,
getlanguse,
savehook_new_data,
uid2gamepath,
gamepath2uid,
savehook_new_data,
findgameuidofpath,
getdefaultsavehook,
)
from ctypes import c_float, pointer, c_void_p
@ -127,9 +127,6 @@ class PriorityQueue:
return bool(len(self._heap) == 0)
searchvndbqueue = PriorityQueue()
def guessmaybetitle(gamepath, title):
__t = []
@ -167,14 +164,20 @@ def guessmaybetitle(gamepath, title):
targetmod = {}
def trysearchforid(gameuid, searchargs: list):
def dispatchsearchfordata(gameuid, target, vid):
targetmod[target].dispatchsearchfordata(gameuid, vid)
def trysearchforid_1(gameuid, searchargs: list):
infoid = None
primitivtemetaorigin = globalconfig["primitivtemetaorigin"]
__ = list(targetmod.keys())
if primitivtemetaorigin not in __:
primitivtemetaorigin = __[0]
__.remove(primitivtemetaorigin)
__.insert(0, primitivtemetaorigin)
__ = [primitivtemetaorigin]
for k in targetmod:
if k == primitivtemetaorigin:
continue
if not globalconfig["metadata"][k]["auto"]:
continue
__.append(k)
for key in __:
vid = None
@ -195,28 +198,15 @@ def trysearchforid(gameuid, searchargs: list):
if key == primitivtemetaorigin:
break
if infoid:
searchvndbqueue.put((1, gameuid, infoid))
return infoid
key, vid = infoid
dispatchsearchfordata(gameuid, key, vid)
gobject.baseobject.translation_ui.displayglobaltooltip.emit(
f"{key}: found {vid}"
)
def everymethodsthread():
while True:
_ = searchvndbqueue.get()
_type, gameuid, arg = _
try:
if _type == 0:
infoid = trysearchforid(gameuid, arg)
key, vid = infoid
gobject.baseobject.translation_ui.displayglobaltooltip.emit(
f"{key}: found {vid}"
)
elif _type == 1:
key, vid = arg
targetmod[key].dispatchsearchfordata(gameuid, vid)
except:
print_exc()
def trysearchforid(gameuid, searchargs: list):
threading.Thread(target=trysearchforid_1, args=(gameuid, searchargs)).start()
def idtypecheck(key, idname, gameuid, vid):
@ -237,30 +227,43 @@ def idtypecheck(key, idname, gameuid, vid):
def gamdidchangedtask(key, idname, gameuid):
vid = savehook_new_data[gameuid][idname]
searchvndbqueue.put((1, gameuid, (key, vid)), 1)
dispatchsearchfordata(gameuid, key, vid)
def titlechangedtask(gameuid, title):
savehook_new_data[gameuid]["title"] = title
savehook_new_data[gameuid]["istitlesetted"] = True
searchvndbqueue.put((0, gameuid, [title]), 1)
trysearchforid(gameuid, [title])
def initanewitem(gamepath, title):
def initanewitem(title):
uid = f"{time.time()}_{uuid.uuid4()}"
gamepath2uid[gamepath] = uid
savehook_new_data[uid] = getdefaultsavehook(gamepath, title)
uid2gamepath[uid] = gamepath
savehook_new_data[uid] = getdefaultsavehook(title)
return uid
def checkifnewgame(targetlist, gamepath, title=None):
if gamepath not in gamepath2uid:
uid = initanewitem(gamepath, title)
searchvndbqueue.put((0, uid, [title] + guessmaybetitle(gamepath, title)))
# 用于添加游戏时,全局查找是否有过历史记录
uids = findgameuidofpath(gamepath, findall=True)
print(uids)
if len(uids) == 0:
uid = initanewitem(title)
if title is None:
savehook_new_data[uid]["title"] = (
os.path.basename(os.path.dirname(gamepath))
+ "/"
+ os.path.basename(gamepath)
)
uid2gamepath[uid] = gamepath
trysearchforid(uid, [title] + guessmaybetitle(gamepath, title))
isnew = True
else:
uid = gamepath2uid[gamepath]
isnew = uid not in targetlist
isnew = True
for uid in uids:
if uid in targetlist:
isnew = False
break
if isnew:
targetlist.insert(0, uid)
return uid
@ -414,7 +417,10 @@ class Process:
pass
"""
)
os.startfile(p)
# os.startfile(p)
threading.Thread(
target=os.system, args=(f'notepad "{os.path.normpath(p)}"',)
).start()
return p
@ -619,24 +625,11 @@ def checkpostusing(name):
return use and checkpostlangmatch(name)
def getpostfile(name):
if name == "myprocess":
mm = "myprocess"
checkpath = "./userconfig/myprocess.py"
else:
mm = "transoptimi." + name
checkpath = "./LunaTranslator/transoptimi/" + name + ".py"
def loadpostsettingwindowmethod(name):
checkpath = "./LunaTranslator/transoptimi/" + name + ".py"
if os.path.exists(checkpath) == False:
return None
return mm
def loadpostsettingwindowmethod(name):
if name == "myprocess":
return lambda _: selectdebugfile("./userconfig/myprocess.py")
mm = getpostfile(name)
if not mm:
return None
mm = "transoptimi." + name
try:
Process = importlib.import_module(mm).Process
@ -739,5 +732,29 @@ for k in globalconfig["metadata"]:
except:
print_exc()
globalcachedmodule = {}
threading.Thread(target=everymethodsthread).start()
def checkmd5reloadmodule(filename, module):
if not os.path.exists(filename):
# reload重新加载不存在的文件时不会报错。
return True, None
key = (filename, module)
md5 = getfilemd5(filename)
cachedmd5 = globalcachedmodule.get(key, {}).get("md5", None)
if md5 != cachedmd5:
try:
_ = importlib.import_module(module)
_ = importlib.reload(_)
except ModuleNotFoundError:
return True, None
# 不要捕获其他错误,缺少模块时直接跳过,只报实现错误
# except:
# print_exc()
# return True, None
globalcachedmodule[key] = {"md5": md5, "module": _}
return True, _
else:
return False, globalcachedmodule.get(key, {}).get("module", None)

View File

@ -8,9 +8,10 @@ from winsharedutils import Is64bit
from myutils.config import globalconfig, savehook_new_data, static_data
from textsource.textsourcebase import basetext
from myutils.utils import checkchaos
from myutils.hwnd import injectdll
from myutils.hwnd import injectdll, test_injectable
from myutils.wrapper import threader
from myutils.utils import getfilemd5
from traceback import print_exc
from ctypes import (
CDLL,
@ -151,6 +152,7 @@ class texthook(basetext):
gobject.baseobject.hookselectdialog.realshowhide.emit(True)
self.delaycollectallselectedoutput()
self.declare()
self.prepares()
def checkmd5prefix(self, gamepath):
md5 = getfilemd5(gamepath)
@ -229,7 +231,7 @@ class texthook(basetext):
self.Luna_FreePtr(ws)
return string
def Luna_Startup(self):
def prepares(self):
procs = [
ProcessEvent(self.onprocconnect),
ProcessEvent(self.connectedpids.remove),
@ -243,15 +245,14 @@ class texthook(basetext):
self.keepref += procs
ptrs = [cast(_, c_void_p).value for _ in procs]
self.Luna_Start(*ptrs)
def start(self):
self.Luna_Startup()
self.setsettings()
def start_unsafe(self):
caninject = test_injectable(self.pids)
injectpids = []
for pid in self.pids:
if self.config["use_yapi"]:
if caninject and self.config["use_yapi"]:
self.Luna_Inject(pid, os.path.abspath("./files/plugins/LunaHook"))
else:
if self.Luna_CreatePipeAndCheck(pid):
@ -264,9 +265,17 @@ class texthook(basetext):
dll = os.path.abspath(
"./files/plugins/LunaHook/LunaHook{}.dll".format(arch)
)
# subprocess.Popen('"{}" dllinject {} "{}"'.format(injecter,pid,dll))
injectdll(injectpids, injecter, dll)
def start(self):
try:
self.start_unsafe()
except:
print_exc()
gobject.baseobject.translation_ui.displaymessagebox.emit(
"错误", "权限不足,请以管理员权限运行!"
)
def onprocconnect(self, pid):
self.connectedpids.append(pid)
time.sleep(savehook_new_data[self.gameuid]["inserthooktimeout"] / 1000)

View File

@ -0,0 +1,27 @@
from translator.basetranslator import basetrans
from myutils.utils import checkmd5reloadmodule
class TS(basetrans):
def mayreinit(self):
isnew, module = checkmd5reloadmodule("./userconfig/selfbuild.py", "selfbuild")
if not isnew:
return
if module:
self.internal = module.TS("selfbuild")
def inittranslator(self):
self.internal = None
self.mayreinit()
def langmap(self):
self.mayreinit()
if not self.internal:
return {}
return self.internal.langmap()
def translate(self, content):
self.mayreinit()
if not self.internal:
return ""
return self.internal.translate(content)

View File

@ -0,0 +1,31 @@
from myutils.utils import selectdebugfile, checkmd5reloadmodule
class Process:
@staticmethod
def get_setting_window(parent_window):
return selectdebugfile("./userconfig/myprocess.py")
def process_after(self, res, contenxt):
self.mayreinit()
if not self.internal:
return res
return self.internal.process_after(res, contenxt)
def process_before(self, s):
self.mayreinit()
if not self.internal:
return s, None
return self.internal.process_before(s)
def __init__(self) -> None:
self.internal = None
self.mayreinit()
def mayreinit(self):
isnew, module = checkmd5reloadmodule("./userconfig/myprocess.py", "myprocess")
if not isnew:
return
if module:
self.internal = module.Process()

View File

@ -900,6 +900,9 @@ def ScreenToClient(hwnd, x, y):
return (P.x, P.y)
INVALID_HANDLE_VALUE = -1
class AutoHandle(HANDLE):
def __new__(cls, value) -> None:
instance = super().__new__(cls, value)
@ -909,6 +912,9 @@ class AutoHandle(HANDLE):
if self:
CloseHandle(self)
def __bool__(self):
return (self.value != INVALID_HANDLE_VALUE) and (self.value != None)
_MapVirtualKey = _user32.MapVirtualKeyW
_MapVirtualKey.argtypes = UINT, UINT

View File

@ -339,6 +339,16 @@ pid_running = utilsdll.pid_running
pid_running.argtypes = (DWORD,)
pid_running.restype = c_bool
def collect_running_pids(pids):
_ = []
for __ in pids:
if not pid_running(__):
continue
_.append(__)
return _
getpidhwndfirst = utilsdll.getpidhwndfirst
getpidhwndfirst.argtypes = (DWORD,)
getpidhwndfirst.restype = HWND

View File

@ -232,6 +232,7 @@
},
"metadata": {
"vndb": {
"auto": true,
"name": "vndb",
"downloadtasks": [],
"searchfordatatasks": [],
@ -243,6 +244,7 @@
}
},
"dlsite": {
"auto": false,
"name": "dlsite",
"downloadtasks": [],
"searchfordatatasks": [],
@ -250,6 +252,7 @@
"target": "dlsiteid"
},
"bangumi": {
"auto": true,
"name": "bangumi",
"downloadtasks": [],
"searchfordatatasks": [],
@ -261,6 +264,7 @@
}
},
"fanza": {
"auto": false,
"name": "FanzaGames",
"downloadtasks": [],
"searchfordatatasks": [],
@ -268,6 +272,7 @@
"target": "fanzaid"
},
"steam": {
"auto": false,
"name": "steam",
"downloadtasks": [],
"searchfordatatasks": [],

View File

@ -485,7 +485,6 @@
"进程号": "رقم العملية",
"最长翻译字数": "أقصى عدد من الكلمات المترجمة",
"过滤控制字符": "تصفية السيطرة الشخصية",
"窗口名": "اسم النافذة",
"法语": "الفرنسية .",
"分词": "اسم الفاعل",
"用户词典1": "قاموس المستخدم",
@ -716,7 +715,6 @@
"过滤历史重复": "تصفية التاريخ تكرار",
"缓存条数": "عدد شرائط التخزين المؤقت",
"腾讯OCR": "تينسنت التعرف الضوئي على الحروف",
"添加列表": "إضافة قائمة",
"删除列表": "حذف قائمة",
"上移": "رفع",
"下移": "نزولا",
@ -813,5 +811,6 @@
"上传游戏": "تحميل العاب",
"以当前md5复制选中行": "حدد صف مع نسخة MD5 الحالي",
"修改列表名称": "تعديل اسم القائمة",
"滚动到最后": "انتقل إلى آخر"
"滚动到最后": "انتقل إلى آخر",
"创建列表": "إنشاء قائمة"
}

View File

@ -388,7 +388,6 @@
"错误": "錯誤",
"所选文件格式错误!": "所選檔案格式錯誤!",
"进程号": "行程號",
"窗口名": "視窗名",
"无法识别的路径!": "無法識別的路徑!",
"进程": "行程",
"到进程": "到行程",
@ -716,7 +715,6 @@
"过滤历史重复": "過濾歷史重複",
"缓存条数": "緩存條數",
"腾讯OCR": "騰訊OCR",
"添加列表": "添加清單",
"删除列表": "删除清單",
"上移": "上移",
"下移": "下移",
@ -813,5 +811,6 @@
"上传游戏": "上傳遊戲",
"以当前md5复制选中行": "以當前md5複製選中行",
"修改列表名称": "修改清單名稱",
"滚动到最后": "滾動到最後"
"滚动到最后": "滾動到最後",
"创建列表": "創建清單"
}

View File

@ -388,7 +388,6 @@
"错误": "Error",
"所选文件格式错误!": "Invalid File Format Selected!",
"进程号": "Process ID",
"窗口名": "Window name",
"无法识别的路径!": "Unrecognizable Path!",
"进程": "Process",
"到进程": "Select Process",
@ -558,7 +557,6 @@
"点击单词复制": "Copy On Click",
"窗口背景透明": "Window Background Transparency",
"背景窗口透明": "Background Window Transparency",
"指定模式": "Specify Mode",
"搜索范围": "Search Range",
"搜索方式": "Search Method",
"使用翻译缓存": "Use Translation Cache",
@ -716,7 +714,6 @@
"过滤历史重复": "Filter Historical Duplicates",
"缓存条数": "Number of Cached Items",
"腾讯OCR": "Tencent OCR",
"添加列表": "Add List",
"删除列表": "Delete List",
"上移": "Move Up",
"下移": "Move Down",
@ -813,5 +810,7 @@
"上传游戏": "Upload Game",
"以当前md5复制选中行": "Copy Selected Row with Current MD5",
"修改列表名称": "Modify List Name",
"滚动到最后": "Scroll to End"
}
"滚动到最后": "Scroll to End",
"指定模块": "Specify modules",
"创建列表": "Create List"
}

View File

@ -388,7 +388,6 @@
"错误": "Error",
"所选文件格式错误!": "¡¡ el archivo seleccionado tiene un formato incorrecto!",
"进程号": "Número de proceso",
"窗口名": "Nombre de la ventana",
"无法识别的路径!": "¡Caminos no reconocibles!",
"进程": "Proceso",
"到进程": "Al proceso",
@ -716,7 +715,6 @@
"过滤历史重复": "Filtrar repetición histórica",
"缓存条数": "Número de barras de caché",
"腾讯OCR": "Tencent OCR",
"添加列表": "Añadir lista",
"删除列表": "Eliminar lista",
"上移": "Subir",
"下移": "Bajar",
@ -813,5 +811,6 @@
"上传游戏": "Sube el juego",
"以当前md5复制选中行": "Copiar la línea seleccionada con el MD5 actual",
"修改列表名称": "Modificar el nombre de la lista",
"滚动到最后": "Rodar hasta el final"
"滚动到最后": "Rodar hasta el final",
"创建列表": "Crear lista"
}

View File

@ -388,7 +388,6 @@
"错误": "Erreur",
"所选文件格式错误!": "Mauvais format de fichier sélectionné!",
"进程号": "Numéro de processus",
"窗口名": "Nom de la fenêtre",
"无法识别的路径!": "Un chemin méconnaissable!",
"进程": "Processus",
"到进程": "Au processus",
@ -716,7 +715,6 @@
"过滤历史重复": "Filtrer historique répétition",
"缓存条数": "Nombre de barres de cache",
"腾讯OCR": "OCR Tencent",
"添加列表": "Ajouter une liste",
"删除列表": "Supprimer une liste",
"上移": "Déplacement vers le Haut",
"下移": "Descendre",
@ -813,5 +811,6 @@
"上传游戏": "Télécharger un jeu",
"以当前md5复制选中行": "Copier la ligne sélectionnée avec le MD5 actuel",
"修改列表名称": "Modifier le nom de la Liste",
"滚动到最后": "Rouler jusqu'à la fin"
"滚动到最后": "Rouler jusqu'à la fin",
"创建列表": "Créer une liste"
}

View File

@ -376,7 +376,6 @@
"错误": "errore",
"所选文件格式错误!": "Il formato del file selezionato non è corretto!",
"进程号": "Numero del processo",
"窗口名": "Nome finestra",
"无法识别的路径!": "Percorso sconosciuto!",
"进程": "processo",
"到进程": "Da elaborare",
@ -716,7 +715,6 @@
"过滤历史重复": "Filtra duplicati storici",
"缓存条数": "Numero di voci della cache",
"腾讯OCR": "Tencent OCR",
"添加列表": "Aggiungi elenco",
"删除列表": "Elimina elenco",
"上移": "Sposta su",
"下移": "Sposta giù",
@ -813,5 +811,6 @@
"上传游戏": "Carica gioco",
"以当前md5复制选中行": "Copia la riga selezionata con l'MD5 corrente",
"修改列表名称": "Modifica nome elenco",
"滚动到最后": "Scorri fino alla fine"
"滚动到最后": "Scorri fino alla fine",
"创建列表": "Crea elenco"
}

View File

@ -388,7 +388,6 @@
"错误": "エラー",
"所选文件格式错误!": "選択したファイルフォーマットが間違っています!",
"进程号": "プロセス番号",
"窗口名": "ウィンドウ名",
"无法识别的路径!": "認識できないパス!",
"进程": "プロセス",
"到进程": "プロセスへ",
@ -716,7 +715,6 @@
"过滤历史重复": "フィルタ履歴の繰り返し",
"缓存条数": "キャッシュ・エントリ数",
"腾讯OCR": "テンセントOCR",
"添加列表": "リストの追加",
"删除列表": "リストの削除",
"上移": "上へ移動",
"下移": "下へ移動",
@ -813,5 +811,6 @@
"上传游戏": "ゲームをアップロード",
"以当前md5复制选中行": "選択した行を現在のmd 5でコピー",
"修改列表名称": "リスト名の変更",
"滚动到最后": "最後までスクロール"
"滚动到最后": "最後までスクロール",
"创建列表": "リストの作成"
}

View File

@ -388,7 +388,6 @@
"错误": "오류",
"所选文件格式错误!": "선택한 파일의 형식이 잘못되었습니다!",
"进程号": "프로세스 번호",
"窗口名": "창 이름",
"无法识别的路径!": "인식할 수 없는 경로!",
"进程": "프로세스",
"到进程": "프로세스로",
@ -716,7 +715,6 @@
"过滤历史重复": "반복된 기록 필터링",
"缓存条数": "캐시 바 수",
"腾讯OCR": "텐센트 OCR",
"添加列表": "목록 추가",
"删除列表": "목록 삭제",
"上移": "위로 이동",
"下移": "아래로 이동",
@ -813,5 +811,6 @@
"上传游戏": "게임 업로드",
"以当前md5复制选中行": "선택된 행을 현재 md5로 복사",
"修改列表名称": "목록 이름 수정",
"滚动到最后": "끝까지 스크롤"
"滚动到最后": "끝까지 스크롤",
"创建列表": "목록 만들기"
}

View File

@ -388,7 +388,6 @@
"错误": "błąd",
"所选文件格式错误!": "Wybrany format pliku jest niepoprawny!",
"进程号": "Numer procesu",
"窗口名": "Nazwa okna",
"无法识别的路径!": "Nierozpoznana ścieżka!",
"进程": "proces",
"到进程": "Do przetwarzania",
@ -716,7 +715,6 @@
"过滤历史重复": "Filtruj duplikaty historyczne",
"缓存条数": "Liczba wpisów pamięci podręcznej",
"腾讯OCR": "Dziesięć OCR",
"添加列表": "Dodaj listę",
"删除列表": "Usuń listę",
"上移": "Przesuń się w górę",
"下移": "Przesuń w dół",
@ -813,5 +811,6 @@
"上传游戏": "Przesyłaj grę",
"以当前md5复制选中行": "Kopiuj zaznaczony wiersz z bieżącym MD5",
"修改列表名称": "Zmień nazwę listy",
"滚动到最后": "Przewiń do końca"
"滚动到最后": "Przewiń do końca",
"创建列表": "Utwórz listę"
}

View File

@ -388,7 +388,6 @@
"错误": "Ошибка",
"所选文件格式错误!": "Ошибка формата выбранного файла!",
"进程号": "Номер процесса",
"窗口名": "Имя окна",
"无法识别的路径!": "Невозможно определить путь!",
"进程": "Процесс",
"到进程": "К процессу",
@ -716,7 +715,6 @@
"过滤历史重复": "Фильтровать повторение истории",
"缓存条数": "Количество кэшированных записей",
"腾讯OCR": "Скачать OCR",
"添加列表": "Добавить список",
"删除列表": "Удалить список",
"上移": "Переместить вверх",
"下移": "Переместить вниз",
@ -813,5 +811,6 @@
"上传游戏": "Загрузить игру",
"以当前md5复制选中行": "Копировать выделенную строку в текущем MD5",
"修改列表名称": "Изменить имя списка",
"滚动到最后": "Прокрутить до конца"
"滚动到最后": "Прокрутить до конца",
"创建列表": "Создать список"
}

View File

@ -59,7 +59,6 @@
"插值算法": "อัลกอริทึมการแทรก",
"显示帧率": "แสดงอัตราเฟรม",
"最新版本": "รุ่นล่าสุด",
"窗口名": "ชื่อหน้าต่าง",
"搜索": "ค้นหา",
"游戏信息": "ข้อมูลเกม",
"特殊码": "รหัสพิเศษ",
@ -716,7 +715,6 @@
"过滤历史重复": "ประวัติการกรอง ทำซ้ำ",
"缓存条数": "จำนวนแถบแคช",
"腾讯OCR": "Tencent โอซีอาร์",
"添加列表": "เพิ่มรายการ",
"删除列表": "ลบรายการ",
"上移": "เลื่อนขึ้น",
"下移": "เลื่อนลง",
@ -813,5 +811,6 @@
"上传游戏": "อัปโหลดเกม",
"以当前md5复制选中行": "คัดลอกแถวที่เลือกด้วย md5 ปัจจุบัน",
"修改列表名称": "แก้ไขชื่อรายการ",
"滚动到最后": "เลื่อนไปจนสุด"
"滚动到最后": "เลื่อนไปจนสุด",
"创建列表": "สร้างรายการ"
}

View File

@ -388,7 +388,6 @@
"错误": "hata",
"所选文件格式错误!": "Seçili dosya format ı yanlış!",
"进程号": "İşlem numarası",
"窗口名": "Pencere İsmi",
"无法识别的路径!": "Bilinmeyen yol!",
"进程": "işlem",
"到进程": "İşleme",
@ -716,7 +715,6 @@
"过滤历史重复": "Tarihi çizgileri sil",
"缓存条数": "Cache girişlerinin sayısı",
"腾讯OCR": "Tencent OCR",
"添加列表": "Liste Ekle",
"删除列表": "Listeyi sil",
"上移": "Yukarı Taşı",
"下移": "Aşağı taşın",
@ -813,5 +811,6 @@
"上传游戏": "Oyunu yükle",
"以当前md5复制选中行": "Seçili satırı mevcut MD5 ile kopyalayın",
"修改列表名称": "Liste ismini değiştir",
"滚动到最后": "Sonuna doğru yürüt"
"滚动到最后": "Sonuna doğru yürüt",
"创建列表": "Liste oluştur"
}

View File

@ -376,7 +376,6 @@
"错误": "помилка",
"所选文件格式错误!": "Вибраний формат файла неправильний!",
"进程号": "Номер процесу",
"窗口名": "Назва вікна",
"无法识别的路径!": "Нерозпізнаний шлях!",
"进程": "процес",
"到进程": "Процес",
@ -716,7 +715,6 @@
"过滤历史重复": "Фільтрувати історичні дублікати",
"缓存条数": "Кількість записів кешу",
"腾讯OCR": "Похильний OCR",
"添加列表": "Додати список",
"删除列表": "Вилучити список",
"上移": "Пересунути вгору",
"下移": "Пересунути вниз",
@ -813,5 +811,6 @@
"上传游戏": "Вивантажити гру",
"以当前md5复制选中行": "Копіювати вибраний рядок поточним MD5",
"修改列表名称": "Змінити назву списку",
"滚动到最后": "Пересунутися до кінця"
"滚动到最后": "Пересунутися до кінця",
"创建列表": "Створити список"
}

View File

@ -388,7 +388,6 @@
"错误": "Lỗi",
"所选文件格式错误!": "Lỗi định dạng tập tin đã chọn!",
"进程号": "Số tiến trình",
"窗口名": "Tên cửa sổ",
"无法识别的路径!": "Đường không nhận ra!",
"进程": "Quy trình",
"到进程": "Tiến trình",
@ -716,7 +715,6 @@
"过滤历史重复": "Lọc lịch sử lặp lại",
"缓存条数": "Số thanh bộ nhớ cache",
"腾讯OCR": "Thông tin OCR",
"添加列表": "Thêm danh sách",
"删除列表": "Xoá danh sách",
"上移": "Di chuyển lên",
"下移": "Di chuyển xuống",
@ -813,5 +811,6 @@
"上传游戏": "Tải lên trò chơi",
"以当前md5复制选中行": "Dòng đã chọn sao chép md5 hiện tại",
"修改列表名称": "Thay đổi tên danh sách",
"滚动到最后": "Cuộn đến cuối"
"滚动到最后": "Cuộn đến cuối",
"创建列表": "Tạo danh sách"
}

View File

@ -60,7 +60,6 @@
"插值算法": "",
"显示帧率": "",
"最新版本": "",
"窗口名": "",
"搜索": "",
"游戏信息": "",
"特殊码": "",
@ -724,7 +723,7 @@
"过滤历史重复": "",
"缓存条数": "",
"腾讯OCR": "",
"添加列表": "",
"创建列表": "",
"删除列表": "",
"上移": "",
"下移": "",

View File

@ -28,7 +28,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/version)
include(generate_product_version)
set(VERSION_MAJOR 5)
set(VERSION_MINOR 5)
set(VERSION_MINOR 6)
set(VERSION_PATCH 0)
add_library(pch pch.cpp)

View File

@ -55,7 +55,9 @@ DECLARE void recoverwindow(HWND hwnd, windowstatus status)
DECLARE bool pid_running(DWORD pid)
{
DWORD code;
GetExitCodeProcess(AutoHandle(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)), &code);
GetExitCodeProcess(AutoHandle(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid)), &code);
// 句柄必須具有 PROCESS_QUERY_INFORMATION 或 PROCESS_QUERY_LIMITED_INFORMATION 訪問許可權。 如需詳細資訊,請參閱 處理安全性和訪問許可權。
// Windows Server 2003 和 Windows XP 句柄必須具有 PROCESS_QUERY_INFORMATION 訪問許可權。
return code == STILL_ACTIVE;
// auto process = AutoHandle(OpenProcess(SYNCHRONIZE, FALSE, pid));
// DWORD ret = WaitForSingleObject(process, 0);
@ -95,7 +97,9 @@ DECLARE bool Is64bit(DWORD pid)
GetNativeSystemInfo(&sysinfo);
if (sysinfo.wProcessorArchitecture == 9 || sysinfo.wProcessorArchitecture == 6)
{
auto hprocess = AutoHandle(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid));
auto hprocess = AutoHandle(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
// 進程的控制碼。 控制碼必須具有PROCESS_QUERY_INFORMATION或PROCESS_QUERY_LIMITED_INFORMATION存取權限。 如需詳細資訊,請參閱 處理安全性和存取權限。
// Windows Server 2003 和 Windows XP 控制碼必須具有PROCESS_QUERY_INFORMATION存取權限。
BOOL b;
IsWow64Process(hprocess, &b);
return !b;