#python #database #serialization #binary
#python #База данных #сериализация #двоичный
Вопрос:
Я пытаюсь создать правильный сериализатор для двоичных scores.db
файлов и collections.db
для игры под названием osu!,
Сначала я использовал синтаксический анализатор, найденный здесь https://github.com/KirkSuD/osudb Это позволило мне разобрать мои файлы БД в список, который я могу редактировать, но я не видел ни одного современного сериализатора, который я мог бы использовать для сериализации данных обратно в новый двоичный файл, поэтому я попытался создать его сам.
Я знаю, что могу просто заменить struct.unpack(format,v1)
на просто struct.pack(format,v1)
, но вот первая проблема:
Я больше не могу прочитать определенное количество байтов, поскольку они просто не являются байтами, и я также не могу этого сделать по понятным причинам, что означает, что я также не могу использовать технику, противоположную используемой разработчиком синтаксического анализатора (для легкого анализа данных).
Прямо сейчас я преобразовал двоичный файл в str
for vizualition
https://controlc.com/6cdcffa3 Мне нужно получить это от https://controlc.com/9ddc4af1
Мне нужно получить точно такой же формат, проблема в том, что я начал создавать это:
def serialize_types_exp(fobj, data_type):
if data_type == "Int": ## 4 bytes unsigned int
return struct.pack("<I", fobj)
def Serialize_score_exp(file_path):
with open(file_path, "r") as fobj:
with open("./osu!MapSync/output_func.txt", "r ") as otp:
otp.truncate(0)
for i in fobj:
fobj = ast.literal_eval(i)
print(type(fobj))
f=0
while f <= 1:
res = str(serialize_types_exp(fobj[f], "Int"))
if f == 0:
print(fobj[f],"->",res[0:-1])
otp.write(str(res)[0:-1])
else:
print(fobj[f],"->",res[2:-1])
otp.write(str(res)[2:-1])
f = 1
Serialize_score_exp('./osu!MapSync/output.txt')
Что, как я понял, в основном будет то же самое, что просто возвращать строку, которую я хочу, иначе:
def Serialize_score_exp(filepath):
return "litteraly a static result copy pasted from the binary file"
Это не то, что я хочу, поэтому мне интересно, что я могу использовать, чтобы сериализовать его обратно в нужный мне формат?
РЕДАКТИРОВАТЬ: вот файлы, если кто-нибудь из вас хочет что-то попробовать https://drive.google.com/file/d/1Qmam_u9mVfqxBZ_U5QvFnq1t_01yOQzT/view?usp=sharing
РЕДАКТИРОВАТЬ 01.02.2020: похоже, что прямая запись двоичного файла в файл с использованием wb и последующее его чтение решают большую часть проблемы с визуализацией, но прежде чем я продолжу, в x0b
исходных сериализованных данных есть байты (включая пробел), и я не знаю, как получить их из моего списка, есть идеи?
вот мой код прямо сейчас
def serialize_type_exp(fobj, data_type):
if data_type == "Boolean": ## False if 0x00 else True
return struct.pack("<?", fobj)
elif data_type == "Byte": ## 1 byte int
return struct.pack("<s", fobj)
elif data_type == "Double": ## 8 bytes floating point
return struct.pack("<d", fobj)
elif data_type == "Int": ## 4 bytes unsigned int
return struct.pack("<I", fobj)
elif data_type == "Long": ## 8 bytes unsigned int
return struct.pack("<Q", fobj)
elif data_type == "Short": ## 2 bytes unsigned int
return struct.pack("<H", fobj)
elif data_type == "Single": ## 4 bytes floating point
return struct.pack("<f", fobj)
elif data_type == "String": ## 0x00 or 0x0b - ULE128(n) - UTF-8(length=n)
bb = fobj
if bb == None:
return None
return fobj.encode("utf-8")
else:
raise NotImplementedError('parse_type(fobj, data_type): Unknown data type: "%s".' % data_type)
def serialize_types_exp(fobj, types):
return [serialize_type_exp(fobj, i) for i in types]
score_data_types = ['Byte', 'Int', 'String', 'String', 'String', 'Short', 'Short', 'Short', 'Short', 'Short', 'Short', 'Int', 'Short', 'Boolean', 'Int', 'String', 'Long', 'Int', 'Long']
def serialize_scoredb_data(file_path):
with open(file_path, "r") as fobj:
with open("./osu!MapSync/output_func.db", "wb") as otp:
otp.truncate(0)
for i in fobj:
fobj = ast.literal_eval(i)
for i in range(2):
otp.write((serialize_type_exp(fobj[i],'Int')))
for maps in fobj[2]:
for i in range(2):
if type(maps[i]) == str:
otp.write(serialize_type_exp(maps[i],'String'))
if type(maps[i]) == int:
otp.write(serialize_type_exp(maps[i],'Int'))
for scores in maps[2]:
for idx, stats in enumerate(scores):
if score_data_types[idx] == 'Byte':
print(stats,"->",stats.to_bytes,":",type(bytes(stats)))
print(type(serialize_type_exp(bytes(stats), score_data_types[idx])))
otp.write(serialize_type_exp(bytes(stats), score_data_types[idx]))
else:
otp.write(serialize_type_exp(stats, score_data_types[idx]))
serialize_scoredb_data('./osu!MapSync/output.txt')
РЕДАКТИРОВАТЬ 3: исправлено это x0b
и x0b
байты, теперь мне приходится сталкиваться с еще одной проблемой, прежде чем она заработает в игре:
некоторые байты, похоже, изменились с x00
на x01
в процессе, и я не знаю почему
import osudb
import ast
import struct
with open("./osu!MapSync/scores.db", "rb") as f:
with open("Exp.txt", "w") as i:
for stuff in f:
i.write(str(stuff))
parse = osudb.parse_score(r"./osu!MapSync/scores.db")
with open('./osu!MapSync/output.txt', 'w') as f:
f.write(str(parse)) #[1:-1].split(',')
def serialize_type_exp(fobj, data_type):
if data_type == "Boolean": ## False if 0x00 else True
return struct.pack("<?", fobj)
elif data_type == "Byte": ## 1 byte int
return struct.pack("<s", fobj)
elif data_type == "Double": ## 8 bytes floating point
return struct.pack("<d", fobj)
elif data_type == "Int": ## 4 bytes unsigned int
return struct.pack("<I", fobj)
elif data_type == "Long": ## 8 bytes unsigned int
return struct.pack("<Q", fobj)
elif data_type == "Short": ## 2 bytes unsigned int
return struct.pack("<H", fobj)
elif data_type == "Single": ## 4 bytes floating point
return struct.pack("<f", fobj)
elif data_type == "String": ## 0x00 or 0x0b - ULE128(n) - UTF-8(length=n)
bb = fobj
if bb == None:
return None
return fobj.encode("utf-8")
else:
raise NotImplementedError('parse_type(fobj, data_type): Unknown data type: "%s".' % data_type)
def serialize_types_exp(fobj, types):
return [serialize_type_exp(fobj, i) for i in types]
score_data_types = ['Byte', 'Int', 'String', 'String', 'String', 'Short', 'Short', 'Short', 'Short', 'Short', 'Short', 'Int', 'Short', 'Boolean', 'Int', 'String', 'Long', 'Int', 'Long']
def serialize_scoredb_data(file_path):
with open(file_path, "r") as fobj:
with open("./new osu!.db/scores.db", "wb") as otp:
otp.truncate(0)
for i in fobj:
fobj = ast.literal_eval(i)
for i in range(2):
otp.write((serialize_type_exp(fobj[i],'Int')))
for maps in fobj[2]:
for i in range(2):
if type(maps[i]) == str:
otp.write(b'x0b ' serialize_type_exp(maps[i],'String'))
if type(maps[i]) == int:
otp.write(serialize_type_exp(maps[i],'Int'))
for scores in maps[2]:
for idx, stats in enumerate(scores):
if score_data_types[idx] == 'Byte':
otp.write(serialize_type_exp(bytes(stats), score_data_types[idx]))
elif stats == None:
otp.write(b'x00')
elif score_data_types[idx] == 'String' and len(stats) == 32:
otp.write(b'x0b ' serialize_type_exp(stats, score_data_types[idx]))
elif idx == 3:
otp.write(b'x0bx06' serialize_type_exp(stats, score_data_types[idx]))
else:
otp.write(serialize_type_exp(stats, score_data_types[idx]))
serialize_scoredb_data('./osu!MapSync/output.txt')
with open("./osu!MapSync/output_func.db", "rb") as f:
with open("output_func.txt", "w") as i:
for stuff in f:
i.write(str(stuff))
это то, что я хочу https://controlc.com/6cdcffa3 и вот что я получил https://controlc.com/4e547b64
уже исправлен используемый мной синтаксический анализатор, теперь я обнаружил, что форматирование имени пользователя неверно и варьируется от игрока к другому, мне, видимо, нужно добавить это в мой сериализатор https://en.wikipedia.org/wiki/LEB128#Encode_unsigned_integer кроме меня, я понятия не имею о том, что они означают set high order bit of byte;
и как это сделать на python
Ответ №1:
ну, я, наконец, закончил с сериализатором и частью слияния, поэтому последним шагом моего проекта будет правильная реализация FTP в надежде, что он будет работать на всех устройствах (включая Android), поэтому в этой теме больше нет необходимости, весь код опубликован на github rn