From f342360c81c1b2265e5bb3f38a5dd2cf149dba8d Mon Sep 17 00:00:00 2001 From: mos9527 Date: Thu, 10 Oct 2024 16:57:21 +0800 Subject: [PATCH] Support RTVL(RLA) format version 1.5 that's introduced by the 4.0 update --- .vscode/launch.json | 12 ++++++++++++ sssekai/fmt/rla.py | 48 +++++++++++++++++++++++++++++++++------------ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index bf6585e..d5ca274 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -179,5 +179,17 @@ "args": [ ] }, + { + "name": "Python: RLA Test (Single File)", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/sssekai/fmt/rla.py", + "console": "integratedTerminal", + "justMyCode": false, + "args": [ + // "D:\\proseka_reverse\\sse_packet_importer\\packets\\streaming_vbs_1\\sekai_30_00000006.rla.bytes" + "D:\\proseka_reverse\\sse_packet_importer\\packets\\4268\\1728191806276-0.bin" + ] + }, ] } \ No newline at end of file diff --git a/sssekai/fmt/rla.py b/sssekai/fmt/rla.py index efc980f..b82f81d 100644 --- a/sssekai/fmt/rla.py +++ b/sssekai/fmt/rla.py @@ -306,6 +306,16 @@ def gen_exception_handler(generator): if version >= (1, 4) else {} ), + **( + { + "characterVisible": get_next_mask(), + "eyeLookAtTargetPositionOffset": get_next_vector3(), + "eyeLookAtAngleLimit": get_next_vector3(), + "isPreloadReverseCharacter": get_next_mask(), + } + if version >= (1, 5) + else {} + ), } read_character_status = lambda: { "costumeIndex": get_next_int(), @@ -354,12 +364,32 @@ def gen_exception_handler(generator): result = defaultdict(dict) +def read_rla_frame(buffer: bytes, version=(1, 0), strict=True) -> dict: + """Parses a single frame of the Sekai RLA file format used in 'streaming_live/archive' assets. + + Args: + buffer (bytes): Frame buffer + version (tuple, optional): RLA version, found in respective RLH (JSON) header files. range: (1,0) to (1,5). Defaults to (1,0). + strict (bool, optional): If False, incomplete packets will be returned as is. Defaults to True. + + Returns: + dict: Parsed frame data + """ + header_signature, data = decode_buffer_base64(buffer) + decoder_signature, data = decode_buffer_payload(data) + assert ( + header_signature == decoder_signature + ), "mismatching signature (header/decoder). packet may be corrupt" + payload = decode_streaming_data(version, decoder_signature, data, strict) + return payload + + def read_rla(src: BytesIO, version=(1, 0), strict=True) -> dict: """Parses the Sekai RLA file format used in 'streaming_live/archive' assets. Args: src (BytesIO): Source RLA file stream - version (tuple, optional): RLA version, found in respective RLH (JSON) header files. i.e. one of (1,0), (1,1), (1,2), (1,3), (1,4) Defaults to (1,0). + version (tuple, optional): RLA version, found in respective RLH (JSON) header files. range: (1,0) to (1,5). Defaults to (1,0). strict (bool, optional): If False, incomplete packets will be returned as is. Defaults to True. Returns: @@ -372,12 +402,7 @@ def read_frames(): if ticks: buffer_length = read_int(src, 4) buffer = src.read(buffer_length) - header_signature, data = decode_buffer_base64(buffer) - decoder_signature, data = decode_buffer_payload(data) - assert ( - header_signature == decoder_signature - ), "mismatching signature (header/decoder). packet may be corrupt" - payload = decode_streaming_data(version, decoder_signature, data, strict) + payload = read_rla_frame(buffer, version, strict) result[ticks].setdefault(payload["type"], list()).append(payload) return True return False @@ -389,11 +414,10 @@ def read_frames(): if __name__ == "__main__": + import sys from timeit import timeit - fp = open( - r"C:\Users\mos9527\Desktop\sekai_streaming\sekai_30_00000060.rla.bytes", "rb" - ) + fp = open(sys.argv[-1], "rb") buffer = fp.read() - fp = BytesIO(buffer) - print("took %.2f seconds" % timeit("read_rla(fp, (1,0))", globals=globals())) + payload = read_rla_frame(buffer) + pass