mirror of
https://github.com/moparisthebest/SickRage
synced 2024-10-31 23:45:02 -04:00
0d9fbc1ad7
This version of SickBeard uses both TVDB and TVRage to search and gather it's series data from allowing you to now have access to and download shows that you couldn't before because of being locked into only what TheTVDB had to offer. Also this edition is based off the code we used in our XEM editon so it does come with scene numbering support as well as all the other features our XEM edition has to offer. Please before using this with your existing database (sickbeard.db) please make a backup copy of it and delete any other database files such as cache.db and failed.db if present, we HIGHLY recommend starting out with no database files at all to make this a fresh start but the choice is at your own risk! Enjoy!
335 lines
12 KiB
Python
335 lines
12 KiB
Python
"""
|
|
Python compiled source code parser.
|
|
|
|
Informations:
|
|
- Python 2.4.2 source code:
|
|
files Python/marshal.c and Python/import.c
|
|
|
|
Author: Victor Stinner
|
|
Creation: 25 march 2005
|
|
"""
|
|
|
|
DISASSEMBLE = False
|
|
|
|
from lib.hachoir_parser import Parser
|
|
from lib.hachoir_core.field import (FieldSet, UInt8,
|
|
UInt16, Int32, UInt32, Int64, ParserError, Float64, Enum,
|
|
Character, Bytes, RawBytes, PascalString8, TimestampUnix32)
|
|
from lib.hachoir_core.endian import LITTLE_ENDIAN
|
|
from lib.hachoir_core.bits import long2raw
|
|
from lib.hachoir_core.text_handler import textHandler, hexadecimal
|
|
from lib.hachoir_core.i18n import ngettext
|
|
if DISASSEMBLE:
|
|
from dis import dis
|
|
|
|
def disassembleBytecode(field):
|
|
bytecode = field.value
|
|
dis(bytecode)
|
|
|
|
# --- String and string reference ---
|
|
def parseString(parent):
|
|
yield UInt32(parent, "length", "Length")
|
|
length = parent["length"].value
|
|
if parent.name == "lnotab":
|
|
bytecode_offset=0
|
|
line_number=parent['../firstlineno'].value
|
|
for i in range(0,length,2):
|
|
bc_off_delta=UInt8(parent, 'bytecode_offset_delta[]')
|
|
yield bc_off_delta
|
|
bytecode_offset+=bc_off_delta.value
|
|
bc_off_delta._description='Bytecode Offset %i'%bytecode_offset
|
|
line_number_delta=UInt8(parent, 'line_number_delta[]')
|
|
yield line_number_delta
|
|
line_number+=line_number_delta.value
|
|
line_number_delta._description='Line Number %i'%line_number
|
|
elif 0 < length:
|
|
yield RawBytes(parent, "text", length, "Content")
|
|
if DISASSEMBLE and parent.name == "compiled_code":
|
|
disassembleBytecode(parent["text"])
|
|
|
|
def parseStringRef(parent):
|
|
yield textHandler(UInt32(parent, "ref"), hexadecimal)
|
|
def createStringRefDesc(parent):
|
|
return "String ref: %s" % parent["ref"].display
|
|
|
|
# --- Integers ---
|
|
def parseInt32(parent):
|
|
yield Int32(parent, "value")
|
|
|
|
def parseInt64(parent):
|
|
yield Int64(parent, "value")
|
|
|
|
def parseLong(parent):
|
|
yield Int32(parent, "digit_count")
|
|
for index in xrange( abs(parent["digit_count"].value) ):
|
|
yield UInt16(parent, "digit[]")
|
|
|
|
|
|
# --- Float and complex ---
|
|
def parseFloat(parent):
|
|
yield PascalString8(parent, "value")
|
|
def parseBinaryFloat(parent):
|
|
yield Float64(parent, "value")
|
|
def parseComplex(parent):
|
|
yield PascalString8(parent, "real")
|
|
yield PascalString8(parent, "complex")
|
|
def parseBinaryComplex(parent):
|
|
yield Float64(parent, "real")
|
|
yield Float64(parent, "complex")
|
|
|
|
|
|
# --- Tuple and list ---
|
|
def parseTuple(parent):
|
|
yield Int32(parent, "count", "Item count")
|
|
count = parent["count"].value
|
|
if count < 0:
|
|
raise ParserError("Invalid tuple/list count")
|
|
for index in xrange(count):
|
|
yield Object(parent, "item[]")
|
|
|
|
def createTupleDesc(parent):
|
|
count = parent["count"].value
|
|
items = ngettext("%s item", "%s items", count) % count
|
|
return "%s: %s" % (parent.code_info[2], items)
|
|
|
|
|
|
# --- Dict ---
|
|
def parseDict(parent):
|
|
"""
|
|
Format is: (key1, value1, key2, value2, ..., keyn, valuen, NULL)
|
|
where each keyi and valuei is an object.
|
|
"""
|
|
parent.count = 0
|
|
while True:
|
|
key = Object(parent, "key[]")
|
|
yield key
|
|
if key["bytecode"].value == "0":
|
|
break
|
|
yield Object(parent, "value[]")
|
|
parent.count += 1
|
|
|
|
def createDictDesc(parent):
|
|
return "Dict: %s" % (ngettext("%s key", "%s keys", parent.count) % parent.count)
|
|
|
|
# --- Code ---
|
|
def parseCode(parent):
|
|
if 0x3000000 <= parent.root.getVersion():
|
|
yield UInt32(parent, "arg_count", "Argument count")
|
|
yield UInt32(parent, "kwonlyargcount", "Keyword only argument count")
|
|
yield UInt32(parent, "nb_locals", "Number of local variables")
|
|
yield UInt32(parent, "stack_size", "Stack size")
|
|
yield UInt32(parent, "flags")
|
|
elif 0x2030000 <= parent.root.getVersion():
|
|
yield UInt32(parent, "arg_count", "Argument count")
|
|
yield UInt32(parent, "nb_locals", "Number of local variables")
|
|
yield UInt32(parent, "stack_size", "Stack size")
|
|
yield UInt32(parent, "flags")
|
|
else:
|
|
yield UInt16(parent, "arg_count", "Argument count")
|
|
yield UInt16(parent, "nb_locals", "Number of local variables")
|
|
yield UInt16(parent, "stack_size", "Stack size")
|
|
yield UInt16(parent, "flags")
|
|
yield Object(parent, "compiled_code")
|
|
yield Object(parent, "consts")
|
|
yield Object(parent, "names")
|
|
yield Object(parent, "varnames")
|
|
if 0x2000000 <= parent.root.getVersion():
|
|
yield Object(parent, "freevars")
|
|
yield Object(parent, "cellvars")
|
|
yield Object(parent, "filename")
|
|
yield Object(parent, "name")
|
|
if 0x2030000 <= parent.root.getVersion():
|
|
yield UInt32(parent, "firstlineno", "First line number")
|
|
else:
|
|
yield UInt16(parent, "firstlineno", "First line number")
|
|
yield Object(parent, "lnotab")
|
|
|
|
class Object(FieldSet):
|
|
bytecode_info = {
|
|
# Don't contains any data
|
|
'0': ("null", None, "NULL", None),
|
|
'N': ("none", None, "None", None),
|
|
'F': ("false", None, "False", None),
|
|
'T': ("true", None, "True", None),
|
|
'S': ("stop_iter", None, "StopIter", None),
|
|
'.': ("ellipsis", None, "ELLIPSIS", None),
|
|
'?': ("unknown", None, "Unknown", None),
|
|
|
|
'i': ("int32", parseInt32, "Int32", None),
|
|
'I': ("int64", parseInt64, "Int64", None),
|
|
'f': ("float", parseFloat, "Float", None),
|
|
'g': ("bin_float", parseBinaryFloat, "Binary float", None),
|
|
'x': ("complex", parseComplex, "Complex", None),
|
|
'y': ("bin_complex", parseBinaryComplex, "Binary complex", None),
|
|
'l': ("long", parseLong, "Long", None),
|
|
's': ("string", parseString, "String", None),
|
|
't': ("interned", parseString, "Interned", None),
|
|
'u': ("unicode", parseString, "Unicode", None),
|
|
'R': ("string_ref", parseStringRef, "String ref", createStringRefDesc),
|
|
'(': ("tuple", parseTuple, "Tuple", createTupleDesc),
|
|
'[': ("list", parseTuple, "List", createTupleDesc),
|
|
'<': ("set", parseTuple, "Set", createTupleDesc),
|
|
'>': ("frozenset", parseTuple, "Frozen set", createTupleDesc),
|
|
'{': ("dict", parseDict, "Dict", createDictDesc),
|
|
'c': ("code", parseCode, "Code", None),
|
|
}
|
|
|
|
def __init__(self, parent, name, **kw):
|
|
FieldSet.__init__(self, parent, name, **kw)
|
|
code = self["bytecode"].value
|
|
if code not in self.bytecode_info:
|
|
raise ParserError('Unknown bytecode: "%s"' % code)
|
|
self.code_info = self.bytecode_info[code]
|
|
if not name:
|
|
self._name = self.code_info[0]
|
|
if code == "l":
|
|
self.createValue = self.createValueLong
|
|
elif code in ("i", "I", "f", "g"):
|
|
self.createValue = lambda: self["value"].value
|
|
elif code == "T":
|
|
self.createValue = lambda: True
|
|
elif code == "F":
|
|
self.createValue = lambda: False
|
|
elif code in ("x", "y"):
|
|
self.createValue = self.createValueComplex
|
|
elif code in ("s", "t", "u"):
|
|
self.createValue = self.createValueString
|
|
self.createDisplay = self.createDisplayString
|
|
if code == 't':
|
|
if not hasattr(self.root,'string_table'):
|
|
self.root.string_table=[]
|
|
self.root.string_table.append(self)
|
|
elif code == 'R':
|
|
if hasattr(self.root,'string_table'):
|
|
self.createValue = self.createValueStringRef
|
|
|
|
def createValueString(self):
|
|
if "text" in self:
|
|
return self["text"].value
|
|
else:
|
|
return ""
|
|
|
|
def createDisplayString(self):
|
|
if "text" in self:
|
|
return self["text"].display
|
|
else:
|
|
return "(empty)"
|
|
|
|
def createValueLong(self):
|
|
is_negative = self["digit_count"].value < 0
|
|
count = abs(self["digit_count"].value)
|
|
total = 0
|
|
for index in xrange(count-1, -1, -1):
|
|
total <<= 15
|
|
total += self["digit[%u]" % index].value
|
|
if is_negative:
|
|
total = -total
|
|
return total
|
|
|
|
def createValueStringRef(self):
|
|
return self.root.string_table[self['ref'].value].value
|
|
|
|
def createDisplayStringRef(self):
|
|
return self.root.string_table[self['ref'].value].display
|
|
|
|
def createValueComplex(self):
|
|
return complex(
|
|
float(self["real"].value),
|
|
float(self["complex"].value))
|
|
|
|
def createFields(self):
|
|
yield Character(self, "bytecode", "Bytecode")
|
|
parser = self.code_info[1]
|
|
if parser:
|
|
for field in parser(self):
|
|
yield field
|
|
|
|
def createDescription(self):
|
|
create = self.code_info[3]
|
|
if create:
|
|
return create(self)
|
|
else:
|
|
return self.code_info[2]
|
|
|
|
class PythonCompiledFile(Parser):
|
|
PARSER_TAGS = {
|
|
"id": "python",
|
|
"category": "program",
|
|
"file_ext": ("pyc", "pyo"),
|
|
"min_size": 9*8,
|
|
"description": "Compiled Python script (.pyc/.pyo files)"
|
|
}
|
|
endian = LITTLE_ENDIAN
|
|
|
|
# Dictionnary which associate the pyc signature (32-bit integer)
|
|
# to a Python version string (eg. "m\xf2\r\n" => "Python 2.4b1").
|
|
# This list comes from CPython source code, see "MAGIC"
|
|
# and "pyc_magic" in file Python/import.c
|
|
MAGIC = {
|
|
# Python 1.x
|
|
20121: ("1.5", 0x1050000),
|
|
|
|
# Python 2.x
|
|
50823: ("2.0", 0x2000000),
|
|
60202: ("2.1", 0x2010000),
|
|
60717: ("2.2", 0x2020000),
|
|
62011: ("2.3a0", 0x2030000),
|
|
62021: ("2.3a0", 0x2030000),
|
|
62041: ("2.4a0", 0x2040000),
|
|
62051: ("2.4a3", 0x2040000),
|
|
62061: ("2.4b1", 0x2040000),
|
|
62071: ("2.5a0", 0x2050000),
|
|
62081: ("2.5a0 (ast-branch)", 0x2050000),
|
|
62091: ("2.5a0 (with)", 0x2050000),
|
|
62092: ("2.5a0 (WITH_CLEANUP opcode)", 0x2050000),
|
|
62101: ("2.5b3", 0x2050000),
|
|
62111: ("2.5b3", 0x2050000),
|
|
62121: ("2.5c1", 0x2050000),
|
|
62131: ("2.5c2", 0x2050000),
|
|
|
|
# Python 3.x
|
|
3000: ("3.0 (3000)", 0x3000000),
|
|
3010: ("3.0 (3010)", 0x3000000),
|
|
3020: ("3.0 (3020)", 0x3000000),
|
|
3030: ("3.0 (3030)", 0x3000000),
|
|
3040: ("3.0 (3040)", 0x3000000),
|
|
3050: ("3.0 (3050)", 0x3000000),
|
|
3060: ("3.0 (3060)", 0x3000000),
|
|
3070: ("3.0 (3070)", 0x3000000),
|
|
3080: ("3.0 (3080)", 0x3000000),
|
|
3090: ("3.0 (3090)", 0x3000000),
|
|
3100: ("3.0 (3100)", 0x3000000),
|
|
3102: ("3.0 (3102)", 0x3000000),
|
|
3110: ("3.0a4", 0x3000000),
|
|
3130: ("3.0a5", 0x3000000),
|
|
3131: ("3.0a5 unicode", 0x3000000),
|
|
}
|
|
|
|
# Dictionnary which associate the pyc signature (4-byte long string)
|
|
# to a Python version string (eg. "m\xf2\r\n" => "2.4b1")
|
|
STR_MAGIC = dict( \
|
|
(long2raw(magic | (ord('\r')<<16) | (ord('\n')<<24), LITTLE_ENDIAN), value[0]) \
|
|
for magic, value in MAGIC.iteritems())
|
|
|
|
def validate(self):
|
|
signature = self.stream.readBits(0, 16, self.endian)
|
|
if signature not in self.MAGIC:
|
|
return "Unknown version (%s)" % signature
|
|
if self.stream.readBytes(2*8, 2) != "\r\n":
|
|
return r"Wrong signature (\r\n)"
|
|
if self.stream.readBytes(8*8, 1) != 'c':
|
|
return "First object bytecode is not code"
|
|
return True
|
|
|
|
def getVersion(self):
|
|
if not hasattr(self, "version"):
|
|
signature = self.stream.readBits(0, 16, self.endian)
|
|
self.version = self.MAGIC[signature][1]
|
|
return self.version
|
|
|
|
def createFields(self):
|
|
yield Enum(Bytes(self, "signature", 4, "Python file signature and version"), self.STR_MAGIC)
|
|
yield TimestampUnix32(self, "timestamp", "Timestamp")
|
|
yield Object(self, "content")
|
|
|