Guest User

Untitled

a guest
Jan 23rd, 2018
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 2.79 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. """A native python implementation of the par2 file format.
  5.  
  6. This is only intended to be able to read packets in par2, not execute
  7. repair, verify, or create new par2 files."""
  8.  
  9. import struct
  10.  
  11. PACKET_HEADER = ("<"
  12. "8s" # MAGIC: PAR2\x00PKT
  13. "Q" # unsigned 64bit length of entire packet in bytes
  14. "16s" # md5 of entire packet except first 3 fields
  15. "16s" # 'setid'; hash of the body of the main packet
  16. "16s" # packet type
  17. )
  18.  
  19. FILE_DESCRIPTION_PACKET = ("<64s" # PACKET_HEADER
  20. "16s" # fileid, hash of [hash16k, length, name]
  21. "16s" # hashfull; hash of the whole file (which?)
  22. "16s" # hash16k; hash of the first 16k of the file (which?)
  23. "Q" # length of the file
  24. )
  25.  
  26. class Header(object):
  27. fmt = PACKET_HEADER
  28. def __init__(self, par2file, offset=0):
  29. self.raw = par2file[offset:offset+struct.calcsize(self.fmt)]
  30. parts = struct.unpack(self.fmt, self.raw)
  31. self.magic = parts[0]
  32. self.length = parts[1]
  33. self.hash = parts[2]
  34. self.setid = parts[3]
  35. self.type = parts[4]
  36.  
  37. def verify(self):
  38. return self.magic == 'PAR2\x00PKT'
  39.  
  40. class UnknownPar2Packet(object):
  41. fmt = PACKET_HEADER
  42. def __init__(self, par2file, offset=0):
  43. self.raw = par2file[offset:offset+struct.calcsize(self.fmt)]
  44. self.header = Header(self.raw)
  45.  
  46. class FileDescriptionPacket(object):
  47. header_type = 'PAR 2.0\x00FileDesc'
  48. fmt = FILE_DESCRIPTION_PACKET
  49.  
  50. def __init__(self, par2file, offset=0):
  51. name_start = offset+struct.calcsize(self.fmt)
  52. self.raw = par2file[offset:name_start]
  53. parts = struct.unpack(self.fmt, self.raw)
  54. self.header = Header(parts[0])
  55. packet = par2file[offset:offset+self.header.length]
  56. self.fileid = parts[1]
  57. self.file_hashfull = parts[2]
  58. self.file_hash16k = parts[3]
  59. self.file_length = parts[4]
  60. self.name = packet[struct.calcsize(self.fmt):].strip('\x00')
  61.  
  62.  
  63. class Par2File(object):
  64. def __init__(self, obj_or_path):
  65. if isinstance(obj_or_path, basestring):
  66. self.contents = open(obj_or_path).read()
  67. else:
  68. self.contents = obj_or_path.read()
  69. self.packets = self.read_packets()
  70.  
  71. def read_packets(self):
  72. offset = 0
  73. filelen = len(self.contents)
  74. packets = []
  75. while offset < filelen:
  76. header = Header(self.contents, offset)
  77. if header.type == FileDescriptionPacket.header_type:
  78. packets.append(FileDescriptionPacket(self.contents, offset))
  79. else:
  80. packets.append(UnknownPar2Packet(self.contents, offset))
  81. offset += header.length
  82. return packets
  83.  
  84. def filenames(self):
  85. return [p.name for p in self.packets if isinstance(p, FileDescriptionPacket)]
Add Comment
Please, Sign In to add comment