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]

FDI形式ファイルの構造を出力するスクリプトを作成

Windows11上のT98-NEXTでWizardryをプレイするとNFD形式でフロッピーディスクイメージが作られます。これをNetBSD/i386上のxnp2で利用するにはFDI形式に変換しなくてはなりません。変換スクリプトを作成する前に、FDI形式について学ぶため、ファイル内の構造を出力するスクリプトを作ってみました。
 
以前なら、GoogleなどでWebを検索して、参考となるサイトを探していました。今でも同様なのですが、最近はGeminiと対話しながら、作成するようになりました。世間一般では、生成されたスクリプトを何も考えずに使うだけなのか、それとも生成されたものを参考にしながら自前で作るのか、どうしているのかはわかりません。私の場合には、Geminiが生成したスクリプトを参考にして、自分で作り上げるようにしています。 

その結果、以下のようなスクリプトが出来上がりました。これを使ってWizardryで使用しているファイルを参照してみると、いろいろと勉強になります。 

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

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

# ヘッダ
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:
        f = open(sys.argv[1], "br")
    except:
        print("usage: FDIdump <filename>")
        exit(1)

    header = FDI_FILE_HEAD()
    f.readinto(header)

    print(f"dwMedia:0x{header.dwMedia:08x} ({header.dwMedia})")
    print(f"dwHeadSize:0x{header.dwHeadSize:08x} ({header.dwHeadSize})")
    print(f"dwDataSize:0x{header.dwDataSize:08x} ({header.dwDataSize})")
    print(f"dwSectSize:0x{header.dwSectSize:08x} ({header.dwDataSize})")
    print(f"dwSecter:0x{header.dwSecter:08x} ({header.dwSecter})")
    print(f"dwHead:0x{header.dwHead:08x} ({header.dwHead})")
    print(f"dwCylinder:0x{header.dwCylinder:08x} ({header.dwCylinder})")

    print("header size:", ctypes.sizeof(FDI_FILE_HEAD))

#[EOF]

NFD形式ファイルの構造を出力するスクリプトを作成

Windows11上のT98-NEXTでWizardryをプレイするとNFD形式でフロッピーディスクイメージが作られます。これをNetBSD/i386上のxnp2で利用するにはFDI形式に変換しなくてはなりません。変換スクリプトを作成する前に、NFD形式について学ぶため、ファイル内の構造を出力するスクリプトを作ってみました。

 

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

 

その結果、以下のようなスクリプトが出来上がりました。これを使ってWizardryで使用しているファイルを参照してみると、いろいろと勉強になります。 

 

#!/usr/bin/python3
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),
    ]

# ヘッダ
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),
    ]

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

    header = NFD_FILE_HEAD()
    f.readinto(header)

    print(f"szFileID:{header.szFileID}")
    print(f"szComment:{header.szComment}")
    print(f"dwHeadSize:{header.dwHeadSize} (0x{header.dwHeadSize:X})")
    print(f"flProtect:{header.flProtect}")
    print(f"byHead:{header.byHead}")

    for i in range(len(header.si)):
        print(f"{i:4}:  ", end="")
        print(f"C:{header.si[i].C:02x} ", end="")
        print(f"H:{header.si[i].H:02x} ", end="")
        print(f"R:{header.si[i].R:02x} ", end="")
        print(f"N:{header.si[i].N:02x} ", end="")
        print(f"MFM:{header.si[i].flMFM} ", end="")
        print(f"DDAM:{header.si[i].flDDAM} ", end="")
        print(f"Status:{header.si[i].byStatus:02x} ", end="")
        print(f"ST0:{header.si[i].byST0:02x} ", end="")
        print(f"ST1:{header.si[i].byST1:02x} ", end="")
        print(f"ST2:{header.si[i].byST2:02x} ", end="")
        print(f"PDA:{header.si[i].byPDA:02x} ", end="")
        print(f"addr:{(header.dwHeadSize + i * 256):08x}")
#[EOF]
 

2026-02-25

NetBSD/i386 9.4でThunderbirdを動かそうとしたが断念したものの、Geminiとの対話は役に立った

NetBSD/i386 9.4のMATEデスクトップ環境が復活したので、Thunderbirdを入れてみようと思い立ちました。随分前にはpkgsrcでThunderbirdをインストールした記憶がありますが、何時の頃からかpkgsrcからは消えてしまいました。

 

NetBSDネイティブでなくても、LibreOfficeのようにLinuxエミュレーション環境でなんとかならないか試してみました。Geminiに相談しながら、インストールを試みます。インストール事態は、入手したファイルを展開するだけなので、別に難しくはありません。しかし実行しようとするとエラーが出ます。ここでGeminiに相談すると、いろいろとアドバイスしてくれます。

 

Geminiとの対話を繰り返しながら、あれこれやってみましたが、最終的にThunderbirdを動かすことはできないという結論に至りました。それはそれで構わないのですが、Geminiとの対話は、とても有効だったと感じました。Geminiのアドバイスが常に正確で間違いないという訳ではないのですが、それは現実の「詳しい知人」であっても似たようなものです。「詳しい知人」に比べてれば、Geminiの方が詳しい(知識量が多い)と思います。しかも「詳しい知人」は、機嫌を損ねると、へそを曲げたりしますが、Geminiには、そのような心配はありません。

 

Gemini(というか生成AI)は、うまく使えば、とても便利だと思います。しかし、Geminiが常に正しい情報を伝えてくれるわけでもなく、その伝えてくる情報を吟味して、何をするのか自分自身が理解している必要があると思います。Geminiがあれば、自分は勉強しなくても良いわけではなく、むしろ更に勉強しなければならないでしょう。

 

Geminiにも出来ることと出来ないことがあると思いますが、うまく使いこなせれば、とてもメリットがあると思います。 

T98-NEXTのNFD形式はxnp2では扱えなかった

NetBSD/i386でxnp2が動くようになり、PC-9801用Wizardryが動作したので、Windows 11上のT98-NEXTで保存したファイルを使おうとしたら、認識されませんでした。T98-NEXTではNFD形式ですが、xnp2はFDI形式を期待しているようです。FDイメージファイルの形式は、なんでも良いかと思っていましたが、そうでもないようです。

 

ネットを検索すると、FDイメージファイルの形式を変換するプログラムが見つかりました。NFD形式からFDI形式に変換できましたが、xnp2では認識されません。

 

変換されたフィルを確認すると、認識されるFDI形式とはファイルサイズが違うようです。強引ですが、バイナリエディタを使い、余分なところを切り詰めてみました。そうしたら、xnp2でも認識されるようになりました。

 

これで、Windows11上のT98-NEXTで途中までプレイした結果ファイル(NFD形式)を、FDI形式に変換し、末尾を切り落として、NetBSD/i386にコピーすれば、xnp2で続きをプレイできます。途中の手順がやや面倒ですが、機械的に処理する方法を探ってみようと思います。

2026-02-24

NetBSD/i386 9.4上のxnp2 0.86でPC-9801用Wizardryが動いた

NetBSD/i386 9.4にxnp2 0.86をインストールできたので、PC-9801用のWizardryを動かしてみました。何のトラブルに見舞われることもなく、あっさりと動作しました。

 

Windows11上でT98-NEXTを使っている場合には、FDイメージのファイルには拡張子「.NFD」がつくのですが、xnp2は「.FDI」か「.D88」を期待するようです。しかしこれは拡張子のネーミングの問題に過ぎないと思うので、FDイメージの構造が異なるわけではないでしょうから、おそらく問題にはならないと思います。

 

 xnp2は、FDイメージファイルをアクセスする際に、FDドライブのシーク音を出します。これはT98-NEXTには無かった機能です。物理的なFDドライブが存在するわけではないので、気分の問題ですが、シーク音を耳にすると懐かしさを覚えます。またリセット直後には、おなじみの「ピポッ」という音がなります。これはT98-NEXTもそうでした。これが聞こえるとPC-9801の思い出が蘇ります。

 

これで、Windows11上のT98-NEXTと、NetBSD/i386上のxnp2で、Wizardryが遊べるようになりました。両方の環境を使って遊ぶには、途中経過を記録したFDイメージの取り扱いが問題になってくるでしょう。何か良い方法を考えようと思います。 

NetBSD/i386 9.4でxnp2 0.86が動作した

NetBSD/i386 9.4環境で、パッケージからnxp2をインストールしようとしました。そうしたら共有ライブラリの不整合が発生し、pkgin full-upgradeを実行しました。するとエラーが出て、Geminiに相談しながら調査したら、パッケージ管理ディレクトリが/var/db/pkgと/usr/pkg/pkgdbの2箇所に見つかり、これが原因で深刻な不整合が起きていることが発覚しました。Geminiのアドバイスを受けながら、インストール済みの全パッケージを削除し、入れ直しました。しかし欠けているパッケージがあるので、確認しながら、追加インストールして、ようやくMATEデスクトップ環境が復活しました。

 

あらためて、本来の目的だったxnp2をインストールしました。何も問題がなくインストールできました。

 

動かしてみたのですが、PC-9801の起動時画面が出るはずですが、文字がかすれて読めません。フォントを別途入手する必要があるようです。Neko Project 21/Wのサイトから「font.bmp」を入手して「~/.np2/font.bmp」に置きました。これで起動時画面の文字が読めるようになり、一歩前進です。

 

次は、Wizardryが動くか確認しようと思います。