Volume   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 208
Duplicated Lines 0 %

Test Coverage

Coverage 90.36%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 18
eloc 101
c 1
b 0
f 0
dl 0
loc 208
ccs 75
cts 83
cp 0.9036
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A loadLPathTable() 0 3 1
A loadMPathTable() 0 3 1
A isMPathTable() 0 3 1
A loadTable() 0 9 2
A isLPathTable() 0 3 1
B loadGenPathTable() 0 43 7
B init() 0 78 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpIso\Descriptor;
6
7
use Carbon\Carbon;
8
use PhpIso\Descriptor;
9
use PhpIso\FileDirectory;
10
use PhpIso\IsoFile;
11
use PhpIso\PathTableRecord;
12
use PhpIso\Util\Buffer;
13
use PhpIso\Util\IsoDate;
14
15
abstract class Volume extends Descriptor
16
{
17
    public string $systemId;
18
    public string $volumeId;
19
    public int $volumeSpaceSize;
20
    public int $volumeSetSize;
21
    public int $volumeSeqNum;
22
    public int $blockSize;
23
    public int $pathTableSize;
24
    public int $lPathTablePos;
25
    public int $optLPathTablePos;
26
    public int $mPathTablePos;
27
    public int $optMPathTablePos;
28
    public FileDirectory $rootDirectory;
29
    public string $volumeSetId;
30
    public string $publisherId;
31
    public string $preparerId;
32
    public string $appId;
33
    public string $copyrightFileId;
34
    public string $abstractFileId;
35
    public string $bibliographicFileId;
36
    public ?Carbon $creationDate = null;
37
    public ?Carbon $modificationDate = null;
38
    public ?Carbon $expirationDate = null;
39
    public ?Carbon $effectiveDate = null;
40
    public int $fileStructureVersion;
41
    public int $jolietLevel = 0;
42
43 17
    public function init(IsoFile $isoFile, int &$offset): void
44
    {
45 17
        if ($this->bytes === null) {
46
            return;
47
        }
48
49
        // unused first entry
50 17
        $unused = $this->bytes[$offset];
51
52 17
        $offset++;
53
54 17
        $this->systemId = trim(Buffer::readAString($this->bytes, 32, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC)));
55 17
        $this->volumeId = trim(Buffer::readDString($this->bytes, 32, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC)));
56
57
        // unused
58 17
        $unused = Buffer::getBytes($this->bytes, 8, $offset);
59
60 17
        $this->volumeSpaceSize = Buffer::readBBO($this->bytes, 8, $offset);
61
62
        // joliet escape sequence
63 17
        $jolietEscapeSequence = Buffer::getBytes($this->bytes, 32, $offset);
64
65
        // Joliet Detection - If this is a Supplementary Volume Descriptor
66 17
        if ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC) {
67
            // Define Joliet escape sequences as their byte values
68 10
            $jolietLevels = [
69 10
                1 => '374764', // %/@ in byte format
70 10
                2 => '374767', // %/C in byte format
71 10
                3 => '374769', // %/E in byte format
72 10
            ];
73
74
            // Check for Joliet escape sequences indicating Unicode support
75 10
            foreach ($jolietLevels as $level => $sequence) {
76 10
                if (str_contains($jolietEscapeSequence, $sequence)) {
77 10
                    $this->jolietLevel = $level; // Record the Joliet level if found
78 10
                    break;
79
                }
80
            }
81
        }
82
83 17
        $this->volumeSetSize = Buffer::readBBO($this->bytes, 4, $offset);
84 17
        $this->volumeSeqNum = Buffer::readBBO($this->bytes, 4, $offset);
85 17
        $this->blockSize = Buffer::readBBO($this->bytes, 4, $offset);
86 17
        $this->pathTableSize = Buffer::readBBO($this->bytes, 8, $offset);
87
88 17
        $this->lPathTablePos = Buffer::readLSB($this->bytes, 4, $offset);
89 17
        $this->optLPathTablePos = Buffer::readLSB($this->bytes, 4, $offset);
90 17
        $this->mPathTablePos = Buffer::readMSB($this->bytes, 4, $offset);
91 17
        $this->optMPathTablePos = Buffer::readMSB($this->bytes, 4, $offset);
92
93 17
        $this->rootDirectory = new FileDirectory();
94 17
        $this->rootDirectory->jolietLevel = $this->jolietLevel;
95 17
        $this->rootDirectory->init($this->bytes, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC));
96
97 17
        $this->volumeSetId = trim(Buffer::readDString($this->bytes, 128, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC)));
98 17
        $this->publisherId = trim(Buffer::readAString($this->bytes, 128, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC)));
99 17
        $this->preparerId = trim(Buffer::readAString($this->bytes, 128, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC)));
100 17
        $this->appId = trim(Buffer::readAString($this->bytes, 128, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC)));
101
102 17
        $this->copyrightFileId = trim(Buffer::readDString($this->bytes, 37, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC)));
103 17
        $this->abstractFileId = trim(Buffer::readDString($this->bytes, 37, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC)));
104
105 17
        $this->bibliographicFileId = trim(Buffer::readDString($this->bytes, 37, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC)));
106
107 17
        $this->creationDate = IsoDate::init17($this->bytes, $offset);
108
109 17
        $this->modificationDate = IsoDate::init17($this->bytes, $offset);
110
111 17
        $this->expirationDate = IsoDate::init17($this->bytes, $offset);
112
113 17
        $this->effectiveDate = IsoDate::init17($this->bytes, $offset);
114
115 17
        $this->fileStructureVersion = $this->bytes[$offset];
116 17
        $offset++;
117
118
        // free some space...
119 17
        $this->bytes = null;
120 17
        unset($unused);
121
    }
122
123
    /**
124
     * Load the path table
125
     *
126
     * @return array<int, PathTableRecord>|null
127
     */
128 3
    public function loadTable(IsoFile $isoFile): ?array
129
    {
130
        // only M-Path should be used for amd64 platforms
131 3
        if ($this->isMPathTable()) {
132 3
            return $this->loadMPathTable($isoFile);
133
        }
134
135
        // unknown path table
136
        return null;
137
    }
138
139
    /**
140
     * Tell if a "M Path Table" is present
141
     */
142 3
    public function isMPathTable(): bool
143
    {
144 3
        return $this->mPathTablePos !== 0;
145
    }
146
147
    /**
148
     * Tell if a "L Path Table" is present
149
     */
150 1
    public function isLPathTable(): bool
151
    {
152 1
        return $this->lPathTablePos !== 0;
153
    }
154
155
    /**
156
     * Load the "M Path Table"
157
     *
158
     * @return array<int, PathTableRecord>|null
159
     */
160 3
    public function loadMPathTable(IsoFile $isoFile): ?array
161
    {
162 3
        return $this->loadGenPathTable($isoFile, $this->mPathTablePos);
163
    }
164
165
    /**
166
     * Load the "L Path Table"
167
     *
168
     * @return array<int, PathTableRecord>|null
169
     */
170
    public function loadLPathTable(IsoFile $isoFile): ?array
171
    {
172
        return $this->loadGenPathTable($isoFile, $this->lPathTablePos);
173
    }
174
175
    /**
176
     * Load the "L Path Table" or "M Path Table"
177
     *
178
     * @return array<int, PathTableRecord>|null
179
     */
180 3
    protected function loadGenPathTable(IsoFile $isoFile, int $pathTablePos): ?array
181
    {
182 3
        if ($pathTablePos === 0 || $this->blockSize === 0) {
183
            return null;
184
        }
185
186 3
        if ($isoFile->seek($pathTablePos * $this->blockSize, SEEK_SET) === -1) {
187
            return null;
188
        }
189
190 3
        $pathTableSize = Buffer::align($this->pathTableSize, $this->blockSize);
191
192 3
        $string = $isoFile->read($pathTableSize);
193
194 3
        if ($string === false) {
195
            return null;
196
        }
197
198
        /** @var array<int, int>|false $bytes */
199 3
        $bytes = unpack('C*', $string);
200
201 3
        if ($bytes === false) {
202
            return null;
203
        }
204
205 3
        $pathTable = [];
206
207 3
        $offset = 1;
208 3
        $dirNum = 1;
209 3
        $ptRec = new PathTableRecord();
210 3
        $bres = $ptRec->init($bytes, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC));
211 3
        while ($bres === true) {
212 3
            $ptRec->setDirectoryNumber($dirNum);
213 3
            $ptRec->loadExtents($isoFile, $this->blockSize, true);
214
215 3
            $pathTable[$dirNum] = $ptRec;
216 3
            $dirNum++;
217
218 3
            $ptRec = new PathTableRecord();
219 3
            $bres = $ptRec->init($bytes, $offset, ($this->type === Type::SUPPLEMENTARY_VOLUME_DESC));
220
        }
221
222 3
        return $pathTable;
223
    }
224
}
225