images

吉里吉里で使用されるファイルは難読化されてるらしい。
.ksdファイルほか、セーブデータをいじったりしたい場合、これを復号化する必要がある。

バイナリで見ると頭の2バイトで読み込みモードを変えているようなので
これを利用してなんやかんやしたい。




……



\引数にファイルを指定してPythonを実行すると復号化するや~つ~/

Descrambler.py


※復号化ファイルは上書きするから一応バックアップファイルも出力するよ。


以下ソース。



吉里吉里/KAGノベルゲーム制作入門
gutchie
秀和システム
2007-05-11


import zlib
import struct
import os
import sys
import shutil

def descramble_mode0(data):
    for i in range(0, len(data), 2):
        if data[i + 1] == 0 and data[i] < 0x20:
            continue
        data[i + 1] ^= (data[i] & 0xFE)
        data[i] ^= 1
    return data

def descramble_mode1(data):
    descrambled_data = bytearray()
    for i in range(0, len(data), 2):
        c = data[i] | (data[i + 1] << 8)
        c = ((c & 0xAAAA) >> 1) | ((c & 0x5555) << 1)
        descrambled_data.extend([c & 0xFF, (c >> 8) & 0xFF])
    return descrambled_data

def descramble_deflate(data):
    compressed_length = struct.unpack("<Q", data[:8])[0]
    uncompressed_length = struct.unpack("<Q", data[8:16])[0]
    zlib_header = struct.unpack("<H", data[16:18])[0]
    decompressed_data = zlib.decompress(data[18:18+compressed_length], -zlib.MAX_WBITS)
    return decompressed_data

def descramble(file_path):
    with open(file_path, "rb") as file:
        magic = file.read(2)
        if magic != b'\xFE\xFE':
            raise ValueError("Not a scrambled Kirikiri file.")

        mode = file.read(1)[0]
        bom = file.read(2)
        if bom != b'\xFF\xFE':
            raise ValueError("Not a scrambled Kirikiri file.")

        if mode == 0:
            data = file.read()
            descrambled_data = descramble_mode0(data)
        elif mode == 1:
            data = file.read()
            descrambled_data = descramble_mode1(data)
        elif mode == 2:
            data = file.read()
            descrambled_data = descramble_deflate(data)
        else:
            raise NotImplementedError(f"File uses unsupported scrambling mode {mode}")

        return descrambled_data.decode("utf-16")

# 引数からファイルパスを取得
if len(sys.argv) < 2:
    print("ファイルパスを指定してください。")
    sys.exit(1)

# 入力ファイルパス
input_file_path = sys.argv[1]

# バックアップファイルパス
backup_file_path = input_file_path + ".bak"

# バックアップ作成
shutil.copy2(input_file_path, backup_file_path)

descrambled_text = descramble(input_file_path)

with open(input_file_path, "wb") as output_file:
    output_file.write(descrambled_text.encode("utf-8"))

print(f"Descrambled text has been saved to: {input_file_path}")
print(f"Backup file has been created: {backup_file_path}")