36 lines
1.3 KiB
Python
36 lines
1.3 KiB
Python
"""DeviceSQL variable-length string encoding.
|
|
|
|
Three on-disk forms (see rekordbox-pdb.ksy `device_sql_string`):
|
|
- odd length_and_kind byte -> short ASCII (< 127 chars)
|
|
- 0x40 -> long ASCII (any length, ASCII bytes)
|
|
- 0x90 -> long UTF-16LE (for non-ASCII content)
|
|
|
|
We pick the cheapest form that fits the content. Long-but-ASCII paths
|
|
must use 0x40, NOT 0x90 -- some player firmwares decode 0x90 strings
|
|
as UTF-16 and then fail the case-sensitive filesystem lookup, so any
|
|
track whose `file_path` lands in the wrong form silently goes missing.
|
|
"""
|
|
|
|
EMPTY = b"\x03"
|
|
|
|
|
|
def encode_devicesql(s: str) -> bytes:
|
|
if not s:
|
|
return EMPTY
|
|
try:
|
|
ascii_bytes = s.encode("ascii")
|
|
except UnicodeEncodeError:
|
|
ascii_bytes = None
|
|
|
|
if ascii_bytes is not None:
|
|
if len(ascii_bytes) < 127:
|
|
length = len(ascii_bytes) + 1
|
|
length_and_kind = ((length << 1) | 1) & 0xFF
|
|
return bytes([length_and_kind]) + ascii_bytes
|
|
total_length = len(ascii_bytes) + 4 # marker + u2 length + u1 pad
|
|
return bytes([0x40]) + total_length.to_bytes(2, "little") + b"\x00" + ascii_bytes
|
|
|
|
payload = s.encode("utf-16-le")
|
|
total_length = len(payload) + 4
|
|
return bytes([0x90]) + total_length.to_bytes(2, "little") + b"\x00" + payload
|