mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-16 09:55:05 +00:00
129 lines
4.9 KiB
Python
129 lines
4.9 KiB
Python
|
"""
|
||
|
ISO 9660 (cdrom) file system parser.
|
||
|
|
||
|
Documents:
|
||
|
- Standard ECMA-119 (december 1987)
|
||
|
http://www.nondot.org/sabre/os/files/FileSystems/iso9660.pdf
|
||
|
|
||
|
Author: Victor Stinner
|
||
|
Creation: 11 july 2006
|
||
|
"""
|
||
|
|
||
|
from hachoir.parser import Parser
|
||
|
from hachoir.field import (FieldSet, ParserError,
|
||
|
UInt8, UInt32, UInt64, Enum,
|
||
|
NullBytes, RawBytes, String)
|
||
|
from hachoir.core.endian import LITTLE_ENDIAN, BIG_ENDIAN
|
||
|
|
||
|
|
||
|
class PrimaryVolumeDescriptor(FieldSet):
|
||
|
static_size = 2041 * 8
|
||
|
|
||
|
def createFields(self):
|
||
|
yield NullBytes(self, "unused[]", 1)
|
||
|
yield String(self, "system_id", 32, "System identifier", strip=" ")
|
||
|
yield String(self, "volume_id", 32, "Volume identifier", strip=" ")
|
||
|
yield NullBytes(self, "unused[]", 8)
|
||
|
yield UInt64(self, "space_size", "Volume space size")
|
||
|
yield NullBytes(self, "unused[]", 32)
|
||
|
yield UInt32(self, "set_size", "Volume set size")
|
||
|
yield UInt32(self, "seq_num", "Sequence number")
|
||
|
yield UInt32(self, "block_size", "Block size")
|
||
|
yield UInt64(self, "path_table_size", "Path table size")
|
||
|
yield UInt32(self, "occu_lpath", "Location of Occurrence of Type L Path Table")
|
||
|
yield UInt32(self, "opt_lpath", "Location of Optional of Type L Path Table")
|
||
|
yield UInt32(self, "occu_mpath", "Location of Occurrence of Type M Path Table")
|
||
|
yield UInt32(self, "opt_mpath", "Location of Optional of Type M Path Table")
|
||
|
yield RawBytes(self, "root", 34, "Directory Record for Root Directory")
|
||
|
yield String(self, "vol_set_id", 128, "Volume set identifier", strip=" ")
|
||
|
yield String(self, "publisher", 128, "Publisher identifier", strip=" ")
|
||
|
yield String(self, "data_preparer", 128, "Data preparer identifier", strip=" ")
|
||
|
yield String(self, "application", 128, "Application identifier", strip=" ")
|
||
|
yield String(self, "copyright", 37, "Copyright file identifier", strip=" ")
|
||
|
yield String(self, "abstract", 37, "Abstract file identifier", strip=" ")
|
||
|
yield String(self, "biographic", 37, "Biographic file identifier", strip=" ")
|
||
|
yield String(self, "creation_ts", 17, "Creation date and time", strip=" ")
|
||
|
yield String(self, "modification_ts", 17, "Modification date and time", strip=" ")
|
||
|
yield String(self, "expiration_ts", 17, "Expiration date and time", strip=" ")
|
||
|
yield String(self, "effective_ts", 17, "Effective date and time", strip=" ")
|
||
|
yield UInt8(self, "struct_ver", "Structure version")
|
||
|
yield NullBytes(self, "unused[]", 1)
|
||
|
yield String(self, "app_use", 512, "Application use", strip=" \0")
|
||
|
yield NullBytes(self, "unused[]", 653)
|
||
|
|
||
|
|
||
|
class BootRecord(FieldSet):
|
||
|
static_size = 2041 * 8
|
||
|
|
||
|
def createFields(self):
|
||
|
yield String(self, "sys_id", 31, "Boot system identifier", strip="\0")
|
||
|
yield String(self, "boot_id", 31, "Boot identifier", strip="\0")
|
||
|
yield RawBytes(self, "system_use", 1979, "Boot system use")
|
||
|
|
||
|
|
||
|
class Terminator(FieldSet):
|
||
|
static_size = 2041 * 8
|
||
|
|
||
|
def createFields(self):
|
||
|
yield NullBytes(self, "null", 2041)
|
||
|
|
||
|
|
||
|
class Volume(FieldSet):
|
||
|
endian = BIG_ENDIAN
|
||
|
TERMINATOR = 255
|
||
|
type_name = {
|
||
|
0: "Boot Record",
|
||
|
1: "Primary Volume Descriptor",
|
||
|
2: "Supplementary Volume Descriptor",
|
||
|
3: "Volume Partition Descriptor",
|
||
|
TERMINATOR: "Volume Descriptor Set Terminator",
|
||
|
}
|
||
|
static_size = 2048 * 8
|
||
|
content_handler = {
|
||
|
0: BootRecord,
|
||
|
1: PrimaryVolumeDescriptor,
|
||
|
TERMINATOR: Terminator,
|
||
|
}
|
||
|
|
||
|
def createFields(self):
|
||
|
yield Enum(UInt8(self, "type", "Volume descriptor type"), self.type_name)
|
||
|
yield RawBytes(self, "signature", 5, "ISO 9960 signature (CD001)")
|
||
|
if self["signature"].value != b"CD001":
|
||
|
raise ParserError("Invalid ISO 9960 volume signature")
|
||
|
yield UInt8(self, "version", "Volume descriptor version")
|
||
|
cls = self.content_handler.get(self["type"].value, None)
|
||
|
if cls:
|
||
|
yield cls(self, "content")
|
||
|
else:
|
||
|
yield RawBytes(self, "raw_content", 2048 - 7)
|
||
|
|
||
|
|
||
|
class ISO9660(Parser):
|
||
|
endian = LITTLE_ENDIAN
|
||
|
MAGIC = b"\x01CD001"
|
||
|
NULL_BYTES = 0x8000
|
||
|
PARSER_TAGS = {
|
||
|
"id": "iso9660",
|
||
|
"category": "file_system",
|
||
|
"description": "ISO 9660 file system",
|
||
|
"min_size": (NULL_BYTES + 6) * 8,
|
||
|
"magic": ((MAGIC, NULL_BYTES * 8),),
|
||
|
}
|
||
|
|
||
|
def validate(self):
|
||
|
if self.stream.readBytes(self.NULL_BYTES * 8, len(self.MAGIC)) != self.MAGIC:
|
||
|
return "Invalid signature"
|
||
|
return True
|
||
|
|
||
|
def createFields(self):
|
||
|
yield self.seekByte(self.NULL_BYTES, null=True)
|
||
|
|
||
|
while True:
|
||
|
volume = Volume(self, "volume[]")
|
||
|
yield volume
|
||
|
if volume["type"].value == Volume.TERMINATOR:
|
||
|
break
|
||
|
|
||
|
if self.current_size < self._size:
|
||
|
yield self.seekBit(self._size, "end")
|