该引擎的游戏主要为Apricot及其分社大概2010年之后的游戏。2010年之前的游戏虽然压缩方式一样,但是文件头我看不懂,留待大佬解决。
文件格式为:
arc.a01
arc.a02
arc.a03
...
arc.dat
工具:GARbro
https://github.com/morkt/GARbro双击arc.dat,解包script文件夹得到脚本文件。
工具:SExtractor
https://github.com/satan53x/SExtractor/tree/main
1、导出
填入正则表达式:09_search=^\s<MenuAdd \d (?P<msg>.+)>$10_search=^<Name (?P<name>.+)>$11_search=^<Text \d{9} (?P<unfinish>.+)>$12_search=^<Text \d{9} (?P<unfinish>.+)13_search=^<Text (?P<unfinish>.+)>$14_search=^<Text (?P<unfinish>.+)16_skip=^//|^[\x20-\x7E\s]+$|//|^;|^\s;|^$|^<|^\t.?<17_search=(?P<unfinish>.+)>$18_search=(?P<unfinish>.+)structure=paragraph
填入正则表达式:
09_search=^\s<MenuAdd \d (?P<msg>.+)>$10_search=^<Name (?P<name>.+)>$11_search=^<Text \d{9} (?P<unfinish>.+)>$12_search=^<Text \d{9} (?P<unfinish>.+)13_search=^<Text (?P<unfinish>.+)>$14_search=^<Text (?P<unfinish>.+)16_skip=^//|^[\x20-\x7E\s]+$|//|^;|^\s;|^$|^<|^\t.?<17_search=(?P<unfinish>.+)>$18_search=(?P<unfinish>.+)structure=paragraph
09_search=^\s<MenuAdd \d (?P<msg>.+)>$
10_search=^<Name (?P<name>.+)>$
11_search=^<Text \d{9} (?P<unfinish>.+)>$
12_search=^<Text \d{9} (?P<unfinish>.+)
13_search=^<Text (?P<unfinish>.+)>$
14_search=^<Text (?P<unfinish>.+)
16_skip=^//|^[\x20-\x7E\s]+$|//|^;|^\s;|^$|^<|^\t.?<
17_search=(?P<unfinish>.+)>$
18_search=(?P<unfinish>.+)
structure=paragraph
选择BIN,导出格式选择json[{name,messageRN}],如图:按下【提取/写入】按钮导出文本。
选择BIN,导出格式选择json[{name,messageRN}],如图:
按下【提取/写入】按钮导出文本。
2、翻译
生成的all.orig.json可以直接用GalTransl翻译;如果要使用AiNiee翻译按照all.orig.json的key值导出文本就行,然后复制文本内容到xlsx表格文件即可,使用T++模式翻译。(建议将人名和对话放到同一单元格中,如:周治「はい。今日の夕飯をどっかで、って。\r\n 俺、今はあんまり金もないんで」)该引擎不支持自动换行,要根据每行最大字符数插入换行符。该引擎支持半角字符,不需要转换成全角字符。
生成的all.orig.json可以直接用GalTransl翻译;
如果要使用AiNiee翻译按照all.orig.json的key值导出文本就行,然后复制文本内容到xlsx表格文件即可,使用T++模式翻译。(建议将人名和对话放到同一单元格中,如:周治「はい。今日の夕飯をどっかで、って。\r\n 俺、今はあんまり金もないんで」)
该引擎不支持自动换行,要根据每行最大字符数插入换行符。
该引擎支持半角字符,不需要转换成全角字符。
3、导入
在设置页选择生成UniversalInjector,如图:选择BIN,导出格式选择json{origRN:origRN},如图:使用此格式导入需要先将翻译转成词典形式,如:{ "女の子": "女孩子", "「日文」": "「中文」", "周治": "周治", "「日文」": "「中文」"}将字典文件命名为transDic.json,放入ctrl文件夹中。之后按下【提取/写入】按钮导入翻译。在这一步可能会遇到报错,说是cp932不包含某字符。修复方法有两个,一个是用其他字符替换它;另一个是用https://github.com/satan53x/SExtractor/tree/main/tools/Font中的font_CN_JP.py,在字体中增加该字符。
在设置页选择生成UniversalInjector,如图:
选择BIN,导出格式选择json{origRN:origRN},如图:
使用此格式导入需要先将翻译转成词典形式,如:
{
"女の子": "女孩子",
"「日文」": "「中文」",
"周治": "周治",
"「日文」": "「中文」"
}
将字典文件命名为transDic.json,放入ctrl文件夹中。
之后按下【提取/写入】按钮导入翻译。
在这一步可能会遇到报错,说是cp932不包含某字符。修复方法有两个,一个是用其他字符替换它;另一个是用https://github.com/satan53x/SExtractor/tree/main/tools/Font中的font_CN_JP.py,在字体中增加该字符。
以下是根据GARbro的解包代码写的封包:
import osimport structimport zlib def walk(adr): mylist=[] for root,dirs,files in os.walk(adr): for name in files: adrlist=os.path.join(root, name) relpath = os.path.relpath(adrlist, adr) mylist.append(relpath) return mylist def Apricot_pack(path='input'): new_pack = bytearray() new_index_data = bytearray() new_content_data = bytearray() count = 0 for file in walk(path): print(file) src = open(f'{path}/{file}', 'rb') unpacked_data = src.read() src.close() file_name = b''.join(struct.pack('<H', ord(c)) for c in file) entry_length = len(file_name)+528 is_deleted = 0 if file[-4:] == '.txt': is_packed = 1 elif file[-4:] == '.bmp': is_packed = 1 else: # .png .ogg .xml 不需要压缩 is_packed = 0 if is_packed == 1: packed_data = zlib.compress(unpacked_data) #print('compress: ',file) else: packed_data = unpacked_data new_index_data += int.to_bytes(entry_length, 4, byteorder='little') # entry_length new_index_data += int.to_bytes(is_deleted, 4, byteorder='little') # is_deleted new_index_data += int.to_bytes(len(new_content_data), 8, byteorder='little') # offset new_index_data += int.to_bytes(is_packed, 4, byteorder='little') # is_packed new_index_data += int.to_bytes(len(packed_data), 4, byteorder='little') # packed_data Size new_index_data += int.to_bytes(len(unpacked_data), 4, byteorder='little') # unpacked_data Size new_index_data.extend(b'\x00'*500) new_index_data += file_name new_content_data += packed_data count += 1 new_z_index_data = zlib.compress(new_index_data) new_pack += b'\x4D\x50\x46\x32' # MPF2 new_pack += b'\x00\x01\x00\x00' new_pack += int.to_bytes(len(new_z_index_data), 4, byteorder='little') # packed file index_length new_pack += int.to_bytes(len(new_index_data), 4, byteorder='little') # unpacked file index_length new_pack += int.to_bytes(len(new_z_index_data)+32, 4, byteorder='little') # file data_offset 原封包是+36 new_pack += b'\x00\x00\x00\x00' new_pack += b'\x01\x00\x00\x00' new_pack += b'\x01\x00\x00\x00' new_pack += new_z_index_data new_pack += new_content_data pack_name = 'patch0.dat' dst = open(pack_name, 'wb') dst.write(new_pack) dst.close() print(pack_name, ':\tpack done') Apricot_pack()
import os
import struct
import zlib
def walk(adr):
mylist=[]
for root,dirs,files in os.walk(adr):
for name in files:
adrlist=os.path.join(root, name)
relpath = os.path.relpath(adrlist, adr)
mylist.append(relpath)
return mylist
def Apricot_pack(path='input'):
new_pack = bytearray()
new_index_data = bytearray()
new_content_data = bytearray()
count = 0
for file in walk(path):
print(file)
src = open(f'{path}/{file}', 'rb')
unpacked_data = src.read()
src.close()
file_name = b''.join(struct.pack('<H', ord(c)) for c in file)
entry_length = len(file_name)+528
is_deleted = 0
if file[-4:] == '.txt':
is_packed = 1
elif file[-4:] == '.bmp':
else: # .png .ogg .xml 不需要压缩
is_packed = 0
if is_packed == 1:
packed_data = zlib.compress(unpacked_data)
#print('compress: ',file)
else:
packed_data = unpacked_data
new_index_data += int.to_bytes(entry_length, 4, byteorder='little') # entry_length
new_index_data += int.to_bytes(is_deleted, 4, byteorder='little') # is_deleted
new_index_data += int.to_bytes(len(new_content_data), 8, byteorder='little') # offset
new_index_data += int.to_bytes(is_packed, 4, byteorder='little') # is_packed
new_index_data += int.to_bytes(len(packed_data), 4, byteorder='little') # packed_data Size
new_index_data += int.to_bytes(len(unpacked_data), 4, byteorder='little') # unpacked_data Size
new_index_data.extend(b'\x00'*500)
new_index_data += file_name
new_content_data += packed_data
count += 1
new_z_index_data = zlib.compress(new_index_data)
new_pack += b'\x4D\x50\x46\x32' # MPF2
new_pack += b'\x00\x01\x00\x00'
new_pack += int.to_bytes(len(new_z_index_data), 4, byteorder='little') # packed file index_length
new_pack += int.to_bytes(len(new_index_data), 4, byteorder='little') # unpacked file index_length
new_pack += int.to_bytes(len(new_z_index_data)+32, 4, byteorder='little') # file data_offset 原封包是+36
new_pack += b'\x00\x00\x00\x00'
new_pack += b'\x01\x00\x00\x00'
new_pack += new_z_index_data
new_pack += new_content_data
pack_name = 'patch0.dat'
dst = open(pack_name, 'wb')
dst.write(new_pack)
dst.close()
print(pack_name, ':\tpack done')
Apricot_pack()
将要封包的文件夹(如script)放入input文件夹中,执行该程序。生成的patch0.dat属于增量补丁,后续如有新增内容可将新补丁命名为patch1.dat、patch2.dat等等(应该,我没试过)。
最后安装https://github.com/satan53x/SExtractor/tree/main/tools/Font中的WenQuanYi_msgothic.otf字体,将patch0.dat复制到游戏根目录下即可,转日区启动原版exe。