2026-02-26

NFD形式ファイルをFDI形式に変換するスクリプトを作成

Windows11上のT98-NEXTでWizardryをプレイするとNFD形式でフロッピーディスクイメージが作られます。これをNetBSD/i386上のxnp2で利用するにはFDI形式に変換しなくてはなりません。NFD形式とFDI形式の構造について調べたので、NFD→FDIの変換をおこなうスクリプトを作りました。この目的はWizardryでプレイしたファイルを変換したいだけなので、大雑把な作りになっています。 

以前なら、GoogleなどでWebを検索して、参考となるサイトを探していました。今でも同様なのですが、最近はGeminiと対話しながら、作成するようになりました。世間一般では、生成されたスクリプトを何も考えずに使うだけなのか、それとも生成されたものを参考にしながら自前で作るのか、どうしているのかはわかりません。私の場合には、Geminiが生成したスクリプトを参考にして、自分で作り上げるようにしています。

その結果、以下のようなスクリプトが出来上がりました。これを使用して、T98-NEXTが出力したNFD形式ファイルを変換してみました。それをxnp2で読み込んでみましたが、問題ないようです。これでT98-NEXT(NFD)→xnp2(FDI)ができるようになったので、途中まで冒険した続きができます。xnp2が出力するファイルはFDI形式になりますが、T98-NEXTはFDI形式も読めるので、FDI→NFD変換スクリプトは作らなくても良いでしょう。

#!/usr/bin/python3
import os
import pathlib
import sys
import ctypes

# 基本的な型のエイリアス
BYTE  = ctypes.c_uint8
WORD  = ctypes.c_uint16
DWORD = ctypes.c_uint32

# セクタ情報
class NFD_SECT_ID(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("C",        BYTE),
        ("H",        BYTE),
        ("R",        BYTE),
        ("N",        BYTE),
        ("flMFM",    BYTE),
        ("flDDAM",   BYTE),
        ("byStatus", BYTE),
        ("byST0",    BYTE),
        ("byST1",    BYTE),
        ("byST2",    BYTE),
        ("byPDA",    BYTE),
        ("Reserve1", ctypes.c_char * 5),
    ]

# NFDヘッダ
class NFD_FILE_HEAD(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("szFileID",    ctypes.c_char * 15),
        ("Reserve1",    ctypes.c_char * 1),
        ("szComment",   ctypes.c_char * 0x100),
        ("dwHeadSize",  DWORD),
        ("flProtect",   BYTE),
        ("byHead",      BYTE),
        ("Reserve2",    ctypes.c_char * 10),
        ("si",          NFD_SECT_ID * (163 * 26)),
        ("Reserve3",    ctypes.c_char * 0x10),
    ]

# FDIヘッダ
class FDI_FILE_HEAD(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("Reserve1",   DWORD),
        ("dwMedia",    DWORD),
        ("dwHeadSize", DWORD),
        ("dwDataSize", DWORD),
        ("dwSectSize", DWORD),
        ("dwSecter",   DWORD),
        ("dwHead",     DWORD),
        ("dwCylinder", DWORD),
        ("Reserve2",   ctypes.c_char * (4096 - 4 * 8)),
    ]

if __name__ == "__main__":
    try:
        nfd = open(sys.argv[1], "br")
    except:
        print("usage: NFD2FDI <filename>")
        exit(1)

    try:
        nfdpath = pathlib.Path(sys.argv[1])
        fdi = open(nfdpath.with_suffix(".FDI"), "wb")
    except:
        print("cannot open FDI file")
        exit(1)

    NFDheader = NFD_FILE_HEAD()
    nfd.readinto(NFDheader)

    FDIheader = FDI_FILE_HEAD()
    FDIheader.dwMedia = 0x90
    FDIheader.dwHeadSize = 4096
    FDIheader.dwSectSize = 256
    FDIheader.dwSecter = 26
    FDIheader.dwHead = 2
    FDIheader.dwCylinder = 77
    FDIheader.dwDataSize = (FDIheader.dwSectSize
                            * FDIheader.dwSecter
                            * FDIheader.dwHead
                            * FDIheader.dwCylinder)


    try:
        nfd.seek(NFDheader.dwHeadSize)
        rawdata = nfd.read(FDIheader.dwDataSize)
        fdi.write(FDIheader)
        fdi.write(rawdata)
    except:
        print("read/write error")
        exit(1)

    exit(0)
#[EOF]

0 件のコメント:

コメントを投稿