This commit is contained in:
恍兮惚兮 2024-08-04 00:35:44 +08:00
parent b1159f5ede
commit bc908638c7
30 changed files with 621 additions and 202 deletions

View File

@ -16,7 +16,7 @@ from myutils.config import (
globalconfig, globalconfig,
static_data, static_data,
) )
from myutils.localetools import getgamecamptoolsname, localeswitchedrun from myutils.localetools import getgamecamptools, localeswitchedrun, maycreatesettings
from myutils.hwnd import getExeIcon from myutils.hwnd import getExeIcon
from myutils.wrapper import ( from myutils.wrapper import (
Singleton_close, Singleton_close,
@ -66,6 +66,7 @@ from gui.usefulwidget import (
MySwitch, MySwitch,
auto_select_webview, auto_select_webview,
Prompt_dialog, Prompt_dialog,
clearlayout,
getsimplecombobox, getsimplecombobox,
D_getsimpleswitch, D_getsimpleswitch,
getspinbox, getspinbox,
@ -751,6 +752,9 @@ class dialog_setting_game_internal(QWidget):
self.methodtab = methodtab self.methodtab = methodtab
vbox.addWidget(methodtab) vbox.addWidget(methodtab)
do() do()
self.__launch_method.currentIndexChanged.emit(
self.__launch_method.currentIndex()
)
def openrefmainpage(self, key, idname, gameuid): def openrefmainpage(self, key, idname, gameuid):
try: try:
@ -767,7 +771,7 @@ class dialog_setting_game_internal(QWidget):
list(targetmod.keys()), list(targetmod.keys()),
globalconfig, globalconfig,
"primitivtemetaorigin", "primitivtemetaorigin",
internallist=list(targetmod.keys()), internal=list(targetmod.keys()),
static=True, static=True,
), ),
) )
@ -823,31 +827,29 @@ class dialog_setting_game_internal(QWidget):
layout.addWidget(w) layout.addWidget(w)
def starttab(self, formLayout: LFormLayout, gameuid): def starttab(self, formLayout: LFormLayout, gameuid):
box = QGroupBox()
settinglayout = LFormLayout()
box.setLayout(settinglayout)
formLayout.addRow( def __(box, layout, config, uid):
"转区启动", clearlayout(layout)
getboxlayout( maycreatesettings(layout, config, uid)
[ if layout.count() == 0:
getsimpleswitch(savehook_new_data[gameuid], "leuse"), box.hide()
getsimplecombobox( else:
getgamecamptoolsname(uid2gamepath[gameuid]), box.show()
savehook_new_data[gameuid],
"localeswitcher", self.__launch_method = getsimplecombobox(
static=True, [_.name for _ in getgamecamptools(uid2gamepath[gameuid])],
), savehook_new_data[gameuid],
] "launch_method",
), internal=[_.id for _ in getgamecamptools(uid2gamepath[gameuid])],
) callback=functools.partial(
__, box, settinglayout, savehook_new_data[gameuid]
formLayout.addRow(
"命令行启动",
getboxlayout(
[
getsimpleswitch(savehook_new_data[gameuid], "startcmduse"),
getlineedit(savehook_new_data[gameuid], "startcmd"),
]
), ),
) )
formLayout.addRow("启动方式", self.__launch_method)
formLayout.addRow(box)
formLayout.addRow( formLayout.addRow(
"自动切换到模式", "自动切换到模式",
@ -1367,7 +1369,7 @@ class dialog_setting_game_internal(QWidget):
static_data["language_list_translator"], static_data["language_list_translator"],
savehook_new_data[gameuid], savehook_new_data[gameuid],
"private_srclang_2", "private_srclang_2",
internallist=static_data["language_list_translator_inner"], internal=static_data["language_list_translator_inner"],
), ),
) )
formLayout2.addRow( formLayout2.addRow(
@ -1376,7 +1378,7 @@ class dialog_setting_game_internal(QWidget):
static_data["language_list_translator"], static_data["language_list_translator"],
savehook_new_data[gameuid], savehook_new_data[gameuid],
"private_tgtlang_2", "private_tgtlang_2",
internallist=static_data["language_list_translator_inner"], internal=static_data["language_list_translator_inner"],
), ),
) )
@ -1620,47 +1622,7 @@ def startgame(gameuid):
) )
gobject.baseobject.starttextsource(use=_[mode], checked=True) gobject.baseobject.starttextsource(use=_[mode], checked=True)
dirpath = os.path.dirname(game) localeswitchedrun(gameuid)
if savehook_new_data[gameuid]["startcmduse"]:
usearg = savehook_new_data[gameuid]["startcmd"].format(exepath=game)
windows.CreateProcess(
None,
usearg,
None,
None,
False,
0,
None,
dirpath,
windows.STARTUPINFO(),
)
return
if savehook_new_data[gameuid]["leuse"] == False or (
game.lower()[-4:] not in [".lnk", ".exe"]
):
# 对于其他文件需要AssocQueryStringW获取命令行才能正确le太麻烦放弃。
windows.ShellExecute(None, "open", game, "", dirpath, windows.SW_SHOW)
return
execheck3264 = game
usearg = '"{}"'.format(game)
if game.lower()[-4:] == ".lnk":
exepath, args, iconpath, dirp = winsharedutils.GetLnkTargetPath(game)
if args != "":
usearg = '"{}" {}'.format(exepath, args)
elif exepath != "":
usearg = '"{}"'.format(exepath)
if exepath != "":
execheck3264 = exepath
if dirp != "":
dirpath = dirp
localeswitcher = savehook_new_data[gameuid]["localeswitcher"]
localeswitchedrun(execheck3264, localeswitcher, usearg, dirpath)
except: except:
print_exc() print_exc()
@ -2085,7 +2047,7 @@ class dialog_savedgame_new(QWidget):
globalconfig, globalconfig,
"currvislistuid", "currvislistuid",
self.resetcurrvislist, self.resetcurrvislist,
internallist=uid, internal=uid,
static=True, static=True,
), ),
) )
@ -2398,22 +2360,18 @@ class dialog_savedgame_lagacy(QWidget):
self.model.insertRow( self.model.insertRow(
row, row,
[ [
QStandardItem(),
QStandardItem(), QStandardItem(),
keyitem, keyitem,
QStandardItem((savehook_new_data[k]["title"])), QStandardItem((savehook_new_data[k]["title"])),
], ],
) )
self.table.setIndexWidget( self.table.setIndexWidget(
self.model.index(row, 0), D_getsimpleswitch(savehook_new_data[k], "leuse") self.model.index(row, 0),
)
self.table.setIndexWidget(
self.model.index(row, 1),
functools.partial(self.delayloadicon, k), functools.partial(self.delayloadicon, k),
) )
self.table.setIndexWidget( self.table.setIndexWidget(
self.model.index(row, 2), self.model.index(row, 1),
D_getIconButton( D_getIconButton(
functools.partial(self.showsettingdialog, k), icon="fa.gear" functools.partial(self.showsettingdialog, k), icon="fa.gear"
), ),
@ -2427,7 +2385,7 @@ class dialog_savedgame_lagacy(QWidget):
formLayout = QVBoxLayout(self) # formLayout = QVBoxLayout(self) #
model = LStandardItemModel() model = LStandardItemModel()
model.setHorizontalHeaderLabels(["转区", "", "设置", "游戏"]) # ,'HOOK']) model.setHorizontalHeaderLabels(["", "设置", "游戏"]) # ,'HOOK'])
self.model = model self.model = model

View File

@ -163,7 +163,7 @@ class edittrans(LMainWindow):
[globalconfig["fanyi"][x]["name"] for x in globalconfig["fanyi"]], [globalconfig["fanyi"][x]["name"] for x in globalconfig["fanyi"]],
globalconfig, globalconfig,
"realtime_edit_target", "realtime_edit_target",
internallist=list(globalconfig["fanyi"]), internal=list(globalconfig["fanyi"]),
) )
) )
qv.addWidget(submit) qv.addWidget(submit)

View File

@ -13,6 +13,7 @@ from gui.usefulwidget import (
getQMessageBox, getQMessageBox,
D_getspinbox, D_getspinbox,
D_getIconButton, D_getIconButton,
clearlayout,
getboxlayout, getboxlayout,
D_getcolorbutton, D_getcolorbutton,
getcolorbutton, getcolorbutton,
@ -89,23 +90,6 @@ class extrahtml(saveposwindow):
self.show() self.show()
def clearlayout(ll: QLayout):
while ll.count():
item = ll.takeAt(0)
if not item:
continue
ll.removeItem(item)
w = item.widget()
if w:
w.deleteLater()
continue
l = item.layout()
if l:
clearlayout(l)
l.deleteLater()
continue
def createinternalfontsettings(self, forml: LFormLayout, group, _type): def createinternalfontsettings(self, forml: LFormLayout, group, _type):
globalconfig["rendertext_using_internal"][group] = _type globalconfig["rendertext_using_internal"][group] = _type
@ -312,7 +296,7 @@ def _createseletengeinecombo(self):
visengine, visengine,
globalconfig, globalconfig,
"rendertext_using", "rendertext_using",
internallist=visengine_internal, internal=visengine_internal,
callback=functools.partial(resetgroudswitchcallback, self), callback=functools.partial(resetgroudswitchcallback, self),
static=True, static=True,
) )

View File

@ -28,7 +28,7 @@ def setTablanglz(self):
static_data["language_list_translator"], static_data["language_list_translator"],
globalconfig, globalconfig,
"srclang4", "srclang4",
internallist=static_data[ internal=static_data[
"language_list_translator_inner" "language_list_translator_inner"
], ],
), ),
@ -39,7 +39,7 @@ def setTablanglz(self):
static_data["language_list_translator"], static_data["language_list_translator"],
globalconfig, globalconfig,
"tgtlang4", "tgtlang4",
internallist=static_data[ internal=static_data[
"language_list_translator_inner" "language_list_translator_inner"
], ],
), ),
@ -64,7 +64,7 @@ def setTablanglz(self):
"languageuse2", "languageuse2",
callback=changelang, callback=changelang,
static=True, static=True,
internallist=inner, internal=inner,
), ),
D_getIconButton( D_getIconButton(
callback=lambda: os.startfile( callback=lambda: os.startfile(

View File

@ -330,7 +330,7 @@ def gethookembedgrid(self):
alltransvis, alltransvis,
globalconfig["embedded"], globalconfig["embedded"],
"translator_2", "translator_2",
internallist=alltrans, internal=alltrans,
), ),
], ],
[ [

View File

@ -154,7 +154,7 @@ def setTab5lz(self):
static_data["audioengine_vis"], static_data["audioengine_vis"],
globalconfig, globalconfig,
"audioengine", "audioengine",
internallist=static_data["audioengine"], internal=static_data["audioengine"],
static=True, static=True,
), ),
], ],

View File

@ -713,8 +713,8 @@ def callbackwrap(d, k, call, _):
print_exc() print_exc()
def comboboxcallbackwrap(internallist, d, k, call, _): def comboboxcallbackwrap(internal, d, k, call, _):
_ = internallist[_] _ = internal[_]
d[k] = _ d[k] = _
if call: if call:
@ -724,8 +724,9 @@ def comboboxcallbackwrap(internallist, d, k, call, _):
print_exc() print_exc()
@tryprint
def getsimplecombobox( def getsimplecombobox(
lst, d, k, callback=None, fixedsize=False, internallist=None, static=False lst, d, k, callback=None, fixedsize=False, internal=None, static=False, emit=False
): ):
if static: if static:
s = FocusCombo() s = FocusCombo()
@ -734,30 +735,29 @@ def getsimplecombobox(
s = LFocusCombo() s = LFocusCombo()
s.addItems(lst) s.addItems(lst)
if internallist: if internal:
if (k not in d) or (d[k] not in internallist): if (k not in d) or (d[k] not in internal):
d[k] = internallist[0] d[k] = internal[0]
s.setCurrentIndex(internallist.index(d[k]))
s.setCurrentIndex(internal.index(d[k]))
s.currentIndexChanged.connect( s.currentIndexChanged.connect(
functools.partial(comboboxcallbackwrap, internallist, d, k, callback) functools.partial(comboboxcallbackwrap, internal, d, k, callback)
) )
else: else:
if (k not in d) or (d[k] >= len(lst)): if (k not in d) or (d[k] >= len(lst)):
d[k] = 0 d[k] = 0
s.setCurrentIndex(d[k]) s.setCurrentIndex(d[k])
s.currentIndexChanged.connect(functools.partial(callbackwrap, d, k, callback)) s.currentIndexChanged.connect(functools.partial(callbackwrap, d, k, callback))
if fixedsize: if fixedsize:
s.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) s.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
return s return s
def D_getsimplecombobox( def D_getsimplecombobox(
lst, d, k, callback=None, fixedsize=False, internallist=None, static=False lst, d, k, callback=None, fixedsize=False, internal=None, static=False
): ):
return lambda: getsimplecombobox( return lambda: getsimplecombobox(lst, d, k, callback, fixedsize, internal, static)
lst, d, k, callback, fixedsize, internallist, static
)
def getlineedit(d, key, callback=None, readonly=False): def getlineedit(d, key, callback=None, readonly=False):
@ -2022,3 +2022,20 @@ class SplitLine(QFrame):
super().__init__(*argc) super().__init__(*argc)
self.setStyleSheet("background-color: gray;") self.setStyleSheet("background-color: gray;")
self.setFixedHeight(2) self.setFixedHeight(2)
def clearlayout(ll: QLayout):
while ll.count():
item = ll.takeAt(0)
if not item:
continue
ll.removeItem(item)
w = item.widget()
if w:
w.deleteLater()
continue
l = item.layout()
if l:
clearlayout(l)
l.deleteLater()
continue

View File

@ -119,16 +119,14 @@ def getdefaultsavehook(title=None):
# "private_tgtlang_2": 0, # "private_tgtlang_2": 0,
"follow_default_ankisettings": True, "follow_default_ankisettings": True,
# "anki_DeckName":str # "anki_DeckName":str
"localeswitcher": 0, # "localeswitcher": 0,废弃
"onloadautochangemode2": 0, "onloadautochangemode2": 0,
"needinserthookcode": [], "needinserthookcode": [],
"embedablehook": [], "embedablehook": [],
"statistic_playtime": 0, "statistic_playtime": 0,
"statistic_wordcount": 0, "statistic_wordcount": 0,
"statistic_wordcount_nodump": 0, "statistic_wordcount_nodump": 0,
"leuse": True, # "leuse": True, 废弃
"startcmd": '"{exepath}"',
"startcmduse": False,
"hook": [], "hook": [],
"inserthooktimeout": 1000, "inserthooktimeout": 1000,
"needinserthookcode": [], "needinserthookcode": [],
@ -193,38 +191,36 @@ def getdefaultsavehook(title=None):
oldlanguage = ["zh","ja","en","ru","es","ko","fr","cht","vi","tr","pl","uk","it","ar","th","bo","de","sv","nl"] oldlanguage = ["zh","ja","en","ru","es","ko","fr","cht","vi","tr","pl","uk","it","ar","th","bo","de","sv","nl"]
# fmt: on # fmt: on
_dfsavehook = getdefaultsavehook("") _dfsavehook = getdefaultsavehook("")
for uid in savehook_new_data: for gameconfig in savehook_new_data.values():
if ( if (
("allow_tts_auto_names_v4" not in savehook_new_data[uid]) ("allow_tts_auto_names_v4" not in gameconfig)
and ("allow_tts_auto_names" in savehook_new_data[uid]) and ("allow_tts_auto_names" in gameconfig)
and len(savehook_new_data[uid]["allow_tts_auto_names"]) and len(gameconfig["allow_tts_auto_names"])
): ):
savehook_new_data[uid]["allow_tts_auto_names_v4"] = savehook_new_data[uid][ gameconfig["allow_tts_auto_names_v4"] = gameconfig[
"allow_tts_auto_names" "allow_tts_auto_names"
].split("|") ].split("|")
if ("allow_tts_auto_names_v4" in savehook_new_data[uid]) and ( if ("allow_tts_auto_names_v4" in gameconfig) and (
"tts_skip_regex" not in savehook_new_data[uid] "tts_skip_regex" not in gameconfig
): ):
savehook_new_data[uid]["tts_skip_regex"] = [] gameconfig["tts_skip_regex"] = []
for name in savehook_new_data[uid]["allow_tts_auto_names_v4"]: for name in gameconfig["allow_tts_auto_names_v4"]:
savehook_new_data[uid]["tts_skip_regex"].append( gameconfig["tts_skip_regex"].append(
{"regex": False, "key": name, "condition": 0} {"regex": False, "key": name, "condition": 0}
) )
if ("private_srclang" in savehook_new_data[uid]) and ( if ("private_srclang" in gameconfig) and ("private_srclang_2" not in gameconfig):
"private_srclang_2" not in savehook_new_data[uid] gameconfig["private_srclang_2"] = oldlanguage[gameconfig["private_srclang"]]
): gameconfig["private_tgtlang_2"] = oldlanguage[gameconfig["private_tgtlang"]]
savehook_new_data[uid]["private_srclang_2"] = oldlanguage[
savehook_new_data[uid]["private_srclang"]
]
savehook_new_data[uid]["private_tgtlang_2"] = oldlanguage[
savehook_new_data[uid]["private_tgtlang"]
]
for __k, __v in _dfsavehook.items(): for __k, __v in _dfsavehook.items():
if __k not in savehook_new_data[uid]: if __k not in gameconfig:
if isinstance(__v, (list, dict)): if isinstance(__v, (list, dict)):
__v = __v.copy() __v = __v.copy()
savehook_new_data[uid][__k] = __v gameconfig[__k] = __v
if not gameconfig.get("leuse", True):
gameconfig.pop("leuse")
gameconfig["launch_method"] = "direct"
class __uid2gamepath: class __uid2gamepath:

View File

@ -1,26 +1,84 @@
import windows, os import windows, os, winreg, winsharedutils, re
from qtsymbols import *
from myutils.config import savehook_new_data, uid2gamepath
from gui.usefulwidget import (
getlineedit,
getsimplecombobox,
getspinbox,
getsimpleswitch,
getspinbox,
)
from traceback import print_exc
class leXX: class Launcher:
name = ... name = ...
id = ...
@staticmethod def valid(self):
def run(bit, config, usearg, dirpath): ... return True
def run(self, gameexe, config): ...
def setting(self, layout, config): ...
class LocaleEmulator(leXX): class LEbase(Launcher):
name = "Locale Emulator"
@staticmethod def runX(self, exe, usearg, dirpath, config): ...
def run(bit, config, usearg, dirpath): def run(self, game, config):
dirpath = os.path.dirname(game)
if game.lower()[-4:] not in [".lnk", ".exe"]:
# 对于其他文件需要AssocQueryStringW获取命令行才能正确le太麻烦放弃。
windows.ShellExecute(None, "open", game, "", dirpath, windows.SW_SHOW)
return
execheck3264 = game
usearg = '"{}"'.format(game)
if game.lower()[-4:] == ".lnk":
exepath, args, iconpath, dirp = winsharedutils.GetLnkTargetPath(game)
if args != "":
usearg = '"{}" {}'.format(exepath, args)
elif exepath != "":
usearg = '"{}"'.format(exepath)
if exepath != "":
execheck3264 = exepath
if dirp != "":
dirpath = dirp
self.runX(execheck3264, usearg, dirpath, config)
class le_internal(LEbase):
name = "内置_Locale Emulator"
id = "le_internal"
default = dict(
LCID=0x11, CodePage=932, RedirectRegistry=False, HookUILanguageAPI=False
)
def loaddf(self, config):
for k, v in self.default.items():
k = "LE_" + k
if k in config:
continue
config[k] = v
def runX(self, exe, usearg, dirpath, config):
shareddllproxy = os.path.abspath("./files/plugins/shareddllproxy32") shareddllproxy = os.path.abspath("./files/plugins/shareddllproxy32")
def _get(k):
return config.get("LE_" + k, self.default[k])
param = '{ANSICodePage} {OEMCodePage} {LCID} "{dirname}" {RedirectRegistry} {HookUILanguageAPI}'.format( param = '{ANSICodePage} {OEMCodePage} {LCID} "{dirname}" {RedirectRegistry} {HookUILanguageAPI}'.format(
LCID=0x11, LCID=_get("LCID"),
OEMCodePage=932, OEMCodePage=_get("CodePage"),
ANSICodePage=932, ANSICodePage=_get("CodePage"),
dirname=dirpath, dirname=dirpath,
RedirectRegistry=int(False), RedirectRegistry=int(_get("RedirectRegistry")),
HookUILanguageAPI=int(False), HookUILanguageAPI=int(_get("HookUILanguageAPI")),
) )
windows.CreateProcess( windows.CreateProcess(
None, None,
@ -34,26 +92,52 @@ class LocaleEmulator(leXX):
windows.STARTUPINFO(), windows.STARTUPINFO(),
) )
def setting(self, layout, config):
self.loaddf(config)
class NTLEAS(leXX): layout.addRow("LCID", getspinbox(0, 0xFFFFF, config, "LE_LCID"))
name = "Ntleas" layout.addRow("CodePage", getspinbox(0, 0xFFFFF, config, "LE_CodePage"))
layout.addRow(
"RedirectRegistry", getsimpleswitch(config, "LE_RedirectRegistry")
)
layout.addRow(
"HookUILanguageAPI", getsimpleswitch(config, "LE_HookUILanguageAPI")
)
@staticmethod
def run(bit, config, usearg, dirpath): class NTLEAS64(LEbase):
name = "内置_Ntleas"
id = "ntlea_internal"
bit = 6
default = dict(LCID=0x411, CodePage=932, TimeZone=540)
def loaddf(self, config):
for k, v in self.default.items():
k = "NT_" + k
if k in config:
continue
config[k] = v
def runX(self, exe, usearg, dirpath, config):
shareddllproxy = os.path.abspath( shareddllproxy = os.path.abspath(
("./files/plugins/shareddllproxy32", "./files/plugins/shareddllproxy64")[ ("./files/plugins/shareddllproxy32", "./files/plugins/shareddllproxy64")[
bit == 6 self.bit == 6
] ]
) )
param = '{dwCompOption} {dwCodePage} {dwLCID} {dwTimeZone}'.format(
def _get(k):
return config.get("NT_" + k, self.default[k])
param = "{dwCompOption} {dwCodePage} {dwLCID} {dwTimeZone}".format(
dwCompOption=0, dwCompOption=0,
dwCodePage=932, dwCodePage=_get("CodePage"),
dwLCID=0x411, dwLCID=_get("LCID"),
dwTimeZone=-540, dwTimeZone=-_get("TimeZone"),
) )
windows.CreateProcess( windows.CreateProcess(
None, None,
'"{}" {} {} {}'.format(shareddllproxy, "ntleas", param,usearg), '"{}" {} {} {}'.format(shareddllproxy, "ntleas", param, usearg),
None, None,
None, None,
False, False,
@ -63,19 +147,43 @@ class NTLEAS(leXX):
windows.STARTUPINFO(), windows.STARTUPINFO(),
) )
def setting(self, layout, config):
self.loaddf(config)
class LocaleRemulator(leXX): layout.addRow("LCID", getspinbox(0, 0xFFFFF, config, "NT_LCID"))
name = "Locale Remulator" layout.addRow("CodePage", getspinbox(0, 0xFFFFF, config, "NT_CodePage"))
layout.addRow("TimeZone", getspinbox(0, 0xFFFFF, config, "NT_TimeZone"))
@staticmethod
def run(bit, config, usearg, dirpath): class NTLEAS32(NTLEAS64):
bit = 3
class lr_internal(LEbase):
name = "内置_Locale Remulator"
id = "lr_internal"
default = dict(LCID=0x411, CodePage=932, TimeZone=540, HookIME=False, HookLCID=True)
def loaddf(self, config):
for k, v in self.default.items():
k = "LR_" + k
if k in config:
continue
config[k] = v
def runX(self, exe, usearg, dirpath, config):
shareddllproxy = os.path.abspath("./files/plugins/shareddllproxy32") shareddllproxy = os.path.abspath("./files/plugins/shareddllproxy32")
def _get(k):
return config.get("LR_" + k, self.default[k])
param = "{CodePage} {LCID} {Bias} {HookIME} {HookLCID}".format( param = "{CodePage} {LCID} {Bias} {HookIME} {HookLCID}".format(
LCID=0x0411, LCID=_get("LCID"),
CodePage=932, CodePage=_get("CodePage"),
Bias=540, Bias=_get("TimeZone"),
HookIME=0, HookIME=int(_get("HookIME")),
HookLCID=1, HookLCID=int(_get("HookLCID")),
) )
windows.CreateProcess( windows.CreateProcess(
None, None,
@ -89,9 +197,307 @@ class LocaleRemulator(leXX):
windows.STARTUPINFO(), windows.STARTUPINFO(),
) )
def setting(self, layout, config):
self.loaddf(config)
x86tools = [LocaleEmulator, LocaleRemulator, NTLEAS] layout.addRow("LCID", getspinbox(0, 0xFFFFF, config, "LR_LCID"))
x64tools = [LocaleRemulator, NTLEAS] layout.addRow("CodePage", getspinbox(0, 0xFFFFF, config, "LR_CodePage"))
layout.addRow("TimeZone", getspinbox(0, 0xFFFFF, config, "LR_TimeZone"))
layout.addRow("HookIME", getsimpleswitch(config, "LR_HookIME"))
layout.addRow("HookLCID", getsimpleswitch(config, "LR_HookLCID"))
class le_installed(LEbase):
name = "Locale Emulator"
id = "le_installed"
def fundleproc(self):
for key in [winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER]:
try:
k = winreg.OpenKeyEx(
key,
r"Software\Classes\CLSID\{C52B9871-E5E9-41FD-B84D-C5ACADBEC7AE}\InprocServer32",
0,
winreg.KEY_QUERY_VALUE,
)
LEContextMenuHandler: str = winreg.QueryValueEx(k, "CodeBase")[0]
winreg.CloseKey(k)
if not LEContextMenuHandler.startswith("file:///"):
continue
LEContextMenuHandler = LEContextMenuHandler[8:]
LEProc = os.path.join(
os.path.dirname(LEContextMenuHandler), "LEProc.exe"
)
if not os.path.exists(LEProc):
continue
return LEProc
except:
continue
return None
def valid(self):
return (self.fundleproc() is not None) and (len(self.profiles()[1]) > 0)
def profiles(self, exe=None):
_Names = []
_Guids = []
def parseone(xmlpath):
Names, Guids = [], []
with open(xmlpath, "r", encoding="utf8") as ff:
for Name, Guid in re.findall('Name="(.*?)" Guid="(.*?)"', ff.read()):
Names.append(Name)
Guids.append(Guid)
return Names, Guids
finds = [os.path.join(os.path.dirname(self.fundleproc()), "LEConfig.xml")]
if exe:
finds.append(exe + ".le.config")
for f in finds:
try:
Names, Guids = parseone(f)
_Guids += Guids
_Names += Names
except:
pass
return _Names, _Guids
def runX(self, exe, usearg, dirpath, config):
LEProc = self.fundleproc()
if not LEProc:
return
guids = self.profiles(config["gamepath"])[1]
guid = config.get("leguid", None)
if guid not in guids:
guids = guids[0]
arg = '"{}" -runas {} {}'.format(LEProc, guid, usearg)
windows.CreateProcess(
None,
arg,
None,
None,
False,
0,
None,
dirpath,
windows.STARTUPINFO(),
)
def setting(self, layout, config):
Names, Guids = self.profiles(config["gamepath"])
layout.addRow(
"Profile",
getsimplecombobox(Names, config, "leguid", internal=Guids),
)
class lr_installed(LEbase):
name = "Locale Remulator"
id = "lr_installed"
def fundleproc(self):
try:
k = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE,
r"SOFTWARE\Classes\LRSubMenus.LRSubMenuExtension\CLSID",
0,
winreg.KEY_QUERY_VALUE,
)
CLSID: str = winreg.QueryValueEx(k, "")[0]
winreg.CloseKey(k)
k = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE,
rf"Software\Classes\CLSID\{CLSID}\InprocServer32",
0,
winreg.KEY_QUERY_VALUE,
)
LRSubMenuExtension: str = winreg.QueryValueEx(k, "CodeBase")[0]
winreg.CloseKey(k)
if not LRSubMenuExtension.startswith("file:///"):
return None
LRSubMenuExtension = LRSubMenuExtension[8:]
LRProc = os.path.join(os.path.dirname(LRSubMenuExtension), "LRProc.exe")
if not os.path.exists(LRProc):
return None
return LRProc
except:
return None
def valid(self):
return (self.fundleproc() is not None) and (len(self.profiles()[1]) > 0)
def profiles(self):
Names, Guids = [], []
try:
with open(
os.path.join(os.path.dirname(self.fundleproc()), "LRConfig.xml"),
"r",
encoding="utf8",
) as ff:
for Name, Guid in re.findall('Name="(.*?)" Guid="(.*?)"', ff.read()):
Names.append(Name)
Guids.append(Guid)
except:
pass
return Names, Guids
def runX(self, exe, usearg, dirpath, config):
LEProc = self.fundleproc()
if not LEProc:
return
guids = self.profiles()[1]
guid = config.get("lrguid", None)
if guid not in guids:
guids = guids[0]
arg = '"{}" {} {}'.format(LEProc, guid, usearg)
windows.CreateProcess(
None,
arg,
None,
None,
False,
0,
None,
dirpath,
windows.STARTUPINFO(),
)
def setting(self, layout, config):
Names, Guids = self.profiles()
layout.addRow(
"Profile",
getsimplecombobox(Names, config, "lrguid", internal=Guids),
)
class ntleas_installed(LEbase):
name = "Ntleas"
id = "ntleas_installed"
bit64 = True
def fundleproc(self):
try:
CLSID = "{9C31DD66-412C-4B28-BD17-1F0BEBE29E8B}"
k = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE,
rf"Software\Classes\CLSID\{CLSID}\InprocServer32",
0,
winreg.KEY_QUERY_VALUE,
)
LRSubMenuExtension: str = winreg.QueryValueEx(k, "")[0]
winreg.CloseKey(k)
LRProc = os.path.join(
os.path.dirname(LRSubMenuExtension),
["x86", "x64"][self.bit64],
"ntleas.exe",
)
if not os.path.exists(LRProc):
return None
return LRProc
except:
return None
def valid(self):
return self.fundleproc() is not None
def runX(self, exe, usearg, dirpath, config):
LEProc = self.fundleproc()
if not LEProc:
return
arg = '"{}" {} {}'.format(
LEProc,
usearg,
config.get("ntleasparam", '"C932" "L1041" "FMS PGothic" "P4"'),
)
windows.CreateProcess(
None,
arg,
None,
None,
False,
0,
None,
dirpath,
windows.STARTUPINFO(),
)
def setting(self, layout, config):
if "ntleasparam" not in config:
config["ntleasparam"] = '"C932" "L1041" "FMS PGothic" "P4"'
layout.addRow(
"params",
getlineedit(config, "ntleasparam"),
)
class ntleas_installed32(ntleas_installed):
bit64 = False
class CommandLine(Launcher):
name = "命令行启动"
id = "cmd"
def run(self, gameexe, config):
dirpath = os.path.dirname(gameexe)
usearg = config.get("startcmd", "{exepath}").format(exepath=gameexe)
windows.CreateProcess(
None,
usearg,
None,
None,
False,
0,
None,
dirpath,
windows.STARTUPINFO(),
)
def setting(self, layout, config):
if "startcmd" not in config:
config["startcmd"] = "{exepath}"
layout.addRow(
"命令行启动",
getlineedit(config, "startcmd"),
)
class Direct(Launcher):
name = "直接启动"
id = "direct"
def run(self, gameexe, argsdict):
dirpath = os.path.dirname(gameexe)
windows.ShellExecute(None, "open", gameexe, "", dirpath, windows.SW_SHOW)
x86tools = [
le_internal,
lr_internal,
NTLEAS32,
le_installed,
lr_installed,
ntleas_installed32,
CommandLine,
Direct,
]
x64tools = [lr_internal, NTLEAS64, lr_installed, ntleas_installed, CommandLine, Direct]
def getgamecamptools(gameexe): def getgamecamptools(gameexe):
@ -100,18 +506,36 @@ def getgamecamptools(gameexe):
_methods = x64tools _methods = x64tools
else: else:
_methods = x86tools _methods = x86tools
return _methods ms = []
for _ in _methods:
if not _().valid():
continue
ms.append(_)
return ms
def getgamecamptoolsname(gameexe): def fundlauncher(_id):
return [_.name for _ in getgamecamptools(gameexe)] for _ in x86tools + x64tools:
if _.id != _id:
continue
return _
return None
def localeswitchedrun(gameexe, idx, usearg, dirpath): def localeswitchedrun(gameuid):
b = windows.GetBinaryType(gameexe) config = savehook_new_data[gameuid]
launch_method = config.get("launch_method", None)
gameexe = uid2gamepath[gameuid]
tools = getgamecamptools(gameexe) tools = getgamecamptools(gameexe)
if idx < 0 or idx >= len(tools): ids = [_.id for _ in getgamecamptools(uid2gamepath[gameuid])]
idx = 0 if launch_method not in ids:
index = 0
else:
index = ids.index(launch_method)
tool: Launcher = tools[index]()
tool.run(gameexe, config)
tool = tools[idx]
tool.run(b, 0, usearg, dirpath) def maycreatesettings(layout, config, launcherid):
launcher = fundlauncher(launcherid)()
launcher.setting(layout, config)

View File

@ -838,5 +838,7 @@
"粘贴": "لصق", "粘贴": "لصق",
"首尾": "الرأس والذيل", "首尾": "الرأس والذيل",
"包含": "احتواء", "包含": "احتواء",
"删除图片文件": "حذف ملف الصورة" "删除图片文件": "حذف ملف الصورة",
"启动方式": "طريقة البدء",
"直接启动": "بدء التشغيل مباشرة"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "粘貼", "粘贴": "粘貼",
"首尾": "首尾", "首尾": "首尾",
"包含": "包含", "包含": "包含",
"删除图片文件": "删除圖片檔案" "删除图片文件": "删除圖片檔案",
"启动方式": "啟動管道",
"直接启动": "直接啟動"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "pasta", "粘贴": "pasta",
"首尾": "Konec k konci", "首尾": "Konec k konci",
"包含": "obsahovat", "包含": "obsahovat",
"删除图片文件": "Smazat obrázkové soubory" "删除图片文件": "Smazat obrázkové soubory",
"启动方式": "Metoda spuštění",
"直接启动": "Přímo spustit"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "Paste", "粘贴": "Paste",
"首尾": "Ende zu Ende", "首尾": "Ende zu Ende",
"包含": "enthalten", "包含": "enthalten",
"删除图片文件": "Bilddateien löschen" "删除图片文件": "Bilddateien löschen",
"启动方式": "Startmethode",
"直接启动": "Direkt starten"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "paste", "粘贴": "paste",
"首尾": "End to end", "首尾": "End to end",
"包含": "contain", "包含": "contain",
"删除图片文件": "Delete image files" "删除图片文件": "Delete image files",
"启动方式": "Startup method",
"直接启动": "Directly start"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "Pegar", "粘贴": "Pegar",
"首尾": "De principio a fin", "首尾": "De principio a fin",
"包含": "Contiene", "包含": "Contiene",
"删除图片文件": "Eliminar archivos de imagen" "删除图片文件": "Eliminar archivos de imagen",
"启动方式": "Modo de arranque",
"直接启动": "Arranque directo"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "Coller", "粘贴": "Coller",
"首尾": "Première queue", "首尾": "Première queue",
"包含": "Contient", "包含": "Contient",
"删除图片文件": "Supprimer un fichier image" "删除图片文件": "Supprimer un fichier image",
"启动方式": "Mode de démarrage",
"直接启动": "Démarrage direct"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "pasta", "粘贴": "pasta",
"首尾": "Fine a fine", "首尾": "Fine a fine",
"包含": "contiene", "包含": "contiene",
"删除图片文件": "Elimina file immagine" "删除图片文件": "Elimina file immagine",
"启动方式": "Metodo di avvio",
"直接启动": "Avvia direttamente"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "貼り付け", "粘贴": "貼り付け",
"首尾": "首尾", "首尾": "首尾",
"包含": "含める", "包含": "含める",
"删除图片文件": "画像ファイルを削除" "删除图片文件": "画像ファイルを削除",
"启动方式": "起動モード",
"直接启动": "ダイレクトスタート"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "붙여넣기", "粘贴": "붙여넣기",
"首尾": "수미", "首尾": "수미",
"包含": "포함", "包含": "포함",
"删除图片文件": "그림 파일 삭제" "删除图片文件": "그림 파일 삭제",
"启动方式": "시작 방법",
"直接启动": "직접 시작"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "plakken", "粘贴": "plakken",
"首尾": "End to end", "首尾": "End to end",
"包含": "bevatten", "包含": "bevatten",
"删除图片文件": "Afbeeldingsbestanden verwijderen" "删除图片文件": "Afbeeldingsbestanden verwijderen",
"启动方式": "Opstartmethode",
"直接启动": "Direct starten"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "pasta", "粘贴": "pasta",
"首尾": "Koniec do końca", "首尾": "Koniec do końca",
"包含": "zawierać", "包含": "zawierać",
"删除图片文件": "Usuń pliki obrazów" "删除图片文件": "Usuń pliki obrazów",
"启动方式": "Metoda uruchomienia",
"直接启动": "Bezpośredni start"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "colar", "粘贴": "colar",
"首尾": "De ponta a ponta", "首尾": "De ponta a ponta",
"包含": "contém", "包含": "contém",
"删除图片文件": "Apagar os ficheiros de imagem" "删除图片文件": "Apagar os ficheiros de imagem",
"启动方式": "Método de arranque",
"直接启动": "Iniciar directamente"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "Вставить", "粘贴": "Вставить",
"首尾": "Голова и хвост", "首尾": "Голова и хвост",
"包含": "Включение", "包含": "Включение",
"删除图片文件": "Удалить файл изображения" "删除图片文件": "Удалить файл изображения",
"启动方式": "Режим запуска",
"直接启动": "Прямой запуск"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "klistra", "粘贴": "klistra",
"首尾": "Slut till slut", "首尾": "Slut till slut",
"包含": "innehåller", "包含": "innehåller",
"删除图片文件": "Ta bort bildfiler" "删除图片文件": "Ta bort bildfiler",
"启动方式": "Startmetod",
"直接启动": "Starta direkt"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "วางบน", "粘贴": "วางบน",
"首尾": "หัวหาง", "首尾": "หัวหาง",
"包含": "ประกอบด้วย", "包含": "ประกอบด้วย",
"删除图片文件": "ลบไฟล์รูปภาพ" "删除图片文件": "ลบไฟล์รูปภาพ",
"启动方式": "วิธีการเริ่มต้น",
"直接启动": "เริ่มต้นโดยตรง"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "Yapıştır", "粘贴": "Yapıştır",
"首尾": "Sonuna kadar", "首尾": "Sonuna kadar",
"包含": "içerir", "包含": "içerir",
"删除图片文件": "Resim dosyalarını sil" "删除图片文件": "Resim dosyalarını sil",
"启动方式": "Başlangıç yöntemi",
"直接启动": "Direkten başla"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "вставити", "粘贴": "вставити",
"首尾": "Кінець до кінця", "首尾": "Кінець до кінця",
"包含": "містити", "包含": "містити",
"删除图片文件": "Вилучити файли зображення" "删除图片文件": "Вилучити файли зображення",
"启动方式": "Метод запуску",
"直接启动": "Прямо запустити"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "Dán", "粘贴": "Dán",
"首尾": "Trang chủ", "首尾": "Trang chủ",
"包含": "Bao gồm", "包含": "Bao gồm",
"删除图片文件": "Xoá tập tin ảnh" "删除图片文件": "Xoá tập tin ảnh",
"启动方式": "Cách bắt đầu",
"直接启动": "Khởi động trực tiếp"
} }

View File

@ -838,5 +838,7 @@
"粘贴": "", "粘贴": "",
"首尾": "", "首尾": "",
"包含": "", "包含": "",
"删除图片文件": "" "删除图片文件": "",
"启动方式": "",
"直接启动": ""
} }

View File

@ -28,8 +28,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/version)
include(generate_product_version) include(generate_product_version)
set(VERSION_MAJOR 5) set(VERSION_MAJOR 5)
set(VERSION_MINOR 20) set(VERSION_MINOR 21)
set(VERSION_PATCH 1) set(VERSION_PATCH 0)
add_library(pch pch.cpp) add_library(pch pch.cpp)
target_precompile_headers(pch PUBLIC pch.h) target_precompile_headers(pch PUBLIC pch.h)