vfat: PHP parsing library

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.9

References

This page hosts a formal specification of vfat using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

PHP source code to parse vfat

Vfat.php

<?php
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

namespace {
    class Vfat extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \Kaitai\Struct\Struct $_parent = null, \Vfat $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_bootSector = new \Vfat\BootSector($this->_io, $this, $this->_root);
        }
        protected $_m_fats;
        public function fats() {
            if ($this->_m_fats !== null)
                return $this->_m_fats;
            $_pos = $this->_io->pos();
            $this->_io->seek($this->bootSector()->posFats());
            $this->_m_fats = [];
            $n = $this->bootSector()->bpb()->numFats();
            for ($i = 0; $i < $n; $i++) {
                $this->_m_fats[] = $this->_io->readBytes($this->bootSector()->sizeFat());
            }
            $this->_io->seek($_pos);
            return $this->_m_fats;
        }
        protected $_m_rootDir;
        public function rootDir() {
            if ($this->_m_rootDir !== null)
                return $this->_m_rootDir;
            $_pos = $this->_io->pos();
            $this->_io->seek($this->bootSector()->posRootDir());
            $this->_m__raw_rootDir = $this->_io->readBytes($this->bootSector()->sizeRootDir());
            $_io__raw_rootDir = new \Kaitai\Struct\Stream($this->_m__raw_rootDir);
            $this->_m_rootDir = new \Vfat\RootDirectory($_io__raw_rootDir, $this, $this->_root);
            $this->_io->seek($_pos);
            return $this->_m_rootDir;
        }
        protected $_m_bootSector;
        protected $_m__raw_rootDir;
        public function bootSector() { return $this->_m_bootSector; }
        public function _raw_rootDir() { return $this->_m__raw_rootDir; }
    }
}

/**
 * Extended BIOS Parameter Block for FAT32
 */

namespace Vfat {
    class ExtBiosParamBlockFat32 extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \Vfat\BootSector $_parent = null, \Vfat $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_lsPerFat = $this->_io->readU4le();
            $this->_m_hasActiveFat = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_reserved1 = $this->_io->readBitsIntLe(3);
            $this->_m_activeFatId = $this->_io->readBitsIntLe(4);
            $this->_io->alignToByte();
            $this->_m_reserved2 = $this->_io->readBytes(1);
            if (!($this->reserved2() == "\x00")) {
                throw new \Kaitai\Struct\Error\ValidationNotEqualError("\x00", $this->reserved2(), $this->_io(), "/types/ext_bios_param_block_fat32/seq/4");
            }
            $this->_m_fatVersion = $this->_io->readU2le();
            $this->_m_rootDirStartClus = $this->_io->readU4le();
            $this->_m_lsFsInfo = $this->_io->readU2le();
            $this->_m_bootSectorsCopyStartLs = $this->_io->readU2le();
            $this->_m_reserved3 = $this->_io->readBytes(12);
            $this->_m_physDriveNum = $this->_io->readU1();
            $this->_m_reserved4 = $this->_io->readU1();
            $this->_m_extBootSign = $this->_io->readU1();
            $this->_m_volumeId = $this->_io->readBytes(4);
            $this->_m_partitionVolumeLabel = \Kaitai\Struct\Stream::bytesToStr(\Kaitai\Struct\Stream::bytesStripRight($this->_io->readBytes(11), 32), "ASCII");
            $this->_m_fsTypeStr = \Kaitai\Struct\Stream::bytesToStr(\Kaitai\Struct\Stream::bytesStripRight($this->_io->readBytes(8), 32), "ASCII");
        }
        protected $_m_lsPerFat;
        protected $_m_hasActiveFat;
        protected $_m_reserved1;
        protected $_m_activeFatId;
        protected $_m_reserved2;
        protected $_m_fatVersion;
        protected $_m_rootDirStartClus;
        protected $_m_lsFsInfo;
        protected $_m_bootSectorsCopyStartLs;
        protected $_m_reserved3;
        protected $_m_physDriveNum;
        protected $_m_reserved4;
        protected $_m_extBootSign;
        protected $_m_volumeId;
        protected $_m_partitionVolumeLabel;
        protected $_m_fsTypeStr;

        /**
         * Logical sectors per file allocation table (corresponds with
         * the old entry `ls_per_fat` in the DOS 2.0 BPB).
         */
        public function lsPerFat() { return $this->_m_lsPerFat; }

        /**
         * If true, then there is "active" FAT, which is designated in
         * `active_fat` attribute. If false, all FATs are mirrored as
         * usual.
         */
        public function hasActiveFat() { return $this->_m_hasActiveFat; }
        public function reserved1() { return $this->_m_reserved1; }

        /**
         * Zero-based number of active FAT, if `has_active_fat`
         * attribute is true.
         */
        public function activeFatId() { return $this->_m_activeFatId; }
        public function reserved2() { return $this->_m_reserved2; }
        public function fatVersion() { return $this->_m_fatVersion; }

        /**
         * Cluster number of root directory start, typically 2 if it
         * contains no bad sector. (Microsoft's FAT32 implementation
         * imposes an artificial limit of 65,535 entries per directory,
         * whilst many third-party implementations do not.)
         */
        public function rootDirStartClus() { return $this->_m_rootDirStartClus; }

        /**
         * Logical sector number of FS Information Sector, typically 1,
         * i.e., the second of the three FAT32 boot sectors. Values
         * like 0 and 0xFFFF are used by some FAT32 implementations to
         * designate abscence of FS Information Sector.
         */
        public function lsFsInfo() { return $this->_m_lsFsInfo; }

        /**
         * First logical sector number of a copy of the three FAT32
         * boot sectors, typically 6.
         */
        public function bootSectorsCopyStartLs() { return $this->_m_bootSectorsCopyStartLs; }
        public function reserved3() { return $this->_m_reserved3; }

        /**
         * Physical drive number (0x00 for (first) removable media,
         * 0x80 for (first) fixed disk as per INT 13h).
         */
        public function physDriveNum() { return $this->_m_physDriveNum; }
        public function reserved4() { return $this->_m_reserved4; }

        /**
         * Should be 0x29 to indicate that an EBPB with the following 3
         * entries exists.
         */
        public function extBootSign() { return $this->_m_extBootSign; }

        /**
         * Volume ID (serial number).
         * 
         * Typically the serial number "xxxx-xxxx" is created by a
         * 16-bit addition of both DX values returned by INT 21h/AH=2Ah
         * (get system date) and INT 21h/AH=2Ch (get system time) for
         * the high word and another 16-bit addition of both CX values
         * for the low word of the serial number. Alternatively, some
         * DR-DOS disk utilities provide a /# option to generate a
         * human-readable time stamp "mmdd-hhmm" build from BCD-encoded
         * 8-bit values for the month, day, hour and minute instead of
         * a serial number.
         */
        public function volumeId() { return $this->_m_volumeId; }
        public function partitionVolumeLabel() { return $this->_m_partitionVolumeLabel; }
        public function fsTypeStr() { return $this->_m_fsTypeStr; }
    }
}

namespace Vfat {
    class BootSector extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \Vfat $_parent = null, \Vfat $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_jmpInstruction = $this->_io->readBytes(3);
            $this->_m_oemName = \Kaitai\Struct\Stream::bytesToStr(\Kaitai\Struct\Stream::bytesStripRight($this->_io->readBytes(8), 32), "ASCII");
            $this->_m_bpb = new \Vfat\BiosParamBlock($this->_io, $this, $this->_root);
            if (!($this->isFat32())) {
                $this->_m_ebpbFat16 = new \Vfat\ExtBiosParamBlockFat16($this->_io, $this, $this->_root);
            }
            if ($this->isFat32()) {
                $this->_m_ebpbFat32 = new \Vfat\ExtBiosParamBlockFat32($this->_io, $this, $this->_root);
            }
        }
        protected $_m_posFats;

        /**
         * Offset of FATs in bytes from start of filesystem
         */
        public function posFats() {
            if ($this->_m_posFats !== null)
                return $this->_m_posFats;
            $this->_m_posFats = ($this->bpb()->bytesPerLs() * $this->bpb()->numReservedLs());
            return $this->_m_posFats;
        }
        protected $_m_lsPerFat;
        public function lsPerFat() {
            if ($this->_m_lsPerFat !== null)
                return $this->_m_lsPerFat;
            $this->_m_lsPerFat = ($this->isFat32() ? $this->ebpbFat32()->lsPerFat() : $this->bpb()->lsPerFat());
            return $this->_m_lsPerFat;
        }
        protected $_m_lsPerRootDir;

        /**
         * Size of root directory in logical sectors
         */
        public function lsPerRootDir() {
            if ($this->_m_lsPerRootDir !== null)
                return $this->_m_lsPerRootDir;
            $this->_m_lsPerRootDir = intval(((($this->bpb()->maxRootDirRec() * 32) + $this->bpb()->bytesPerLs()) - 1) / $this->bpb()->bytesPerLs());
            return $this->_m_lsPerRootDir;
        }
        protected $_m_isFat32;

        /**
         * Determines if filesystem is FAT32 (true) or FAT12/16 (false)
         * by analyzing some preliminary conditions in BPB. Used to
         * determine whether we should parse post-BPB data as
         * `ext_bios_param_block_fat16` or `ext_bios_param_block_fat32`.
         */
        public function isFat32() {
            if ($this->_m_isFat32 !== null)
                return $this->_m_isFat32;
            $this->_m_isFat32 = $this->bpb()->maxRootDirRec() == 0;
            return $this->_m_isFat32;
        }
        protected $_m_sizeFat;

        /**
         * Size of one FAT in bytes
         */
        public function sizeFat() {
            if ($this->_m_sizeFat !== null)
                return $this->_m_sizeFat;
            $this->_m_sizeFat = ($this->bpb()->bytesPerLs() * $this->lsPerFat());
            return $this->_m_sizeFat;
        }
        protected $_m_posRootDir;

        /**
         * Offset of root directory in bytes from start of filesystem
         */
        public function posRootDir() {
            if ($this->_m_posRootDir !== null)
                return $this->_m_posRootDir;
            $this->_m_posRootDir = ($this->bpb()->bytesPerLs() * ($this->bpb()->numReservedLs() + ($this->lsPerFat() * $this->bpb()->numFats())));
            return $this->_m_posRootDir;
        }
        protected $_m_sizeRootDir;

        /**
         * Size of root directory in bytes
         */
        public function sizeRootDir() {
            if ($this->_m_sizeRootDir !== null)
                return $this->_m_sizeRootDir;
            $this->_m_sizeRootDir = ($this->lsPerRootDir() * $this->bpb()->bytesPerLs());
            return $this->_m_sizeRootDir;
        }
        protected $_m_jmpInstruction;
        protected $_m_oemName;
        protected $_m_bpb;
        protected $_m_ebpbFat16;
        protected $_m_ebpbFat32;
        public function jmpInstruction() { return $this->_m_jmpInstruction; }
        public function oemName() { return $this->_m_oemName; }

        /**
         * Basic BIOS parameter block, present in all versions of FAT
         */
        public function bpb() { return $this->_m_bpb; }

        /**
         * FAT12/16-specific extended BIOS parameter block
         */
        public function ebpbFat16() { return $this->_m_ebpbFat16; }

        /**
         * FAT32-specific extended BIOS parameter block
         */
        public function ebpbFat32() { return $this->_m_ebpbFat32; }
    }
}

namespace Vfat {
    class BiosParamBlock extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \Vfat\BootSector $_parent = null, \Vfat $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_bytesPerLs = $this->_io->readU2le();
            $this->_m_lsPerClus = $this->_io->readU1();
            $this->_m_numReservedLs = $this->_io->readU2le();
            $this->_m_numFats = $this->_io->readU1();
            $this->_m_maxRootDirRec = $this->_io->readU2le();
            $this->_m_totalLs2 = $this->_io->readU2le();
            $this->_m_mediaCode = $this->_io->readU1();
            $this->_m_lsPerFat = $this->_io->readU2le();
            $this->_m_psPerTrack = $this->_io->readU2le();
            $this->_m_numHeads = $this->_io->readU2le();
            $this->_m_numHiddenSectors = $this->_io->readU4le();
            $this->_m_totalLs4 = $this->_io->readU4le();
        }
        protected $_m_bytesPerLs;
        protected $_m_lsPerClus;
        protected $_m_numReservedLs;
        protected $_m_numFats;
        protected $_m_maxRootDirRec;
        protected $_m_totalLs2;
        protected $_m_mediaCode;
        protected $_m_lsPerFat;
        protected $_m_psPerTrack;
        protected $_m_numHeads;
        protected $_m_numHiddenSectors;
        protected $_m_totalLs4;

        /**
         * Bytes per logical sector
         */
        public function bytesPerLs() { return $this->_m_bytesPerLs; }

        /**
         * Logical sectors per cluster
         */
        public function lsPerClus() { return $this->_m_lsPerClus; }

        /**
         * Count of reserved logical sectors. The number of logical
         * sectors before the first FAT in the file system image.
         */
        public function numReservedLs() { return $this->_m_numReservedLs; }

        /**
         * Number of File Allocation Tables
         */
        public function numFats() { return $this->_m_numFats; }

        /**
         * Maximum number of FAT12 or FAT16 root directory entries. 0
         * for FAT32, where the root directory is stored in ordinary
         * data clusters.
         */
        public function maxRootDirRec() { return $this->_m_maxRootDirRec; }

        /**
         * Total logical sectors (if zero, use total_ls_4)
         */
        public function totalLs2() { return $this->_m_totalLs2; }

        /**
         * Media descriptor
         */
        public function mediaCode() { return $this->_m_mediaCode; }

        /**
         * Logical sectors per File Allocation Table for
         * FAT12/FAT16. FAT32 sets this to 0 and uses the 32-bit value
         * at offset 0x024 instead.
         */
        public function lsPerFat() { return $this->_m_lsPerFat; }

        /**
         * Physical sectors per track for disks with INT 13h CHS
         * geometry, e.g., 15 for a "1.20 MB" (1200 KB) floppy. A zero
         * entry indicates that this entry is reserved, but not used.
         */
        public function psPerTrack() { return $this->_m_psPerTrack; }

        /**
         * Number of heads for disks with INT 13h CHS geometry,[9]
         * e.g., 2 for a double sided floppy.
         */
        public function numHeads() { return $this->_m_numHeads; }

        /**
         * Number of hidden sectors preceding the partition that
         * contains this FAT volume. This field should always be zero
         * on media that are not partitioned. This DOS 3.0 entry is
         * incompatible with a similar entry at offset 0x01C in BPBs
         * since DOS 3.31.  It must not be used if the logical sectors
         * entry at offset 0x013 is zero.
         */
        public function numHiddenSectors() { return $this->_m_numHiddenSectors; }

        /**
         * Total logical sectors including hidden sectors. This DOS 3.2
         * entry is incompatible with a similar entry at offset 0x020
         * in BPBs since DOS 3.31. It must not be used if the logical
         * sectors entry at offset 0x013 is zero.
         */
        public function totalLs4() { return $this->_m_totalLs4; }
    }
}

namespace Vfat {
    class RootDirectoryRec extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \Vfat\RootDirectory $_parent = null, \Vfat $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_fileName = $this->_io->readBytes(11);
            $this->_m__raw_attrs = $this->_io->readBytes(1);
            $_io__raw_attrs = new \Kaitai\Struct\Stream($this->_m__raw_attrs);
            $this->_m_attrs = new \Vfat\RootDirectoryRec\AttrFlags($_io__raw_attrs, $this, $this->_root);
            $this->_m_reserved = $this->_io->readBytes(10);
            $this->_m__raw_lastWriteTime = $this->_io->readBytes(4);
            $_io__raw_lastWriteTime = new \Kaitai\Struct\Stream($this->_m__raw_lastWriteTime);
            $this->_m_lastWriteTime = new \DosDatetime($_io__raw_lastWriteTime);
            $this->_m_startClus = $this->_io->readU2le();
            $this->_m_fileSize = $this->_io->readU4le();
        }
        protected $_m_fileName;
        protected $_m_attrs;
        protected $_m_reserved;
        protected $_m_lastWriteTime;
        protected $_m_startClus;
        protected $_m_fileSize;
        protected $_m__raw_attrs;
        protected $_m__raw_lastWriteTime;
        public function fileName() { return $this->_m_fileName; }
        public function attrs() { return $this->_m_attrs; }
        public function reserved() { return $this->_m_reserved; }
        public function lastWriteTime() { return $this->_m_lastWriteTime; }
        public function startClus() { return $this->_m_startClus; }
        public function fileSize() { return $this->_m_fileSize; }
        public function _raw_attrs() { return $this->_m__raw_attrs; }
        public function _raw_lastWriteTime() { return $this->_m__raw_lastWriteTime; }
    }
}

namespace Vfat\RootDirectoryRec {
    class AttrFlags extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \Vfat\RootDirectoryRec $_parent = null, \Vfat $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_readOnly = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_hidden = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_system = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_volumeId = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_isDirectory = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_archive = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_reserved = $this->_io->readBitsIntLe(2);
        }
        protected $_m_longName;
        public function longName() {
            if ($this->_m_longName !== null)
                return $this->_m_longName;
            $this->_m_longName =  (($this->readOnly()) && ($this->hidden()) && ($this->system()) && ($this->volumeId())) ;
            return $this->_m_longName;
        }
        protected $_m_readOnly;
        protected $_m_hidden;
        protected $_m_system;
        protected $_m_volumeId;
        protected $_m_isDirectory;
        protected $_m_archive;
        protected $_m_reserved;
        public function readOnly() { return $this->_m_readOnly; }
        public function hidden() { return $this->_m_hidden; }
        public function system() { return $this->_m_system; }
        public function volumeId() { return $this->_m_volumeId; }
        public function isDirectory() { return $this->_m_isDirectory; }
        public function archive() { return $this->_m_archive; }
        public function reserved() { return $this->_m_reserved; }
    }
}

namespace Vfat {
    class RootDirectory extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \Vfat $_parent = null, \Vfat $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_records = [];
            $n = $this->_root()->bootSector()->bpb()->maxRootDirRec();
            for ($i = 0; $i < $n; $i++) {
                $this->_m_records[] = new \Vfat\RootDirectoryRec($this->_io, $this, $this->_root);
            }
        }
        protected $_m_records;
        public function records() { return $this->_m_records; }
    }
}

/**
 * Extended BIOS Parameter Block (DOS 4.0+, OS/2 1.0+). Used only
 * for FAT12 and FAT16.
 */

namespace Vfat {
    class ExtBiosParamBlockFat16 extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \Vfat\BootSector $_parent = null, \Vfat $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_physDriveNum = $this->_io->readU1();
            $this->_m_reserved1 = $this->_io->readU1();
            $this->_m_extBootSign = $this->_io->readU1();
            $this->_m_volumeId = $this->_io->readBytes(4);
            $this->_m_partitionVolumeLabel = \Kaitai\Struct\Stream::bytesToStr(\Kaitai\Struct\Stream::bytesStripRight($this->_io->readBytes(11), 32), "ASCII");
            $this->_m_fsTypeStr = \Kaitai\Struct\Stream::bytesToStr(\Kaitai\Struct\Stream::bytesStripRight($this->_io->readBytes(8), 32), "ASCII");
        }
        protected $_m_physDriveNum;
        protected $_m_reserved1;
        protected $_m_extBootSign;
        protected $_m_volumeId;
        protected $_m_partitionVolumeLabel;
        protected $_m_fsTypeStr;

        /**
         * Physical drive number (0x00 for (first) removable media,
         * 0x80 for (first) fixed disk as per INT 13h).
         */
        public function physDriveNum() { return $this->_m_physDriveNum; }
        public function reserved1() { return $this->_m_reserved1; }

        /**
         * Should be 0x29 to indicate that an EBPB with the following 3
         * entries exists.
         */
        public function extBootSign() { return $this->_m_extBootSign; }

        /**
         * Volume ID (serial number).
         * 
         * Typically the serial number "xxxx-xxxx" is created by a
         * 16-bit addition of both DX values returned by INT 21h/AH=2Ah
         * (get system date) and INT 21h/AH=2Ch (get system time) for
         * the high word and another 16-bit addition of both CX values
         * for the low word of the serial number. Alternatively, some
         * DR-DOS disk utilities provide a /# option to generate a
         * human-readable time stamp "mmdd-hhmm" build from BCD-encoded
         * 8-bit values for the month, day, hour and minute instead of
         * a serial number.
         */
        public function volumeId() { return $this->_m_volumeId; }
        public function partitionVolumeLabel() { return $this->_m_partitionVolumeLabel; }
        public function fsTypeStr() { return $this->_m_fsTypeStr; }
    }
}