楔子
突然想起来小时玩的上古神器这个游戏,于是下载下来玩玩。用的ruffle来打开swf文件。
遇到bug卡关过不去,想着修改存档,网上找了一圈,没找到有用的东西。
只好自己写了个解析游戏存档json的python代码,方便自己改存档,哈哈哈
sol文件解析 AS2.0
头部信息
2个字节(文件标识):00 BF
4个字节(文件字节数 - 6):00 00 08 24
10个字节(固定的):54 43 53 4F 00 04 00 00 00 00
2个字节(SharedObject表名的长度):00 08
N个字节(表名:savegame):73 61 76 65 67 61 6D 65
4个字节(AS版本号 2.0版本):00 00 00 00
1个字节(分隔符):00
数据区
type: 00
如:03 63 6A 31 00 40 20 00 00 00 00 00 00 00 00
1个字节(key长度):03
N个字节(key:cj1):63 6A 31
1个字节(value类型,8字节数字):00
N个字节(value,根据类型判断字节数):40 20 00 00 00 00 00 00
2个字节(结束符):00 00
type: 06
如:09 73 68 6F 75 77 65 69 32 31 06 00 00
1个字节(key长度):09
N个字节(key:shouwei21):73 68 6F 75 77 65 69 32 31
1个字节(value类型,null):06
2个字节(结束符):00 00
type: 01
如:09 6A 69 6E 67 73 68 75 69 31 01 01 00 00
1个字节(key长度):09
N个字节(key:jingshui1):6A 69 6E 67 73 68 75 69 31
1个字节(value类型,布尔类型,1个字节):01
N个字节(value,根据类型判断字节数):01
2个字节(结束符):00 00
Python解析代码
将解析的文件保存为json
import json
paramsMapList = []
def decode_sol():
with open("./savegame.sol", "rb") as f:
data = f.read()
fileNameLen = int(data[16:18].hex(), 16)
startAt = 2 + 4 + 10 + 2 + fileNameLen + 4 + 1
print(len(data), startAt)
i = startAt
while i < len(data):
param = {}
keyLen = int(data[i:i+1].hex(), 16)
keyName = data[i+1:i+1+keyLen].decode("utf-8")
valueType = str(data[i+1+keyLen:i+2+keyLen].hex())
if valueType == "00":
value = str(data[i+2+keyLen:i+2+keyLen+8].hex())
i = i + 2 + keyLen + 8 + 2
elif valueType == "06":
value = "null"
i = i + 2 + keyLen + 2
elif valueType == "01":
value = str(data[i+2+keyLen:i+2+keyLen+1].hex())
i = i + 2 + keyLen + 1 + 2
else:
raise BaseException(f"value type unknow: {valueType}\nlocation: {i+1+keyLen}")
param["keyLen"] = keyLen
param["keyName"] = keyName
param["valueType"] = valueType
param["value"] = value
paramsMapList.append(param)
try:
decode_sol()
except BaseException as e:
print(e)
finally:
print(json.dumps(paramsMapList))
Python根据Json生成sol
import json
sol_name = "savegame" # 表名
sol_json_path = "./savegame.json"
sol_save_path = "./savegame_save.sol"
def save_sol(sol: list):
sol_bytes = bytearray()
param_len = 2 + 4 + 10 + 2 + len(sol_name) + 4 + 1
for each in sol:
param_len += 1 + 1 + 2
param_len += each['keyLen']
if each['valueType'] == "00":
param_len += 8
elif each['valueType'] == "01":
param_len += 1
else:
pass
param_len -= 1 # 最后的分隔符只有1个00
file_bytes_len = (param_len - 6).to_bytes(4, "big")
# 头部信息
sol_bytes.extend(b"\x00\xBF") # 文件标识
sol_bytes.extend(file_bytes_len) # 文件字节数 - 6
sol_bytes.extend(b"\x54\x43\x53\x4F\x00\x04\x00\x00\x00\x00") # 固定10字节
sol_bytes.extend(len(sol_name).to_bytes(2, "big")) # 表名长度
sol_bytes.extend(sol_name.encode("utf-8")) # 表名
sol_bytes.extend(b"\x00\x00\x00\x00") # AS 2.0版本
sol_bytes.extend(b"\x00") # 分隔符
# 数据区
for index, each in enumerate(sol):
key_len: int = each["keyLen"]
key_name: str = each["keyName"]
value_type: str = each["valueType"]
value: str = each["value"]
each_bytes = bytearray()
each_bytes.extend(key_len.to_bytes(1, "big")) # key长度
each_bytes.extend(key_name.encode("utf-8")) # key
if value_type == "00":
each_bytes.extend(b"\x00") # 00类型
each_bytes.extend(int(value, 16).to_bytes(8, "big")) # value
elif value_type == "01":
each_bytes.extend(b"\x01") # 01类型
each_bytes.extend(int(value, 16).to_bytes(1, "big")) # value
elif value_type == "06":
each_bytes.extend(b"\x06") # 06类型
else:
raise BaseException(f"unknown value type: {value_type}")
if index < len(sol)-1:
each_bytes.extend(b"\x00\x00")
else:
each_bytes.extend(b"\x00")
sol_bytes.extend(each_bytes)
with open(sol_save_path, "wb") as sol_f:
sol_f.write(sol_bytes)
with open(sol_json_path, "r") as f:
sol_json = json.load(f)
save_sol(sol_json)