本案例,通过分析《AI少女》游戏的PNG图片捏人存档,解析了存档的数据格式,编写了便于修改数据的python脚本。 脚本过于BUG,可随意替换经验/性格/属性等,仅供学习研究数据结构及代码学习之用。
脚本内参考了多篇网页,无法一一列举,欢迎原作者投诉,以便添加引用文献。
脚本文件可以见 链接:https://pan.baidu.com/s/1DPxVOVoSuP5Nhuf7qjSEsA
提取码:jlz0
玩家或读者,需要复制下面的代码为一个python脚本文件,替换存档路径信息,运行即可生成原图片的作弊版,不伤原始文件,如果因为使用此代码而无法打开游戏,可以删掉作弊图片。
玩家也可修改自定义函数 do_it_yourself 变更属性。
#coding=gbk
#python 2.7
import struct,uuid,os,binascii
import collections
import pprint
debug = False
'''
仅支持修改:
0. 文件头的uuid 自动修改uuid,以免重复
1. Parameter 89 9个元素字典 (可以无序)
version 0.0.1
sex 00男 01女
把女的图片改成男的,放到male文件夹,结果女的特征不见了,头发还在。变成男的了
fullname 全名 字符串 支持UTF8编码
personality 性格 整数值 取值范围:00~05
编号 类型 对声音的影响 性格特点
00 机械的无感情 无感 感情淡薄,经常机械地行动。重视效率的类型。
01 豁达温和 温柔 如果是为了主人公的话就什么都愿做的温柔大姐姐类型
02 意志坚定的自信家 职场女强人 不借助他人的力量,自己什么都能干的自信满满的类型。
03 任性自私 任性女子 以蔑视的目光命令却无法独立生存的类型
04 无精打采 蔫不拉几 无精打采怕麻烦的人。坐享其成的类型。
05 积极开朗 积极活泼 不去想讨厌的事情,永远活力满满积极进取的类型。
birthMonth 0A 整数
birthDay 整数
voiceRate 语速 浮点数 取值范围 -1.0~2.0
界面显示 实际取值
-10 -1.0
-9 -0.09
-5 -0.05
0 0.0
5 0.05
100 1.0
150 1.5
200 2.0
hsWish 愿望 18个选0~3个,数组
编号 类型
00 温柔
01 快乐
02 小恶魔
03 刺激
04 礼貌
05 禁忌
06 稳重
07 拜金
08 知性
09 信念
0A 忠诚
0B 持久
0C 可怜
0D 无邪气
0E 不完全
0F 理解力
10 判断力
11 治愈
futanari 是否双性人 布尔值
C2 False
C3 True
2. GameInfo 字典的(可以无序)
DE0012 18个元素的字典
1 version 0.0.0
2 gameRegistration 布尔值 C3 True 载入时左上角有五角星 C2 False 没有
#如果此项为False,那么视为新开局,moodBound/flavorState/totalFlavor/desireDefVal/desireBuffVal会被清空
3 tempBound 82 2个元素的字典 体温
体温为某个值 0.~100.,此处定义了三个区间的分界点,心情落在哪个区间则判定心情 蓝绿黄
lower CA41A00000 20.0 天天温度正常: CA3F800000 1.0
upper CA42A00000 80.0 天天温度正常: CA42C60000 99.0
4 moodBound 82 2个元素的字典 心情
心情为某个值 0~100,此处定义了三个区间的分界点,心情落在哪个区间则判定心情 蓝绿黄
lower CA41900000 35.0 天天好心情: CA3F800000 1.0
upper CA42600000 71.0 天天好心情: CA40000000 2.0
5 flavorState
88 8个元素的字典 整数值 可以7位数,但只能显示6位 例如:CE000F423F对应999999 CE0001869F对应99999
键 值 说明
00 女子力 洗澡、换衣服的频率,以及随时摆Pose的机会,魅力高的妹子经常会做一些好玩的小动作
01 信任度 这个影响到妹子会不会接收你的建议,比如吃饭睡觉
02 人性 指的是AI会不会自己睡觉,会不会自己吃饭喝水
03 本能 是指该AI运用各种生活技能的熟练度,包括料理等级和收集材料的等级
04 变态 高了容易发神经,比如裸奔。
05 警惕 警戒影响偷袭成功率以及偷袭后后的妹子心情
06 黑暗 (注意不按显示顺序!) 据说不要超过200,不然晕倒。为0的话,有些工具用不了。
07 社交 是指该AI和其他AI的交流。
6 totalFlavor 整数 总属性? 可以不等于各属性之和
CD080D 2061 示例
CDFFFF 65535
7 desireDefVal 某种内部参数,可能是出现各种行为的概率?
DE0010 16个元素的字典 16个浮点数 0~51
00 CA00000000
01 CA00000000
02 CA3F800000 CA424C0000
03 CA40000000 CA40800000
04 CA00000000
05 CA00000000
06 CA00000000
07 CA41400000
08 CA00000000
09 CA00000000
0A CA40800000 CA41400000
0B CA41F00000
0C CA40800000
0D CA00000000
0E CA40400000
0F CA41300000
8 desireBuffVal 日常行为?某种内部参数 0~18
DE0010 16个元素的字典 16个CA值
00 CA00000000
01 CA00000000
02 CA00000000
03 CA41300000
04 CA00000000
05 CA00000000
06 CA00000000
07 CA00000000
08 CA41A00000
09 CA00000000
0A CA41000000
0B CA3F800000
0C CA41A00000
0D CA00000000
0E CA40E00000
0F CA40C00000
9 phase 整数或索引号 取值范围 00~03 00 一颗心 03 四颗心
03
10 normalSkill 普通技能
85 5个元素字典,推荐技能
00 04 巫女
01 09 贤妻良母
02 0E 不知疲倦
03 18 淫魔诅咒
04 22 心灵相通
46个技能表: FF 无
00 厨师 ★★ 经常去做饭
01 泡澡 ★★ 经常泡澡
02 爱好动物 ★★ 与动物互动更容易
03 睡美人 ★★ 睡得好
04 巫女 ★★★ 防止人体潮湿时体温下降,并使黑暗渡难以增加
05 学龄前 ★★ 在行动中,成长很少会比平时快
06 轻松 ★★ 心情轻松很容易
07 礼尚往来 ★★ 更多机会送礼物
08 亲爱 ★★ 随之而来的成功率有所提高
09 贤妻良母 ★★★ 每次采取行动时,信任增加,最大动力增加10%
0A 垂钓爱好者 ★★ 喜欢钓鱼
0B 易受伤 ★ 有很多受伤案例
0C 农民 ★★ 更容易种田,作物加快生长
0D 物质少女 ★★ 更容易乞求一些东西
0E 不知疲倦 ★★★ 不容易劳累,搜索起来更容易
0F 暴食 ★★ 更容易吃饭
10 天气之子 ★★ 下雨也要出去
11 野性 ★★ 使用家具的次数变少
12 手工收集 ★★ 易于手工收集
13 兽王 ★★★ 容易抓猫
14 污 ★★ 顽皮的感觉可能会有所上升
15 爱玩 ★★ 更多的女孩去玩
16 性欲旺盛 ★★ 一个女孩经常来做爱
17 好奇心 ★★ 对调皮的对话表现出兴趣更容易
18 淫魔诅咒 ★★★ 顽皮情绪的增加速度不会一直保持疲倦,反而会曾家,并且将邀请更多的人进行蚀刻(多人?)
19 神经质 ★ 情绪恶化
1A 可疑 ★ 顽皮谈话失败后,降低心情
1B 防备 ★★ 当提供建议时,很难听从
1C 爱惜身体 ★★ 当疲劳积累时,更容易先休息
1D 忍耐 ★★★ 我很饿
1E 寂寞 ★★ 与英雄交谈变得更加容易
1F 更好说话 ★★ 与其他女孩聊天可以更轻松地相处
20 好说话 ★ 与其他女孩聊天变得更加容易
21 行动派 ★★ 最大动力提高10%
22 心灵相通 ★★★ 打电话时,即使英雄很远,也可以响应
23 懒惰 ★ 这样可以轻松地休息一下
24 洁癖 ★ 如果不洗澡,心情会变差
25 不幸 ★★ 较容易拾取一级物品
26 易感冒 ★★ 容易感冒
27 腹黑 ★★★ 与其他人交谈,其他人积累疲劳
28 勇气 ★ 较容易移动到较远的地方
29 收藏 ★★ 拾起物品更容易
2A 地形掌握 ★ 更容易挖矿
2B 毅力坚决 ★★ 难以成为异常状态的高温状态
2C 欧皇 ★★★ 更容易拿到三级物品
2D 体温超控 ★ 饮料的温度控制效果减半
2E 好运 ★★ 更容易拿到二级物品
2F 挖掘 ★★ 用铲子收集更容易
30 捕虫 ★★ 更容易用虫网
31 猎手 ★★★ 获取物品时变得更容易掌握技能
11 hSkill H技能
85 5个元素字典,推荐技能
00 03 敏感
01 06 下体敏感
02 0F 屁股
03 14 受虐
04 16 性欲
18个技能表: FF 无
00 胸部敏感 08 外射 10 电臀
01 名器 09 M 11 性厌恶
02 浪漫主义 0A S 12 侍奉
03 敏感 0B 欲火 13 女同2
04 服务爱好 0C 女同1 14 受虐
05 中出 0D 痴女 15 不满
06 下体敏感 0E 小便处 16 性欲
07 喜好下体 0F 屁股敏感 17 寂寞
12 favoritePlace 对话时喜欢的地点?索引类型?
00
13 lifestyle 生活习惯6种 索引类型 单个值
00 ゲッター 刺客 导致贪婪的贪婪类型。对食物和物品的需求增加。
01 ベイビー 婴儿 总是喜欢的类型。当他感到孤独时,他会给我礼物或礼物。
02 ドライバー 螺丝刀 一种类型,其自身在不依赖他人的情况下起作用。容易收集食物和物品。
03 コントローラー 控制器 一种不吸引人的类型。我经常休息或睡觉。
04 シーカー 纸箱 这种类型立即引起关注,但很累。搬到另一个地方很容易,但是累了就开始做另一件事。
05 アームチェア 扶手椅 永远是我的步伐类型。我喜欢享受我的世界,玩耍和洗个澡。
14 morality 整数 道德? 此值高了是不是不会裸奔呢?
31 似乎单字节
15 motivation 整数 干劲?活动性? 抓鱼等的参数?
F6 似乎是有符号整数? -10
16 immoral 整数 淫荡程度 自嗨?
0F 15
17 isHAddTaii0 特殊H有关参数? 布尔型
C3
18 isHAddTaii1 特殊H有关参数? 布尔型
C3
'''
'''
总共3个大数据区
大数据区由长度标记+数据组成
小数据区没有长度标记
第1个数据区的长度标记为4字节,小端在前
第2个数据区的长度标记为4字节,小端在前
第3个数据区的长度标记为8字节,小端在前
第3个数据区,细分为 5个大数据区 + 3个小数据区 + 1个可选小数据区
第1~3个大数据区 为 Custom 脸型/体型/发型
第4~5个大数据区 为 Coordinate 服装/饰品
3个小数据区分别为 Parameter GameInfo Status
可选区为 KKEx
lstInfo 的信息记录了第3个数据区的起始位置及数据长度,(第3个数据区的总长度标记,不算在内)
'''
#待修改的属性
def do_it_yourself():
ParameterDict = {
'sex' : 1, #女
'fullname' : '山村野女',
'personality': 2, #0~5 意志坚定的自信家
'voiceRate' : 50, #-10~200
'hsWish' : [0,4,10],#温柔 礼貌 忠诚
'futanari' : False, #单性
}
GameInfo = {
'gameRegistration':True, #如果此项为False,那么视为新开局,moodBound/flavorState/totalFlavor/desireDefVal/desireBuffVal会被清空
'tempBound':{ #98%的范围体温正常
'lower':1.0 ,
'upper':99.0},
'moodBound':{ #98%的范围开心
'lower':2.0 ,
'upper':8.0},
'flavorState':{
0:99999, #力量
1:99999, #信任
2:99999, #人性
3:99999, #本能
4: 0, #变态
5: 0, #警惕
6: 0, #黑暗
7:99999, #社交
},
'desireDefVal':{#某种内部参数,可能是出现各种行为的概率?
0: 88.0,
1: 88.0,
2: 88.0,
3: 88.0,
4: 88.0,
5: 88.0,
6: 88.0,
7: 88.0,
8: 88.0,
9: 88.0,
10: 88.0,
11: 88.0,
12: 88.0,
13: 88.0,
14: 88.0,
15: 88.0,
},
'desireDefVal':{#某种内部参数,可能是出现各种行为的缓冲概率?
0: 88.0,
1: 88.0,
2: 88.0,
3: 88.0,
4: 88.0,
5: 88.0,
6: 88.0,
7: 88.0,
8: 88.0,
9: 88.0,
10: 88.0,
11: 88.0,
12: 88.0,
13: 88.0,
14: 88.0,
15: 88.0,
},
'phase': 3, #四颗心
'normalSkill':{
0: 4, #巫女
1: 9, #贤妻良母
2:14, #不知疲倦
3:24, #淫魔诅咒
4:34, #心灵相通
},
'hSkill':{
0: 3, #敏感
1: 6, #下体敏感
2:15, #屁股
3:20, #受虐
4:22, #性欲
},
'favoritePlace':0,
'lifestyle':2, #螺丝刀
'morality':100,
'motivation':100,
'immoral':10,
'isHAddTaii0':True,
'isHAddTaii1':True,
}
return ParameterDict,GameInfo
#############################################################
def str2bin(instr): #'high'-->'\x....'
return binascii.unhexlify(binascii.hexlify(instr))
def hex2bin(instr): #'64' --> '\x64'
return binascii.unhexlify(instr)
def bin2str(instr): #'\x64'--'64'
return binascii.b2a_hex(instr)
def voiceRate_int2float(val): #-10~200 整数转为浮点数 仅用于计算 voiceRate
assert type(val) is int
assert -10 <= val <= 200
if val == -10:
return -1.0
else:
return val*0.01
#########
def pack_Parameter(origin,usrData):
for key,value in usrData.items():
if key == 'fullname':
origin[key] = value.decode('gbk')
elif key == 'voiceRate':
origin[key] = voiceRate_int2float(value)
else:
origin[key] = value
dic = origin
if debug: print origin
raw = ['\x89']
for k,v in dic.items():
raw.append(pack_a_data(k))
if k == 'hsWish': #整数列表
raw.append(struct.pack('b',-112+len(v)))
for i in v: raw.append(pack_a_data(i))
else:
raw.append(pack_a_data(v))
if debug: print raw
return ''.join(raw)
def pack_GameInfo(origin,usrData):
for key,value in usrData.items():
if type(value) is dict: #tempBound moodBound flavorState desireDefVal desireBuffVal normalSkill hSkill
for k,v in value.items():
origin[key][k] = v
else:
origin[key] = value
t = 0
for v in origin['flavorState'].values(): t+=v
# if t>1000:
# t = 1000
origin['totalFlavor'] = t
dic = origin
raw = ['\xDE\x00\x12']
for key,value in origin.items():
raw.append(pack_a_data(key))
if isinstance(value,collections.OrderedDict):
raw.append(pack_a_basic_dict(value))
else:
raw.append(pack_a_data(value))
if debug: print raw
return ''.join(raw)
def pack_lstInfo_block(lstInfo):
raw = ['\x81']
raw.append(pack_a_data('lstInfo'))
raw.append(pack_a_data(-112 + len(lstInfo)))
for dic in lstInfo:
raw.append(pack_a_basic_dict(dic))
tmp = ''.join(raw)
return pack_length_tag_4(len(tmp)) + tmp
def block0_png(fp):
raw = []
header = fp.read(8) #文件格式
raw.append(header)
while 1:
sizeBlock = fp.read(4) #长度
size = struct.unpack('>I',sizeBlock)[0]
dtype = fp.read(4) #类型
data = fp.read(size) #数据
crc32 = fp.read(4) #校验
raw.append(sizeBlock+dtype+data+crc32)
if dtype == 'IEND': break
return ''.join(raw)
def block1_header(fpin): #第一个数据区
#读取原来的数据区,重新生成uuid
rst = []
raw = fpin.read(4)
rst.append(raw)
size = struct.unpack(',raw)[0]
#同时更更换两个 uuid 不识别
# rst.append(fpin.read(size-37*2)) #扣除2个uuid及长度标记
# rst.append('\x24' + str2bin(str(uuid.uuid1())))
# rst.append('\x24' + str2bin(str(uuid.uuid1())))
# fpin.read(37*2)
#更换一个uuid 部分不识别
# rst.append(fpin.read(size-37)) #扣除1个uuid及长度标记
# rst.append('\x24' + str2bin(str(uuid.uuid1())))
# fpin.read(37)
# uuid 可以完全相同! 可以同时识别
rst.append(fpin.read(size))
return ''.join(rst)
def block2_lstInfo_get(fpin): #第二个数据区 数据索引信息 获取数据供后续修改
#由于至改动
raw = fpin.read(4)
if debug:print bin2str(raw)
if debug:print hex(fpin.tell())
size = struct.unpack(',raw)[0]
return size,read_a_data(fpin)
def block3_data_get(fp,lstInfo): #第三个数据区
#第3个数据区的总长度
if debug:print hex(fp.tell())
globalsize = struct.unpack(',fp.read(8))[0]
with file('tmp.ai4','wb') as fpot:
fpot.write(fp.read(globalsize))
with file('tmp.ai4','rb') as fp:
for dic in lstInfo:
fp.seek(dic['pos'])
if dic['name'] == 'Parameter':
Parameter = read_a_data(fp)
elif dic['name'] == 'GameInfo':
GameInfo = read_a_data(fp)
elif dic['name'] == 'KKEx':
raw_KKEx = fp.read(dic['size'])
elif dic['name'] == 'Custom':
raw_Custom = fp.read(dic['size'])
elif dic['name'] == 'Coordinate':
raw_Coordinate = fp.read(dic['size'])
elif dic['name'] == 'Status':
raw_Status = fp.read(dic['size'])
else:
raise TypeError,'unkown data tag'
os.remove('tmp.ai4')
if debug:
with file('tmp.cus','wb') as fp: fp.write(raw_Custom)
with file('tmp.coo','wb') as fp: fp.write(raw_Coordinate)
with file('tmp.sta','wb') as fp: fp.write(raw_Status)
print Parameter
print GameInfo
if len(lstInfo) == 6:
return raw_KKEx, raw_Custom, raw_Coordinate, Parameter, GameInfo, raw_Status
elif len(lstInfo) == 5:
return raw_Custom, raw_Coordinate, Parameter, GameInfo, raw_Status
else:
raise TypeError,'unkown data tag'
def read_a_data(fp,isOrderedDict=True): #从文件读取一个数据 数据包含数据标签及数据
raw = fp.read(1) #数据类型
flag = struct.unpack('b',raw)[0] #有符号整数
# print repr(raw),flag
if flag <=-113:#8F #80~8F 字典 key:value --> (key,value),(key,value)
L = flag - (-128)#80
tmp = collections.OrderedDict() if isOrderedDict else {}
for i in range(L):
key = read_a_data(fp)
val = read_a_data(fp)
tmp[key] = val
return tmp
elif flag <= -97:#9F # 90~9F 数组,可以混合类型 (列表)
L = flag - (-112) #90
return [read_a_data(fp) for i in range(L)]
elif flag <= -65:#BF #A0~BF 字符串
L = flag - (-96)#A0
return fp.read(L) #.decode('utf8')
elif flag == -62: #C2布尔型
return False
elif flag == -61: #C3布尔型
return True
elif flag == -60: #C4 单字节整数数组 C4 □□ 单字节整数数组 □□为长度
L = struct.unpack('B',fp.read(1))[0] #无符号整数标识长度
return [struct.unpack('b',fp.read(1))[0] for i in range(L)] #有符号
elif flag == -54: #CA 单精度浮点数 Big Endian大端在前
return struct.unpack('>f',fp.read(4))[0]
#CB--00
elif flag == -52: #CC □□ 无符号整数 单字节 其后为整数值
return struct.unpack('B',fp.read(1))[0]
elif flag == -51: #CD □□ □□ 整数 双字节 > 大端在前 0~65535
return struct.unpack('>H',fp.read(2))[0]
elif flag == -50: #CE □□ □□ □□ □□ 整数 四字节 大端在前
return struct.unpack('>I',fp.read(4))[0]
elif flag == -39:#D9 D9 □□ 字符串 □□表示长度,无符号,32~255个字节
L = struct.unpack('B',fp.read(1))[0] #无符号整数标识长度
return fp.read(L) #decode
elif flag == -38:#DA DA □□ □□ 字符串 两个字节表示字符串长度,无符号,256~65535个字符 >
L = struct.unpack('>H',fp.read(2))[0] #无符号整数标识长度
return fp.read(L) #decode
elif flag == -36:#DC DC □□ □□ 布尔数组,CA数组 0~65535?, 两个字节表示长度,无符号 >大端在前 布尔数组?
L = struct.unpack('>H',fp.read(2))[0] #无符号整数标识长度
return [read_a_data(fp) for i in range(L)]
elif flag == -34: #DE 长字典 DE □□ □□ 混合数据结构? >? ?~? DE001A DE0012 DE0010 一种映射型数据 00 CA 01 CA 0F CA
L = struct.unpack('>H',fp.read(2))[0] #无符号整数标识长度
tmp = collections.OrderedDict() if isOrderedDict else {}
for i in range(L):
key = read_a_data(fp)
val = read_a_data(fp)
tmp[key] = val
return tmp
elif flag>=-32: #整数值 -32~127
return flag
else:
print struct.pack('b',flag)
return None
def pack_length_tag_4(value): #标识长度的整数,4字节,
#□□ □□ □□ □□
return struct.pack(',value)
def pack_length_tag_8(value): #标识长度的整数,8字节,
#□□ □□ □□ □□ □□ □□ □□ □□
return struct.pack(',value)
def pack_length_tag_1(value): #标识长度的整数,1字节,B 无符号
return struct.pack('B',value)
def pack_a_data(data):#打包一个数据为二进制
#浮点数 CA □□ □□ □□ □□
#布尔值 C2 或 C3
#整数值
#字符串
dtype = type(data)
if dtype is float:
return '\xCA' + struct.pack('>f',data)
elif dtype is bool:
return '\xC3' if data else '\xC2'
elif dtype is int:
if data<-128:
raise RangeError,'not supported integer range: %d' % data
elif data < 128: #-128~127 □□ 有符号整数,单字节标识
return struct.pack('b',data)
elif data<256: #128~255 CC □□ 无符号整数 单字节 其后为整数值
return '\xCC'+struct.pack('B',data)
elif data<65536:#256~65535 CD □□ □□ 无符号整数 双字节 > 大端在前 0~65535
return '\xCD'+struct.pack('>H',data)
else: #>=65536 CE □□ □□ □□ □□ 无符号整数 四字节 大端在前
#更长的整数会出错
return '\xCE'+struct.pack('>I',data)
elif dtype is str:
raw = data
length = len(raw)
if length<= 31: ##A0~BF 字符串
return struct.pack('b',-96+length) + raw
elif length <=255:# D9 □□ 字符串 □□表示长度,无符号,32~255个字节
return '\xD9'+struct.pack('B',length) + raw
else: #256~65535 DA □□ □□ 字符串 两个字节表示字符串长度,无符号,256~65535个字符 >
return '\xDA'+struct.pack('>H',length) + raw
elif dtype is unicode:
raw = data.encode('utf8')
# if 1: print repr(raw)
if debug: print repr(raw)
length = len(raw)
if length<= 31: ##A0~BF 字符串
# print repr(struct.pack('b',-96+length) + raw)
return struct.pack('b',-96+length) + raw
elif length <=255:# D9 □□ 字符串 □□表示长度,无符号,32~255个字节
return '\xD9'+struct.pack('B',length) + raw
else: #256~65535 DA □□ □□ 字符串 两个字节表示字符串长度,无符号,256~65535个字符 >
return '\xDA'+struct.pack('>H',length) + raw
def pack_a_basic_dict(dic): #单层有序字典
raw = []
if len(dic) <=15:
raw.append(pack_a_data(-128+len(dic)))
else: #16~65535
raw.append('\xDE')
raw.append(struct.pack('>H',len(dic)))
for k,v in dic.items():
raw.append(pack_a_data(k))
raw.append(pack_a_data(v))
return ''.join(raw)
def converter(fileName,ends='_cheated.png'):
with file(fileName,'rb') as fp:
block0 = block0_png(fp)
if debug:
with file('tmp.png','wb') as ftmp:ftmp.write(block0)
block1 = block1_header(fp)
if debug:
with file('tmp.blk1','wb') as ftmp:ftmp.write(block1)
block2_size,block2_info = block2_lstInfo_get(fp)
if debug:
print hex(fp.tell())
print block2_size,block2_info
lstInfo = block2_info['lstInfo']
if debug:
print lstInfo
block3_lst = block3_data_get(fp,lstInfo)
usrParam,usrGameInfo = do_it_yourself()
if len(block3_lst) == 6:
isKKEx = True
raw_KKEx, raw_Custom, raw_Coordinate, Parameter, GameInfo, raw_Status = block3_lst
else:
isKKEx = False
raw_Custom, raw_Coordinate, Parameter, GameInfo, raw_Status = block3_lst
raw_Parameter = pack_Parameter(Parameter,usrParam)
raw_GameInfo = pack_GameInfo(GameInfo,usrGameInfo)
len_custom = len(raw_Custom)
len_coordinate = len(raw_Coordinate)
len_parameter = len(raw_Parameter)
len_gameInfo = len(raw_GameInfo)
len_status = len(raw_Status)
if isKKEx: #有序为前提,如果无序则不行!此处为列表,有序,所以没有问题
len_KKEx = len(raw_KKEx)
globalsize = len_custom+len_coordinate+len_parameter+len_gameInfo+len_status+len_KKEx
lstInfo[3]['size'] = len_parameter
lstInfo[3]['pos'] = lstInfo[2]['pos']+lstInfo[2]['size']
lstInfo[4]['size'] = len_gameInfo
lstInfo[4]['pos'] = lstInfo[3]['pos']+lstInfo[3]['size']
lstInfo[5]['size'] = len_status
lstInfo[5]['pos'] = lstInfo[4]['pos']+lstInfo[4]['size']
lstInfo[0]['pos'] = lstInfo[5]['pos']+lstInfo[5]['size']
block3 = pack_length_tag_8(globalsize) + raw_Custom + raw_Coordinate + raw_Parameter+raw_GameInfo+raw_Status+raw_KKEx
else:
globalsize = len_custom+len_coordinate+len_parameter+len_gameInfo+len_status
lstInfo[2]['size'] = len_parameter
lstInfo[2]['pos'] = lstInfo[1]['pos']+lstInfo[1]['size']
lstInfo[3]['size'] = len_gameInfo
lstInfo[3]['pos'] = lstInfo[2]['pos']+lstInfo[2]['size']
lstInfo[4]['size'] = len_status
lstInfo[4]['pos'] = lstInfo[3]['pos']+lstInfo[3]['size']
block3 = pack_length_tag_8(globalsize) + raw_Custom + raw_Coordinate + raw_Parameter+raw_GameInfo+raw_Status
block2 = pack_lstInfo_block(lstInfo)
with file(fileName[:-4]+ends,'wb') as fp:
fp.write(block0)
fp.write(block1)
fp.write(block2)
fp.write(block3)
if __name__ == '__main__':
#此处输入安装路径,可自定义修改do_it_yourself函数
path = r'C:\Game\ai\AI-Syoujyo\UserData\chara\female'
for name in os.listdir(path):
if name.endswith('.png') and not name.endswith('_cheated.png'):
converter(os.path.join(path,name))