macOS '.DS_Store' format: PHP parsing library

Apple macOS '.DS_Store' file format.

File extension

DS_Store

KS implementation details

License: MIT
Minimal Kaitai Struct required: 0.9

References

This page hosts a formal specification of macOS '.DS_Store' format 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 macOS '.DS_Store' format

DsStore.php

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

/**
 * Apple macOS '.DS_Store' file format.
 */

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

        private function _read() {
            $this->_m_alignmentHeader = $this->_io->readBytes(4);
            if (!($this->alignmentHeader() == "\x00\x00\x00\x01")) {
                throw new \Kaitai\Struct\Error\ValidationNotEqualError("\x00\x00\x00\x01", $this->alignmentHeader(), $this->_io(), "/seq/0");
            }
            $this->_m_buddyAllocatorHeader = new \DsStore\BuddyAllocatorHeader($this->_io, $this, $this->_root);
        }
        protected $_m_buddyAllocatorBody;
        public function buddyAllocatorBody() {
            if ($this->_m_buddyAllocatorBody !== null)
                return $this->_m_buddyAllocatorBody;
            $_pos = $this->_io->pos();
            $this->_io->seek(($this->buddyAllocatorHeader()->ofsBookkeepingInfoBlock() + 4));
            $this->_m__raw_buddyAllocatorBody = $this->_io->readBytes($this->buddyAllocatorHeader()->lenBookkeepingInfoBlock());
            $_io__raw_buddyAllocatorBody = new \Kaitai\Struct\Stream($this->_m__raw_buddyAllocatorBody);
            $this->_m_buddyAllocatorBody = new \DsStore\BuddyAllocatorBody($_io__raw_buddyAllocatorBody, $this, $this->_root);
            $this->_io->seek($_pos);
            return $this->_m_buddyAllocatorBody;
        }
        protected $_m_blockAddressMask;

        /**
         * Bitmask used to calculate the position and the size of each block
         * of the B-tree from the block addresses.
         */
        public function blockAddressMask() {
            if ($this->_m_blockAddressMask !== null)
                return $this->_m_blockAddressMask;
            $this->_m_blockAddressMask = 31;
            return $this->_m_blockAddressMask;
        }
        protected $_m_alignmentHeader;
        protected $_m_buddyAllocatorHeader;
        protected $_m__raw_buddyAllocatorBody;
        public function alignmentHeader() { return $this->_m_alignmentHeader; }
        public function buddyAllocatorHeader() { return $this->_m_buddyAllocatorHeader; }
        public function _raw_buddyAllocatorBody() { return $this->_m__raw_buddyAllocatorBody; }
    }
}

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

        private function _read() {
            $this->_m_magic = $this->_io->readBytes(4);
            if (!($this->magic() == "\x42\x75\x64\x31")) {
                throw new \Kaitai\Struct\Error\ValidationNotEqualError("\x42\x75\x64\x31", $this->magic(), $this->_io(), "/types/buddy_allocator_header/seq/0");
            }
            $this->_m_ofsBookkeepingInfoBlock = $this->_io->readU4be();
            $this->_m_lenBookkeepingInfoBlock = $this->_io->readU4be();
            $this->_m_copyOfsBookkeepingInfoBlock = $this->_io->readU4be();
            $this->_m__unnamed4 = $this->_io->readBytes(16);
        }
        protected $_m_magic;
        protected $_m_ofsBookkeepingInfoBlock;
        protected $_m_lenBookkeepingInfoBlock;
        protected $_m_copyOfsBookkeepingInfoBlock;
        protected $_m__unnamed4;

        /**
         * Magic number 'Bud1'.
         */
        public function magic() { return $this->_m_magic; }
        public function ofsBookkeepingInfoBlock() { return $this->_m_ofsBookkeepingInfoBlock; }
        public function lenBookkeepingInfoBlock() { return $this->_m_lenBookkeepingInfoBlock; }

        /**
         * Needs to match 'offset_bookkeeping_info_block'.
         */
        public function copyOfsBookkeepingInfoBlock() { return $this->_m_copyOfsBookkeepingInfoBlock; }

        /**
         * Unused field which might simply be the unused space at the end of the block,
         * since the minimum allocation size is 32 bytes.
         */
        public function _unnamed4() { return $this->_m__unnamed4; }
    }
}

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

        private function _read() {
            $this->_m_numBlocks = $this->_io->readU4be();
            $this->_m__unnamed1 = $this->_io->readBytes(4);
            $this->_m_blockAddresses = [];
            $n = $this->numBlockAddresses();
            for ($i = 0; $i < $n; $i++) {
                $this->_m_blockAddresses[] = new \DsStore\BuddyAllocatorBody\BlockDescriptor($this->_io, $this, $this->_root);
            }
            $this->_m_numDirectories = $this->_io->readU4be();
            $this->_m_directoryEntries = [];
            $n = $this->numDirectories();
            for ($i = 0; $i < $n; $i++) {
                $this->_m_directoryEntries[] = new \DsStore\BuddyAllocatorBody\DirectoryEntry($this->_io, $this, $this->_root);
            }
            $this->_m_freeLists = [];
            $n = $this->numFreeLists();
            for ($i = 0; $i < $n; $i++) {
                $this->_m_freeLists[] = new \DsStore\BuddyAllocatorBody\FreeList($this->_io, $this, $this->_root);
            }
        }
        protected $_m_numBlockAddresses;
        public function numBlockAddresses() {
            if ($this->_m_numBlockAddresses !== null)
                return $this->_m_numBlockAddresses;
            $this->_m_numBlockAddresses = 256;
            return $this->_m_numBlockAddresses;
        }
        protected $_m_numFreeLists;
        public function numFreeLists() {
            if ($this->_m_numFreeLists !== null)
                return $this->_m_numFreeLists;
            $this->_m_numFreeLists = 32;
            return $this->_m_numFreeLists;
        }
        protected $_m_directories;

        /**
         * Master blocks of the different B-trees.
         */
        public function directories() {
            if ($this->_m_directories !== null)
                return $this->_m_directories;
            $io = $this->_root()->_io();
            $this->_m_directories = [];
            $n = $this->numDirectories();
            for ($i = 0; $i < $n; $i++) {
                $this->_m_directories[] = new \DsStore\MasterBlockRef($i, $io, $this, $this->_root);
            }
            return $this->_m_directories;
        }
        protected $_m_numBlocks;
        protected $_m__unnamed1;
        protected $_m_blockAddresses;
        protected $_m_numDirectories;
        protected $_m_directoryEntries;
        protected $_m_freeLists;

        /**
         * Number of blocks in the allocated-blocks list.
         */
        public function numBlocks() { return $this->_m_numBlocks; }

        /**
         * Unknown field which appears to always be 0.
         */
        public function _unnamed1() { return $this->_m__unnamed1; }

        /**
         * Addresses of the different blocks.
         */
        public function blockAddresses() { return $this->_m_blockAddresses; }

        /**
         * Indicates the number of directory entries.
         */
        public function numDirectories() { return $this->_m_numDirectories; }

        /**
         * Each directory is an independent B-tree.
         */
        public function directoryEntries() { return $this->_m_directoryEntries; }
        public function freeLists() { return $this->_m_freeLists; }
    }
}

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

        private function _read() {
            $this->_m_addressRaw = $this->_io->readU4be();
        }
        protected $_m_offset;
        public function offset() {
            if ($this->_m_offset !== null)
                return $this->_m_offset;
            $this->_m_offset = (($this->addressRaw() & ~($this->_root()->blockAddressMask())) + 4);
            return $this->_m_offset;
        }
        protected $_m_size;
        public function size() {
            if ($this->_m_size !== null)
                return $this->_m_size;
            $this->_m_size = (1 << ($this->addressRaw() & $this->_root()->blockAddressMask()));
            return $this->_m_size;
        }
        protected $_m_addressRaw;
        public function addressRaw() { return $this->_m_addressRaw; }
    }
}

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

        private function _read() {
            $this->_m_lenName = $this->_io->readU1();
            $this->_m_name = \Kaitai\Struct\Stream::bytesToStr($this->_io->readBytes($this->lenName()), "UTF-8");
            $this->_m_blockId = $this->_io->readU4be();
        }
        protected $_m_lenName;
        protected $_m_name;
        protected $_m_blockId;
        public function lenName() { return $this->_m_lenName; }
        public function name() { return $this->_m_name; }
        public function blockId() { return $this->_m_blockId; }
    }
}

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

        private function _read() {
            $this->_m_counter = $this->_io->readU4be();
            $this->_m_offsets = [];
            $n = $this->counter();
            for ($i = 0; $i < $n; $i++) {
                $this->_m_offsets[] = $this->_io->readU4be();
            }
        }
        protected $_m_counter;
        protected $_m_offsets;
        public function counter() { return $this->_m_counter; }
        public function offsets() { return $this->_m_offsets; }
    }
}

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

        private function _read() {
        }
        protected $_m_masterBlock;
        public function masterBlock() {
            if ($this->_m_masterBlock !== null)
                return $this->_m_masterBlock;
            $_pos = $this->_io->pos();
            $this->_io->seek($this->_parent()->blockAddresses()[$this->_parent()->directoryEntries()[$this->idx()]->blockId()]->offset());
            $this->_m__raw_masterBlock = $this->_io->readBytes($this->_parent()->blockAddresses()[$this->_parent()->directoryEntries()[$this->idx()]->blockId()]->size());
            $_io__raw_masterBlock = new \Kaitai\Struct\Stream($this->_m__raw_masterBlock);
            $this->_m_masterBlock = new \DsStore\MasterBlockRef\MasterBlock($_io__raw_masterBlock, $this, $this->_root);
            $this->_io->seek($_pos);
            return $this->_m_masterBlock;
        }
        protected $_m_idx;
        protected $_m__raw_masterBlock;
        public function idx() { return $this->_m_idx; }
        public function _raw_masterBlock() { return $this->_m__raw_masterBlock; }
    }
}

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

        private function _read() {
            $this->_m_blockId = $this->_io->readU4be();
            $this->_m_numInternalNodes = $this->_io->readU4be();
            $this->_m_numRecords = $this->_io->readU4be();
            $this->_m_numNodes = $this->_io->readU4be();
            $this->_m__unnamed4 = $this->_io->readU4be();
        }
        protected $_m_rootBlock;
        public function rootBlock() {
            if ($this->_m_rootBlock !== null)
                return $this->_m_rootBlock;
            $io = $this->_root()->_io();
            $_pos = $io->pos();
            $io->seek($this->_root()->buddyAllocatorBody()->blockAddresses()[$this->blockId()]->offset());
            $this->_m_rootBlock = new \DsStore\Block($io, $this, $this->_root);
            $io->seek($_pos);
            return $this->_m_rootBlock;
        }
        protected $_m_blockId;
        protected $_m_numInternalNodes;
        protected $_m_numRecords;
        protected $_m_numNodes;
        protected $_m__unnamed4;

        /**
         * Block number of the B-tree's root node.
         */
        public function blockId() { return $this->_m_blockId; }

        /**
         * Number of internal node levels.
         */
        public function numInternalNodes() { return $this->_m_numInternalNodes; }

        /**
         * Number of records in the tree.
         */
        public function numRecords() { return $this->_m_numRecords; }

        /**
         * Number of nodes in the tree.
         */
        public function numNodes() { return $this->_m_numNodes; }

        /**
         * Always 0x1000, probably the B-tree node page size.
         */
        public function _unnamed4() { return $this->_m__unnamed4; }
    }
}

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

        private function _read() {
            $this->_m_mode = $this->_io->readU4be();
            $this->_m_counter = $this->_io->readU4be();
            $this->_m_data = [];
            $n = $this->counter();
            for ($i = 0; $i < $n; $i++) {
                $this->_m_data[] = new \DsStore\Block\BlockData($this->mode(), $this->_io, $this, $this->_root);
            }
        }
        protected $_m_rightmostBlock;

        /**
         * Rightmost child block pointer.
         */
        public function rightmostBlock() {
            if ($this->_m_rightmostBlock !== null)
                return $this->_m_rightmostBlock;
            if ($this->mode() > 0) {
                $io = $this->_root()->_io();
                $_pos = $io->pos();
                $io->seek($this->_root()->buddyAllocatorBody()->blockAddresses()[$this->mode()]->offset());
                $this->_m_rightmostBlock = new \DsStore\Block($io, $this, $this->_root);
                $io->seek($_pos);
            }
            return $this->_m_rightmostBlock;
        }
        protected $_m_mode;
        protected $_m_counter;
        protected $_m_data;

        /**
         * If mode is 0, this is a leaf node, otherwise it is an internal node.
         */
        public function mode() { return $this->_m_mode; }

        /**
         * Number of records or number of block id + record pairs.
         */
        public function counter() { return $this->_m_counter; }
        public function data() { return $this->_m_data; }
    }
}

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

        private function _read() {
            if ($this->mode() > 0) {
                $this->_m_blockId = $this->_io->readU4be();
            }
            $this->_m_record = new \DsStore\Block\BlockData\Record($this->_io, $this, $this->_root);
        }
        protected $_m_block;
        public function block() {
            if ($this->_m_block !== null)
                return $this->_m_block;
            if ($this->mode() > 0) {
                $io = $this->_root()->_io();
                $_pos = $io->pos();
                $io->seek($this->_root()->buddyAllocatorBody()->blockAddresses()[$this->blockId()]->offset());
                $this->_m_block = new \DsStore\Block($io, $this, $this->_root);
                $io->seek($_pos);
            }
            return $this->_m_block;
        }
        protected $_m_blockId;
        protected $_m_record;
        protected $_m_mode;
        public function blockId() { return $this->_m_blockId; }
        public function record() { return $this->_m_record; }
        public function mode() { return $this->_m_mode; }
    }
}

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

        private function _read() {
            $this->_m_filename = new \DsStore\Block\BlockData\Record\Ustr($this->_io, $this, $this->_root);
            $this->_m_structureType = new \DsStore\Block\BlockData\Record\FourCharCode($this->_io, $this, $this->_root);
            $this->_m_dataType = \Kaitai\Struct\Stream::bytesToStr($this->_io->readBytes(4), "UTF-8");
            switch ($this->dataType()) {
                case "long":
                    $this->_m_value = $this->_io->readU4be();
                    break;
                case "shor":
                    $this->_m_value = $this->_io->readU4be();
                    break;
                case "comp":
                    $this->_m_value = $this->_io->readU8be();
                    break;
                case "bool":
                    $this->_m_value = $this->_io->readU1();
                    break;
                case "ustr":
                    $this->_m_value = new \DsStore\Block\BlockData\Record\Ustr($this->_io, $this, $this->_root);
                    break;
                case "dutc":
                    $this->_m_value = $this->_io->readU8be();
                    break;
                case "type":
                    $this->_m_value = new \DsStore\Block\BlockData\Record\FourCharCode($this->_io, $this, $this->_root);
                    break;
                case "blob":
                    $this->_m_value = new \DsStore\Block\BlockData\Record\RecordBlob($this->_io, $this, $this->_root);
                    break;
            }
        }
        protected $_m_filename;
        protected $_m_structureType;
        protected $_m_dataType;
        protected $_m_value;
        public function filename() { return $this->_m_filename; }

        /**
         * Description of the entry's property.
         */
        public function structureType() { return $this->_m_structureType; }

        /**
         * Data type of the value.
         */
        public function dataType() { return $this->_m_dataType; }
        public function value() { return $this->_m_value; }
    }
}

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

        private function _read() {
            $this->_m_length = $this->_io->readU4be();
            $this->_m_value = $this->_io->readBytes($this->length());
        }
        protected $_m_length;
        protected $_m_value;
        public function length() { return $this->_m_length; }
        public function value() { return $this->_m_value; }
    }
}

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

        private function _read() {
            $this->_m_length = $this->_io->readU4be();
            $this->_m_value = \Kaitai\Struct\Stream::bytesToStr($this->_io->readBytes((2 * $this->length())), "UTF-16BE");
        }
        protected $_m_length;
        protected $_m_value;
        public function length() { return $this->_m_length; }
        public function value() { return $this->_m_value; }
    }
}

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

        private function _read() {
            $this->_m_value = \Kaitai\Struct\Stream::bytesToStr($this->_io->readBytes(4), "UTF-8");
        }
        protected $_m_value;
        public function value() { return $this->_m_value; }
    }
}