FAT FS フォーマットの実装についての覚え書き

2004-05-09公開
2004-05-10MBR拡張フォーマット・ヘッド情報
LNF 文字パディングを修正


この文書は、FAT ファイル・システムについての技術内容を書き表したものである。
FAT は、パソコンをはじめ、デジカメなどのさまざまな機器で利用可能なファイル・システムである。 ハード・ディスクをはじめ、各種メモリー・カードなどの外部記憶装置・記録媒体のフォーマットとして利用可能だ。
しかし、何らかの理由で FAT を利用した機器を作ろうとしたとき、 FAT を利用する方法については数多くの情報が散在するが、 実際の機器に実装する場合の(日本語の)技術情報がきわめて少ないことに気がついた。
このページを作った本来の趣旨は、趣味のマイコン工作で MMC (Multi-Media Card) や SD カードを使おうとしたときに集めた FAT の情報を書き留めておこうとしたものである。 まだ、情報を集めただけであるが、FAT に関する技術解説が少ないことから Web 上で公開することとした。 間違いや追加すべき情報があれば、適宜変更している予定である。
小型の組み込み機器や趣味のマイコン工作で FAT を使い、 大容量の記憶媒体を利用するための日本語の情報源として、このページを利用していただければ幸いである。

このページを読むためには、少なくても趣味レベルのマイコン工作についての知識が必要である。 さらに、プログラミング言語 (C) に関する知識があればなお良い。

1. はじめに

(1) FAT とは

FAT (File Allocation Table) は、かつて MS-DOS のフロッピー・ディスクのフォーマットとして利用された。 その後、歴史を経て、さまざまなOSや機器で利用され、最も普及したファイル・システムとなった。

初期の記録媒体は容量が少なく FAT12 と呼ばれる記録方式を取っていた。その後、記録容量の増大にしたがって、 2 GBytes までの記録が可能な FAT16、さらに 2 TBytes まで記録可能な FAT32 へと拡張された。

MS-DOS では、ファイルの名前は、8.3 形式というべき、8文字+3文字で表していた。 その後、長いファイル名 (LNF: Long File Name) が利用可能な VFAT (Virtual FAT) が現れた。

このページでは、FAT16 を主に取り上げる。右の図に FAT16 フォーマットの概要を示す。 FAT12 は FAT16 とほとんど変わらない。

(2) 若干の用語説明

FAT は、ディスク・ドライブのはじめのほうにある、ファイルの記録順を記憶した配列 (table) を示す。 このテーブルの名前がファイル・システムの名前として普及したのである。

記録媒体はセクタ (sector) と呼ばれるブロック単位で読み書きを行う。このセクタは、 MS-DOS からの伝統に従って 512 Bytes が標準的な大きさとなっている。

実際のファイルは、セクタをさらにまとめたクラスタ (cluster) 単位で読み書きを行う。 1クラスタは、複数 (1 〜 64) 個のセクタをまとめたものとなっているのだ。 そのため、一般に知られているように、たとえ1バイトのファイルであろうとも、 1クラスタ分の容量を記録媒体上に占めることとなる。

MMC や SD カードのような記録媒体の最初のセクタ (sector zero) のことを MBR (Master Boot Recode) とよぶ。 この MBR には、物理的な記録媒体を1個から4個のドライブに分割するためのパーティション情報が記録されている。

その他に、レコード、トラック、ヘッドなどという用語があるが、ほとんど使わないので、この文書では無視する。

(3) データ構造について

記録媒体上に書き込んでいる FAT のデータ構造を表すため、C言語風のデータ構造表記を行っている。 これは、実際に FAT ファイル・システムを実装することを念頭において、このページを書いているためである。

実際のシステム上にプログラムを組むときは、FAT のデータ構造がパック状態 (1バイトのすき間も無く詰め込まれた状態) であることと、バイト並びがビッグ・エンディアン (Intel 並び) であることに留意する必要がある。

(4) 特許について

FAT に関する知的所有権は、米国 Microsoft 社にある。
米国 Microsoft 社は、FAT 関連の特許をライセンス供与する方針である。
ある技術が広く普及してから、特許権を主張するのは道義的にすばらしいといえない。 しかし、FAT に関する米国特許は、米国 Microsoft 社にあることは確かなようだ。 FAT (とくに VFAT) の独占使用権は、米国 Microsoft 社にあると見てよいだろう。

2. FAT フォーマット

(1) MBR (Master Boot Recode)

MMC や SD カードのような記録媒体の先頭セクタ (sector 0) に、MBR (Master Boot Recode) がある。 これは、次のような 512 Bytes のデータ構造となっている。

typedef struct MasterBootRecode {
    Byte    checkRoutionOnx86[446];
    struct {
        Byte    bootDescriptor;             /* 0x80: bootable device, 0x00: non-bootable */
        Byte    firstPartitionSector[3];    /* 1st sector number */
        Byte    fileSystemDescriptor;       /* 1:FAT12, 4:FAT16(less than 32MB), 5:拡張 DOS パーティション,
                                               6:FAT16(more 32MB), 0xb:FAT32(more 2GB),
                                               0xc:FAT32 Int32h 拡張, 0xe:FAT16 Int32h 拡張,
                                               0xf:拡張 DOS パーティションの Int32h 拡張 */
        Byte    lastPartitionSector[3];
        Byte    firstSectorNumbers[4];      /* first sector number (link to BPB sector) */
        Byte    numberOfSectors[4];
    }   partitionTable[4];
    Byte    sig[2];                         /* 0x55, 0xaa */
}   MBRecode;

(32 bit 以上の CPU では、partionTable 構造体がパックされないかもしれない)

MBR 中のパーティション・テーブルに各パーティションのファイル・システム・タイプと先頭セクタが記述している。
パーティション・テーブル (partitionTable[]) の fileSystemDescriptor が 4 or 6 ならば FAT16 を示し、 11 ならば FAT32 を表す。 パーティション・テーブルにあるパーティションの先頭セクタ (partitionTable[].firstSectorNumbers) の セクタ番号にパーティションの先頭セクタ BPB (BIOS Parameter Block) が存在する。
最後の 2 Bytes の 0x55, 0xaa 固定パターンで、MBR セクタであることを示している。

下の16進ダンプは、MBR の内容のダンプ例である。

この16進ダンプは、32 MBytes の MMC (Multi-Media Card) での例である。(SDカードなどでも同じであろう)

上記例では、アドレス 0x5101be からパーティション・テーブルが始まっている (着色部分) 。
パーティションは、1つしかなく、その fileSystemDescriptor が 4 であるため、 FAT16 ファイル・システムであることがわかる。 また、firstSectorNumbers が 0x00000020 であるので、このパーティションの情報を 0x0020 セクタに格納していることが分かる。

なお、拡張パーティションのような場合、パーティションの先頭セクタ BPB は、firstPartitionSector に記録している。
firstPartitionSector[0] に、パーティションの先頭セクタのヘッド番号が記録されている。
firstPartitionSector[1] の bit 0 〜 5 に、パーティションの先頭セクタの (1 から始まる) セクタ番号が記録されている。
firstPartitionSector[1] の bit 6, 7 bits (上位 2 bits) と firstPartitionSector[2] (下位 8 bits) の 合計 10 bits に、パーティションの先頭セクタのトラック番号が記録されている。

(2) BPB (BIOS Parameter Block)

BPB は、MBR のパーティション・テーブル中に記録している先頭セクタに存在する。 そのデータ構造は、パーティションのパラメータを次のような 512 Bytes のデータで表している。

typedef union BPBlock_t {
    /* FAT16 or FAT12 BPB */
    struct FAT16BPB_t {
        Byte    jmpOpeCode[3];          /* 0xeb ?? 0x90 */
        Byte    OEMName[8];
        /* FAT16 */
        Byte    bytesPerSector[2];      /* bytes/sector */
        Byte    sectorsPerCluster;      /* sectors/cluster */
        Byte    reservedSectors[2];     /* reserved sector, beginning with sector 0 */
        Byte    numberOfFATs;           /* file allocation table */
        Byte    rootEntries[2];         /* root entry (512) */
        Byte    totalSectors[2];        /* partion total secter */
        Byte    mediaDescriptor;        /* 0xf8: Hard Disk */
        Byte    sectorsPerFAT[2];       /* sector/FAT (FAT32 always zero: see bigSectorsPerFAT) */
        Byte    sectorsPerTrack[2];     /* sector/track (not use) */
        Byte    heads[2];               /* heads number (not use) */
        Byte    hiddenSectors[4];       /* hidden sector number */
        Byte    bigTotalSectors[4];     /* total sector number */
        /* info */
        Byte    driveNumber;
        Byte    unused;
        Byte    extBootSignature;
        Byte    serialNumber[4];
        Byte    volumeLabel[11];
        Byte    fileSystemType[8];      /* "FAT16   " */
        Byte    loadProgramCode[448];
        Byte    sig[2];                 /* 0x55, 0xaa */
    }    fat16;
    /* FAT32 BPB */
    struct FAT32BPB_t {
        Byte    jmpOpeCode[3];          /* 0xeb ?? 0x90 */
        Byte    OEMName[8];
        /* FAT32 */
        Byte    bytesPerSector[2];
        Byte    sectorsPerCluster;
        Byte    reservedSectors[2];
        Byte    numberOfFATs;
        Byte    rootEntries[2];
        Byte    totalSectors[2];
        Byte    mediaDescriptor;
        Byte    sectorsPerFAT[2];
        Byte    sectorsPerTrack[2];
        Byte    heads[2];
        Byte    hiddenSectors[4];
        Byte    bigTotalSectors[4];
        Byte    bigSectorsPerFAT[4];    /* sector/FAT for FAT32 */
        Byte    extFlags[2];            /* use index zero (follows) */
                                        /* bit 7      0: enable FAT mirroring, 1: disable mirroring */
                                        /* bit 0-3    active FAT number (bit 7 is 1) */
        Byte    FS_Version[2];
        Byte    rootDirStrtClus[4];     /* root directory cluster */
        Byte    FSInfoSec[2];           /* 0xffff: no FSINFO, other: FSINFO sector */
        Byte    bkUpBootSec[2];         /* 0xffff: no back-up, other: back up boot sector number */
        Byte    reserved[12];
        /* info */
        Byte    driveNumber;
        Byte    unused;
        Byte    extBootSignature;
        Byte    serialNumber[4];
        Byte    volumeLabel[11];
        Byte    fileSystemType[8];      /* "FAT32   " */
        Byte    loadProgramCode[420];
        Byte    sig[2];                 /* 0x55, 0xaa */
    }    fat32;
}   BPBlock;

この BPB は FAT16 と FAT32 で異なる。FAT32 は、68 バイト目から FAT16 を拡張した構造となっている。 FAT32 の BPB は、sectorsPerFAT がゼロとなることで区別することが可能である。 また、FAT12 は FAT16 と同じ構造となっている。

ここで重要なパラメータは、bytesPerSector, sectorsPerCluster, sectorsPerFAT (FAT32 の場合 bigSectorsPerFAT) である。
bytesPerSector は、1セクタ当たりのバイト数を表す (たいてい、このパラメータは 512 であろう) 。
sectorsPerCluster は、1クラスタ当たりのセクタ数を表す (2 の n 乗個となっているはずだ) 。
sectorsPerFAT は、1つの FAT が占有するセクタ数を表す。

また、numberOfFATs はパーティション中に含まれる FAT の数を示す。FAT16 の場合、numberOfFATs は、常に2であり、2つの FAT を持つことを示す。

reservedSectors (たいてい、この値は 1 であろう) は、BPB セクタを含めた予約セクタ数を示す。 BPB 領域が占めるセクタ数と思って問題無い。

下の16進ダンプは、BPB の内容のダンプ例である。

この16進ダンプは、32 MBytes の MMC で FAT16 フォーマットの例である。

この例では、bytesPerSector が 0x0200 (512)、sectorsPerCluster が 0x04、 sectorsPerFAT が 0x003d (61) となっている。
最後の 2 Bytes の 0x55, 0xaa 固定パターンで、BPB セクタであることを示している。

(3) FAT (File Allocation Table)

BPB の次のセクタから FAT が始まる。
この、FAT 領域のセクタ数は BPB のパラメータから sectorsPerFAT × numberOfFATs で算出できる。

FAT16 の場合、BPB の numberOfFATs は 2 に固定となっている。 これは、同じ内容を持つ FAT を2つ用意し、FAT を二重化することで、信頼性を向上させる目的を持つ。 ただし、この二重化は FAT が破壊されていることしか判別できない。書き込み/読み出し不良による FAT の破壊は、 たいていドライブ自体が持つエラー検出/訂正機能によって、判別可能である。 そのため、現在では無意味な二重化となっている。

FAT32 の場合、BPB の extFlags パラメータにより、有効な FAT を識別する。

FAT は File Allcation Table の名の通り、ファイルのデータが配置されていることを示すテーブルである。 このテーブルの要素が 16 ビットのフォーマットを FAT16 と呼び、32 ビットのものを FAT32 と呼ぶ。
例えば FAT16 の場合、BPB のパラメータから sectorsPerFAT × bytesPerSector ÷ 2 個の要素 (クラスタ) が存在することが分かる。 FAT の n 番目の要素は、n 番目のクラスタの配置状況を示す (これをクラスタ番号と呼ぼう) 。 クラスタが未使用なら 0x0000、次のクラスタへと続くなら次のクラスタ番号、最後のクラスタなら 0xfff8 〜 0xffff が FAT 領域に書き込まれている。
また、クラスタ番号 0x0001 は予約領域であることを示す。 (クラスタ番号 0x0000 も予約領域となる)

FAT に記録しているクラスタ番号を次の表にまとめる。

FAT12FAT16FAT32
未使用クラスタ0x0000x00000x00000000
予約クラスタ0x0010x00010x00000001
有効なユーザ・データ・クラスタ 0x002 〜 0xfff60x0002 〜 0xffff60x00000002 〜 0x0ffffff6
不良クラスタ0xff70xfff70x0ffffff7
最終クラスタ 0xff8 〜 0xfff0xfff8 〜 0xffff0x0ffffff8 〜 0x0fffffff

上の表のように FAT32 といっても、28 bits しか使用していない。上位 4 bits は将来のために予約されている。 FAT32 を使用する際は、上位 4 bits をゼロにクリアして使用すべきである。

下の16進ダンプは、FAT の先頭領域のダンプ例である。

この16進ダンプは、例によって 32 MBytes の MMC で FAT16 フォーマットである。

最初の 0xfff8 は、クラスタ 0x0000 が最終クラスタであることを示している。(実際は未使用クラスタ)
次の 0xffff は、クラスタ 0x0001 も最終クラスタであることを示している。(実際は未使用クラスタ)

その次の 0x0003 は、クラスタ 0x0002 の次のクラスタが 0x0003 であることを示している。 以下順に、クラスタ 0x0004、0x0005 と続き、クラスタ 0x0005 にある最終クラスタのマーク (0xffff) で、 このファイルのクラスタが終了することを示している。 (クラスタ 0x0002 〜 0x0005 が同じファイルのデータであることを示している)
このように、FAT 領域に次のクラスタ情報を記録することで、 連続したデータ領域を示すことをクラスタ・チェインと呼ぶ。

クラスタ 0x0006 以降の FAT 要素が 0x0000 となっているのは、これらのクラスタは未使用状態であることを示している。

(4) RDE (Root Directory Entry)

RDE は、ルート・ディレクトリの情報を記録した配列であり、FAT16 の場合 FAT の次のセクタから記録される。
(FAT32 の場合、BPB の rootDirStrtClus で示すクラスタから記録される)

FAT16 の場合、格納されるルート・ディレクトリ情報の数は、BPB の rootEntries (この個数は 512 個の固定となっている) に記述している。
(FAT32 の場合、クラスタ・チェインで RDE を示しているので、RDE の大きさは可変である)
ひとつのディレクトリ・エントリは 32 Bytes のデータ長を持つ。FAT16 の場合、これが 512 個あるため、 RDE 全体では 32 × 512 = 16384 Bytes (32 セクタ) の領域を占める。

ひとつのディレクトリ・エントリは次のような 32 Bytes のデータ構造を持つ。

typedef struct DirEntry_t {
    Byte    name[8];            /* file name */
    Byte    extension[3];       /* file name extension */
    Byte    attribute;          /* file attribute
                                     bit 4    directory flag
                                     bit 3    volume flag
                                     bit 2    hidden flag
                                     bit 1    system flag
                                     bit 0    read only flag */
    Byte    reserved;           /* use NT or same OS */
    Byte    createTimeMs;       /* VFAT で使用するファイル作成時刻の10ミリ秒 (0 〜 199) */
    Byte    createTime[2];      /* VFAT で使用するファイル作成時間 */
    Byte    createDate[2];      /* VFAT で使用するファイル作成日付 */
    Byte    accessDate[2];      /* VFAT で使用するファイル・アクセス日付 */
    Byte    clusterHighWord[2]; /* FAT32 で使用するクラスタ番号の上位 16 bits */
    Byte    updateTime[2];
    Byte    updateDate[2];
    Byte    cluster[2];         /* start cluster number */
    Byte    fileSize[4];        /* file size in bytes (directory is always zero) */
}   DirEntry;

name および extension

FAT の 8.3 形式のファイル名を格納する。ファイル名の条件は、先頭の1文字目が英数字で、 . " / \ [ ] : ; | = , 以外の文字が使用可能である。全ての英小文字は英大文字に変換して記憶する。 また、CON AUX COM1 COM2 COM3 COM4 LPT1 LPT2 LPT3 PRN NUL の名前は使用できない。

先頭の1文字目によって、次の意味がある。
0x00  空エントリである。
0x05  (0xe5 を参照)
0x2e  ディレクトリ・エントリの場合のみ使用できる。
      0x2e (.)          カレント・ディレクトリ
      0x2e, 0x2e (..)   親ディレクトリ
0xe5  削除されたエントリ (使用可能)

attribute

属性フラグである。この値が 0x0f ならば、LNF (Long File Name) を表す特別なエントリとなる。
bit 5    アーカイブ属性 (ファイル作成・変更時 1 にする)
bit 4    ディレクトリ属性
bit 3    ボリューム属性
bit 2    隠し属性
bit 1    システム属性
bit 0    読み取り専用属性

updateTime

ファイルが更新された時間を示す。時間は、次のように 16 bits を分割し、時分秒の2秒単位で表す。
bit 15 〜 11       時
bit 10 〜  5       分
bit  4 〜  0       秒÷2

updateDate

ファイルが更新された日付を示す。日付は、次のように 16 bits を分割し、年月日で表す。
bit 15 〜 9      年 (1980年をゼロ年とする)
bit  8 〜 5      月 (1 〜 12)
bit  4 〜 0      日 (1 〜 31)

cluster

このディレクトリ・エントリが指し示すファイル内容の先頭クラスタ番号
(FAT32 の場合、下位 16 bits を示し、上位 16 bits は clusterHighWord で表す)

fileSize

ファイルの大きさをバイト単位で表す。エントリがディレクトリ属性ならば、このファイル・サイズはゼロとなる。

下の16進ダンプは、RDE の先頭領域のダンプ例である。

上記例は、H8MMC.MOT という名前のファイルのディレクトリ・エントリを示している。
attribute は、アーカイブ属性となっている。
ファイル作成時刻は、0xabf6 (10101b 011111b 10110b) の 0x36 ms となっている。(21時31分44.54秒)
ファイル作成日付は、0x3099 (0011000b 0100b 11001b) となっている。(2004年4月25日)
アクセス日付が 0x30a5 (2004年5月5日)、ファイル更新が 0x3099 (2004年4月25日) の 0xa736 (20時57分44秒) 。
先頭クラスタは 0x00000002 クラスタとなっている。ファイル・サイズは、0x00001a00 バイトである。

(4) ユーザ・データ領域

ユーザ・データ領域は、実際のファイル・データやディレクトリ・エントリを保存する。 情報は、クラスタ単位で管理され、クラスタ番号によって識別される。 FAT16 の場合、ユーザ・データ領域は、RDE の次のセクタから始まる。

このユーザ・データ領域の開始セクタは、MBR のパーティション先頭セクタと BPB に記録されている情報から次のように計算できる。

reservedSectors + numberOfFATs×sectorsPerFAT + rootEntries×32÷bytesPerSector + [パーティション先頭セクタ]
(FAT16 では rootEntries を 512、FAT32 では rootEntries をゼロとして計算する)

ユーザ・データ領域のセクタ数は、BPB に記録されている情報から次のように計算できる。

bigTotalSectors - reservedSectors - numberOfFATs×sectorsPerFAT - rootEntries×32÷bytesPerSector
(FAT16 では rootEntries を 512、FAT32 では rootEntries をゼロとして計算する)

クラスタ番号 0x0000 と 0x0001 は予約されているので、実際にデータが格納されるクラスタ番号は 0x0002 から始まる。 そのため、ユーザ・データ領域の先頭は、クラスタ番号 0x0002 となる。
クラスタ番号 n が格納されているセクタは、ユーザ・データ領域の先頭セクタから、 (n - 2) × sectorsPerCluster だけ離れていることになる。

また、子ディレクトリ・エントリのデータは、次の例のような内容を持つファイルとして記録される。

上の例では、ディレクトリ・エントリが2あり、それぞれのファイル名が . と .. となっている。 このファイル名は、ディレクトリを示す。そのため、属性フラグもディレクトリ属性を示している
ファイル名 . (dot) は、カレント・ディレクトリを示し、その開始クラスタ番号は 0x0006 となっている。 このこれは、カレント・ディレクトリ情報がクラスタ 0x0006 から記録されていることを示している。
同様に、ファイル名 .. (dot-dot) の開始クラスタ番号が 0x0cf7 となっているのは、 親ディレクトリ情報がクラスタ 0x0cf7 から格納されていることを示す。 この親ディレクトリの開始クラスタ番号が 0x0000 ならば、親ディレクトリはルート・ディレクトリであることを示す。

3. VFAT

VFAT は、以前との互換性を維持しながら MS-DOS 時代の 8.3 形式のファイル名より長いファイルを扱う。 そのために、従来の FAT のディレクトリ・エントリが拡張されている。

(1) VFAT のディレクトリ・エントリ

ディレクトリ・エントリについは、前の RDE にて説明している。 VFAT の場合、8.3 形式の MS-DOS ファイル名のディレクトリ・エントリとともに、 次のような 13 文字の Unicode を記録する LNF ディレクトリ・エントリを記録する。
typedef struct LNFEntry_t {
    Byte    LFNSeqNumber;           /* LNF recode sequence number
                                         bit 7        削除されると 1 になる
                                         bit 6        0: LNF は続く、1: LNF の最後のシーケンス
                                         bit 5 〜 0   LNF の順番を表す (1 〜 63) */
    Byte    name1st[10];            /* Unicode 5 characters */
    Byte    attribute;              /* file attribute (always 0x0f) */
    Byte    reserved;               /* always 0x00 */
    Byte    checkCodeForShortName;  /* DOS 名のチェック・コード */
    Byte    name2nd[12];            /* Unicode 6 characters */
    Byte    cluster[2];             /* always 0x0000 */
    Byte    name3rd[4];             /* Unicode 2 characters */
}   LNFEntry;

長いファイル名を 13 文字ごとに分割して、この LNF ディレクトリ・エントリを記録する。

LFNSeqNumber13 文字ごとに分割した LNF ディレクトリ・エントリの順番を示す。
name1st13 文字中の最初の 1 〜 5 文字
attribute0x0f で LNF ディレクトリ・エントリであることを示す。
reserved常に 0x00
checkCodeForShortName8.3 形式のファイル名のチェック・コード
name2nd13 文字中の 6 〜 11 文字
cluster常に 0x00
name3rd13 文字中の 12, 13 文字

実際の例を見てみよう。

この例では、Object.class という名前のディレクトリ・エントリを示している。 先頭の 32 Bytes が LNF ディレクトリ・エントリである。 次の 32 Bytes が 8.3 形式の MS-DOS 互換ディレクトリ・エントリである (OBJECT~1.CLA という名前となっている)。
例のように LNF ディレクトリ・エントリの後に MS-DOS 互換ディレクトリ・エントリが続く。 複数の LNF ディレクトリ・エントリが必要な場合は、後ろの文字の方からディレクトリ・エントリを記録する。
(2つ以上の LNF ディレクトリ・エントリが必要な場合は、(2) も参照すること)

LNF エントリの部分は、attribute が 0x0f で LNF ディレクトリ・エントリであることを示し、 先頭の LFNSeqNumber が 0x41 となっていることから、先頭の LNF エントリでかつ LNF の終わりであることを示している。
また、Object.class が 12 文字と 13 文字に満たないので、13 文字目を 0x0000 で埋めている。 (2文字以上の空が生じる場合、2文字以降 0xffff で埋める)

checkCodeForShortName の値は、8.3 形式の MS-DOS 名から求める。算出方法は、次のようになっている。 ("OBJECT~1.CLA" を計算すると、0x76 になる)
int shortNameCheckCode(const DirEntry* dir) {
    unsigned char*  p = dir->name;
    unsigned char*  end = p + 11;
    int             ret = 0;
    while (p < end) ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + *p++;
    return ret;
}

(2) 2バイト・コード (日本語ファイル名)

2バイト・コードの LNF ディレクトリ・エントリの例を下に示す。(これは、LNF が2エントリ以上ある例にもなっている)

この MS-DOS ファイル名は "日本語~1.PDF" である。
MS-DOS 互換ディレクトリ・エントリのファイル名は、SJIS で 0x93fa (日), 0x967b (本), 0x8cea (語), ... のようになっている。
一方、LNF ディレクトリ・エントリは、Unicode で 0x65e5 (日), 0x672c (本), 0x8a9e (語), ... となる。SJIS と Unicode のバイト並びの違いに注意する必要がある。
(ちなみに、SJIS コードと Unicode の相互変換はテーブル引きで変換する方法しかない)

なお、最後の LNF エントリが 11 文字以内の場合は、 最後の文字の後に 0x0000 の終端文字を格納し、残りの文字を 0xffff で埋める。 (最後の LNF エントリが 12 文字なら、最後の文字を 0x0000 で埋める)

home