def dump(self, filename): ''' 将当前 self.body 列表中的内容转储到本地指定文件 ''' try: Message.ShowInfo('正在保存翻译对照表...') _body = [] for x in self.body: _body.append({ 'Original': quoted(x['Original']), 'Translation': quoted(x['Translation']) }) restruct = { 'Header': { 'Type': self.header_type, 'Version': self.header_version }, 'Body' : _body } with open(filename, 'w+', encoding='UTF-8-SIG') as f: yaml.dump( restruct, f, allow_unicode=True, default_flow_style=False, width=2048, sort_keys=False ) f.close() Message.ShowInfo('保存到: %s' % os.path.relpath(os.path.abspath(filename), project_slndir)) return os.path.abspath(filename) except Exception as _err: print(_err) Message.ShowError('保存翻译对照表期间发生错误, 请重试...') return None
def __searchMark(self, filename): line_num = 0 charset = self.__detectCharset(filename) regex = r'\s*?' + self.__options__['mark_format'] + r'\s*?' if '../../src/'.replace('/', os.path.sep) in filename and charset.upper() != 'UTF-8-SIG': Message.ShowWarning('期望文件 %s 的编码为 UTF-8-SIG 而实际上是 %s' % (filename, charset.upper())) return try: textfile = open(filename, encoding=charset) for line in textfile: line_num = line_num + 1 if '//' not in line: continue re_match = re.match(regex, line) if not re_match: continue if str(re_match.group(1)) in self.mark_dict: Message.ShowError('发现重复的代码注入标记: ' + re_match.group(0)) Common.exit_with_pause(-1) self.mark_dict[re_match.group(1)] = { 'index' : int(re_match.group(1)), 'filepath' : filename, 'line' : line_num } textfile.close() except Exception as err: Message.ShowError('文件 : %s | 错误信息 : %s' % (filename, err))
def build(self, src_dir): ''' 根据指定的源代码目录, 构建新的数据保存到 self.body 列表 ''' Message.ShowStatus('正在扫描源代码目录, 提取可被翻译的字符串...') extract_content = [] for dirpath, _dirnames, filenames in os.walk(src_dir): for filename in filenames: _base_name, extension_name = os.path.splitext(filename.lower()) if extension_name.lower() in process_exts: filepath = os.path.normpath('%s/%s' % (dirpath, filename)) content = self.__step1_extract_single_file(filepath) content = self.__step2_replace_constants(content) content = self.__step3_except_nonstring(content) content = self.__step4_field_offset(content) content = self.__step5_drop_params(content) content = self.__step6_unescape(content) extract_content.extend(content) # 对提取到的内容进行消重处理 extract_content = self.__make_distinct(extract_content) self.body = [] for x in extract_content: self.body.append({'Original': x['text'], 'Translation': ''}) Message.ShowInfo('扫描完毕, 共有 %d 条可翻译的字符串' % len(self.body))
def main(): os.chdir(os.path.split(os.path.realpath(__file__))[0]) Common.welcome('版本号修改辅助脚本') print('') # 读取当前的 Pandas 主程序版本号 pandas_ver = Common.get_pandas_ver(os.path.abspath(project_slndir), origin=True) Message.ShowInfo('当前模拟器的主版本是 %s' % pandas_ver) print('') # 询问获取升级后的目标版本 newver = Inputer().requireText( {'tips': '请输入新的版本号 (四段式: 1.0.0.1, 最后一段为 1 则表示为开发版)'}) if not isVersionFormatValid(newver): Message.ShowError('你输入的版本号: %s 不符合四段式规则, 请重试...' % newver) Common.exit_with_pause(-1) # 执行更新操作 ver = VersionUpdater(options) ver.updateDirectory(slndir('src'), newver) Common.exit_with_pause()
def welcome(scriptname = None): ''' 用于在脚本程序开始时显示欢迎信息 ''' LINE_NORMAL = Back.RED + Style.BRIGHT LINE_GREENS = Back.RED + Style.BRIGHT + Fore.GREEN LINE_WHITED = Back.RED + Style.BRIGHT + Fore.WHITE LINE_ENDING = '\033[K' + Style.RESET_ALL print('') print(LINE_NORMAL + r" " + LINE_ENDING) print(LINE_WHITED + r" Pandas Dev Team Presents " + LINE_ENDING) print(LINE_NORMAL + r" ____ _ " + LINE_ENDING) print(LINE_NORMAL + r" | _ \ __ _ _ __ __| | __ _ ___ " + LINE_ENDING) print(LINE_NORMAL + r" | |_) |/ _` || '_ \ / _` | / _` |/ __| " + LINE_ENDING) print(LINE_NORMAL + r" | __/| (_| || | | || (_| || (_| |\__ \ " + LINE_ENDING) print(LINE_NORMAL + r" |_| \__,_||_| |_| \__,_| \__,_||___/ " + LINE_ENDING) print(LINE_NORMAL + r" " + LINE_ENDING) print(LINE_GREENS + r" https://pandas.ws/ " + LINE_ENDING) print(LINE_NORMAL + r" " + LINE_ENDING) print(LINE_WHITED + r" Pandas is only for learning and research purposes. " + LINE_ENDING) print(LINE_WHITED + r" Please don't use it for commercial. " + LINE_ENDING) print(LINE_NORMAL + r" " + LINE_ENDING) print('') if scriptname is not None: Message.ShowInfo('您现在启动的是: {scriptname}'.format(scriptname = scriptname)) Message.ShowInfo('在使用此脚本之前, 建议确保 src 目录的工作区是干净的.') Message.ShowInfo('这样当处理结果不符合预期时, 可以轻松的利用 git 进行重置操作.')
def compile_sub(define_val, name, version, scheme='Release|Win32'): ''' 用于执行编译 ''' # 获取第一个支持的 Visual Studio 对应的 vcvarsall 路径 vcvarsall = get_vcvarsall_path() # 根据名称确定一下标记 modetag = ('RE' if name == '复兴后' else 'PRE') # 执行编译 Common.cmd_execute([ '"%s" x86' % vcvarsall, 'set CL=%s' % define_val, 'Devenv rAthena.sln /clean', 'Devenv rAthena.sln /Rebuild "%s"' % scheme ], project_slndir, modetag, slndir(compile_logfile)) # 编译完成后, 判断编译是否成功 result, succ, faild, _skip = get_compile_result() if not result: Message.ShowError('无法获取 %s 的编译结果' % name) return False # 若成功, 则进行符号文件的存储 if result and int(succ) > 1 and int(faild) == 0: return True else: Message.ShowWarning('%s: 存在编译失败的工程, 请进行检查...' % modetag) return False
def main(): os.chdir(os.path.split(os.path.realpath(__file__))[0]) welecome() print('') confirm = InputController().requireBool({ 'tips': '是否立刻进行文件编码转换?', 'default': False }) if not confirm: Message.ShowInfo('您取消了操作, 程序终止...\n') Common.exitWithPause() count = CharsetConverter({ 'ignore_files': ['Makefile', 'Makefile.in', 'CMakeLists.txt'], 'process_exts': ['.hpp', '.cpp'] }).convertDirectory('../../src', 'UTF-8-SIG') if count <= 0: Message.ShowInfo('很好! 源代码文件都已转换为 UTF-8-SIG 编码.') print('') Common.exitWithPause()
def convertFile(self, filepath, to_charset, directory): ''' 给定一个文本文件, 将它转换成指定的编码并保存 ''' origin_charset = self.__detectCharset(filepath).upper() to_charset = to_charset.upper() if origin_charset == to_charset: return False try: absolutely_path = os.path.abspath(filepath) Message.ShowInfo( '将 {filename} 的编码从 {origin_charset} 转换为 {to_charset}'.format( filename=os.path.relpath(absolutely_path, directory), origin_charset=origin_charset, to_charset=to_charset)) origin_file = codecs.open(filepath, 'r', origin_charset) content = origin_file.read() origin_file.close() target_file = codecs.open(filepath, 'w', to_charset) target_file.write(content) target_file.close() return True except IOError as err: Message.ShowError("I/O error: {0}".format(err)) return False
def toTraditional(self): ''' 将当前 self.body 中的译文结果转换到目标结果 ''' Message.ShowInfo('正在将译文转换成繁体中文...') for x in self.body: x['Translation'] = self.opencc.convert(x['Translation']) Message.ShowInfo('译文已经顺利转换成繁体中文')
def __init__(self, options): self.__options__ = options self.mark_dict = {} if not self.detect(self.__options__['source_dirs']): Message.ShowError('无法成功定位所有需要的代码注入点, 程序终止!') Common.exit_with_pause(-1) else: Message.ShowStatus('已成功定位所有代码注入点.\n')
def welecome(): print('=' * 70) print('') print('NPC 事件添加助手'.center(62)) print('') print('=' * 70) print('') Message.ShowInfo('在使用此脚本之前, 建议确保 src 目录的工作区是干净的.') Message.ShowInfo('这样添加结果如果不符合预期, 可以轻松的利用 git 进行重置操作.')
def welecome(): print('=' * 70) print('') print('源代码文件编码转换脚本'.center(62)) print('') print('=' * 70) print('') Message.ShowInfo('在使用此脚本之前, 建议确保 src 目录的工作区是干净的.') Message.ShowInfo('这样处理结果如果不符合预期, 可以轻松的利用 git 进行重置操作.')
def update_store(): repo = git.Repo(project_symstoredir) if repo.is_dirty() or repo.untracked_files: return False Message.ShowStatus('正在尝试拉取最新的符号数据, 请稍候...') try: repo.remotes.origin.pull() except git.GitError as _err: Message.ShowWarning('更新失败, 原因: %s' % _err) return False else: return True
def load(self, filename): ''' ''' Message.ShowInfo('正在加载: %s' % filename) with open(filename, encoding='UTF-8-SIG') as f: content = yaml.load(f, Loader=yaml.FullLoader) header = content['Header'] self.header_version = header['Version'] self.header_type = header['Type'] self.body = content['Body'] Message.ShowInfo('已读取 %d 条记录, 数据版本号为: %d' % (len(self.body), self.header_version))
def requireBool(self, options): print('-' * 70) Message.ShowSelect(options['tips'] + ' [Y/N]:', end="") user_input = input() if user_input.lower() in ['y', 'yes']: result = True elif user_input.lower() in ['n', 'no']: result = False else: result = options['default'] Message.ShowInfo('您输入的是: ' + ('Yes 是的' if result else 'No 不是')) print('-' * 70) print('\n') return result
def updateall(self, increase_version=False): ''' 更新全部翻译对照表文件, 并保留现有的翻译结果 ''' yamlfiles = glob.glob('../../conf/msg_conf/translation_*.yml') Message.ShowStatus('即将更新全部翻译对照表, 并保留现有的翻译结果...') for relpath in yamlfiles: fullpath = os.path.abspath(relpath) Message.ShowInfo('正在升级: %s' % os.path.relpath(fullpath, project_slndir)) _backup_body = self.body[:] self.updatefrom(fullpath, increase_version) self.dump(fullpath) self.body = _backup_body Message.ShowStatus('感谢您的使用, 全部对照表翻译完毕.')
def requireText(self, options): print('-' * 70) Message.ShowSelect(options['tips'] + ':') result = input(options['prefix']) if not result: Message.ShowError('请至少输入一个字符. 程序终止') print('-' * 70) Common.exitWithPause(-1) result = options['prefix'] + result if options['upper']: result = result.upper() Message.ShowInfo('您输入的是: ' + result) print('-' * 70) print('\n') return result
def detect(self, src_dir): if isinstance(src_dir, str): src_dir = [src_dir] for single_dir in src_dir: for dirpath, _dirnames, filenames in os.walk(single_dir): for filename in filenames: _base_name, extension_name = os.path.splitext(filename.lower()) if extension_name not in self.__options__['process_exts']: continue fullpath = os.path.normpath('%s/%s' % (dirpath, filename)) self.__searchMark(fullpath) bMarkPassed = True for configure in self.__options__['mark_configure']: if str(int(configure['id'])) not in self.mark_dict: Message.ShowError('无法定位代码注入点: {index} : {constant} : {desc}'.format( index = str(int(configure['id'])), constant = str(configure['id']), desc = configure['desc'] )) bMarkPassed = False return bMarkPassed
def clean(export_file): ''' 执行一些清理工作 ''' Message.ShowStatus('正在进行一些善后清理工作...') if os.path.exists(export_file): os.remove(export_file)
def processFile(self, filename, rules, version): displaypath = filename if self.__lastdir__ == '' else os.path.relpath(filename, os.path.join(self.__lastdir__, '../')) Message.ShowStatus('正在更新 %s 中的版本号到: %s' % (displaypath, version)) for rule in rules: usever = self.versionFormat(version, rule['vmode']) replto = rule['replto'] % usever Common.match_file_resub(filename, rule['regex'], replto)
def process(project_dir, lang='zh-cn', silent=False): try: for v in configures: operate_params = v['operate_params'] operate_params['lang'] = lang operate_params['project_dir'] = project_dir operate_params['silent'] = silent operate = globals()[v['operate']](**operate_params) if 'filepath' in v: for path in v['filepath']: fullpath = os.path.abspath(project_dir + path) operate.execute(fullpath) if 'globpath' in v: for path in v['globpath']: files = glob.glob(os.path.abspath(project_dir + path), recursive=True) for x in files: relpath = os.path.relpath(x, project_dir).replace( '\\', '/') is_exclude = False if 'globexclude' in v: for e in v['globexclude']: if e in relpath: is_exclude = True break if not is_exclude: operate.execute(x) except Exception as _err: Message.ShowError(str(_err)) Common.exit_with_pause(-1)
def compile_renewal(version): ''' 编译复兴后版本 ''' time.sleep(3) print('') define_options = {} if os.getenv("DEFINE_CRASHRPT_APPID"): define_options["CRASHRPT_APPID"] = '_CT(\\"%s\\")' % os.getenv( "DEFINE_CRASHRPT_APPID") if os.getenv("DEFINE_CRASHRPT_PUBLICKEY"): define_options["CRASHRPT_PUBLICKEY"] = '_CT(\\"%s\\")' % os.getenv( "DEFINE_CRASHRPT_PUBLICKEY") define_options["GIT_BRANCH"] = '\\"%s\\"' % Common.get_pandas_branch( project_slndir) define_options["GIT_HASH"] = '\\"%s\\"' % Common.get_pandas_hash( project_slndir) define_values = define_builder(define_options) if not compile_sub(define_values, '复兴后', version): Message.ShowError('编译复兴前版本时发生了一些错误, 请检查...') Common.exit_with_pause(-1) print('')
def cmd_execute(cmds, cwd, prefix=None, logfile=None): ''' 在指定的 cwd 工作目录下执行一系列 cmd 指令 并将执行的结果记录带 logfile 文件中 ''' if platform.system() != 'Windows': print('被调用的 cmd_execute 函数只能在 Windows 环境上运行') exit_with_pause(-1) if logfile is not None: os.makedirs(os.path.dirname(logfile), exist_ok=True) logger = open(logfile, 'w', encoding='utf8') cmdline = ' && '.join(cmds) cmdProc = subprocess.Popen(r'cmd /c "%s"' % cmdline, stdout=subprocess.PIPE, universal_newlines=True, cwd=os.path.abspath(cwd)) while True: stdout_data = cmdProc.stdout.readline() if not stdout_data and cmdProc.poll() is not None: break if stdout_data: Message.ShowInfo('%s%s' % ('%s: ' % prefix if prefix is not None else '', stdout_data.strip())) if stdout_data and logfile is not None: logger.write(stdout_data) logger.flush() time.sleep(0.01) if logfile is not None: logger.close() return cmdProc.returncode
def compile_renewal(version): ''' 编译复兴后版本 ''' time.sleep(3) print('') define_options = {} if os.getenv("DEFINE_CRASHRPT_APPID"): define_options["CRASHRPT_APPID"] = '\\"%s\\"' % os.getenv( "DEFINE_CRASHRPT_APPID") if os.getenv("DEFINE_CRASHRPT_PUBLICKEY"): define_options["CRASHRPT_PUBLICKEY"] = '\\"%s\\"' % os.getenv( "DEFINE_CRASHRPT_PUBLICKEY") define_options["GIT_BRANCH"] = '\\"%s\\"' % Common.get_pandas_branch( project_slndir) define_options["GIT_HASH"] = '\\"%s\\"' % Common.get_pandas_hash( project_slndir) define_values = define_builder(define_options) if not compile_sub(define_values, '复兴后', version): Message.ShowError('编译复兴前版本时发生了一些错误, 请检查...') Common.exit_with_pause(-1) # 将之前 compile_prere 中临时重命名的复兴前产物全部改回正常的文件名 if 'pre' in os.getenv('DEFINE_COMPILE_MODE').split(','): move_product_files('-pre-t', '-pre') print('')
def compile_prere(version): ''' 编译复兴前版本 ''' time.sleep(3) print('') define_options = { "PRERE": "" } if os.getenv("DEFINE_CRASHRPT_APPID"): define_options["CRASHRPT_APPID"] = '\\"%s\\"' % os.getenv("DEFINE_CRASHRPT_APPID") if os.getenv("DEFINE_CRASHRPT_PUBLICKEY"): define_options["CRASHRPT_PUBLICKEY"] = '\\"%s\\"' % os.getenv("DEFINE_CRASHRPT_PUBLICKEY") define_options["GIT_BRANCH"] = '\\"%s\\"' % Common.get_pandas_branch(project_slndir) define_options["GIT_HASH"] = '\\"%s\\"' % Common.get_pandas_hash(project_slndir) define_values = define_builder(define_options) if not compile_sub(define_values, '复兴前', version): Message.ShowError('编译复兴前版本时发生了一些错误, 请检查...') Common.exit_with_pause(-1) # 将复兴前版本的编译产物重命名一下, 避免编译复兴后版本时被覆盖 # 因 ab7a827 的修改每次清理工程时, 也会同时清理复兴前的编译产物, 所以这里需要临时重命名 shutil.move(slndir('login-server.exe'), slndir('login-server-pre-t.exe')) shutil.move(slndir('login-server.pdb'), slndir('login-server-pre-t.pdb')) shutil.move(slndir('char-server.exe'), slndir('char-server-pre-t.exe')) shutil.move(slndir('char-server.pdb'), slndir('char-server-pre-t.pdb')) shutil.move(slndir('map-server.exe'), slndir('map-server-pre-t.exe')) shutil.move(slndir('map-server.pdb'), slndir('map-server-pre-t.pdb')) print('')
def compile_renewal(version): ''' 编译复兴后版本 ''' time.sleep(3) print('') define_options = {} if os.getenv("DEFINE_CRASHRPT_APPID"): define_options["CRASHRPT_APPID"] = '\\"%s\\"' % os.getenv("DEFINE_CRASHRPT_APPID") if os.getenv("DEFINE_CRASHRPT_PUBLICKEY"): define_options["CRASHRPT_PUBLICKEY"] = '\\"%s\\"' % os.getenv("DEFINE_CRASHRPT_PUBLICKEY") define_options["GIT_BRANCH"] = '\\"%s\\"' % Common.get_pandas_branch(project_slndir) define_options["GIT_HASH"] = '\\"%s\\"' % Common.get_pandas_hash(project_slndir) define_values = define_builder(define_options) if not compile_sub(define_values, '复兴后', version): Message.ShowError('编译复兴前版本时发生了一些错误, 请检查...') Common.exit_with_pause(-1) # 将之前 compile_prere 中临时重命名的复兴前产物全部改回正常的文件名 shutil.move(slndir('login-server-pre-t.exe'), slndir('login-server-pre.exe')) shutil.move(slndir('login-server-pre-t.pdb'), slndir('login-server-pre.pdb')) shutil.move(slndir('char-server-pre-t.exe'), slndir('char-server-pre.exe')) shutil.move(slndir('char-server-pre-t.pdb'), slndir('char-server-pre.pdb')) shutil.move(slndir('map-server-pre-t.exe'), slndir('map-server-pre.exe')) shutil.move(slndir('map-server-pre-t.pdb'), slndir('map-server-pre.pdb')) print('')
def __encoding_check(self, text, encoding, line): try: for i, element in enumerate(text): element.encode(encoding) except Exception as _err: Message.ShowError( '%s 第 %-5d 行中的 "%s" 字符不存在于 "%s" 编码中, 此错误必须被消除' % (os.path.relpath(self.__filename), line, element, encoding))
def print(self): for _mark_index, mark_value in self.mark_dict.items(): Message.ShowDebug('代码注入点 {index} : {constant} 位于文件 {filepath} : {line} 行.'.format( index = '%-2s' % mark_value['index'], constant = '%-40s' % str(self.__options__['mark_enum'](mark_value['index'])), filepath = mark_value['filepath'], line = mark_value['line'] ))
def process_sub(export_file, renewal, langinfo): print('') # 确认当前的版本号 version = Common.get_pandas_ver(os.path.abspath(project_slndir), 'v') Message.ShowStatus('正在准备生成 {model} - {lang} 的打包目录...'.format( model = '复兴后(RE)' if renewal else '复兴前(PRE)', lang = langinfo['name'] )) # 构建解压的打包目录 packagedir = '../Release/Pandas/{version}/Pandas_{version}_{timestamp}_{model}_{lang}'.format( version = version, model = 'RE' if renewal else 'PRE', timestamp = Common.timefmt(True), lang = langinfo['dirname'] ) # 获取压缩文件的保存路径 zipfilename = os.path.abspath(project_slndir + packagedir) + '.zip' # 获取打包的绝对路径 packagedir = os.path.abspath(project_slndir + packagedir) + os.path.sep # 确保目标文件夹存在 os.makedirs(os.path.dirname(packagedir), exist_ok = True) # 若之前目录已经存在, 先删掉 if os.path.exists(packagedir) and os.path.isdir(packagedir): shutil.rmtree(packagedir) # 将 zip 文件解压到指定的目录中去 Message.ShowStatus('正在解压归档文件到: %s' % os.path.relpath( packagedir, os.path.abspath(os.path.join(packagedir, r'../../')) )) if not zip_unpack(export_file, packagedir): clean(export_file) Message.ShowError('很抱歉, 解压归档文件失败, 程序终止.') Common.exit_with_pause(-1) # 进行文本的翻译工作 trans.process(packagedir, langinfo['trans'], True) # 进行后期处理 Message.ShowStatus('正在对打包源目录进行后期处理...') if renewal: arrange_renewal(packagedir) else: arrange_pre_renewal(packagedir) Message.ShowStatus('后期处理完毕, 即将把打包源压缩成 ZIP 文件...') # 执行打包操作 if not zip_pack(packagedir, zipfilename): clean(export_file) Message.ShowError('打包成 zip 文件时失败了, 请联系开发者协助定位问题, 程序终止.') Common.exit_with_pause(-1) Message.ShowStatus('已成功构建 {model} - {lang} 的压缩文件.'.format( model = '复兴后(RE)' if renewal else '复兴前(PRE)', lang = langinfo['name'] ))
def make_commit(): try: repo = git.Repo(project_symstoredir) if not repo.is_dirty() and not repo.untracked_files: Message.ShowWarning('工作区很干净, 没有任何变化文件, 无需提交.') return False repo.git.add('.') repo.git.commit('-m', '归档 Pandas {version} 版本的符号文件和编译产物, 代码源自 PandasWS/Pandas@{githash} 仓库'.format( version = Common.get_pandas_ver(project_slndir, 'v'), githash = Common.get_pandas_hash(project_slndir) )) except git.GitError as _err: Message.ShowWarning('提交失败, 原因: %s' % _err) return False else: return True