K1Guitar/tools/log_decode.py

96 lines
3.0 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""
log_decode.py K1 LOG.BINW25Q 日志分区镜像解码为可读时间线
布局:
[0x0000 .. 0x0FFF] header (magic K1LG ...)
[0x1000 .. end] 文本环形区行以 \\n 结束
用法:
python log_decode.py LOG.BIN -o problem.log
python log_decode.py LOG.BIN --write-off 1234 --wrap 1 -o problem.log
"""
from __future__ import annotations
import argparse
import struct
MAGIC = 0x4B314C47 # 'K1LG'
HDR_SIZE = 0x1000
def unpack_header(blob: bytes) -> dict:
if len(blob) < 36:
return {"valid": False}
magic = struct.unpack_from("<I", blob, 0)[0]
ver = struct.unpack_from("<H", blob, 4)[0]
boot = struct.unpack_from("<I", blob, 8)[0]
write_off = struct.unpack_from("<I", blob, 12)[0]
wrap = struct.unpack_from("<I", blob, 16)[0]
lines = struct.unpack_from("<I", blob, 20)[0]
overflow = struct.unpack_from("<I", blob, 24)[0]
seq = struct.unpack_from("<I", blob, 28)[0]
return {
"valid": magic == MAGIC,
"magic": magic,
"ver": ver,
"boot_count": boot,
"write_off": write_off,
"wrap_count": wrap,
"line_count": lines,
"overflow": overflow,
"seq": seq,
}
def extract_text(data: bytes, write_off: int, wrapped: bool) -> str:
"""Linearize ring: if wrapped, [write_off..end) + [0..write_off); else [0..write_off)."""
if not data:
return ""
if write_off > len(data):
write_off = len(data)
if wrapped and write_off < len(data):
raw = data[write_off:] + data[:write_off]
else:
raw = data[:write_off] if write_off else data
text = raw.decode("utf-8", errors="replace")
text = text.replace("\xff", "")
return text
def decode_file(bin_path: str, out_path: str, write_off=None, wrap_count=None) -> None:
with open(bin_path, "rb") as f:
blob = f.read()
hdr = unpack_header(blob)
data = blob[HDR_SIZE:] if len(blob) > HDR_SIZE else b""
wo = write_off if write_off is not None else hdr.get("write_off", 0)
wrap = wrap_count if wrap_count is not None else hdr.get("wrap_count", 0)
text = extract_text(data, wo, wrap > 0)
lines = [ln for ln in text.splitlines() if ln.strip()]
with open(out_path, "w", encoding="utf-8", newline="\n") as f:
f.write(
f"# K1 log decode magic_ok={hdr.get('valid')} ver={hdr.get('ver')} "
f"boot={hdr.get('boot_count')} write_off={wo} wrap={wrap} "
f"line_count={hdr.get('line_count')} decoded_lines={len(lines)}\n"
)
for ln in lines:
f.write(ln.rstrip("\r") + "\n")
print(f"decoded {len(lines)} lines -> {out_path}")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("bin")
ap.add_argument("-o", "--out", default="problem.log")
ap.add_argument("--write-off", type=int, default=None)
ap.add_argument("--wrap", type=int, default=None)
args = ap.parse_args()
decode_file(args.bin, args.out, args.write_off, args.wrap)
if __name__ == "__main__":
main()