__init__.krk 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. import fileio
  2. def to_int(little: bool, data: bytes):
  3. let out = 0
  4. if little: data = reversed(data)
  5. for x in data:
  6. out *= 0x100
  7. out += x
  8. return out
  9. def to_bytes(little: bool, val: int, length: int):
  10. let out = [0] * length
  11. let i = 0
  12. while val and i < length:
  13. out[i] = val & 0xFF
  14. val >>= 8
  15. i++
  16. if not little:
  17. out = reversed(out)
  18. return bytes(out)
  19. def read_struct(fmt: str, data: bytes, offset: int):
  20. if not fmt or not isinstance(fmt,str):
  21. raise ValueError
  22. # First, read the endianness
  23. let littleEndian = True
  24. if fmt[0] in '@<>#!':
  25. if fmt[0] == '>': littleEndian = False
  26. else if fmt[0] == '!': littleEndian = False
  27. fmt = fmt[1:]
  28. # Then read length
  29. let length = None
  30. if fmt[0] in '0123456789':
  31. length = int(fmt[0])
  32. fmt = fmt[1:]
  33. while fmt[0] in '012345679':
  34. length *= 10
  35. length += int(fmt[0])
  36. fmt = fmt[1:]
  37. # Then read type
  38. if fmt[0] == 'B':
  39. return int(data[offset]), offset + 1
  40. else if fmt[0] == 'b':
  41. let out = int(data[offset])
  42. if out > 0x7F: out = -out
  43. return out, offset + 1
  44. else if fmt[0] == 's':
  45. return bytes([data[x] for x in range(offset,offset+length)]), offset + length
  46. else if fmt[0] == 'I':
  47. return to_int(littleEndian, bytes([data[x] for x in range(offset,offset+4)])), offset + 4
  48. else if fmt[0] == 'H':
  49. return to_int(littleEndian, bytes([data[x] for x in range(offset,offset+2)])), offset + 2
  50. raise ValueError("Huh")
  51. def pack_into(fmt: str, data: bytes, offset: int, val: any):
  52. if not fmt or not isinstance(fmt,str):
  53. raise ValueError
  54. # First, read the endianness
  55. let littleEndian = True
  56. if fmt[0] in '@<>#!':
  57. if fmt[0] == '>': littleEndian = False
  58. else if fmt[0] == '!': littleEndian = False
  59. fmt = fmt[1:]
  60. # Then read length
  61. let length = None
  62. if fmt[0] in '0123456789':
  63. length = int(fmt[0])
  64. fmt = fmt[1:]
  65. while fmt[0] in '012345679':
  66. length *= 10
  67. length += int(fmt[0])
  68. fmt = fmt[1:]
  69. # Then read type
  70. if fmt[0] == 'B':
  71. data[offset] = val
  72. return offset + 1
  73. else if fmt[0] == 'b':
  74. data[offset] = val
  75. return offset + 1
  76. else if fmt[0] == 's':
  77. for x in range(length):
  78. data[offset+x] = val[x]
  79. return offset + length
  80. else if fmt[0] == 'I':
  81. for x in to_bytes(littleEndian, val, 4):
  82. data[offset] = x
  83. offset++
  84. return offset
  85. else if fmt[0] == 'H':
  86. for x in to_bytes(littleEndian, val, 2):
  87. data[offset] = x
  88. offset++
  89. return offset
  90. raise ValueError("Huh")
  91. class ISO(object):
  92. def __init__(self, path):
  93. let data
  94. with fileio.open(path, 'rb') as f:
  95. self.data = bytearray(f.read())
  96. self.sector_size = 2048
  97. let o = 0x10 * self.sector_size
  98. let _unused
  99. self.type, o = read_struct('B',self.data,o)
  100. self.id, o = read_struct('5s',self.data,o)
  101. self.version, o = read_struct('B',self.data,o)
  102. _unused, o = read_struct('B',self.data,o)
  103. self.system_id, o = read_struct('32s',self.data,o)
  104. self.volume_id, o = read_struct('32s',self.data,o)
  105. _unused, o = read_struct('8s',self.data,o)
  106. self.volume_space_lsb, o = read_struct('<I',self.data,o)
  107. self.volume_space_msb, o = read_struct('>I',self.data,o)
  108. _unused, o = read_struct('32s',self.data,o)
  109. self.volume_set_lsb, o = read_struct('<H',self.data,o)
  110. self.volume_set_msb, o = read_struct('>H',self.data,o)
  111. self.volume_seq_lsb, o = read_struct('<H',self.data,o)
  112. self.volume_seq_msb, o = read_struct('>H',self.data,o)
  113. self.logical_block_size_lsb, o = read_struct('<H',self.data,o)
  114. self.logical_block_size_msb, o = read_struct('>H',self.data,o)
  115. self.path_table_size_lsb, o = read_struct('<I',self.data,o)
  116. self.path_table_size_msb, o = read_struct('>I',self.data,o)
  117. self.path_table_lsb, o = read_struct('<I',self.data,o)
  118. self.optional_path_table_lsb, o = read_struct('<I',self.data,o)
  119. self.path_table_msb, o = read_struct('>I',self.data,o)
  120. self.optional_path_table_msb, o = read_struct('>I',self.data,o)
  121. let _offset = o
  122. self.root_dir_entry, o = read_struct('34s',self.data,o)
  123. self.root = ISOFile(self,_offset)
  124. self._cache = {}
  125. def get_file(self, path):
  126. if path == '/':
  127. return self.root
  128. else:
  129. if path in self._cache:
  130. return self._cache[path]
  131. let units = path.split('/')
  132. units = units[1:] # remove root
  133. let me = self.root
  134. for i in units:
  135. let next_file = me.find(i)
  136. if not next_file:
  137. me = None
  138. break
  139. else:
  140. me = next_file
  141. self._cache[path] = me
  142. return me
  143. class ISOFile(object):
  144. def __init__(self, iso, offset):
  145. self.iso = iso
  146. self.offset = offset
  147. let o = offset
  148. self.length, o = read_struct('B', self.iso.data, o)
  149. if not self.length:
  150. return
  151. self.ext_length, o = read_struct('B', self.iso.data, o)
  152. self.extent_start_lsb, o = read_struct('<I',self.iso.data, o)
  153. self.extent_start_msb, o = read_struct('>I',self.iso.data, o)
  154. self.extent_length_lsb, o = read_struct('<I',self.iso.data, o)
  155. self.extent_length_msb, o = read_struct('>I',self.iso.data, o)
  156. self.date_data, o = read_struct('7s', self.iso.data, o)
  157. self.flags, o = read_struct('b', self.iso.data, o)
  158. self.interleave_units, o = read_struct('b', self.iso.data, o)
  159. self.interleave_gap, o = read_struct('b', self.iso.data, o)
  160. self.volume_seq_lsb, o = read_struct('<H',self.iso.data, o)
  161. self.volume_seq_msb, o = read_struct('>H',self.iso.data, o)
  162. self.name_len, o = read_struct('b', self.iso.data, o)
  163. self.name, o = read_struct('{}s'.format(self.name_len), self.iso.data, o)
  164. self.name = self.name.decode()
  165. def write_extents(self):
  166. pack_into('<I', self.iso.data, self.offset + 2, self.extent_start_lsb)
  167. pack_into('>I', self.iso.data, self.offset + 6, self.extent_start_lsb)
  168. pack_into('<I', self.iso.data, self.offset + 10, self.extent_length_lsb)
  169. pack_into('>I', self.iso.data, self.offset + 14, self.extent_length_lsb)
  170. def readable_name(self):
  171. if not ';' in self.name:
  172. return self.name.lower()
  173. else:
  174. let tmp, _
  175. tmp, _ = self.name.split(';')
  176. return tmp.lower()
  177. def list(self):
  178. 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]
  179. let offset = 0
  180. while 1:
  181. let f = ISOFile(self.iso, self.extent_start_lsb * self.iso.sector_size + offset)
  182. yield f
  183. offset += f.length
  184. if not f.length:
  185. break
  186. def find(self, name):
  187. 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]
  188. let offset = 0
  189. if '.' in name and len(name.split('.')[0]) > 8:
  190. let a, b
  191. a, b = name.split('.')
  192. name = a[:8] + '.' + b
  193. if '-' in name:
  194. name = name.replace('-','_')
  195. while 1:
  196. let f = ISOFile(self.iso, self.extent_start_lsb * self.iso.sector_size + offset)
  197. if not f.length:
  198. if offset < self.extent_length_lsb:
  199. offset += 1
  200. continue
  201. else:
  202. break
  203. if ';' in f.name:
  204. let tmp, _
  205. tmp, _ = f.name.split(';')
  206. if tmp.endswith('.'):
  207. tmp = tmp[:-1]
  208. if tmp.lower() == name.lower():
  209. return f
  210. elif f.name.lower() == name.lower():
  211. return f
  212. offset += f.length
  213. return None
  214. class FAT(object):
  215. def __init__(self, iso, offset):
  216. self.iso = iso
  217. self.offset = offset
  218. let _
  219. self.bytespersector, _ = read_struct('H', self.iso.data, offset + 11)
  220. self.sectorspercluster, _ = read_struct('B', self.iso.data, offset + 13)
  221. self.reservedsectors, _ = read_struct('H', self.iso.data, offset + 14)
  222. self.numberoffats, _ = read_struct('B', self.iso.data, offset + 16)
  223. self.numberofdirs, _ = read_struct('H', self.iso.data, offset + 17)
  224. self.fatsize, _ = read_struct('H', self.iso.data, offset + 22)
  225. self.root_dir_sectors = (self.numberofdirs * 32 + (self.bytespersector - 1)) // self.bytespersector
  226. self.first_data_sector = self.reservedsectors + (self.numberoffats * self.fatsize) + self.root_dir_sectors
  227. self.root_sector= self.first_data_sector - self.root_dir_sectors
  228. self.root = FATDirectory(self, self.offset + self.root_sector * self.bytespersector)
  229. def get_offset(self, cluster):
  230. return self.offset + ((cluster - 2) * self.sectorspercluster + self.first_data_sector) * self.bytespersector
  231. def get_file(self, path):
  232. let units = path.split('/')
  233. units = units[1:]
  234. let me = self.root
  235. let out = None
  236. for i in units:
  237. for fatfile in me.list():
  238. if fatfile.readable_name() == i:
  239. me = fatfile.to_dir()
  240. out = fatfile
  241. break
  242. return out
  243. class FATDirectory(object):
  244. def __init__(self, fat, offset):
  245. self.fat = fat
  246. self.offset = offset
  247. def list(self):
  248. let o = self.offset
  249. while 1:
  250. let out = FATFile(self.fat, o)
  251. if out.name != '\0\0\0\0\0\0\0\0':
  252. yield out
  253. else:
  254. break
  255. o += out.size
  256. class FATFile(object):
  257. def __init__(self, fat, offset):
  258. self.fat = fat
  259. self.offset = offset
  260. self.magic_long = None
  261. self.size = 0
  262. self.long_name = ''
  263. let o = self.offset
  264. self.actual_offset = o
  265. let _
  266. self.attrib, _ = read_struct('B',self.fat.iso.data,o+11)
  267. while (self.attrib & 0x0F) == 0x0F:
  268. # Long file name entry
  269. let tmp = read_struct('10s',self.fat.iso.data,o+1)[0]
  270. tmp += read_struct('12s',self.fat.iso.data,o+14)[0]
  271. tmp += read_struct('4s',self.fat.iso.data,o+28)[0]
  272. let s = []
  273. for i = 0; i < len(tmp); i += 2:
  274. if tmp[x] != '\xFF':
  275. s.append(chr(tmp[x]))
  276. tmp = "".join(s).strip('\x00')
  277. self.long_name = tmp + self.long_name
  278. self.size += 32
  279. o = self.offset + self.size
  280. self.actual_offset = o
  281. self.attrib, _ = read_struct('B',self.fat.iso.data,o+11)
  282. o = self.offset + self.size
  283. self.name, o = read_struct('8s',self.fat.iso.data,o)
  284. self.ext, o = read_struct('3s',self.fat.iso.data,o)
  285. self.attrib, o = read_struct('B',self.fat.iso.data,o)
  286. self.userattrib, o = read_struct('B',self.fat.iso.data,o)
  287. self.undelete, o = read_struct('b',self.fat.iso.data,o)
  288. self.createtime, o = read_struct('H',self.fat.iso.data,o)
  289. self.createdate, o = read_struct('H',self.fat.iso.data,o)
  290. self.accessdate, o = read_struct('H',self.fat.iso.data,o)
  291. self.clusterhi, o = read_struct('H',self.fat.iso.data,o)
  292. self.modifiedti, o = read_struct('H',self.fat.iso.data,o)
  293. self.modifiedda, o = read_struct('H',self.fat.iso.data,o)
  294. self.clusterlow, o = read_struct('H',self.fat.iso.data,o)
  295. self.filesize, o = read_struct('I',self.fat.iso.data,o)
  296. self.name = self.name.decode()
  297. self.ext = self.ext.decode()
  298. self.size += 32
  299. self.cluster = (self.clusterhi << 16) + self.clusterlow
  300. def is_dir(self):
  301. return bool(self.attrib & 0x10)
  302. def is_long(self):
  303. return bool((self.attrib & 0x0F) == 0x0F)
  304. def to_dir(self):
  305. return FATDirectory(self.fat, self.fat.get_offset(self.cluster))
  306. def get_offset(self):
  307. return self.fat.get_offset(self.cluster)
  308. def readable_name(self):
  309. if self.long_name:
  310. return self.long_name
  311. if self.ext.strip():
  312. return (self.name.strip() + '.' + self.ext.strip()).lower()
  313. else:
  314. return self.name.strip().lower()