Skip to content

Commit 82842c7

Browse files
author
cradu
committed
Added UDF detection
1 parent c789331 commit 82842c7

File tree

8 files changed

+124
-8
lines changed

8 files changed

+124
-8
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
PHP Library used to read metadata from ISO files based on [php-iso-file](https://github.com/php-classes/php-iso-file)
44

5-
This library follows the [ECMA-119](https://www.ecma-international.org/wp-content/uploads/ECMA-119_4th_edition_june_2019.pdf) standard.
5+
This library follows the [ISO 9660 / ECMA-119](https://www.ecma-international.org/wp-content/uploads/ECMA-119_4th_edition_june_2019.pdf) standard.
66

77
Basic concepts
88
-----
@@ -18,6 +18,16 @@ Basic concepts
1818
- `PathTableRecord` - object which contains the record information for a file/directory
1919
- Each class contains various properties which can be used to interact with them, most of them `public`
2020

21+
Known limitations
22+
------------
23+
- ISO extensions currently not supported:
24+
- El Torito
25+
- Joliet
26+
- Rock Ridge
27+
- UDF file format not supported
28+
- Reading metadata requires manually processing the descriptors
29+
- Some Iterator implementation would be nice to have
30+
2131
Installation
2232
------------
2333

src/Descriptor/Factory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ class Factory
1111
/**
1212
* @param array<int, int> $bytes
1313
*/
14-
public static function create(int $type, string $stdId = '', int $version = 0, ?array $bytes = null): Descriptor
14+
public static function create(int $type, string $stdId = '', int $version = 0, ?array $bytes = null, string $udfType = ''): Descriptor
1515
{
1616
return match ($type) {
1717
Type::BOOT_RECORD_DESC => new Boot($stdId, $version, $bytes),
1818
Type::PRIMARY_VOLUME_DESC => new PrimaryVolume($stdId, $version, $bytes),
1919
Type::SUPPLEMENTARY_VOLUME_DESC => new SupplementaryVolume($stdId, $version, $bytes),
2020
Type::PARTITION_VOLUME_DESC => new Partition($stdId, $version, $bytes),
2121
Type::TERMINATOR_DESC => new Terminator($stdId, $version, $bytes),
22+
Type::UDF_VOLUME_DESC => new UdfDescriptor($stdId, $version, $bytes, $udfType),
2223
default => throw new Exception('Invalid descriptor type received: ' . $type),
2324
};
2425
}

src/Descriptor/Reader.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,36 @@ public function read(): ?Descriptor
4646
$version = $bytes[$offset];
4747
$offset++;
4848

49-
$descriptor = Factory::create($type, $stdId, $version, $bytes);
49+
$udfType = '';
50+
51+
// Check for UDF-specific descriptors
52+
if ($type === Type::BOOT_RECORD_DESC) {
53+
switch ($stdId) {
54+
case 'CD001':
55+
$type = Type::BOOT_RECORD_DESC;
56+
break;
57+
case UdfType::BEA01:
58+
$type = Type::UDF_VOLUME_DESC;
59+
$udfType = $stdId;
60+
break;
61+
case UdfType::NSR02:
62+
$type = Type::UDF_VOLUME_DESC;
63+
$udfType = $stdId;
64+
break;
65+
case UdfType::NSR03:
66+
$type = Type::UDF_VOLUME_DESC;
67+
$udfType = $stdId;
68+
break;
69+
case UdfType::TEA01:
70+
$type = Type::UDF_VOLUME_DESC;
71+
$udfType = $stdId;
72+
break;
73+
default:
74+
throw new Exception('Failed to detect UDF');
75+
}
76+
}
77+
78+
$descriptor = Factory::create($type, $stdId, $version, $bytes, $udfType);
5079
$descriptor->init($this->isoFile, $offset);
5180

5281
return $descriptor;

src/Descriptor/Type.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ class Type
1010
public const PRIMARY_VOLUME_DESC = 1;
1111
public const SUPPLEMENTARY_VOLUME_DESC = 2;
1212
public const PARTITION_VOLUME_DESC = 3;
13+
public const UDF_VOLUME_DESC = 4;
1314
public const TERMINATOR_DESC = 255;
1415
}

src/Descriptor/UdfDescriptor.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpIso\Descriptor;
6+
7+
use PhpIso\IsoFile;
8+
9+
class UdfDescriptor extends Volume
10+
{
11+
public string $name = 'UDF descriptor';
12+
protected int $type = Type::UDF_VOLUME_DESC;
13+
14+
public function __construct(public string $stdId = '', public int $version = 0, protected ?array $bytes = null, public string $udfType = '')
15+
{
16+
}
17+
18+
public function init(IsoFile $isoFile, int &$offset): void
19+
{
20+
// TODO - add UDF processing
21+
22+
// free some space...
23+
unset($this->bytes);
24+
}
25+
}

src/Descriptor/UdfType.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpIso\Descriptor;
6+
7+
class UdfType
8+
{
9+
public const BEA01 = 'BEA01';
10+
public const NSR02 = 'NSR02';
11+
public const NSR03 = 'NSR03';
12+
public const TEA01 = 'TEA01';
13+
}

src/IsoFile.php

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use PhpIso\Descriptor\Reader;
88
use PhpIso\Descriptor\Type;
9+
use PhpIso\Descriptor\UdfDescriptor;
10+
use PhpIso\Descriptor\UdfType;
911

1012
class IsoFile
1113
{
@@ -86,11 +88,46 @@ protected function processFile(): bool
8688

8789
$reader = new Reader($this);
8890

89-
while (($descriptor = $reader->read()) !== null) {
90-
$this->descriptors[$descriptor->getType()] = $descriptor;
91+
$foundTerminator = false;
92+
while (true) {
93+
try {
94+
$descriptor = $reader->read();
95+
96+
if ($descriptor === null) {
97+
throw new Exception('Finished reading');
98+
}
99+
100+
if (isset($this->descriptors[$descriptor->getType()])) {
101+
throw new Exception('Descriptor already exists');
102+
}
103+
104+
$this->descriptors[$descriptor->getType()] = $descriptor;
105+
} catch (Exception $ex) {
106+
if ($foundTerminator) {
107+
break;
108+
}
109+
throw $ex;
110+
}
111+
112+
// If it's a UDF descriptor, handle it separately
113+
if ($descriptor instanceof UdfDescriptor) {
114+
if ($descriptor->udfType === UdfType::TEA01) {
115+
break; // Stop at Terminating Extended Area Descriptor
116+
}
117+
} else {
118+
if ($foundTerminator) {
119+
break;
120+
}
121+
}
91122

92123
if ($descriptor->getType() === Type::TERMINATOR_DESC) {
93-
break;
124+
if ($foundTerminator) {
125+
break;
126+
}
127+
128+
$foundTerminator = true;
129+
// Keep going if UDF might still be present
130+
continue;
94131
}
95132
}
96133

tests/IsoFileTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public function testDescriptorsTestIso(): void
5858
{
5959
$testFile = dirname(__FILE__, 2) . '/fixtures/test.iso';
6060
$isoFile = new IsoFile($testFile);
61-
$this->assertCount(2, $isoFile->descriptors);
61+
$this->assertCount(3, $isoFile->descriptors);
6262

6363
$this->assertArrayHasKey(Type::TERMINATOR_DESC, $isoFile->descriptors);
6464

@@ -434,7 +434,7 @@ public function testDescriptorsDosIso(): void
434434
public static function isoFilesDataProvider(): Iterator
435435
{
436436
yield [dirname(__FILE__, 2) . '/fixtures/1mb.iso', 3];
437-
yield [dirname(__FILE__, 2) . '/fixtures/test.iso', 2];
437+
yield [dirname(__FILE__, 2) . '/fixtures/test.iso', 3];
438438
yield [dirname(__FILE__, 2) . '/fixtures/subdir.iso', 2];
439439
yield [dirname(__FILE__, 2) . '/fixtures/test-dir.iso', 2];
440440
yield [dirname(__FILE__, 2) . '/fixtures/DOS4.01_bootdisk.iso', 4];

0 commit comments

Comments
 (0)