123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- import fileio
- def to_int(little: bool, data: bytes):
- let out = 0
- if little: data = reversed(data)
- for x in data:
- out *= 0x100
- out += x
- return out
- def to_bytes(little: bool, val: int, length: int):
- let out = [0] * length
- let i = 0
- while val and i < length:
- out[i] = val & 0xFF
- val >>= 8
- i++
- if not little:
- out = reversed(out)
- return bytes(out)
- def read_struct(fmt: str, data: bytes, offset: int):
- if not fmt or not isinstance(fmt,str):
- raise ValueError
- # First, read the endianness
- let littleEndian = True
- if fmt[0] in '@<>#!':
- if fmt[0] == '>': littleEndian = False
- else if fmt[0] == '!': littleEndian = False
- fmt = fmt[1:]
- # Then read length
- let length = None
- if fmt[0] in '0123456789':
- length = int(fmt[0])
- fmt = fmt[1:]
- while fmt[0] in '012345679':
- length *= 10
- length += int(fmt[0])
- fmt = fmt[1:]
- # Then read type
- if fmt[0] == 'B':
- return int(data[offset]), offset + 1
- else if fmt[0] == 'b':
- let out = int(data[offset])
- if out > 0x7F: out = -out
- return out, offset + 1
- else if fmt[0] == 's':
- return bytes([data[x] for x in range(offset,offset+length)]), offset + length
- else if fmt[0] == 'I':
- return to_int(littleEndian, bytes([data[x] for x in range(offset,offset+4)])), offset + 4
- else if fmt[0] == 'H':
- return to_int(littleEndian, bytes([data[x] for x in range(offset,offset+2)])), offset + 2
- raise ValueError("Huh")
- def pack_into(fmt: str, data: bytes, offset: int, val: any):
- if not fmt or not isinstance(fmt,str):
- raise ValueError
- # First, read the endianness
- let littleEndian = True
- if fmt[0] in '@<>#!':
- if fmt[0] == '>': littleEndian = False
- else if fmt[0] == '!': littleEndian = False
- fmt = fmt[1:]
- # Then read length
- let length = None
- if fmt[0] in '0123456789':
- length = int(fmt[0])
- fmt = fmt[1:]
- while fmt[0] in '012345679':
- length *= 10
- length += int(fmt[0])
- fmt = fmt[1:]
- # Then read type
- if fmt[0] == 'B':
- data[offset] = val
- return offset + 1
- else if fmt[0] == 'b':
- data[offset] = val
- return offset + 1
- else if fmt[0] == 's':
- for x in range(length):
- data[offset+x] = val[x]
- return offset + length
- else if fmt[0] == 'I':
- for x in to_bytes(littleEndian, val, 4):
- data[offset] = x
- offset++
- return offset
- else if fmt[0] == 'H':
- for x in to_bytes(littleEndian, val, 2):
- data[offset] = x
- offset++
- return offset
- raise ValueError("Huh")
- class ISO(object):
- def __init__(self, path):
- let data
- with fileio.open(path, 'rb') as f:
- self.data = bytearray(f.read())
- self.sector_size = 2048
- let o = 0x10 * self.sector_size
- let _unused
- self.type, o = read_struct('B',self.data,o)
- self.id, o = read_struct('5s',self.data,o)
- self.version, o = read_struct('B',self.data,o)
- _unused, o = read_struct('B',self.data,o)
- self.system_id, o = read_struct('32s',self.data,o)
- self.volume_id, o = read_struct('32s',self.data,o)
- _unused, o = read_struct('8s',self.data,o)
- self.volume_space_lsb, o = read_struct('<I',self.data,o)
- self.volume_space_msb, o = read_struct('>I',self.data,o)
- _unused, o = read_struct('32s',self.data,o)
- self.volume_set_lsb, o = read_struct('<H',self.data,o)
- self.volume_set_msb, o = read_struct('>H',self.data,o)
- self.volume_seq_lsb, o = read_struct('<H',self.data,o)
- self.volume_seq_msb, o = read_struct('>H',self.data,o)
- self.logical_block_size_lsb, o = read_struct('<H',self.data,o)
- self.logical_block_size_msb, o = read_struct('>H',self.data,o)
- self.path_table_size_lsb, o = read_struct('<I',self.data,o)
- self.path_table_size_msb, o = read_struct('>I',self.data,o)
- self.path_table_lsb, o = read_struct('<I',self.data,o)
- self.optional_path_table_lsb, o = read_struct('<I',self.data,o)
- self.path_table_msb, o = read_struct('>I',self.data,o)
- self.optional_path_table_msb, o = read_struct('>I',self.data,o)
- let _offset = o
- self.root_dir_entry, o = read_struct('34s',self.data,o)
- self.root = ISOFile(self,_offset)
- self._cache = {}
- def get_file(self, path):
- if path == '/':
- return self.root
- else:
- if path in self._cache:
- return self._cache[path]
- let units = path.split('/')
- units = units[1:] # remove root
- let me = self.root
- for i in units:
- let next_file = me.find(i)
- if not next_file:
- me = None
- break
- else:
- me = next_file
- self._cache[path] = me
- return me
- class ISOFile(object):
- def __init__(self, iso, offset):
- self.iso = iso
- self.offset = offset
- let o = offset
- self.length, o = read_struct('B', self.iso.data, o)
- if not self.length:
- return
- self.ext_length, o = read_struct('B', self.iso.data, o)
- self.extent_start_lsb, o = read_struct('<I',self.iso.data, o)
- self.extent_start_msb, o = read_struct('>I',self.iso.data, o)
- self.extent_length_lsb, o = read_struct('<I',self.iso.data, o)
- self.extent_length_msb, o = read_struct('>I',self.iso.data, o)
- self.date_data, o = read_struct('7s', self.iso.data, o)
- self.flags, o = read_struct('b', self.iso.data, o)
- self.interleave_units, o = read_struct('b', self.iso.data, o)
- self.interleave_gap, o = read_struct('b', self.iso.data, o)
- self.volume_seq_lsb, o = read_struct('<H',self.iso.data, o)
- self.volume_seq_msb, o = read_struct('>H',self.iso.data, o)
- self.name_len, o = read_struct('b', self.iso.data, o)
- self.name, o = read_struct('{}s'.format(self.name_len), self.iso.data, o)
- self.name = self.name.decode()
- def write_extents(self):
- pack_into('<I', self.iso.data, self.offset + 2, self.extent_start_lsb)
- pack_into('>I', self.iso.data, self.offset + 6, self.extent_start_lsb)
- pack_into('<I', self.iso.data, self.offset + 10, self.extent_length_lsb)
- pack_into('>I', self.iso.data, self.offset + 14, self.extent_length_lsb)
- def readable_name(self):
- if not ';' in self.name:
- return self.name.lower()
- else:
- let tmp, _
- tmp, _ = self.name.split(';')
- return tmp.lower()
- def list(self):
- let sectors = self.iso.data[self.extent_start_lsb * self.iso.sector_size: self.extent_start_lsb * self.iso.sector_size+ 3 * self.iso.sector_size]
- let offset = 0
- while 1:
- let f = ISOFile(self.iso, self.extent_start_lsb * self.iso.sector_size + offset)
- yield f
- offset += f.length
- if not f.length:
- break
- def find(self, name):
- let sectors = self.iso.data[self.extent_start_lsb * self.iso.sector_size: self.extent_start_lsb * self.iso.sector_size+ 3 * self.iso.sector_size]
- let offset = 0
- if '.' in name and len(name.split('.')[0]) > 8:
- let a, b
- a, b = name.split('.')
- name = a[:8] + '.' + b
- if '-' in name:
- name = name.replace('-','_')
- while 1:
- let f = ISOFile(self.iso, self.extent_start_lsb * self.iso.sector_size + offset)
- if not f.length:
- if offset < self.extent_length_lsb:
- offset += 1
- continue
- else:
- break
- if ';' in f.name:
- let tmp, _
- tmp, _ = f.name.split(';')
- if tmp.endswith('.'):
- tmp = tmp[:-1]
- if tmp.lower() == name.lower():
- return f
- elif f.name.lower() == name.lower():
- return f
- offset += f.length
- return None
- class FAT(object):
- def __init__(self, iso, offset):
- self.iso = iso
- self.offset = offset
- let _
- self.bytespersector, _ = read_struct('H', self.iso.data, offset + 11)
- self.sectorspercluster, _ = read_struct('B', self.iso.data, offset + 13)
- self.reservedsectors, _ = read_struct('H', self.iso.data, offset + 14)
- self.numberoffats, _ = read_struct('B', self.iso.data, offset + 16)
- self.numberofdirs, _ = read_struct('H', self.iso.data, offset + 17)
- self.fatsize, _ = read_struct('H', self.iso.data, offset + 22)
- self.root_dir_sectors = (self.numberofdirs * 32 + (self.bytespersector - 1)) // self.bytespersector
- self.first_data_sector = self.reservedsectors + (self.numberoffats * self.fatsize) + self.root_dir_sectors
- self.root_sector= self.first_data_sector - self.root_dir_sectors
- self.root = FATDirectory(self, self.offset + self.root_sector * self.bytespersector)
- def get_offset(self, cluster):
- return self.offset + ((cluster - 2) * self.sectorspercluster + self.first_data_sector) * self.bytespersector
- def get_file(self, path):
- let units = path.split('/')
- units = units[1:]
- let me = self.root
- let out = None
- for i in units:
- for fatfile in me.list():
- if fatfile.readable_name() == i:
- me = fatfile.to_dir()
- out = fatfile
- break
- return out
- class FATDirectory(object):
- def __init__(self, fat, offset):
- self.fat = fat
- self.offset = offset
- def list(self):
- let o = self.offset
- while 1:
- let out = FATFile(self.fat, o)
- if out.name != '\0\0\0\0\0\0\0\0':
- yield out
- else:
- break
- o += out.size
- class FATFile(object):
- def __init__(self, fat, offset):
- self.fat = fat
- self.offset = offset
- self.magic_long = None
- self.size = 0
- self.long_name = ''
- let o = self.offset
- self.actual_offset = o
- let _
- self.attrib, _ = read_struct('B',self.fat.iso.data,o+11)
- while (self.attrib & 0x0F) == 0x0F:
- # Long file name entry
- let tmp = read_struct('10s',self.fat.iso.data,o+1)[0]
- tmp += read_struct('12s',self.fat.iso.data,o+14)[0]
- tmp += read_struct('4s',self.fat.iso.data,o+28)[0]
- let s = []
- for i = 0; i < len(tmp); i += 2:
- if tmp[x] != '\xFF':
- s.append(chr(tmp[x]))
- tmp = "".join(s).strip('\x00')
- self.long_name = tmp + self.long_name
- self.size += 32
- o = self.offset + self.size
- self.actual_offset = o
- self.attrib, _ = read_struct('B',self.fat.iso.data,o+11)
- o = self.offset + self.size
- self.name, o = read_struct('8s',self.fat.iso.data,o)
- self.ext, o = read_struct('3s',self.fat.iso.data,o)
- self.attrib, o = read_struct('B',self.fat.iso.data,o)
- self.userattrib, o = read_struct('B',self.fat.iso.data,o)
- self.undelete, o = read_struct('b',self.fat.iso.data,o)
- self.createtime, o = read_struct('H',self.fat.iso.data,o)
- self.createdate, o = read_struct('H',self.fat.iso.data,o)
- self.accessdate, o = read_struct('H',self.fat.iso.data,o)
- self.clusterhi, o = read_struct('H',self.fat.iso.data,o)
- self.modifiedti, o = read_struct('H',self.fat.iso.data,o)
- self.modifiedda, o = read_struct('H',self.fat.iso.data,o)
- self.clusterlow, o = read_struct('H',self.fat.iso.data,o)
- self.filesize, o = read_struct('I',self.fat.iso.data,o)
- self.name = self.name.decode()
- self.ext = self.ext.decode()
- self.size += 32
- self.cluster = (self.clusterhi << 16) + self.clusterlow
- def is_dir(self):
- return bool(self.attrib & 0x10)
- def is_long(self):
- return bool((self.attrib & 0x0F) == 0x0F)
- def to_dir(self):
- return FATDirectory(self.fat, self.fat.get_offset(self.cluster))
- def get_offset(self):
- return self.fat.get_offset(self.cluster)
- def readable_name(self):
- if self.long_name:
- return self.long_name
- if self.ext.strip():
- return (self.name.strip() + '.' + self.ext.strip()).lower()
- else:
- return self.name.strip().lower()
|