Open lometsj opened 2 years ago
import os
import sys
from until import *
test_eex = 'D:/ccz_origin/三国志曹操传简体中文经典版 [水木清华特别版v4带全部动画].ccz.水木年华/test.eex'
magic_eex = b'\x45\x45\x58\x00\x01\x02\x00\x00\x00\x00'
class parser_eex():
def read_until(self, end):
ret = b''
end_len = len(end)
ret = ret + self.f.read(end_len)
if ret == end:
return ret
while True:
# input("read")
# print(ret)
ret = ret + self.f.read(1)
if ret[-end_len:] == end:
return ret
def read_u32(self):
return u32(self.f.read(4))
def read_u16(self):
return u16(self.f.read(2))
def read_magic(self, magic):
# print(magic)
# print(hex(self.f.tell()))
buf = self.f.read(len(magic))
# print(buf)
if buf != magic:
raise RuntimeError('format error expect {} but {}'.format(magic, buf))
def read_bool(self):
bool_msg = self.f.read(2)
if bool_msg == '\x01\x00':
return True
elif bool_msg == '\x00\x00':
return False
def __init__(self, eex_path):
self.event_len = None
self.section_state = None
if not os.access(eex_path, os.F_OK | os.R_OK):
raise RuntimeError("file not exist or permit denied")
self.f = f = open(eex_path, 'rb')
self.read_magic(magic_eex)
self.scene_offset = []
self.scene_offset.append(u32(f.read(4)))
while f.tell() != self.scene_offset[0]:
self.scene_offset.append(u32(f.read(4)))
self.func_cmd = {
0x0: self.cmd_event_end,
0x1: self.cmd_child_event_start,
0x2: self.cmd_inside_msg,
0x3: self.cmd_else,
0x4: self.cmd_ask_test,
0x5: self.cmd_var_test,
0x6: self.cmd_team_battle_set,
0x7: self.cmd_battle_test,
0x8: self.cmd_menu_control,
0x9: self.cmd_sleep,
0xa: self.cmd_init_part_var,
0xb: self.cmd_var_set,
0xc: self.cmd_end_section,
0xd: self.cmd_end_scene,
0xe: self.cmd_battle_failed,
0xf: self.cmd_end_set
}
def get_scene_count(self):
return len(self.scene_offset)
def parse_section(self, idx):
self.section_state = 'test'
print('section {}'.format(idx))
self.f.seek(self.scene_offset[idx])
section_len = self.read_u16()
while True:
cmd_type = self.read_u16()
# print('cmd_type: {} seek: {}'.format(hex(cmd_type), hex(self.f.tell())))
if cmd_type in self.func_cmd.keys():
func = self.func_cmd[cmd_type]
ret = func()
if ret == 'end_event':
break
else:
print('err type: {}'.format(hex(cmd_type)))
print('section end')
pass
def parse_all(self):
for scene_idx in range(self.get_scene_count()):
self.parse_section(scene_idx)
pass
def cmd_event_end(self):
print('enter {}'.format(self.f.tell()))
if self.section_state == 'test':
self.event_len = self.read_u16()
self.section_state = 'event'
elif self.section_state == 'event':
return 'end_event'
print('事件结束')
pass
def cmd_child_event_start(self):
print('子事件设定')
pass
def cmd_inside_msg(self):
self.read_magic(b'\x05\x00')
msg = self.read_until(b'\x00')
print('内部消息 : {}'.format(msg))
def cmd_else(self):
print('else')
def cmd_ask_test(self):
self.read_magic(b'\x26\x00')
bool_msg = self.read_bool()
if bool_msg:
print("询问测试 是")
else:
print("询问测试 否")
def cmd_var_test(self):
self.read_magic(b'\x35\x00')
true_var = []
false_var = []
true_var_count = u16(self.f.read(2))
for i in range(true_var_count):
true_var.append(u16(self.f.read(2)))
self.read_magic(b'\x35\x00')
false_var_count = u16(self.f.read(2))
for i in range(false_var_count):
false_var.append(u16(self.f.read(2)))
print('变量测试 true: {} false: {}'.format(true_var, false_var))
def cmd_team_battle_set(self):
self.read_magic(b'\x2e\x00')
bool_msg = self.read_bool()
self.read_magic(b'\x04\x00')
unit_num = self.read_u32()
enforce_on_unit = []
enforce_out_unit = []
for i in range(5):
self.read_magic(b'\x38\x00')
enforce_on_unit.append(self.read_u16())
for i in range(5):
self.read_magic(b'\x39\x00')
enforce_out_unit.append(self.read_u16())
print('我军出场设定 强制{}人 强制出场: {} 强制不出场: {}'.format(unit_num, enforce_on_unit, enforce_out_unit))
def cmd_battle_test(self):
print('出战测试')
def cmd_menu_control(self):
self.read_magic(b'\x2e\x00')
bool_msg = self.read_bool()
print('菜单控制 {}'.format(bool_msg))
def cmd_sleep(self):
self.read_magic(b'\x04\x00')
sec = 10*self.read_u32()
print('延时 {}秒'.format(sec))
def cmd_init_part_var(self):
print('初始化局部变量')
def cmd_var_set(self):
self.read_magic(b'\x04\x00')
var_idx = self.read_u32()
self.read_magic(b'\x27\x00')
bool_msg = self.read_bool()
print("变量赋值 var_{} = {}".format(var_idx, bool_msg))
def cmd_end_section(self):
print('结束section')
def cmd_end_scene(self):
print('结束scene')
def cmd_battle_failed(self):
print('战斗失败')
def cmd_end_set(self):
self.read_magic(b'\x12\x00')
end_type = self.read_u16()
type_txt = {0: '红线', 1: '黄线', 2:'蓝线'}
print('结局设定 {}'.format(type_txt[end_type]))
if __name__ == '__main__':
p = parser_eex(test_eex)
p.parse_all()
先参考张生的帖子完成0xf以下命令解析
13 00 04 00 xx xx xx xx case序号
12 00 05 00 文字 00 02 00 xx xx(武将序号
11 00 37 00 xx xx(剧本序号 r s 顺序
14 00 05 00 文字 00
15 00 02 00 xx xx yy yy (武将序号 两个 02 00 05 00 文字 00
16 00 05 00 文字 00
17 00 05 00 文字 00
18 00 05 00 文字 00
19 00 05 00 文字 00
1a 00 05 00 文字 00
1b 00 02 00 xx xx 27 00 yy yy (x武将序号 y true/false
1c 00
1d 00
1e 00
1f 00 04 00 xx xx xx xx 04 00 yy yy yy yy (x,y)坐标
20 00 4a 00 xx xx 依次是
00 曹操 普通
01 惊讶
02 愤怒
03 惊喜
04 夏侯惇 蒙眼
05 孔明 邪恶
06 曹丕 称帝
07 夏侯惇 独眼
08 孔明 正常
21 00 04 00 xx xx xx xx 04 00 yy yy yy yy 10 00 zz zz (放火 00 00 回复 01 00 26 00 00 00 26 00 00 00
22 00 1b 00 xx xx(视频序号
23 00 1e 00 xx xx (音频序号 04 00 yy yy yy yy (00 无限循环 ff停止 其他次数
Se_e_00----蝉鸣、鸟叫
Se_e_01----蝉鸣
Se_e_02----夜晚蝈蝈叫、狼叫
Se_e_03----夜晚风呼号、狼吼
Se_e_04----电闪雷鸣
Se_e_05----树林鸟声瞅瞅
Se_e_06----孩子叫、马车声
Se_e_07----惨烈的人叫马嘶
Se_e_08----河水流动,浪涛拍岸
Se_m_00----纵火
Se_m_01----烈燃
Se_m_02----磨刀声
Se_m_03----狂风刮过
Se_m_04----消失
Se_m_05----狂风刮过
Se_m_06----落石
Se_m_07----居石落下
Se_m_08----放炮
Se_m_09----轰鸣
Se_m_10----水流
Se_m_11----急流
Se_m_12----呼啸声(朱雀)
Se_m_13----鬼怪声(青龙)
Se_m_14----野潴叫声(玄武)
Se_m_15----鬼怪出笼(白虎)
Se_m_16----惨叫声
Se_m_17----雷雨交加
Se_m_18----轰隆擦过
Se_m_19----骤风起
Se_m_20----咕噜冒泡
Se_m_21----蜂蛰
Se_m_22----车碾声
Se_m_23----抽取声
Se_m_24----扑扑声
Se_m_25----快吸取
Se_m_26----慢吸取
Se_m_27----充满
Se_m_28----整体充满
Se_m_29----中计陷于混乱
Se_m_30----快速走路声
Se_m_31----没有命中
Se_m_32----醒来,解除
Se_m_33----加固
Se_m_34----天空由阴转晴
Se_m_35----大水冲过
Se_m_36----北风呼号
Se00-----点击声
Se01-----取消
Se02-----装备
Se03-----选人
Se04-----战役开始音效
Se05-----士兵加油
Se06-----沙沙走路声
Se07-----挥武器声
Se08-----走路声
Se09-----开门声
Se10-----树林里行走
Se11-----用物品、吃果子
Se12-----等级上升
Se13-----道具升级、兵种升级
Se14-----拣到物品
Se15-----选取人物或者取消人物
Se16-----人物撤退
Se17-----刮风
Se18-----关闭、打开城门
Se19-----放火
Se20-----鸣罗收兵
Se21-----口哨声
Se22-----呜咽、死去
Se23-----步军走路声
Se24-----骑兵行军
Se25-----炮车行走
Se26-----急速行走
Se27-----走过草地、树林沙沙声
Se28-----沼泽行走声
Se29-----划船声
Se30-----轻格挡声1
Se31-----重格挡声2
Se32-----吹气声(非剑类攻击声)
Se33-----吹醒
Se34-----抽剑声
Se35-----抽打声
Se36-----被石头击中
Se37-----小门开(拉弓声)
Se38-----落石声
Se39-----呼啸声
Se40-----失败、哀乐
Se41-----玎玲声
Se42-----木人释放出来
```·
24 00 09 00 xx xx (音轨序号
25 00 02 00 zz zz 00 04 xx xx xx xx 00 04 yy yy yy yy (x,y) zz武将序号
26 00 02 00 zz zz 04 00 xx xx xx xx 04 00 yy yy yy yy 04 00 xx xx xx xx 04 00 yy yy yy yy
27 00 2d 00 qq qq(类别 0c 00 yy yy 1a 00 ff ff 1c 00 yy yy 15 00 zz zz x y z对应类别的编号
外场景
中国地图 中国地图只有一张所以是ff
内场景
战场地图
29 00 02 00 zz zz 04 00 xx xx xx xx 04 00 yy yy yy yy (x,y)出现zz头像
2a 00 其余同上
2b 00 02 00 xx xx
2c 00 05 00 文字 00 26 00 xx xx (换页 26 00 xx xx (不换行 26 00 xx xx (不等待
2d 00 02 00 xx xx(武将序号
2e 00 02 00 xx xx 02 00 yy yy x y武将序号 26 00 zz zz (相邻 01 00 可攻击 00 00
2f 00
30 00 02 00 xx xx 武将序号 04 00 xx xx xx xx 04 00 yy yy yy yy 2b 00 xx xx(方向 北东南西递增 默认ffff 0d 00 xx xx (动作
分析了0x30个了,简单总结下 曹操传的指令基本都是tv结构即tag value,除了子事件和section没有标识长度的字段,依靠00事件结束来标志终止 cmd_type依序递增 成员type value
31 00 2c 00 xx xx (0单人 1区域 02 00 xx xx(武将序号 04 00 xx4 04 00 yy4 04 00 xx4 04 00 yy4
32 00 40 00 xx xx(0charindex 1 武将序号 02 00 xx xx (武将序号 04 00 xx xx xx xx (index 04 00 xx xx xx xx 04 00 yy yy yy yy 2b 00 xx xx(方向 北东南西递增 默认ffff
33 00 02 00 xx xx(武将序号 0d 00 yy yy (动作 2b 00 zz zz (方向
34 00 02 00 xx xx 0d 00 yy yy
35 00 02 00 xx xx 13 00 yy yy (形象序号
36 00 02 00 xx xx(武将序号 23 00 yy yy (某个属性 04 00 zz zz zz zz(数值
24 00 xx xx(运算符 >=, <, =
37 00 28 00 xx xx (测试类型 04 00 xx xx xx xx 数值 24 00 xx xx 运算符
38 00 02 00 xx xx 武将序号 23 00 xx xx 某个属性 34 00 xx xx 运算符 04 00 xx xx xx xx 数值
39 00 02 00 xx xx武将序号 04 00 xx xx xx xx 数值
3a 00 28 00 xx xx (测试类型 34 00 xx xx 运算符 04 00 xx xx xx xx 数值
3b 00 02 00 xx xx 武将序号 0e 00 xx xx 加入还是离开 3e 00 xx xx 加入等级+x
3c 00 02 00 xx xx 武将序号 0e 00 xx xx 加入还是离开 3a 00 xx xx 运算符
3d 00 17 00 xx xx 物品序号 49 00 xx xx 物品等级 26 00 xx xx 是否显示动作 02 00 xx xx武将序号
02 00 xx xx 武将序号 3b 00 xx xx武器序号 0 默认 1没有 2开始序号 49 00 xx xx 武器等级 3c 00 xx xx防具序号 49 00 xx xx防具等级 3d 00 xx xx 辅助等级
3f 00 04 00 xx xx xx xx 回合数 24 00 xx xx 运算符
40 00 48 00 xx xx
41 00 03 00 xx xx 战斗方 04 00 xx xx 人数 24 00 xx xx 运算符 3f 00 xx xx 区域还是整个战场 04 00 xx xx xx xx *4 区域坐标
42 00
43 00
44 00
45 00 26 00 xx xx 未知1 26 00 xx xx 未知2 04 00 xx xx xx xx 回合数 3e 00 xx xx 等级补正 00默认 32 00 xx xx 无用 02 00 xx xx 敌方主将 (ff ff无 32 00 xx xx无用 02 00 xx xx 我方主将 (ff ff 无 47 00 xx xx 天气类别 22 00 xx xx 起始天气
46 00
下面乘以20
02 00 xx xx 武将编号 26 00 xx xx 是否隐藏 04 00 xx xx xx xx 横坐标 04 00 xx xx xx xx 纵坐标 2b 00 xx xx 面朝方向 ff ff 默认 3e 00 xx xx 等级补正 45 00 xx xx 兵种级别限制 07 00 xx xx ai类别
47 00 剩下参考上一条
48 00 02 00 xx xx武将编号 3b 00 xx xx武器编号 49 00 xx xxlv 3c 00 xx xx防具编号 49 00 xx xx lv 3d 00 xx xx 辅助编号
49 00
4a 00 04 00 xx xx xx xx 人数 38 00 xx xx 5 出场武将编号 39 00 xx xx 5 强制不出场武将编号
4b 00 04 00 xx xx xx xx 武将编号 04 00 xx xx xx xx 横坐标 04 00 xx xx xx xx 纵坐标 2b 00 xx xx 方向 26 00 xx xx 隐藏
4c 00 40 00 xx xx 武将编号00 00 战场编号01 00 02 00 xx xx 武将编号 04 00 xx xx xx xx 战场编号
4d 00 41 00 xx xx 02 00 xx xx 武将编号 04 00 xx xx xx xx 战场编号 04 00 xx xx xx xx 4 区域 03 00 xx xx 2f 00 xx xx 属性 18 00 xx xx 变化 30 00 xx xx 状态 04 00 xx xx 2 hp,mp
4e 00 2c 00 xx xx 单人00 00 范围 01 00 02 00 xx xx 武将编号 04 00 xx xx xx xx 4 范围 03 00 xx xx 战斗方 07 00 xx xx ai类型 02 00 xx xx ai类型下武将编号 04 00 xx xx xx xx 2 坐标
4f 00 02 00 xx xx 2 武将编号 2b 00 xx xx 方向 26 00 xx xx 3
50 00 02 00 xx xx 武将编号 46 00 xx xx 动作编号 26 00 xx xx *2
51 00
52 00 02 00 xx xx武将编号 03 00 xx xx 兵种编号
53 00 2c 00 xx xx 单人00 范围 01 02 00 xx xx 武将编号 04 00 xx xx xx xx *4 范围坐标 03 00 xx xx 战斗方 26 00 xx xx 是否死亡
54 00
55 00 40 00 xx xx 武将还是战场编号 02 00 xx xx 武将编号 04 00 xx xx xx xx 战场编号 04 00 xx xx xx xx *2 坐标 2b 00 xx xx 方向
56 00 47 00 xx xx
57 00 22 00 xx xx
58 00 42 00 xx xx 物体 43 00 xx xx 是否显示 44 00 xx xx 地形 04 00 xx xx xx xx 2 坐标 26 00 xx xx 2 未知
59 00 04 00 xx xx xx xx 额外金钱 17 00 xx xx 49 00 xx xx 物品编号 等级 *3 26 00 xx xx 是否结局
5a 00
5b 00 04 00 xx xx xx xx *4 区域坐标 26 00 xx xx 战斗中是否
5c 00 02 00 xx xx 武将编号
5d 00 34 00 xx xx 运算符 04 00 xx xx xx xx 数值
5e 00 02 00 xx xx *2 武将编号
5f 00
60 00 26 00 xx xx 是否我方武将 05 00 xx 00 出场文字 4c 00 xx xx 单挑动作
61 00
62 00 26 00 xx xx 是否我方武将
63 00 26 00 xx xx 是否我方武将 05 00 xx 00 文字 26 00 xx xx 延时
64 00 26 00 xx xx 是否我方武将 4c 00 xx xx 单挑动作
65 00 26 00 xx xx 是否我方武将 4d 00 xx xx 26 00 xx xx 是否致命一击
66 00 26 00 xx xx 是否我方武将 4e 00 xx xx 26 00 xx xx 是否命中
67 00 04 00 xx xx xx xx 章数 05 00 xx 00 章名
68 00 02 00 xx xx *2 武将编号
69 00 05 00 xx 00
6b 00 04 00 xx xx xx xx *2 坐标 4b 00 xx xx 法术编号 26 00 xx xx 未知
参考博雅张生的帖子 http://xycq.online/forum/viewthread.php?tid=79466&highlight=%B2%DC%B2%D9%B4%AB%BE%E7%B1%BE%B4%FA%C2%EB%CF%EA%BD%E2