mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-01 08:53:37 +00: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!
331 lines
10 KiB
Python
331 lines
10 KiB
Python
"""
|
|
Gimp image parser (XCF file, ".xcf" extension).
|
|
|
|
You can find informations about XCF file in Gimp source code. URL to read
|
|
CVS online:
|
|
http://cvs.gnome.org/viewcvs/gimp/app/xcf/
|
|
\--> files xcf-read.c and xcf-load.c
|
|
|
|
Author: Victor Stinner
|
|
"""
|
|
|
|
from lib.hachoir_parser import Parser
|
|
from lib.hachoir_core.field import (StaticFieldSet, FieldSet, ParserError,
|
|
UInt8, UInt32, Enum, Float32, String, PascalString32, RawBytes)
|
|
from lib.hachoir_parser.image.common import RGBA
|
|
from lib.hachoir_core.endian import NETWORK_ENDIAN
|
|
|
|
class XcfCompression(FieldSet):
|
|
static_size = 8
|
|
COMPRESSION_NAME = {
|
|
0: u"None",
|
|
1: u"RLE",
|
|
2: u"Zlib",
|
|
3: u"Fractal"
|
|
}
|
|
|
|
def createFields(self):
|
|
yield Enum(UInt8(self, "compression", "Compression method"), self.COMPRESSION_NAME)
|
|
|
|
class XcfResolution(StaticFieldSet):
|
|
format = (
|
|
(Float32, "xres", "X resolution in DPI"),
|
|
(Float32, "yres", "Y resolution in DPI")
|
|
)
|
|
|
|
class XcfTattoo(StaticFieldSet):
|
|
format = ((UInt32, "tattoo", "Tattoo"),)
|
|
|
|
class LayerOffsets(StaticFieldSet):
|
|
format = (
|
|
(UInt32, "ofst_x", "Offset X"),
|
|
(UInt32, "ofst_y", "Offset Y")
|
|
)
|
|
|
|
class LayerMode(FieldSet):
|
|
static_size = 32
|
|
MODE_NAME = {
|
|
0: u"Normal",
|
|
1: u"Dissolve",
|
|
2: u"Behind",
|
|
3: u"Multiply",
|
|
4: u"Screen",
|
|
5: u"Overlay",
|
|
6: u"Difference",
|
|
7: u"Addition",
|
|
8: u"Subtract",
|
|
9: u"Darken only",
|
|
10: u"Lighten only",
|
|
11: u"Hue",
|
|
12: u"Saturation",
|
|
13: u"Color",
|
|
14: u"Value",
|
|
15: u"Divide",
|
|
16: u"Dodge",
|
|
17: u"Burn",
|
|
18: u"Hard light",
|
|
19: u"Soft light",
|
|
20: u"Grain extract",
|
|
21: u"Grain merge",
|
|
22: u"Color erase"
|
|
}
|
|
|
|
def createFields(self):
|
|
yield Enum(UInt32(self, "mode", "Layer mode"), self.MODE_NAME)
|
|
|
|
class GimpBoolean(UInt32):
|
|
def __init__(self, parent, name):
|
|
UInt32.__init__(self, parent, name)
|
|
|
|
def createValue(self):
|
|
return 1 == UInt32.createValue(self)
|
|
|
|
class XcfUnit(StaticFieldSet):
|
|
format = ((UInt32, "unit", "Unit"),)
|
|
|
|
class XcfParasiteEntry(FieldSet):
|
|
def createFields(self):
|
|
yield PascalString32(self, "name", "Name", strip="\0", charset="UTF-8")
|
|
yield UInt32(self, "flags", "Flags")
|
|
yield PascalString32(self, "data", "Data", strip=" \0", charset="UTF-8")
|
|
|
|
class XcfLevel(FieldSet):
|
|
def createFields(self):
|
|
yield UInt32(self, "width", "Width in pixel")
|
|
yield UInt32(self, "height", "Height in pixel")
|
|
yield UInt32(self, "offset", "Offset")
|
|
offset = self["offset"].value
|
|
if offset == 0:
|
|
return
|
|
data_offsets = []
|
|
while (self.absolute_address + self.current_size)/8 < offset:
|
|
chunk = UInt32(self, "data_offset[]", "Data offset")
|
|
yield chunk
|
|
if chunk.value == 0:
|
|
break
|
|
data_offsets.append(chunk)
|
|
if (self.absolute_address + self.current_size)/8 != offset:
|
|
raise ParserError("Problem with level offset.")
|
|
previous = offset
|
|
for chunk in data_offsets:
|
|
data_offset = chunk.value
|
|
size = data_offset - previous
|
|
yield RawBytes(self, "data[]", size, "Data content of %s" % chunk.name)
|
|
previous = data_offset
|
|
|
|
class XcfHierarchy(FieldSet):
|
|
def createFields(self):
|
|
yield UInt32(self, "width", "Width")
|
|
yield UInt32(self, "height", "Height")
|
|
yield UInt32(self, "bpp", "Bits/pixel")
|
|
|
|
offsets = []
|
|
while True:
|
|
chunk = UInt32(self, "offset[]", "Level offset")
|
|
yield chunk
|
|
if chunk.value == 0:
|
|
break
|
|
offsets.append(chunk.value)
|
|
for offset in offsets:
|
|
padding = self.seekByte(offset, relative=False)
|
|
if padding is not None:
|
|
yield padding
|
|
yield XcfLevel(self, "level[]", "Level")
|
|
# yield XcfChannel(self, "channel[]", "Channel"))
|
|
|
|
class XcfChannel(FieldSet):
|
|
def createFields(self):
|
|
yield UInt32(self, "width", "Channel width")
|
|
yield UInt32(self, "height", "Channel height")
|
|
yield PascalString32(self, "name", "Channel name", strip="\0", charset="UTF-8")
|
|
for field in readProperties(self):
|
|
yield field
|
|
yield UInt32(self, "hierarchy_ofs", "Hierarchy offset")
|
|
yield XcfHierarchy(self, "hierarchy", "Hierarchy")
|
|
|
|
def createDescription(self):
|
|
return 'Channel "%s"' % self["name"].value
|
|
|
|
class XcfLayer(FieldSet):
|
|
def createFields(self):
|
|
yield UInt32(self, "width", "Layer width in pixels")
|
|
yield UInt32(self, "height", "Layer height in pixels")
|
|
yield Enum(UInt32(self, "type", "Layer type"), XcfFile.IMAGE_TYPE_NAME)
|
|
yield PascalString32(self, "name", "Layer name", strip="\0", charset="UTF-8")
|
|
for prop in readProperties(self):
|
|
yield prop
|
|
|
|
# --
|
|
# TODO: Hack for Gimp 1.2 files
|
|
# --
|
|
|
|
yield UInt32(self, "hierarchy_ofs", "Hierarchy offset")
|
|
yield UInt32(self, "mask_ofs", "Layer mask offset")
|
|
padding = self.seekByte(self["hierarchy_ofs"].value, relative=False)
|
|
if padding is not None:
|
|
yield padding
|
|
yield XcfHierarchy(self, "hierarchy", "Hierarchy")
|
|
# TODO: Read layer mask if needed: self["mask_ofs"].value != 0
|
|
|
|
def createDescription(self):
|
|
return 'Layer "%s"' % self["name"].value
|
|
|
|
class XcfParasites(FieldSet):
|
|
def createFields(self):
|
|
size = self["../size"].value * 8
|
|
while self.current_size < size:
|
|
yield XcfParasiteEntry(self, "parasite[]", "Parasite")
|
|
|
|
class XcfProperty(FieldSet):
|
|
PROP_COMPRESSION = 17
|
|
PROP_RESOLUTION = 19
|
|
PROP_PARASITES = 21
|
|
TYPE_NAME = {
|
|
0: u"End",
|
|
1: u"Colormap",
|
|
2: u"Active layer",
|
|
3: u"Active channel",
|
|
4: u"Selection",
|
|
5: u"Floating selection",
|
|
6: u"Opacity",
|
|
7: u"Mode",
|
|
8: u"Visible",
|
|
9: u"Linked",
|
|
10: u"Lock alpha",
|
|
11: u"Apply mask",
|
|
12: u"Edit mask",
|
|
13: u"Show mask",
|
|
14: u"Show masked",
|
|
15: u"Offsets",
|
|
16: u"Color",
|
|
17: u"Compression",
|
|
18: u"Guides",
|
|
19: u"Resolution",
|
|
20: u"Tattoo",
|
|
21: u"Parasites",
|
|
22: u"Unit",
|
|
23: u"Paths",
|
|
24: u"User unit",
|
|
25: u"Vectors",
|
|
26: u"Text layer flags",
|
|
}
|
|
|
|
handler = {
|
|
6: RGBA,
|
|
7: LayerMode,
|
|
8: GimpBoolean,
|
|
9: GimpBoolean,
|
|
10: GimpBoolean,
|
|
11: GimpBoolean,
|
|
12: GimpBoolean,
|
|
13: GimpBoolean,
|
|
15: LayerOffsets,
|
|
17: XcfCompression,
|
|
19: XcfResolution,
|
|
20: XcfTattoo,
|
|
21: XcfParasites,
|
|
22: XcfUnit
|
|
}
|
|
|
|
def __init__(self, *args, **kw):
|
|
FieldSet.__init__(self, *args, **kw)
|
|
self._size = (8 + self["size"].value) * 8
|
|
|
|
def createFields(self):
|
|
yield Enum(UInt32(self, "type", "Property type"), self.TYPE_NAME)
|
|
yield UInt32(self, "size", "Property size")
|
|
|
|
size = self["size"].value
|
|
if 0 < size:
|
|
cls = self.handler.get(self["type"].value, None)
|
|
if cls:
|
|
yield cls(self, "data", size=size*8)
|
|
else:
|
|
yield RawBytes(self, "data", size, "Data")
|
|
|
|
def createDescription(self):
|
|
return "Property: %s" % self["type"].display
|
|
|
|
def readProperties(parser):
|
|
while True:
|
|
prop = XcfProperty(parser, "property[]")
|
|
yield prop
|
|
if prop["type"].value == 0:
|
|
return
|
|
|
|
class XcfFile(Parser):
|
|
PARSER_TAGS = {
|
|
"id": "xcf",
|
|
"category": "image",
|
|
"file_ext": ("xcf",),
|
|
"mime": (u"image/x-xcf", u"application/x-gimp-image"),
|
|
"min_size": (26 + 8 + 4 + 4)*8, # header+empty property+layer offset+channel offset
|
|
"magic": (
|
|
('gimp xcf file\0', 0),
|
|
('gimp xcf v002\0', 0),
|
|
),
|
|
"description": "Gimp (XCF) picture"
|
|
}
|
|
endian = NETWORK_ENDIAN
|
|
IMAGE_TYPE_NAME = {
|
|
0: u"RGB",
|
|
1: u"Gray",
|
|
2: u"Indexed"
|
|
}
|
|
|
|
def validate(self):
|
|
if self.stream.readBytes(0, 14) not in ('gimp xcf file\0', 'gimp xcf v002\0'):
|
|
return "Wrong signature"
|
|
return True
|
|
|
|
def createFields(self):
|
|
# Read signature
|
|
yield String(self, "signature", 14, "Gimp picture signature (ends with nul byte)", charset="ASCII")
|
|
|
|
# Read image general informations (width, height, type)
|
|
yield UInt32(self, "width", "Image width")
|
|
yield UInt32(self, "height", "Image height")
|
|
yield Enum(UInt32(self, "type", "Image type"), self.IMAGE_TYPE_NAME)
|
|
for prop in readProperties(self):
|
|
yield prop
|
|
|
|
# Read layer offsets
|
|
layer_offsets = []
|
|
while True:
|
|
chunk = UInt32(self, "layer_offset[]", "Layer offset")
|
|
yield chunk
|
|
if chunk.value == 0:
|
|
break
|
|
layer_offsets.append(chunk.value)
|
|
|
|
# Read channel offsets
|
|
channel_offsets = []
|
|
while True:
|
|
chunk = UInt32(self, "channel_offset[]", "Channel offset")
|
|
yield chunk
|
|
if chunk.value == 0:
|
|
break
|
|
channel_offsets.append(chunk.value)
|
|
|
|
# Read layers
|
|
for index, offset in enumerate(layer_offsets):
|
|
if index+1 < len(layer_offsets):
|
|
size = (layer_offsets[index+1] - offset) * 8
|
|
else:
|
|
size = None
|
|
padding = self.seekByte(offset, relative=False)
|
|
if padding:
|
|
yield padding
|
|
yield XcfLayer(self, "layer[]", size=size)
|
|
|
|
# Read channels
|
|
for index, offset in enumerate(channel_offsets):
|
|
if index+1 < len(channel_offsets):
|
|
size = (channel_offsets[index+1] - offset) * 8
|
|
else:
|
|
size = None
|
|
padding = self.seekByte(offset, relative=False)
|
|
if padding is not None:
|
|
yield padding
|
|
yield XcfChannel(self, "channel[]", "Channel", size=size)
|
|
|