mirror of
https://github.com/HIllya51/LunaTranslator.git
synced 2024-12-28 08:04:13 +08:00
so many update
Update static_data.json
This commit is contained in:
parent
1a206bc938
commit
973417b48e
73
LunaTranslator/LunaTranslator/.vscode/settings.json
vendored
Normal file
73
LunaTranslator/LunaTranslator/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"xstring": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bit": "cpp",
|
||||
"cctype": "cpp",
|
||||
"charconv": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"deque": "cpp",
|
||||
"exception": "cpp",
|
||||
"filesystem": "cpp",
|
||||
"format": "cpp",
|
||||
"forward_list": "cpp",
|
||||
"fstream": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"ios": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"iterator": "cpp",
|
||||
"limits": "cpp",
|
||||
"list": "cpp",
|
||||
"locale": "cpp",
|
||||
"memory": "cpp",
|
||||
"new": "cpp",
|
||||
"optional": "cpp",
|
||||
"ostream": "cpp",
|
||||
"queue": "cpp",
|
||||
"ranges": "cpp",
|
||||
"ratio": "cpp",
|
||||
"span": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"string": "cpp",
|
||||
"system_error": "cpp",
|
||||
"thread": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"utility": "cpp",
|
||||
"vector": "cpp",
|
||||
"xfacet": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xiosbase": "cpp",
|
||||
"xlocale": "cpp",
|
||||
"xlocbuf": "cpp",
|
||||
"xlocinfo": "cpp",
|
||||
"xlocmes": "cpp",
|
||||
"xlocmon": "cpp",
|
||||
"xlocnum": "cpp",
|
||||
"xloctime": "cpp",
|
||||
"xmemory": "cpp",
|
||||
"xtr1common": "cpp",
|
||||
"xutility": "cpp"
|
||||
}
|
||||
}
|
@ -109,7 +109,13 @@ class MAINUI() :
|
||||
|
||||
return content,(mp1,mp2,mp3)
|
||||
|
||||
|
||||
def parsemayberegexreplace(self,dict,res):
|
||||
for item in dict:
|
||||
if item['regex']:
|
||||
res=re.sub(codecs.escape_decode(bytes(item['key'], "utf-8"))[0].decode("utf-8"),codecs.escape_decode(bytes(item['value'], "utf-8"))[0].decode("utf-8"),res)
|
||||
else:
|
||||
res=res.replace(item['key'],item['value'])
|
||||
return res
|
||||
def solveaftertrans(self,res,mp):
|
||||
mp1,mp2,mp3=mp
|
||||
#print(res,mp)#hello
|
||||
@ -133,11 +139,7 @@ class MAINUI() :
|
||||
if key in res:
|
||||
res=res.replace(key,value['text'])
|
||||
if transerrorfixdictconfig['use']:
|
||||
for item in transerrorfixdictconfig['dict_v2']:
|
||||
if item['regex']:
|
||||
res=re.sub(codecs.escape_decode(bytes(item['key'], "utf-8"))[0].decode("utf-8"),codecs.escape_decode(bytes(item['value'], "utf-8"))[0].decode("utf-8"),res)
|
||||
else:
|
||||
res=res.replace(item['key'],item['value'])
|
||||
res=self.parsemayberegexreplace(transerrorfixdictconfig['dict_v2'])
|
||||
return res
|
||||
|
||||
def _POSTSOLVE(self,s):
|
||||
@ -325,7 +327,8 @@ class MAINUI() :
|
||||
def readcurrent(self,force=False):
|
||||
try:
|
||||
if force or globalconfig['autoread']:
|
||||
self.reader.read(self.currentread)
|
||||
text=self.parsemayberegexreplace(globalconfig['ttscommon']['tts_repair_regex'],self.currenttext)
|
||||
self.reader.read(text)
|
||||
except:
|
||||
print_exc()
|
||||
@threader
|
||||
|
@ -6,9 +6,149 @@ from PyQt5.QtGui import QCloseEvent, QStandardItem, QStandardItemModel
|
||||
from PyQt5.QtGui import QColor
|
||||
import qtawesome,importlib
|
||||
from myutils.config import globalconfig ,_TR,_TRL
|
||||
from gui.usefulwidget import MySwitch ,selectcolor
|
||||
from gui.usefulwidget import MySwitch ,selectcolor,getsimpleswitch
|
||||
from myutils.utils import makehtml
|
||||
from myutils.wrapper import Singleton
|
||||
|
||||
@Singleton
|
||||
class noundictconfigdialog1(QDialog):
|
||||
def newline(self,row,item):
|
||||
self.model.insertRow(row,[QStandardItem(),QStandardItem(item['key']),QStandardItem(item['value'])])
|
||||
self.table.setIndexWidget(self.model.index(row, 0),getsimpleswitch(item,'regex'))
|
||||
def __init__(self,parent,configdict,configkey,title,label) -> None:
|
||||
super().__init__(parent,Qt.WindowCloseButtonHint)
|
||||
|
||||
self.setWindowTitle(_TR(title))
|
||||
#self.setWindowModality(Qt.ApplicationModal)
|
||||
|
||||
formLayout = QVBoxLayout(self) # 配置layout
|
||||
|
||||
self.model=QStandardItemModel()
|
||||
self.model.setHorizontalHeaderLabels(_TRL(label))
|
||||
table = QTableView(self)
|
||||
table.setModel(self.model)
|
||||
|
||||
table.horizontalHeader().setSectionResizeMode(2,QHeaderView.Stretch)
|
||||
table.horizontalHeader().setSectionResizeMode(1,QHeaderView.Stretch)
|
||||
table.horizontalHeader().setSectionResizeMode(0,QHeaderView.ResizeToContents)
|
||||
|
||||
self.table=table
|
||||
for row,item in enumerate(configdict[configkey]):
|
||||
self.newline(row,item)
|
||||
|
||||
|
||||
search=QHBoxLayout()
|
||||
searchcontent=QLineEdit()
|
||||
search.addWidget(searchcontent)
|
||||
button4=QPushButton()
|
||||
button4.setText(_TR('搜索'))
|
||||
def clicked4():
|
||||
text=searchcontent.text()
|
||||
|
||||
rows=self.model.rowCount()
|
||||
cols=self.model.columnCount()
|
||||
for row in range(rows):
|
||||
ishide=True
|
||||
for c in range(cols):
|
||||
if text in self.model.item(row,c).text():
|
||||
ishide=False
|
||||
break
|
||||
table.setRowHidden(row,ishide)
|
||||
|
||||
|
||||
button4.clicked.connect(clicked4)
|
||||
search.addWidget(button4)
|
||||
|
||||
|
||||
button=QPushButton(self)
|
||||
button.setText(_TR('添加行'))
|
||||
def clicked1():
|
||||
self.configdict[configkey].insert(0,{'key':'','value':'','regex':False})
|
||||
self.newline(0,self.configdict[configkey][0])
|
||||
button.clicked.connect(clicked1)
|
||||
button2=QPushButton(self)
|
||||
button2.setText(_TR('删除选中行'))
|
||||
def clicked2():
|
||||
self.model.removeRow(table.currentIndex().row())
|
||||
self.configdict[configkey].pop(table.currentIndex().row())
|
||||
button2.clicked.connect(clicked2)
|
||||
self.button=button
|
||||
self.configdict=configdict
|
||||
self.configkey=configkey
|
||||
formLayout.addWidget(table)
|
||||
formLayout.addLayout(search)
|
||||
formLayout.addWidget(button)
|
||||
formLayout.addWidget(button2)
|
||||
self.resize(QSize(600,400))
|
||||
self.show()
|
||||
def closeEvent(self, a0: QCloseEvent) -> None:
|
||||
self.button.setFocus()
|
||||
rows=self.model.rowCount()
|
||||
rowoffset=0
|
||||
dedump=set()
|
||||
for row in range(rows):
|
||||
k,v=self.model.item(row,1).text(),self.model.item(row,2).text()
|
||||
if k=="" or k in dedump:
|
||||
self.configdict[self.configkey].pop(row-rowoffset)
|
||||
rowoffset+=1
|
||||
continue
|
||||
self.configdict[self.configkey][row-rowoffset].update({
|
||||
'key':k,'value':v
|
||||
})
|
||||
dedump.add(k)
|
||||
@Singleton
|
||||
class regexedit(QDialog):
|
||||
def __init__(self,parent,regexlist) -> None:
|
||||
super().__init__(parent,Qt.WindowCloseButtonHint)
|
||||
self.regexlist=regexlist
|
||||
self.setWindowTitle(_TR('正则匹配'))
|
||||
|
||||
formLayout = QVBoxLayout(self) # 配置layout
|
||||
|
||||
self.model=QStandardItemModel()
|
||||
self.model.setHorizontalHeaderLabels(_TRL(['正则']))
|
||||
table = QTableView(self)
|
||||
table.setModel(self.model)
|
||||
|
||||
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
|
||||
self.table=table
|
||||
for row,regex in enumerate(regexlist):
|
||||
self.model.insertRow(row,[QStandardItem(regex)])
|
||||
|
||||
|
||||
button=QPushButton(self)
|
||||
button.setText(_TR('添加行'))
|
||||
def clicked1():
|
||||
regexlist.insert(0,'')
|
||||
self.model.insertRow(0,[QStandardItem()])
|
||||
button.clicked.connect(clicked1)
|
||||
button2=QPushButton(self)
|
||||
button2.setText(_TR('删除选中行'))
|
||||
def clicked2():
|
||||
self.model.removeRow(table.currentIndex().row())
|
||||
regexlist.pop(table.currentIndex().row())
|
||||
button2.clicked.connect(clicked2)
|
||||
self.button=button
|
||||
formLayout.addWidget(table)
|
||||
formLayout.addWidget(button)
|
||||
formLayout.addWidget(button2)
|
||||
self.resize(QSize(600,400))
|
||||
self.show()
|
||||
def closeEvent(self,_) -> None:
|
||||
self.button.setFocus()
|
||||
rows=self.model.rowCount()
|
||||
rowoffset=0
|
||||
dedump=set()
|
||||
for row in range(rows):
|
||||
regex=self.model.item(row,0).text()
|
||||
if regex=="" or regex in dedump:
|
||||
self.regexlist.pop(row-rowoffset)
|
||||
rowoffset+=1
|
||||
continue
|
||||
self.regexlist[row-rowoffset]=regex
|
||||
dedump.add(regex)
|
||||
|
||||
def autoinitdialog_items(dict):
|
||||
items=[]
|
||||
for arg in dict['args']:
|
||||
|
@ -4,41 +4,29 @@ from PyQt5.QtCore import Qt,QSize
|
||||
import sqlite3
|
||||
import json
|
||||
from traceback import print_exc
|
||||
import os
|
||||
import functools
|
||||
from myutils.config import globalconfig,_TR
|
||||
from myutils.utils import autosql
|
||||
from gui.usefulwidget import getQMessageBox
|
||||
def sqlite2json(self):
|
||||
f=QFileDialog.getOpenFileName(directory='./translation_record', filter="*.sqlite")
|
||||
if f[0]=='' :
|
||||
return
|
||||
|
||||
def sqlite2json2(self,sqlitefile,targetjson=None):
|
||||
try:
|
||||
sql=autosql(sqlite3.connect(f[0],check_same_thread=False))
|
||||
sql=autosql(sqlite3.connect(sqlitefile,check_same_thread=False))
|
||||
ret=sql.execute('SELECT * FROM artificialtrans ').fetchall()
|
||||
js={}
|
||||
js_format2={}
|
||||
collect={}
|
||||
collect=set()
|
||||
for _aret in ret:
|
||||
if len(_aret)==4:
|
||||
|
||||
_id,source,mt,ut=_aret
|
||||
js[source]={'userTrans':ut,'machineTrans':mt}
|
||||
js_format2[source]=mt
|
||||
_id,source,mt,realsource=_aret
|
||||
js_format2[realsource]=mt
|
||||
elif len(_aret)==3:
|
||||
_id,source,mt =_aret
|
||||
js[source]={'userTrans':'','machineTrans':''}
|
||||
js_format2[source]=''
|
||||
js_format2[source]=mt
|
||||
mtjs=json.loads(mt)
|
||||
for _i,_t in enumerate(mtjs):
|
||||
if _i==0 :
|
||||
js[source]['machineTrans']=mtjs[_t]
|
||||
js_format2[source]=mtjs[_t]
|
||||
js[source]['result_'+str(_i)]=mtjs[_t]
|
||||
js[source]['api_'+str(_i)]=_t
|
||||
|
||||
collect[_t]='result_'+str(_i)
|
||||
js_format2[source]=mtjs
|
||||
|
||||
collect=collect.union(set(mtjs.keys()))
|
||||
collect=list(collect)
|
||||
except:
|
||||
print_exc()
|
||||
getQMessageBox(self,"错误","所选文件格式错误!")
|
||||
@ -57,11 +45,11 @@ def sqlite2json(self):
|
||||
formLayout.addRow(_TR("首选翻译"),combo)
|
||||
|
||||
|
||||
e=QLineEdit(f[0][:-(len('.sqlite'))])
|
||||
e=QLineEdit(sqlitefile[:-(len('.sqlite'))])
|
||||
|
||||
bu=QPushButton(_TR('选择路径') )
|
||||
def __selectsavepath():
|
||||
ff=QFileDialog.getSaveFileName(dialog,directory=f[0][:-(len('.sqlite'))])
|
||||
ff=QFileDialog.getSaveFileName(dialog,directory=sqlitefile[:-(len('.sqlite'))])
|
||||
if ff[0]=='' :
|
||||
return
|
||||
e.setText(ff[0])
|
||||
@ -69,25 +57,36 @@ def sqlite2json(self):
|
||||
hori=QHBoxLayout()
|
||||
hori.addWidget(e)
|
||||
hori.addWidget(bu)
|
||||
|
||||
if targetjson is None:
|
||||
formLayout.addRow(_TR("保存路径"),hori)
|
||||
|
||||
button = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
|
||||
formLayout.addRow(button)
|
||||
button.rejected.connect(dialog.close)
|
||||
|
||||
def __savefunction():
|
||||
if len(collect.keys())>0:
|
||||
transkirokuuse=list(collect.keys())[combo.currentIndex()]
|
||||
for k in js:
|
||||
if collect[transkirokuuse] in js[k]:
|
||||
js[k]['machineTrans']=js[k][collect[transkirokuuse]]
|
||||
def __savefunction(target):
|
||||
if len(collect)>0:
|
||||
transkirokuuse=collect[combo.currentIndex()]
|
||||
for k in js_format2:
|
||||
js_format2[k]=js_format2[k].get(transkirokuuse,'')
|
||||
|
||||
# with open(e.text()+'.complex.json','w',encoding='utf8') as ff:
|
||||
# ff.write(json.dumps(js,ensure_ascii=False,sort_keys=False, indent=4))
|
||||
with open(e.text()+'.json','w',encoding='utf8') as ff:
|
||||
|
||||
if target is None:
|
||||
target=e.text()+'.json'
|
||||
with open(target,'w',encoding='utf8') as ff:
|
||||
ff.write(json.dumps(js_format2,ensure_ascii=False,sort_keys=False, indent=4))
|
||||
dialog.close()
|
||||
button.accepted.connect(__savefunction)
|
||||
button.accepted.connect(functools.partial(__savefunction,targetjson))
|
||||
button.button(QDialogButtonBox.Ok).setText(_TR('确定'))
|
||||
button.button(QDialogButtonBox.Cancel).setText(_TR('取消'))
|
||||
dialog.show()
|
||||
|
||||
def sqlite2json(self):
|
||||
f=QFileDialog.getOpenFileName(directory='./translation_record', filter="*.sqlite")
|
||||
if f[0]=='' :
|
||||
return
|
||||
|
||||
sqlite2json2(self,f[0])
|
@ -1,18 +1,19 @@
|
||||
|
||||
import functools,os,shutil
|
||||
import functools,os,shutil,windows,json
|
||||
from PyQt5.QtGui import QFont,QStandardItem,QStandardItemModel
|
||||
from PyQt5.QtCore import Qt,QSize
|
||||
from traceback import print_exc
|
||||
from PyQt5.QtWidgets import QFontComboBox,QDialog,QLabel,QComboBox,QPushButton,QFileDialog,QVBoxLayout,QTableView,QHeaderView,QHBoxLayout,QLineEdit
|
||||
from gui.pretransfile import sqlite2json2
|
||||
from gui.settingpage_ocr import getocrgrid
|
||||
from myutils.config import globalconfig ,_TR,_TRL
|
||||
from myutils.config import globalconfig ,_TR,_TRL,savehook_new_data,savehook_new_list
|
||||
from gui.dialog_savedgame import dialog_savedgame
|
||||
import threading,gobject,requests,datetime,zipfile
|
||||
from gui.inputdialog import autoinitdialog
|
||||
from gui.inputdialog import autoinitdialog,regexedit
|
||||
from gui.usefulwidget import getsimplecombobox,getspinbox,getcolorbutton,yuitsu_switch,getsimpleswitch,Singleton,getQMessageBox
|
||||
from gui.codeacceptdialog import codeacceptdialog
|
||||
from textsource.fridahook import fridahook
|
||||
from myutils.utils import loadfridascriptslist,checkifnewgame,makehtml
|
||||
from myutils.utils import loadfridascriptslist,checkifnewgame,makehtml,getfilemd5
|
||||
from myutils.proxy import getproxy
|
||||
def gethookgrid(self) :
|
||||
|
||||
@ -129,59 +130,47 @@ def getfridahookgrid(self) :
|
||||
|
||||
return grids
|
||||
|
||||
@Singleton
|
||||
class safeembedcheckdialog(QDialog):
|
||||
def newline(self,row):
|
||||
self.model.insertRow(row,[QStandardItem(globalconfig['embedded']['safecheckregexs'][row])])
|
||||
def __init__(self,parent) -> None:
|
||||
super().__init__(parent,Qt.WindowCloseButtonHint)
|
||||
self.setWindowTitle(_TR('正则匹配'))
|
||||
def doexportchspatch(exe):
|
||||
|
||||
formLayout = QVBoxLayout(self) # 配置layout
|
||||
b=windows.GetBinaryType(exe)
|
||||
is64=(b==6)
|
||||
arch=['32','64'][is64]
|
||||
|
||||
self.model=QStandardItemModel()
|
||||
self.model.setHorizontalHeaderLabels(_TRL(['正则']))
|
||||
table = QTableView(self)
|
||||
table.setModel(self.model)
|
||||
dllhook=os.path.abspath('./files/plugins/LunaHook/LunaHook{}.dll'.format(arch))
|
||||
dllhost=os.path.abspath('./files/plugins/DLL{}/LunaHost{}.dll'.format(arch,arch))
|
||||
runner=os.path.abspath('./files/plugins/shareddllproxy{}.exe'.format(arch))
|
||||
|
||||
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
windows.CopyFile(dllhook,os.path.join(os.path.dirname(exe),os.path.basename(dllhook)),False)
|
||||
windows.CopyFile(dllhost,os.path.join(os.path.dirname(exe),os.path.basename(dllhost)),False)
|
||||
windows.CopyFile(runner,os.path.join(os.path.dirname(exe),'LunaPatch.exe'),False)
|
||||
|
||||
self.table=table
|
||||
for row,regex in enumerate(globalconfig['embedded']['safecheckregexs']):
|
||||
self.model.insertRow(row,[QStandardItem(regex)])
|
||||
embedconfig={
|
||||
'translation_file':'translation.json',
|
||||
'target_exe':os.path.basename(exe),
|
||||
'startup_argument':None,
|
||||
'isbit64':is64,
|
||||
'inject_timeout':1000,
|
||||
'embedhook':savehook_new_data[exe]['embedablehook'],
|
||||
'embedsettings':{
|
||||
'font':globalconfig['embedded']['changefont_font'] if globalconfig['embedded']['changefont'] else '',
|
||||
'insertspace_policy':globalconfig['embedded']['insertspace_policy'],
|
||||
'keeprawtext':globalconfig['embedded']['keeprawtext']
|
||||
}
|
||||
}
|
||||
with open(os.path.join(os.path.dirname(exe),'LunaPatch.json'),'w',encoding='utf8') as ff:
|
||||
ff.write(json.dumps(embedconfig,ensure_ascii=False,indent=4))
|
||||
|
||||
|
||||
button=QPushButton(self)
|
||||
button.setText(_TR('添加行'))
|
||||
def clicked1():
|
||||
globalconfig['embedded']['safecheckregexs'].insert(0,'')
|
||||
self.model.insertRow(0,[QStandardItem()])
|
||||
button.clicked.connect(clicked1)
|
||||
button2=QPushButton(self)
|
||||
button2.setText(_TR('删除选中行'))
|
||||
def clicked2():
|
||||
self.model.removeRow(table.currentIndex().row())
|
||||
globalconfig['embedded']['safecheckregexs'].pop(table.currentIndex().row())
|
||||
button2.clicked.connect(clicked2)
|
||||
self.button=button
|
||||
formLayout.addWidget(table)
|
||||
formLayout.addWidget(button)
|
||||
formLayout.addWidget(button2)
|
||||
self.resize(QSize(600,400))
|
||||
self.show()
|
||||
def closeEvent(self,_) -> None:
|
||||
self.button.setFocus()
|
||||
rows=self.model.rowCount()
|
||||
rowoffset=0
|
||||
dedump=set()
|
||||
for row in range(rows):
|
||||
regex=self.model.item(row,0).text()
|
||||
if regex=="" or regex in dedump:
|
||||
globalconfig['embedded']['safecheckregexs'].pop(row-rowoffset)
|
||||
rowoffset+=1
|
||||
continue
|
||||
globalconfig['embedded']['safecheckregexs'][row-rowoffset]=regex
|
||||
dedump.add(regex)
|
||||
def exportchspatch(self):
|
||||
f=QFileDialog.getOpenFileName(filter='*.exe')
|
||||
exe=f[0]
|
||||
if exe=='':return
|
||||
exe=exe.replace('/','\\')
|
||||
if exe not in savehook_new_list:return
|
||||
doexportchspatch(exe)
|
||||
md5=getfilemd5(exe)
|
||||
name= os.path.basename(exe).replace('.'+os.path.basename(exe).split('.')[-1],'')
|
||||
sqlfname_all='./translation_record/'+name+'_'+md5+'.sqlite'
|
||||
sqlite2json2(self,sqlfname_all,os.path.join(os.path.dirname(exe),'translation.json'))
|
||||
def gethookembedgrid(self) :
|
||||
self.gamefont_comboBox = QFontComboBox( )
|
||||
def callback(x):
|
||||
@ -194,6 +183,8 @@ def gethookembedgrid(self) :
|
||||
self.gamefont_comboBox.setCurrentFont(QFont(globalconfig['embedded']['changefont_font']))
|
||||
grids=[
|
||||
|
||||
[('导出翻译补丁',5),getcolorbutton(globalconfig,'',callback=lambda x:exportchspatch(self),icon='fa.gear',constcolor="#FF69B4")],
|
||||
[],
|
||||
[('保留原文',5),(getsimpleswitch( globalconfig['embedded'],'keeprawtext',callback=lambda _:gobject.baseobject.textsource.flashembedsettings()) ,1) ],
|
||||
|
||||
[('翻译等待时间(s)',5),'',(getspinbox(0,30,globalconfig['embedded'],'timeout_translate',double=True,step=0.1,callback=lambda x:gobject.baseobject.textsource.flashembedsettings()),3) ],
|
||||
@ -203,7 +194,7 @@ def gethookembedgrid(self) :
|
||||
[('在重叠显示的字间插入空格',5),'',(getsimplecombobox(_TRL(['不插入空格','每个字后插入空格','仅在无法编码的字后插入']),globalconfig['embedded'],'insertspace_policy',callback=lambda _:gobject.baseobject.textsource.flashembedsettings()),5) ],
|
||||
[('修改游戏字体',5),(getsimpleswitch( globalconfig['embedded'] ,'changefont',callback=lambda _:gobject.baseobject.textsource.flashembedsettings()),1), (self.gamefont_comboBox,5)],
|
||||
[],
|
||||
[('内嵌安全性检查',5),getcolorbutton(globalconfig,'',callback=lambda x:safeembedcheckdialog(self),icon='fa.gear',constcolor="#FF69B4")]
|
||||
[('内嵌安全性检查',5),getsimpleswitch(globalconfig['embedded'],'safecheck_use' ),getcolorbutton(globalconfig,'',callback=lambda x:regexedit(self,globalconfig['embedded']['safecheckregexs']),icon='fa.gear',constcolor="#FF69B4")]
|
||||
#[('修改字体字符集',5),(getsimpleswitch( globalconfig['embedded'] ,'changecharset',callback=lambda _:gobject.baseobject.textsource.flashembedsettings()),1) ,(getsimplecombobox(_TRL(static_data["charsetmapshow"]),globalconfig['embedded'],'changecharset_charset',callback=lambda _:gobject.baseobject.textsource.flashembedsettings()),5)],
|
||||
|
||||
]
|
||||
|
@ -9,7 +9,7 @@ from myutils.config import globalconfig ,postprocessconfig,noundictconfig,transe
|
||||
import functools ,gobject
|
||||
from gui.usefulwidget import getcolorbutton,getsimpleswitch
|
||||
from gui.codeacceptdialog import codeacceptdialog
|
||||
from gui.inputdialog import getsomepath1 ,postconfigdialog,autoinitdialog,autoinitdialog_items
|
||||
from gui.inputdialog import getsomepath1 ,postconfigdialog,autoinitdialog,autoinitdialog_items,noundictconfigdialog1
|
||||
from myutils.utils import selectdebugfile
|
||||
from myutils.wrapper import Singleton
|
||||
from myutils.config import savehook_new_data
|
||||
@ -44,7 +44,7 @@ def savegameprocesstext():
|
||||
def settab7direct(self):
|
||||
self.comparelayout=getcomparelayout(self)
|
||||
self.button_noundict=getcolorbutton(globalconfig,'' ,callback=lambda x: noundictconfigdialog(self,noundictconfig,'专有名词翻译设置(游戏ID 0表示全局)'),icon='fa.gear',constcolor="#FF69B4")
|
||||
self.button_fix=getcolorbutton(globalconfig,'',callback=lambda x: noundictconfigdialog1(self,transerrorfixdictconfig,'翻译结果替换设置',['正则','翻译','替换']),icon='fa.gear',constcolor="#FF69B4")
|
||||
self.button_fix=getcolorbutton(globalconfig,'',callback=lambda x: noundictconfigdialog1(self,transerrorfixdictconfig,'dict_v2','翻译结果替换设置',['正则','翻译','替换']),icon='fa.gear',constcolor="#FF69B4")
|
||||
def setTab7(self) :
|
||||
self.tabadd_lazy(self.tab_widget, ('文本处理'), lambda :setTab7_lazy(self))
|
||||
def getcomparelayout(self):
|
||||
@ -163,92 +163,6 @@ def setTab7_lazy(self) :
|
||||
])
|
||||
|
||||
return self.makevbox([tab,self.comparelayout ])
|
||||
|
||||
@Singleton
|
||||
class noundictconfigdialog1(QDialog):
|
||||
def newline(self,row,item):
|
||||
self.model.insertRow(row,[QStandardItem(),QStandardItem(item['key']),QStandardItem(item['value'])])
|
||||
self.table.setIndexWidget(self.model.index(row, 0),getsimpleswitch(item,'regex'))
|
||||
def __init__(self,parent,configdict,title,label) -> None:
|
||||
super().__init__(parent,Qt.WindowCloseButtonHint)
|
||||
|
||||
self.setWindowTitle(_TR(title))
|
||||
#self.setWindowModality(Qt.ApplicationModal)
|
||||
|
||||
formLayout = QVBoxLayout(self) # 配置layout
|
||||
|
||||
self.model=QStandardItemModel()
|
||||
self.model.setHorizontalHeaderLabels(_TRL(label))
|
||||
table = QTableView(self)
|
||||
table.setModel(self.model)
|
||||
|
||||
table.horizontalHeader().setSectionResizeMode(2,QHeaderView.Stretch)
|
||||
table.horizontalHeader().setSectionResizeMode(1,QHeaderView.Stretch)
|
||||
table.horizontalHeader().setSectionResizeMode(0,QHeaderView.ResizeToContents)
|
||||
|
||||
self.table=table
|
||||
for row,item in enumerate(configdict['dict_v2']):
|
||||
self.newline(row,item)
|
||||
|
||||
|
||||
search=QHBoxLayout()
|
||||
searchcontent=QLineEdit()
|
||||
search.addWidget(searchcontent)
|
||||
button4=QPushButton()
|
||||
button4.setText(_TR('搜索'))
|
||||
def clicked4():
|
||||
text=searchcontent.text()
|
||||
|
||||
rows=self.model.rowCount()
|
||||
cols=self.model.columnCount()
|
||||
for row in range(rows):
|
||||
ishide=True
|
||||
for c in range(cols):
|
||||
if text in self.model.item(row,c).text():
|
||||
ishide=False
|
||||
break
|
||||
table.setRowHidden(row,ishide)
|
||||
|
||||
|
||||
button4.clicked.connect(clicked4)
|
||||
search.addWidget(button4)
|
||||
|
||||
|
||||
button=QPushButton(self)
|
||||
button.setText(_TR('添加行'))
|
||||
def clicked1():
|
||||
self.configdict['dict_v2'].insert(0,{'key':'','value':'','regex':False})
|
||||
self.newline(0,self.configdict['dict_v2'][0])
|
||||
button.clicked.connect(clicked1)
|
||||
button2=QPushButton(self)
|
||||
button2.setText(_TR('删除选中行'))
|
||||
def clicked2():
|
||||
self.model.removeRow(table.currentIndex().row())
|
||||
self.configdict['dict_v2'].pop(table.currentIndex().row())
|
||||
button2.clicked.connect(clicked2)
|
||||
self.button=button
|
||||
self.configdict=configdict
|
||||
formLayout.addWidget(table)
|
||||
formLayout.addLayout(search)
|
||||
formLayout.addWidget(button)
|
||||
formLayout.addWidget(button2)
|
||||
self.resize(QSize(600,400))
|
||||
self.show()
|
||||
def closeEvent(self, a0: QCloseEvent) -> None:
|
||||
self.button.setFocus()
|
||||
rows=self.model.rowCount()
|
||||
rowoffset=0
|
||||
dedump=set()
|
||||
for row in range(rows):
|
||||
k,v=self.model.item(row,1).text(),self.model.item(row,2).text()
|
||||
if k=="" or k in dedump:
|
||||
self.configdict['dict_v2'].pop(row-rowoffset)
|
||||
rowoffset+=1
|
||||
continue
|
||||
self.configdict['dict_v2'][row-rowoffset].update({
|
||||
'key':k,'value':v
|
||||
})
|
||||
dedump.add(k)
|
||||
@Singleton
|
||||
class noundictconfigdialog(QDialog):
|
||||
def closeEvent(self, a0: QCloseEvent) -> None:
|
||||
|
@ -1,7 +1,7 @@
|
||||
import functools
|
||||
|
||||
from PyQt5.QtWidgets import QComboBox
|
||||
from gui.inputdialog import getsomepath1
|
||||
from gui.inputdialog import getsomepath1,noundictconfigdialog1
|
||||
from myutils.config import globalconfig ,_TRL
|
||||
import os,functools
|
||||
import gobject
|
||||
@ -59,6 +59,8 @@ def setTab5lz(self) :
|
||||
[ ('自动朗读',6),(getsimpleswitch(globalconfig,'autoread' ),1)],
|
||||
[ ('朗读原文',6),(getsimpleswitch(globalconfig,'read_raw' ),1),'','',('朗读翻译',6),(getsimpleswitch(globalconfig,'read_trans' ),1)],
|
||||
[('朗读的翻译',6),(getsimplecombobox(_TRL([globalconfig['fanyi'][x]['name'] for x in globalconfig['fanyi']]),globalconfig,'read_translator'),15) ],
|
||||
[],
|
||||
[('语音修正',6),getsimpleswitch(globalconfig['ttscommon'],'tts_repair' ),getcolorbutton(globalconfig,'',callback=lambda x: noundictconfigdialog1(self,globalconfig['ttscommon'],'tts_repair_regex','语音修正',['正则','原文','替换']),icon='fa.gear',constcolor="#FF69B4") ],
|
||||
]
|
||||
gridlayoutwidget=self.makegrid(grids )
|
||||
gridlayoutwidget=self.makescroll( gridlayoutwidget )
|
||||
|
@ -142,8 +142,11 @@ if len(globalconfig['toolbutton']['rank'])!=len(globalconfig['toolbutton']['butt
|
||||
def setlanguage():
|
||||
global language,languageshow
|
||||
language=globalconfig['languageuse']
|
||||
try:
|
||||
with open('./files/lang/{}.json'.format(static_data["language_list_translator_inner"][language]),'r',encoding='utf8') as ff:
|
||||
languageshow=json.load(ff)
|
||||
except:
|
||||
languageshow={}
|
||||
setlanguage()
|
||||
|
||||
def _TR(k):
|
||||
|
@ -130,7 +130,7 @@ class texthook(basetext ):
|
||||
self.Luna_FindHooks_waiting=LunaHost.Luna_FindHooks_waiting
|
||||
self.Luna_FindHooks_waiting.argtypes=POINTER(c_int),
|
||||
self.Luna_EmbedSettings=LunaHost.Luna_EmbedSettings
|
||||
self.Luna_EmbedSettings.argtypes=DWORD,c_uint32,c_uint8,c_bool,c_wchar_p,c_uint32,c_uint32
|
||||
self.Luna_EmbedSettings.argtypes=DWORD,c_uint32,c_uint8,c_bool,c_wchar_p,c_uint32,c_uint32,c_bool
|
||||
self.Luna_checkisusingembed=LunaHost.Luna_checkisusingembed
|
||||
self.Luna_checkisusingembed.argtypes=DWORD,c_uint64,c_uint64,c_uint64
|
||||
self.Luna_checkisusingembed.restype=c_bool
|
||||
@ -204,6 +204,8 @@ class texthook(basetext ):
|
||||
self.useembed(addr,_ctx1,_ctx2,True)
|
||||
def safeembedcheck(self,text):
|
||||
try:
|
||||
if globalconfig['embedded']['safecheck_use']==False:
|
||||
return True
|
||||
for regex in (globalconfig['embedded']['safecheckregexs']):
|
||||
if re.match(codecs.escape_decode(bytes(regex,"utf-8"))[0].decode("utf-8"),text):
|
||||
return False
|
||||
@ -213,6 +215,7 @@ class texthook(basetext ):
|
||||
def getembedtext(self,text,tp):
|
||||
if self.safeembedcheck(text)==False:
|
||||
self.embedcallback(text,text)
|
||||
self.newline.put((text,False, lambda trans:1,True))
|
||||
return
|
||||
if globalconfig['autorun']==False:
|
||||
self.embedcallback(text,text)
|
||||
@ -237,7 +240,8 @@ class texthook(basetext ):
|
||||
False,#globalconfig['embedded']['changecharset']
|
||||
globalconfig['embedded']['changefont_font'] if globalconfig['embedded']['changefont'] else '',
|
||||
globalconfig['embedded']['insertspace_policy'],
|
||||
globalconfig['embedded']['keeprawtext'])
|
||||
globalconfig['embedded']['keeprawtext'],
|
||||
True)
|
||||
|
||||
def onremovehook(self,tp):
|
||||
toremove=[]
|
||||
|
@ -5,36 +5,65 @@ import os
|
||||
import windows
|
||||
from traceback import print_exc
|
||||
from tts.basettsclass import TTSbase
|
||||
import ctypes
|
||||
import ctypes,subprocess
|
||||
from myutils.subproc import subproc_w,autoproc
|
||||
class TTS(TTSbase):
|
||||
def init(self):
|
||||
|
||||
self.rate=globalconfig["ttscommon"]["rate"]
|
||||
self.voice=self.config["voice"]
|
||||
def checkchange(self):
|
||||
fname=str(time.time())
|
||||
savepath=os.path.join(os.getcwd(),'cache/tts',fname)
|
||||
exepath=os.path.join(os.getcwd(),'files/plugins/shareddllproxy32.exe')
|
||||
self.savepath=savepath
|
||||
|
||||
|
||||
t=time.time()
|
||||
t= str(t)
|
||||
pipename='\\\\.\\Pipe\\voiceroid2_'+t
|
||||
waitsignal='voiceroid2waitload_'+t
|
||||
|
||||
self.engine=autoproc(subproc_w('"{}" neospeech {} {} "{}"'.format(exepath,pipename,waitsignal,savepath),name='neospeech'))
|
||||
idx=self.config["voice"].split('_')[-1]
|
||||
hkey=self.config["voice"][:-len(idx)-1]
|
||||
|
||||
if self.voicexx!=(hkey,idx):
|
||||
self.voicexx=(hkey,idx)
|
||||
cmd='"{}" neospeech {} {} "{}" {} {} '.format(exepath,pipename,waitsignal,savepath,hkey,idx)
|
||||
|
||||
self.engine=autoproc(subproc_w(cmd,name='neospeech'))
|
||||
|
||||
windows.WaitForSingleObject(windows.AutoHandle(windows.CreateEvent(False, False, waitsignal)),windows.INFINITE);
|
||||
windows.WaitNamedPipe(pipename,windows.NMPWAIT_WAIT_FOREVER)
|
||||
self.hPipe = windows.AutoHandle(windows.CreateFile( pipename, windows.GENERIC_READ | windows.GENERIC_WRITE, 0,
|
||||
None, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, None))
|
||||
def init(self):
|
||||
|
||||
self.voicexx=(0,0)
|
||||
self.voicelist=self.getvoicelist()
|
||||
if globalconfig['reader'][self.typename]['voice'] not in self.voicelist:
|
||||
globalconfig['reader'][self.typename]['voice']=self.voicelist[0]
|
||||
|
||||
self.checkchange()
|
||||
def voiceshowmap(self,voice):
|
||||
|
||||
idx=voice.split('_')[-1]
|
||||
hk=voice[:-len(idx)-1]
|
||||
|
||||
return self.mapx[(hk,idx)]
|
||||
def getvoicelist(self):
|
||||
cachefname=os.path.abspath('./cache/{}.txt'.format(time.time()))
|
||||
exe=os.path.abspath('./files/plugins/shareddllproxy32.exe')
|
||||
subprocess.run('"{}" neospeechlist "{}"'.format(exe,cachefname))
|
||||
|
||||
return ['VW Misaki']
|
||||
with open(cachefname,'r',encoding='utf-16-le') as ff:
|
||||
readf=(ff.read())
|
||||
|
||||
os.remove(cachefname)
|
||||
datas=(readf.split('\n'))[:-1]
|
||||
|
||||
self.mapx={}
|
||||
xx=[]
|
||||
for i in range(len(datas)//3):
|
||||
self.mapx[(datas[i*3+1],datas[i*3+2])]=datas[i*3]
|
||||
xx.append('{}_{}'.format(datas[i*3+1],datas[i*3+2]))
|
||||
|
||||
return xx
|
||||
def speak(self,content,rate,voice,voice_idx):
|
||||
self.checkchange()
|
||||
windows.WriteFile(self.hPipe,bytes(ctypes.c_uint(rate)))
|
||||
windows.WriteFile(self.hPipe,content.encode('utf-16-le'))
|
||||
fname=windows.ReadFile(self.hPipe,1024,None).decode('utf-16-le')
|
||||
|
@ -767,3 +767,9 @@ def CreatePipe(lpsecu=None,sz=0):
|
||||
hwrite=HANDLE()
|
||||
_CreatePipe(pointer(hread),pointer(hwrite),lpsecu,sz)
|
||||
return AutoHandle(hread.value),AutoHandle(hwrite.value)
|
||||
|
||||
_CopyFile=_kernel32.CopyFileW
|
||||
_CopyFile.argtypes=LPCWSTR,LPCWSTR,BOOL
|
||||
_CopyFile.restype=BOOL
|
||||
def CopyFile(src,dst,bFailIfExists):
|
||||
return _CopyFile(src,dst,bFailIfExists)
|
@ -17,6 +17,7 @@
|
||||
"direct_filterrepeat":false,
|
||||
"allow_set_text_name":false,
|
||||
"embedded": {
|
||||
"safecheck_use":true,
|
||||
"safecheckregexs": [
|
||||
"(.*?)\\{(.*?)\\}(.*?)",
|
||||
"(.*?)\\[(.*?)\\](.*?)"
|
||||
@ -683,7 +684,9 @@
|
||||
"outputtopasteboard": false,
|
||||
"ttscommon": {
|
||||
"rate": 1.0,
|
||||
"volume": 100.0
|
||||
"volume": 100.0,
|
||||
"tts_repair":false,
|
||||
"tts_repair_regex":[]
|
||||
},
|
||||
"allowmulti": true,
|
||||
"sourcestatus2": {
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"version":"v2.39.3",
|
||||
"language_list_show":["简体中文","日本語","English","Русский язык","Español","한국어","Français","繁體中文","Tiếng Việt","Türkçe","Polski","Українська Мова","Italiano","اللغة العربية","ภาษาไทย"] ,
|
||||
"language_list_translator":["简体中文","日文","英文","俄语","西班牙语","韩语","法语","繁体中文","越南语","土耳其语","波兰语","乌克兰语","意大利语","阿拉伯语","泰语"],
|
||||
"language_list_translator_inner":["zh", "ja", "en","ru","es","ko","fr","cht","vi","tr","pl","uk","it","ar","th"],
|
||||
"version":"v2.40.0",
|
||||
"language_list_show":["简体中文","日本語","English","Русский язык","Español","한국어","Français","繁體中文","Tiếng Việt","Türkçe","Polski","Українська Мова","Italiano","اللغة العربية","ภาษาไทย","བོད་སྐད།"] ,
|
||||
"language_list_translator":["简体中文","日文","英文","俄语","西班牙语","韩语","法语","繁体中文","越南语","土耳其语","波兰语","乌克兰语","意大利语","阿拉伯语","泰语","藏语"],
|
||||
"language_list_translator_inner":["zh", "ja", "en","ru","es","ko","fr","cht","vi","tr","pl","uk","it","ar","th","bo"],
|
||||
"codepage_display":["日语(CP932,SHIFT-JIS)","UTF8(CP65001)","简体中文(CP936,GBK)","繁体中文(CP950,BIG5)","韩语(CP949,EUC-KR)","越南语(CP1258)","泰语(CP874)","阿拉伯语(CP1256)","希伯来语(CP1255)","土耳其语(CP1254)","希腊语(CP1253)","北欧(CP1257)","中东欧(CP1250)","西里尔(CP1251)","拉丁(CP1252)","英语(CP437)"],
|
||||
"codepage_real":[932,65001,936,950,949,1258,874,1256,1255,1254,1253,1257,1250,1251,1252,437],
|
||||
"allkata":"ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヽヾ",
|
||||
@ -125,9 +125,13 @@
|
||||
"name":"语音合成",
|
||||
"sources":[
|
||||
{
|
||||
"name": "NeoSpeech",
|
||||
"name": "NeoSpeech Misaki",
|
||||
"link": "https://github.com/HIllya51/RESOURCES/releases/download/speech_synthesis_packages/NeoSpeech.Japanese.Misaki.zip"
|
||||
},
|
||||
{
|
||||
"name": "NeoSpeech Show",
|
||||
"link": "https://github.com/HIllya51/RESOURCES/releases/download/speech_synthesis_packages/https://github.com/HIllya51/RESOURCES/releases/download/speech_synthesis_packages/NeoSpeech.TTS.NeoSpeech.Japanese.Show_v3.10.0.0.zip.zip"
|
||||
},
|
||||
{
|
||||
"name": "VOICEVOX",
|
||||
"link": "https://github.com/VOICEVOX/voicevox/releases"
|
||||
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "جزءا لا يتجزأ من فحص السلامة",
|
||||
"正则匹配": "مطابقة العادية",
|
||||
"非官方": "غير رسمي",
|
||||
"离线": "غير متصل"
|
||||
"离线": "غير متصل",
|
||||
"导出翻译补丁": "تصدير ترجمة التصحيح",
|
||||
"语音修正": "صوت تصحيح",
|
||||
"藏语": "التيبتية"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "內嵌安全性檢查",
|
||||
"正则匹配": "正則匹配",
|
||||
"非官方": "非官方",
|
||||
"离线": "離線"
|
||||
"离线": "離線",
|
||||
"导出翻译补丁": "匯出翻譯補丁",
|
||||
"语音修正": "語音修正",
|
||||
"藏语": "藏語"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "Embedded security check",
|
||||
"正则匹配": "Regular matching",
|
||||
"非官方": "Unofficial",
|
||||
"离线": "off-line"
|
||||
"离线": "off-line",
|
||||
"导出翻译补丁": "Export translation patches",
|
||||
"语音修正": "Voice correction",
|
||||
"藏语": "Tibetan"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "Inspección de Seguridad incorporada",
|
||||
"正则匹配": "Coincidencia regular",
|
||||
"非官方": "No oficial",
|
||||
"离线": "Fuera de línea"
|
||||
"离线": "Fuera de línea",
|
||||
"导出翻译补丁": "Exportar parches de traducción",
|
||||
"语音修正": "Corrección de voz",
|
||||
"藏语": "Tibetano"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "Inspection de sécurité intégrée",
|
||||
"正则匹配": "Match régulier",
|
||||
"非官方": "Non officiel",
|
||||
"离线": "Hors ligne"
|
||||
"离线": "Hors ligne",
|
||||
"导出翻译补丁": "Exporter le patch de traduction",
|
||||
"语音修正": "Correction vocale",
|
||||
"藏语": "Tibétain"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "Controllo di sicurezza integrato",
|
||||
"正则匹配": "Corrispondenza regolare",
|
||||
"非官方": "Non ufficiale",
|
||||
"离线": "off-line"
|
||||
"离线": "off-line",
|
||||
"导出翻译补丁": "Esporta patch di traduzione",
|
||||
"语音修正": "Correzione vocale",
|
||||
"藏语": "Tibetano"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "インラインセキュリティチェック",
|
||||
"正则匹配": "正規一致",
|
||||
"非官方": "非公式",
|
||||
"离线": "オフライン"
|
||||
"离线": "オフライン",
|
||||
"导出翻译补丁": "翻訳パッチのエクスポート",
|
||||
"语音修正": "音声修正",
|
||||
"藏语": "チベット語"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "내장 보안 검사",
|
||||
"正则匹配": "정규 일치",
|
||||
"非官方": "비공식",
|
||||
"离线": null
|
||||
"离线": null,
|
||||
"导出翻译补丁": "번역 패치 내보내기",
|
||||
"语音修正": "음성 수정",
|
||||
"藏语": "티베트어"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "Wbudowana kontrola bezpieczeństwa",
|
||||
"正则匹配": "Regularne dopasowanie",
|
||||
"非官方": "Nieoficjalne",
|
||||
"离线": "off-line"
|
||||
"离线": "off-line",
|
||||
"导出翻译补丁": "Eksportuj łatki tłumaczeniowe",
|
||||
"语音修正": "Korekta głosu",
|
||||
"藏语": "Tybetański"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "Проверка встроенной безопасности",
|
||||
"正则匹配": "Регулярное согласование",
|
||||
"非官方": null,
|
||||
"离线": "В автономном режиме"
|
||||
"离线": "В автономном режиме",
|
||||
"导出翻译补丁": "Экспорт исправлений перевода",
|
||||
"语音修正": "Голосовая коррекция",
|
||||
"藏语": "Тибетский язык"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "การตรวจสอบความปลอดภัยแบบอินไลน์",
|
||||
"正则匹配": "จับคู่ปกติ",
|
||||
"非官方": "ไม่เป็นทางการ",
|
||||
"离线": "ออฟไลน์"
|
||||
"离线": "ออฟไลน์",
|
||||
"导出翻译补丁": "ส่งออกแพทช์การแปล",
|
||||
"语音修正": "แก้ไขเสียง",
|
||||
"藏语": "ทิเบต"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "İçeri giriş güvenlik kontrolü",
|
||||
"正则匹配": "Normal eşleşme",
|
||||
"非官方": "Resmi olmayan",
|
||||
"离线": "off line"
|
||||
"离线": "off line",
|
||||
"导出翻译补丁": "Çeviri örneklerini dışarı aktar",
|
||||
"语音修正": "Ses düzeltmesi",
|
||||
"藏语": "TibetanKCharselect unicode block name"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "Вбудована перевірка безпеки",
|
||||
"正则匹配": "Регулярне збігання",
|
||||
"非官方": "Неофіційний",
|
||||
"离线": "вимкнено"
|
||||
"离线": "вимкнено",
|
||||
"导出翻译补丁": "Експортувати латки перекладу",
|
||||
"语音修正": "Виправлення голосу",
|
||||
"藏语": "Тибетський"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "Kiểm tra an ninh nội tuyến",
|
||||
"正则匹配": "Khớp chính quy",
|
||||
"非官方": null,
|
||||
"离线": "Ngoại tuyến"
|
||||
"离线": "Ngoại tuyến",
|
||||
"导出翻译补丁": "Xuất bản bản vá dịch",
|
||||
"语音修正": "Sửa giọng nói",
|
||||
"藏语": "Tây Tạng"
|
||||
}
|
@ -717,5 +717,8 @@
|
||||
"内嵌安全性检查": "",
|
||||
"正则匹配": "",
|
||||
"非官方": "",
|
||||
"离线": ""
|
||||
"离线": "",
|
||||
"导出翻译补丁": "",
|
||||
"语音修正": "",
|
||||
"藏语": ""
|
||||
}
|
24674
plugins/libs/nlohmann/json.hpp
Normal file
24674
plugins/libs/nlohmann/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
project(shareddllproxy)
|
||||
|
||||
|
||||
add_executable(shareddllproxy shareddllproxy.cpp dllinject.cpp ntleas.cpp)
|
||||
add_executable(shareddllproxy shareddllproxy.cpp dllinject.cpp ntleas.cpp aspatch.cpp)
|
||||
if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
|
||||
add_library(x64lib magpie.cpp lossless.cpp)
|
||||
target_link_libraries(shareddllproxy x64lib ${Detours})
|
||||
|
291
plugins/shareddllproxy/aspatch.cpp
Normal file
291
plugins/shareddllproxy/aspatch.cpp
Normal file
@ -0,0 +1,291 @@
|
||||
#include<nlohmann/json.hpp>
|
||||
#include<filesystem>
|
||||
#include<fstream>
|
||||
#include<optional>
|
||||
#include<thread>
|
||||
#include<Windows.h>
|
||||
#include<unordered_set>
|
||||
#include<set>
|
||||
#include<tlhelp32.h>
|
||||
static std::wstring StringToWideString(const std::string& text, UINT encoding=CP_UTF8)
|
||||
{
|
||||
std::vector<wchar_t> buffer(text.size() + 1);
|
||||
int length = MultiByteToWideChar(encoding, 0, text.c_str(), text.size() + 1, buffer.data(), buffer.size());
|
||||
return std::wstring(buffer.data(), length - 1);
|
||||
}
|
||||
std::string WideStringToString(const std::wstring& text,UINT cp=CP_UTF8)
|
||||
{
|
||||
std::vector<char> buffer((text.size() + 1) * 4);
|
||||
|
||||
WideCharToMultiByte(cp, 0, text.c_str(), -1, buffer.data(), buffer.size(), nullptr, nullptr);
|
||||
return buffer.data();
|
||||
}
|
||||
HANDLE runexe(const std::wstring &exe,const std::optional<std::wstring> &startup_argument)
|
||||
{
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
ZeroMemory( &si, sizeof(si) );
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory( &pi, sizeof(pi) );
|
||||
|
||||
std::vector<wchar_t>argu;
|
||||
if(startup_argument.has_value()){
|
||||
argu.resize(startup_argument.value().size()+1);
|
||||
wcscpy(argu.data(),startup_argument.value().c_str());
|
||||
}
|
||||
CreateProcessW( exe.c_str(), // No module name (use command line)
|
||||
startup_argument.has_value()?argu.data(): NULL,
|
||||
NULL, // Process handle not inheritable
|
||||
NULL, // Thread handle not inheritable
|
||||
FALSE, // Set handle inheritance to FALSE
|
||||
0, // No creation flags
|
||||
NULL, // Use parent's environment block
|
||||
NULL, // Use parent's starting directory
|
||||
&si, // Pointer to STARTUPINFO structure
|
||||
&pi ); // Pointer to PROCESS_INFORMATION structure
|
||||
|
||||
return pi.hProcess;
|
||||
}
|
||||
|
||||
std::wstring stolower(const std::wstring& s1){
|
||||
auto s=s1;
|
||||
std::transform(s.begin(), s.end(), s.begin(), tolower);
|
||||
return s;
|
||||
}
|
||||
std::vector<DWORD> EnumerateProcesses(const std::wstring& exe)
|
||||
{
|
||||
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
PROCESSENTRY32 pe32;
|
||||
pe32.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
if (!Process32First(hSnapshot, &pe32))
|
||||
{
|
||||
CloseHandle(hSnapshot);
|
||||
return {};
|
||||
}
|
||||
std::vector<DWORD> pids;
|
||||
do
|
||||
{
|
||||
if(stolower(exe)==stolower(pe32.szExeFile))
|
||||
pids.push_back(pe32.th32ProcessID);
|
||||
} while (Process32Next(hSnapshot, &pe32));
|
||||
|
||||
CloseHandle(hSnapshot);
|
||||
return pids;
|
||||
}
|
||||
enum { STRING = 12, MESSAGE_SIZE = 500, PIPE_BUFFER_SIZE = 50000, SHIFT_JIS = 932, MAX_MODULE_SIZE = 120, PATTERN_SIZE = 30, HOOK_NAME_SIZE = 60, FIXED_SPLIT_VALUE = 0x10001 ,
|
||||
HOOKCODE_LEN=500};
|
||||
struct ThreadParam
|
||||
{
|
||||
bool operator==(ThreadParam other) const { return processId == other.processId && addr == other.addr && ctx == other.ctx && ctx2 == other.ctx2; }
|
||||
DWORD processId;
|
||||
uint64_t addr;
|
||||
uint64_t ctx; // The context of the hook: by default the first value on stack, usually the return address
|
||||
uint64_t ctx2; // The subcontext of the hook: 0 by default, generated in a method specific to the hook
|
||||
};
|
||||
struct messagelist{
|
||||
bool read;
|
||||
int type;
|
||||
DWORD pid;
|
||||
char name[HOOK_NAME_SIZE];
|
||||
wchar_t hookcode[HOOKCODE_LEN];
|
||||
ThreadParam tp;
|
||||
wchar_t* stringptr;
|
||||
uint64_t addr;
|
||||
};
|
||||
|
||||
class lunapatch{
|
||||
public:
|
||||
HANDLE hMessage;
|
||||
HANDLE hwait;
|
||||
std::wstring target_exe;
|
||||
nlohmann::json config;
|
||||
std::map<std::string,std::string>translation;
|
||||
std::unordered_set<DWORD>connectedpids;
|
||||
void (*Luna_Start)( HANDLE* hRead );
|
||||
void (*Luna_Inject)(DWORD pid,LPCWSTR basepath);
|
||||
void (*Luna_EmbedSettings)(DWORD pid,UINT32 waittime,UINT8 fontCharSet,bool fontCharSetEnabled,wchar_t *fontFamily,UINT32 spaceadjustpolicy,UINT32 keeprawtext,bool fastskipignore);
|
||||
void (*Luna_useembed)(DWORD pid,uint64_t address,uint64_t ctx1,uint64_t ctx2,bool use);
|
||||
bool (*Luna_checkisusingembed)(DWORD pid,uint64_t address,uint64_t ctx1,uint64_t ctx2);
|
||||
void (*Luna_embedcallback)(DWORD pid,LPCWSTR text,LPCWSTR trans);
|
||||
std::set<std::string>notranslation;
|
||||
lunapatch(std::wstring dll,nlohmann::json&&_translation,nlohmann::json&&_config):translation(_translation),config(_config){
|
||||
auto LunaHost=LoadLibraryW(dll.c_str());
|
||||
|
||||
Luna_Start=(decltype(Luna_Start))GetProcAddress(LunaHost,"Luna_Start");
|
||||
Luna_EmbedSettings=(decltype(Luna_EmbedSettings))GetProcAddress(LunaHost,"Luna_EmbedSettings");
|
||||
Luna_Inject=(decltype(Luna_Inject))GetProcAddress(LunaHost,"Luna_Inject");
|
||||
Luna_useembed=(decltype(Luna_useembed))GetProcAddress(LunaHost,"Luna_useembed");
|
||||
Luna_checkisusingembed=(decltype(Luna_checkisusingembed))GetProcAddress(LunaHost,"Luna_checkisusingembed");
|
||||
Luna_embedcallback=(decltype(Luna_embedcallback))GetProcAddress(LunaHost,"Luna_embedcallback");
|
||||
|
||||
|
||||
Luna_Start(&hMessage);
|
||||
std::thread([&](){Parsehostmessage();}).detach();
|
||||
}
|
||||
void run(){
|
||||
target_exe=StringToWideString(config["target_exe"]);
|
||||
|
||||
auto _startup_argument=config["startup_argument"];
|
||||
|
||||
std::optional<std::wstring> startup_argument;
|
||||
if(_startup_argument.is_null())
|
||||
startup_argument={};
|
||||
else
|
||||
startup_argument=StringToWideString(config["startup_argument"]);
|
||||
hwait=runexe(target_exe,startup_argument);
|
||||
}
|
||||
~lunapatch(){
|
||||
if(notranslation.size()){
|
||||
for(auto &text:notranslation){
|
||||
translation[text]="";
|
||||
}
|
||||
auto notrs=nlohmann::json(notranslation).dump(4);
|
||||
std::ofstream of;
|
||||
of.open("no_translation.json");
|
||||
of<<notrs;
|
||||
of.close();
|
||||
notrs=nlohmann::json(translation).dump(4);
|
||||
|
||||
of.open("no_translation_and_translation.json");
|
||||
of<<notrs;
|
||||
of.close();
|
||||
}
|
||||
}
|
||||
void wait(){
|
||||
WaitForSingleObject(hwait,INFINITE);
|
||||
}
|
||||
void inject(){
|
||||
//chrome has multi process
|
||||
Sleep(config["inject_timeout"]);
|
||||
auto pids=EnumerateProcesses(target_exe);
|
||||
for(auto pid:pids){
|
||||
Luna_Inject(pid,L"");
|
||||
}
|
||||
|
||||
}
|
||||
std::wstring findtranslation(const std::wstring& text){
|
||||
auto utf8text=WideStringToString(text);
|
||||
if(translation.find(utf8text)==translation.end()){
|
||||
//wprintf(L"%s\n",text.c_str());
|
||||
notranslation.insert(utf8text);
|
||||
return {};
|
||||
}
|
||||
return StringToWideString(translation.at(utf8text));
|
||||
}
|
||||
void Parsehostmessage(){
|
||||
while (true)
|
||||
{
|
||||
messagelist message;
|
||||
DWORD _;
|
||||
ReadFile(hMessage,&message,sizeof(message),&_,NULL);
|
||||
switch (message.type)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
auto font =StringToWideString(config["embedsettings"]["font"]);
|
||||
auto insertspace_policy=config["embedsettings"]["insertspace_policy"];
|
||||
auto keeprawtext=config["embedsettings"]["keeprawtext"];
|
||||
Luna_EmbedSettings(message.pid,1000,2,false,font.data(),insertspace_policy,keeprawtext,false);
|
||||
connectedpids.insert(message.pid);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
connectedpids.erase(message.pid);
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
{
|
||||
std::wstring text=message.stringptr;
|
||||
auto tp=message.tp;
|
||||
for(auto pid:connectedpids)
|
||||
{
|
||||
if((Luna_checkisusingembed(pid,tp.addr,tp.ctx,tp.ctx2)))
|
||||
{
|
||||
auto trans=findtranslation(text);
|
||||
Luna_embedcallback(pid,text.c_str(),trans.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
{
|
||||
std::wstring newhookcode=message.stringptr;
|
||||
for(auto hook:config["embedhook"]){
|
||||
auto hookcode= StringToWideString(hook[0]);
|
||||
uint64_t _addr=hook[1];
|
||||
uint64_t _ctx1=hook[2];
|
||||
uint64_t _ctx2=hook[3];
|
||||
if(hookcode==newhookcode){
|
||||
for(auto pid:connectedpids){
|
||||
Luna_useembed(pid,message.addr,_ctx1,_ctx2,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(message.stringptr)
|
||||
free(message.stringptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
std::wstring GetExecutablePath()
|
||||
{
|
||||
WCHAR buffer[MAX_PATH];
|
||||
GetModuleFileNameW(NULL, buffer, MAX_PATH);
|
||||
|
||||
std::wstring fullPath(buffer);
|
||||
size_t pos = fullPath.find_last_of(L"\\/");
|
||||
if (pos != std::wstring::npos)
|
||||
{
|
||||
return fullPath.substr(0, pos);
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
bool checkisapatch(){
|
||||
auto curr=std::filesystem::path(GetExecutablePath());
|
||||
auto config=curr/"LunaPatch.json";
|
||||
if(std::filesystem::exists(config)==false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::ifstream jsonfile;
|
||||
jsonfile.open(config);
|
||||
auto configjson=nlohmann::json::parse(jsonfile);
|
||||
jsonfile.close();
|
||||
std::string translation_file=configjson["translation_file"];
|
||||
|
||||
jsonfile.open(translation_file);
|
||||
|
||||
std::map<std::string,std::string> translation=nlohmann::json::parse(jsonfile);
|
||||
jsonfile.close();
|
||||
|
||||
|
||||
bool isbit64=configjson["isbit64"];
|
||||
auto bitappendix=isbit64?L"64":L"32";
|
||||
auto LunaHost=(curr/(std::wstring(L"LunaHost")+bitappendix)).wstring();
|
||||
auto LunaHook=(curr/(std::wstring(L"LunaHook")+bitappendix)).wstring();
|
||||
|
||||
lunapatch _lunapatch(LunaHost,std::move(translation),std::move(configjson));
|
||||
|
||||
_lunapatch.run();
|
||||
_lunapatch.inject();
|
||||
_lunapatch.wait();
|
||||
return true;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include<Windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
@ -100,15 +101,31 @@ std::vector<std::wstring>_List(const wchar_t* token) {
|
||||
}
|
||||
|
||||
|
||||
int neospeech(int argc, wchar_t* argv[]) {
|
||||
auto speechs = _List(L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices");
|
||||
int idx = -1;
|
||||
int neospeechlist(int argc, wchar_t* argv[]) {
|
||||
FILE* f=_wfopen(argv[1],L"wb");
|
||||
for(auto key:{L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices",L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech_OneCore\\Voices"})
|
||||
{
|
||||
auto speechs=_List(key);
|
||||
for (int i = 0; i < speechs.size(); i++) {
|
||||
if (speechs[i] == L"VW Misaki") {
|
||||
idx = i;
|
||||
if (speechs[i].substr(0,2) == L"VW") {
|
||||
fwrite(speechs[i].c_str(),1,speechs[i].size()*2,f);
|
||||
fwrite(L"\n",1,2,f);
|
||||
fwrite(key,1,wcslen(key)*2,f);
|
||||
fwrite(L"\n",1,2,f);
|
||||
auto idx=std::to_wstring(i);
|
||||
fwrite(idx.c_str(),1,idx.size()*2,f);
|
||||
fwrite(L"\n",1,2,f);
|
||||
}
|
||||
}
|
||||
if (idx == -1)return 0;
|
||||
}
|
||||
fclose(f);
|
||||
return 0;
|
||||
|
||||
}
|
||||
int neospeech(int argc, wchar_t* argv[]) {
|
||||
auto hkey=argv[4];
|
||||
auto idx=std::stoi(argv[5]);
|
||||
|
||||
|
||||
HANDLE hPipe = CreateNamedPipe(argv[1], PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT
|
||||
, PIPE_UNLIMITED_INSTANCES, 65535, 65535, NMPWAIT_WAIT_FOREVER, 0);
|
||||
@ -134,7 +151,7 @@ int neospeech(int argc, wchar_t* argv[]) {
|
||||
wchar_t newname[1024] = { 0 };
|
||||
wsprintf(newname, L"%s%d.wav", argv[3], II);
|
||||
std::wstring newname_ = newname;
|
||||
_Speak(content, L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices", idx, speed, 100, newname_);
|
||||
_Speak(content, hkey, idx, speed, 100, newname_);
|
||||
WriteFile(hPipe, newname, wcslen(newname)*2, &_, NULL);
|
||||
}
|
||||
return 0;
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
int dllinjectwmain(int argc, wchar_t* argv[]);
|
||||
int ntleaswmain(int argc, wchar_t* wargv[]);
|
||||
|
||||
bool checkisapatch();
|
||||
#ifndef _WIN64
|
||||
int LRwmain(int argc, wchar_t* argv[]);
|
||||
int jbjwmain(int argc, wchar_t* argv[]);
|
||||
@ -18,6 +18,7 @@ int kingsoftwmain(int argc, wchar_t* argv[]);
|
||||
int voiceroid2wmain(int argc, wchar_t* argv[]);
|
||||
int lewmain(int argc, wchar_t* argv[]);
|
||||
int neospeech(int argc, wchar_t* argv[]);
|
||||
int neospeechlist(int argc, wchar_t* argv[]);
|
||||
#else
|
||||
int magpiewmain(int argc, wchar_t* wargv[]);
|
||||
int losslesswmain(int argc, wchar_t* wargv[]);
|
||||
@ -55,6 +56,7 @@ int listprocessmodule(int argc,wchar_t *argv[]){
|
||||
|
||||
int wmain(int argc, wchar_t* argv[])
|
||||
{
|
||||
if(checkisapatch())return 1;
|
||||
auto argv0 = std::wstring(argv[1]);
|
||||
if (argv0 == L"dllinject")
|
||||
return dllinjectwmain(argc - 1, argv + 1);
|
||||
@ -78,6 +80,8 @@ int wmain(int argc, wchar_t* argv[])
|
||||
return voiceroid2wmain(argc - 1, argv + 1);
|
||||
else if (argv0 == L"neospeech")
|
||||
return neospeech(argc - 1, argv + 1);
|
||||
else if (argv0 == L"neospeechlist")
|
||||
return neospeechlist(argc - 1, argv + 1);
|
||||
#else
|
||||
else if (argv0 == L"magpie")
|
||||
return magpiewmain(argc - 1, argv + 1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user