Batocera 對漢化版遊戲的專用 BIOS 處理方式

一般漢化版遊戲可能有專用的 BIOS,通常這些BIOS除了官方的BIOS程式外還會含有中文字型的程式碼在裡頭。但不同漢化的遊戲的 BIOS 可能是不可以通用的。當你用漢化版 A 的遊戲蓋過原版的 BIOS 時可能會造成漢化版 B 的遊戲中文就亂碼了。這篇文章就是要說明如何用 Batocera 的程式功能來解決這個問題。

Batocera scripts 在這篇官方的文章裡有一段是說明可在遊戲執行前先執行自定程式。而我們要作的就是根據遊戲的名稱如果需要變更 BIOS 就把專用的 BIOS 蓋過官方的 BIOS,等遊戲結束後再把官方的 BIOS 蓋過專用的 BIOS 。程式的流程就是這樣。

這邊我是用 PYTHON3 來作這個動作。由於每個人拿到的遊戲檔名可能不一樣,請自行修改一下程式上的檔名。為把官方的 BIOS 跟專用的 BIOS 依下方程式作相對應的命之後。把下方的程式存成 run_script.py 。copy 至 Batocera 的 /userdata/system/scripts 。再執行 chmod 755 /userdata/system/scripts/run_script.py 。這樣在遊戲執行的前跟後就會作相對應的 COPY 跟蓋過 BIOS 的動作。

#!/usr/bin/env python3
"""
游戏启动/停止时自动切换 BIOS 文件的脚本
用法: python3 run_script.py <event> <param2> <param3> <param4> <rom_path>
"""

import sys
import shutil
from pathlib import Path

# 常量定义
LOGFILE = Path("/tmp/scriptlog.txt")
BIOS_DIR = Path("/userdata/bios")
ROMS_DIR = Path("/userdata/roms")

# BIOS 配置映射
BIOS_CONFIG = {
    # PC Engine CD 游戏
    "/userdata/roms/pcenginecd/Tengai Makyou Fuun Kabuki Den CN.cue": {
        "start": (BIOS_DIR / "syscard3.cn.pce", BIOS_DIR / "syscard3.pce"),
        "stop": (BIOS_DIR / "syscard3.bak.pce", BIOS_DIR / "syscard3.pce")
    },
    "/userdata/roms/pcenginecd/Akumajou Dracula X Chi no Rondo CN.cue": {
        "start": (BIOS_DIR / "syscard3.cn.pce", BIOS_DIR / "syscard3.pce"),
        "stop": (BIOS_DIR / "syscard3.bak.pce", BIOS_DIR / "syscard3.pce")
    },
    "/userdata/roms/pcenginecd/Shiawase_Usagi_CN.cue": {
        "start": (BIOS_DIR / "syscard3.cn.v2.pce", BIOS_DIR / "syscard3.pce"),
        "stop": (BIOS_DIR / "syscard3.bak.pce", BIOS_DIR / "syscard3.pce")
    },
    "/userdata/roms/pcenginecd/HIMITSU_NO_HANAZONO(CN).cue": {
        "start": (BIOS_DIR / "syscard3.cn.v2.pce", BIOS_DIR / "syscard3.pce"),
        "stop": (BIOS_DIR / "syscard3.bak.pce", BIOS_DIR / "syscard3.pce")
    },
    # PC-FX 游戏
    "/userdata/roms/pcenginecd/First Kiss Monogatari(CN).cue": {
        "start": (BIOS_DIR / "pcfx(CN).rom", BIOS_DIR / "pcfx.rom"),
        "stop": (BIOS_DIR / "pcfx.bak.rom", BIOS_DIR / "pcfx.rom")
    },
    "/userdata/roms/pcenginecd/Pia Carrot He Youkoso(CN).cue": {
        "start": (BIOS_DIR / "pcfx(CN).rom", BIOS_DIR / "pcfx.rom"),
        "stop": (BIOS_DIR / "pcfx.bak.rom", BIOS_DIR / "pcfx.rom")
    },
    # PSX 游戏
    "/userdata/roms/psx/PoPoLoCrois_Monogatari_CN.cue": {
        "start": (BIOS_DIR / "PoPoLoCrois_bios.BIN", BIOS_DIR / "psxonpsp660.bin"),
        "stop": (BIOS_DIR / "psxonpsp660.bin.bak", BIOS_DIR / "psxonpsp660.bin")
    },
    "/userdata/roms/psx/Gensou Suiko Gaiden Vol. 1 - Harmonia no Kenshi(CHS).cue": {
        "start": (BIOS_DIR / "GensouSuikoGaidenVol1_bios.bin", BIOS_DIR / "psxonpsp660.bin"),
        "stop": (BIOS_DIR / "psxonpsp660.bin.bak", BIOS_DIR / "psxonpsp660.bin")
    },
    "/userdata/roms/psx/Gensou Suiko Gaiden Vol. 2 - Crystal Valley no Kettou(CHS).cue": {
        "start": (BIOS_DIR / "GensouSuikoGaidenVol1_bios.bin", BIOS_DIR / "psxonpsp660.bin"),
        "stop": (BIOS_DIR / "psxonpsp660.bin.bak", BIOS_DIR / "psxonpsp660.bin")
    }
}


def log_message(message):
    """写入日志文件"""
    try:
        with open(LOGFILE, "a") as f:
            f.write(f"{message}\n")
    except Exception as e:
        print(f"日志写入失败: {e}", file=sys.stderr)


def copy_bios(source, destination):
    """复制 BIOS 文件"""
    try:
        shutil.copy2(source, destination)
        log_message(f"已复制: {source} -> {destination}")
        return True
    except Exception as e:
        log_message(f"复制失败: {source} -> {destination}, 错误: {e}")
        return False


def handle_game_start(rom_path):
    """处理游戏启动事件"""
    log_message("START")
    log_message(f"启动游戏: {rom_path}")
    
    if rom_path in BIOS_CONFIG:
        log_message("change bios")
        source, dest = BIOS_CONFIG[rom_path]["start"]
        copy_bios(source, dest)
    else:
        log_message("无需更换 BIOS")


def handle_game_stop(rom_path):
    """处理游戏停止事件"""
    log_message("END")
    log_message(f"停止游戏: {rom_path}")
    
    if rom_path in BIOS_CONFIG:
        log_message("restore bios")
        source, dest = BIOS_CONFIG[rom_path]["stop"]
        copy_bios(source, dest)
    else:
        log_message("无需恢复 BIOS")


def main():
    """主函数"""
    if len(sys.argv) < 2:
        print("用法: python3 run_script.py <event> [参数...]")
        print("事件类型: gameStart, gameStop")
        sys.exit(1)
    
    event = sys.argv[1]
    
    # 记录所有参数
    log_message(" ".join(sys.argv))
    
    # 获取 ROM 路径(第5个参数,索引为5)
    rom_path = sys.argv[5] if len(sys.argv) > 5 else ""
    
    if event == "gameStart":
        handle_game_start(rom_path)
    elif event == "gameStop":
        handle_game_stop(rom_path)
    else:
        log_message(f"未知事件: {event}")
        print(f"未知事件: {event}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()

延伸閱讀

blog.ocam.live
blog.ocam.live

關注科技新聞、SEO、人工智慧、電玩模擬器、程式設言、與 IT 日常等議題,深入簡出文章的說明並持續追蹤相關新聞的發展與報導。