Remove submodule, just put Dependencies in ./libs

This commit is contained in:
Ruud
2011-02-28 15:25:42 +01:00
parent 2fb578d4de
commit 6888798ef6
541 changed files with 173394 additions and 4 deletions
+14
View File
@@ -0,0 +1,14 @@
from hachoir_parser.misc.file_3do import File3do
from hachoir_parser.misc.file_3ds import File3ds
from hachoir_parser.misc.torrent import TorrentFile
from hachoir_parser.misc.ttf import TrueTypeFontFile
from hachoir_parser.misc.chm import ChmFile
from hachoir_parser.misc.lnk import LnkFile
from hachoir_parser.misc.pcf import PcfFile
from hachoir_parser.misc.ole2 import OLE2_File
from hachoir_parser.misc.pdf import PDFDocument
from hachoir_parser.misc.pifv import PIFVFile
from hachoir_parser.misc.hlp import HlpFile
from hachoir_parser.misc.gnome_keyring import GnomeKeyring
from hachoir_parser.misc.bplist import BPList
+299
View File
@@ -0,0 +1,299 @@
"""
Apple/NeXT Binary Property List (BPLIST) parser.
Also includes a .createXML() function which produces an XML representation of the object.
Note that it will discard unknown objects, nulls and fill values, but should work for most files.
Documents:
- CFBinaryPList.c
http://src.gnu-darwin.org/DarwinSourceArchive/expanded/CF/CF-299/Parsing.subproj/CFBinaryPList.c
- ForFoundationOnly.h (for structure formats)
http://src.gnu-darwin.org/DarwinSourceArchive/expanded/CF/CF-299/Base.subproj/ForFoundationOnly.h
- XML <-> BPList converter
http://scw.us/iPhone/plutil/plutil.pl
Author: Robert Xiao
Created: 2008-09-21
"""
from hachoir_parser import HachoirParser
from hachoir_core.field import (RootSeekableFieldSet, FieldSet, Enum,
Bits, GenericInteger, Float32, Float64, UInt8, UInt64, Bytes, NullBytes, RawBytes, String)
from hachoir_core.endian import BIG_ENDIAN
from hachoir_core.text_handler import displayHandler
from hachoir_core.tools import humanDatetime
from datetime import datetime, timedelta
class BPListTrailer(FieldSet):
def createFields(self):
yield NullBytes(self, "unused", 6)
yield UInt8(self, "offsetIntSize", "Size (in bytes) of offsets in the offset table")
yield UInt8(self, "objectRefSize", "Size (in bytes) of object numbers in object references")
yield UInt64(self, "numObjects", "Number of objects in this file")
yield UInt64(self, "topObject", "Top-level object reference")
yield UInt64(self, "offsetTableOffset", "File offset to the offset table")
def createDescription(self):
return "Binary PList trailer"
class BPListOffsetTable(FieldSet):
def createFields(self):
size = self["../trailer/offsetIntSize"].value*8
for i in range(self["../trailer/numObjects"].value):
yield Bits(self, "offset[]", size)
class BPListSize(FieldSet):
def createFields(self):
yield Bits(self, "size", 4)
if self['size'].value == 0xF:
yield BPListObject(self, "fullsize")
def createValue(self):
if 'fullsize' in self:
return self['fullsize'].value
else:
return self['size'].value
class BPListObjectRef(GenericInteger):
def __init__(self, parent, name, description=None):
size = parent['/trailer/objectRefSize'].value*8
GenericInteger.__init__(self, parent, name, False, size, description)
def getRef(self):
return self.parent['/object[' + str(self.value) + ']']
def createDisplay(self):
return self.getRef().display
def createXML(self, prefix=''):
return self.getRef().createXML(prefix)
class BPListArray(FieldSet):
def __init__(self, parent, name, size, description=None):
FieldSet.__init__(self, parent, name, description=description)
self.numels = size
def createFields(self):
for i in range(self.numels):
yield BPListObjectRef(self, "ref[]")
def createValue(self):
return self.array('ref')
def createDisplay(self):
return '[' + ', '.join([x.display for x in self.value]) + ']'
def createXML(self,prefix=''):
return prefix + '<array>\n' + ''.join([x.createXML(prefix + '\t' ) + '\n' for x in self.value]) + prefix + '</array>'
class BPListDict(FieldSet):
def __init__(self, parent, name, size, description=None):
FieldSet.__init__(self, parent, name, description=description)
self.numels = size
def createFields(self):
for i in range(self.numels):
yield BPListObjectRef(self, "keyref[]")
for i in range(self.numels):
yield BPListObjectRef(self, "valref[]")
def createValue(self):
return zip(self.array('keyref'),self.array('valref'))
def createDisplay(self):
return '{' + ', '.join(['%s: %s'%(k.display,v.display) for k,v in self.value]) + '}'
def createXML(self, prefix=''):
return prefix + '<dict>\n' + ''.join(['%s\t<key>%s</key>\n%s\n'%(prefix,k.getRef().value.encode('utf-8'),v.createXML(prefix + '\t')) for k,v in self.value]) + prefix + '</dict>'
class BPListObject(FieldSet):
def createFields(self):
yield Enum(Bits(self, "marker_type", 4),
{0: "Simple",
1: "Int",
2: "Real",
3: "Date",
4: "Data",
5: "ASCII String",
6: "UTF-16-BE String",
8: "UID",
10: "Array",
13: "Dict",})
markertype = self['marker_type'].value
if markertype == 0:
# Simple (Null)
yield Enum(Bits(self, "value", 4),
{0: "Null",
8: "False",
9: "True",
15: "Fill Byte",})
if self['value'].display == "False":
self.xml=lambda prefix:prefix + "<false/>"
elif self['value'].display == "True":
self.xml=lambda prefix:prefix + "<true/>"
else:
self.xml=lambda prefix:prefix + ""
elif markertype == 1:
# Int
yield Bits(self, "size", 4, "log2 of number of bytes")
size=self['size'].value
# 8-bit (size=0), 16-bit (size=1) and 32-bit (size=2) numbers are unsigned
# 64-bit (size=3) numbers are signed
yield GenericInteger(self, "value", (size>=3), (2**size)*8)
self.xml=lambda prefix:prefix + "<integer>%s</integer>"%self['value'].value
elif markertype == 2:
# Real
yield Bits(self, "size", 4, "log2 of number of bytes")
if self['size'].value == 2: # 2**2 = 4 byte float
yield Float32(self, "value")
elif self['size'].value == 3: # 2**3 = 8 byte float
yield Float64(self, "value")
else:
# FIXME: What is the format of the real?
yield Bits(self, "value", (2**self['size'].value)*8)
self.xml=lambda prefix:prefix + "<real>%s</real>"%self['value'].value
elif markertype == 3:
# Date
yield Bits(self, "extra", 4, "Extra value, should be 3")
# Use a heuristic to determine which epoch to use
def cvt_time(v):
v=timedelta(seconds=v)
epoch2001 = datetime(2001,1,1)
epoch1970 = datetime(1970,1,1)
if (epoch2001 + v - datetime.today()).days > 5*365:
return epoch1970 + v
return epoch2001 + v
yield displayHandler(Float64(self, "value"),lambda x:humanDatetime(cvt_time(x)))
self.xml=lambda prefix:prefix + "<date>%sZ</date>"%(cvt_time(self['value'].value).isoformat())
elif markertype == 4:
# Data
yield BPListSize(self, "size")
if self['size'].value:
yield Bytes(self, "value", self['size'].value)
self.xml=lambda prefix:prefix + "<data>\n%s\n%s</data>"%(self['value'].value.encode('base64').strip(),prefix)
else:
self.xml=lambda prefix:prefix + '<data></data>'
elif markertype == 5:
# ASCII String
yield BPListSize(self, "size")
if self['size'].value:
yield String(self, "value", self['size'].value, charset="ASCII")
self.xml=lambda prefix:prefix + "<string>%s</string>"%(self['value'].value.replace('&','&amp;').encode('iso-8859-1'))
else:
self.xml=lambda prefix:prefix + '<string></string>'
elif markertype == 6:
# UTF-16-BE String
yield BPListSize(self, "size")
if self['size'].value:
yield String(self, "value", self['size'].value*2, charset="UTF-16-BE")
self.xml=lambda prefix:prefix + "<string>%s</string>"%(self['value'].value.replace('&','&amp;').encode('utf-8'))
else:
self.xml=lambda prefix:prefix + '<string></string>'
elif markertype == 8:
# UID
yield Bits(self, "size", 4, "Number of bytes minus 1")
yield GenericInteger(self, "value", False, (self['size'].value + 1)*8)
self.xml=lambda prefix:prefix + "" # no equivalent?
elif markertype == 10:
# Array
yield BPListSize(self, "size")
size = self['size'].value
if size:
yield BPListArray(self, "value", size)
self.xml=lambda prefix:self['value'].createXML(prefix)
elif markertype == 13:
# Dict
yield BPListSize(self, "size")
yield BPListDict(self, "value", self['size'].value)
self.xml=lambda prefix:self['value'].createXML(prefix)
else:
yield Bits(self, "value", 4)
self.xml=lambda prefix:''
def createValue(self):
if 'value' in self:
return self['value'].value
elif self['marker_type'].value in [4,5,6]:
return u''
else:
return None
def createDisplay(self):
if 'value' in self:
return unicode(self['value'].display)
elif self['marker_type'].value in [4,5,6]:
return u''
else:
return None
def createXML(self, prefix=''):
if 'value' in self:
try:
return self.xml(prefix)
except AttributeError:
return ''
return ''
def getFieldType(self):
return '%s<%s>'%(FieldSet.getFieldType(self), self['marker_type'].display)
class BPList(HachoirParser, RootSeekableFieldSet):
endian = BIG_ENDIAN
MAGIC = "bplist00"
PARSER_TAGS = {
"id": "bplist",
"category": "misc",
"file_ext": ("plist",),
"magic": ((MAGIC, 0),),
"min_size": 8 + 32, # bplist00 + 32-byte trailer
"description": "Apple/NeXT Binary Property List",
}
def __init__(self, stream, **args):
RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
HachoirParser.__init__(self, stream, **args)
def validate(self):
if self.stream.readBytes(0, len(self.MAGIC)) != self.MAGIC:
return "Invalid magic"
return True
def createFields(self):
yield Bytes(self, "magic", 8, "File magic (bplist00)")
if self.size:
self.seekByte(self.size//8-32, True)
else:
# FIXME: UNTESTED
while True:
try:
self.seekByte(1024)
except:
break
self.seekByte(self.size//8-32)
yield BPListTrailer(self, "trailer")
self.seekByte(self['trailer/offsetTableOffset'].value)
yield BPListOffsetTable(self, "offset_table")
for i in self.array("offset_table/offset"):
if self.current_size > i.value*8:
self.seekByte(i.value)
elif self.current_size < i.value*8:
# try to detect files with gaps or unparsed content
yield RawBytes(self, "padding[]", i.value-self.current_size//8)
yield BPListObject(self, "object[]")
def createXML(self, prefix=''):
return '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
''' + self['/object[' + str(self['/trailer/topObject'].value) + ']'].createXML(prefix) + '''
</plist>'''
+200
View File
@@ -0,0 +1,200 @@
"""
InfoTech Storage Format (ITSF) parser, used by Microsoft's HTML Help (.chm)
Document:
- Microsoft's HTML Help (.chm) format
http://www.wotsit.org (search "chm")
- chmlib library
http://www.jedrea.com/chmlib/
Author: Victor Stinner
Creation date: 2007-03-04
"""
from hachoir_parser import Parser
from hachoir_core.field import (Field, FieldSet, ParserError,
Int32, UInt32, UInt64,
RawBytes, PaddingBytes,
Enum, String)
from hachoir_core.endian import LITTLE_ENDIAN
from hachoir_parser.common.win32 import GUID
from hachoir_parser.common.win32_lang_id import LANGUAGE_ID
from hachoir_core.text_handler import textHandler, hexadecimal, filesizeHandler
class CWord(Field):
"""
Compressed double-word
"""
def __init__(self, parent, name, description=None):
Field.__init__(self, parent, name, 8, description)
endian = self._parent.endian
stream = self._parent.stream
addr = self.absolute_address
value = 0
byte = stream.readBits(addr, 8, endian)
while byte & 0x80:
value <<= 7
value += (byte & 0x7f)
self._size += 8
if 64 < self._size:
raise ParserError("CHM: CWord is limited to 64 bits")
addr += 8
byte = stream.readBits(addr, 8, endian)
value += byte
self.createValue = lambda: value
class Filesize_Header(FieldSet):
def createFields(self):
yield textHandler(UInt32(self, "unknown[]", "0x01FE"), hexadecimal)
yield textHandler(UInt32(self, "unknown[]", "0x0"), hexadecimal)
yield filesizeHandler(UInt64(self, "file_size"))
yield textHandler(UInt32(self, "unknown[]", "0x0"), hexadecimal)
yield textHandler(UInt32(self, "unknown[]", "0x0"), hexadecimal)
class ITSP(FieldSet):
def __init__(self, *args):
FieldSet.__init__(self, *args)
self._size = self["size"].value * 8
def createFields(self):
yield String(self, "magic", 4, "ITSP", charset="ASCII")
yield UInt32(self, "version", "Version (=1)")
yield filesizeHandler(UInt32(self, "size", "Length (in bytes) of the directory header (84)"))
yield UInt32(self, "unknown[]", "(=10)")
yield filesizeHandler(UInt32(self, "block_size", "Directory block size"))
yield UInt32(self, "density", "Density of quickref section, usually 2")
yield UInt32(self, "index_depth", "Depth of the index tree")
yield Int32(self, "nb_dir", "Chunk number of root index chunk")
yield UInt32(self, "first_pmgl", "Chunk number of first PMGL (listing) chunk")
yield UInt32(self, "last_pmgl", "Chunk number of last PMGL (listing) chunk")
yield Int32(self, "unknown[]", "-1")
yield UInt32(self, "nb_dir_chunk", "Number of directory chunks (total)")
yield Enum(UInt32(self, "lang_id", "Windows language ID"), LANGUAGE_ID)
yield GUID(self, "system_uuid", "{5D02926A-212E-11D0-9DF9-00A0C922E6EC}")
yield filesizeHandler(UInt32(self, "size2", "Same value than size"))
yield Int32(self, "unknown[]", "-1")
yield Int32(self, "unknown[]", "-1")
yield Int32(self, "unknown[]", "-1")
class ITSF(FieldSet):
def createFields(self):
yield String(self, "magic", 4, "ITSF", charset="ASCII")
yield UInt32(self, "version")
yield UInt32(self, "header_size", "Total header length (in bytes)")
yield UInt32(self, "one")
yield UInt32(self, "last_modified")
yield Enum(UInt32(self, "lang_id", "Windows Language ID"), LANGUAGE_ID)
yield GUID(self, "dir_uuid", "{7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC}")
yield GUID(self, "stream_uuid", "{7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC}")
yield UInt64(self, "filesize_offset")
yield filesizeHandler(UInt64(self, "filesize_len"))
yield UInt64(self, "dir_offset")
yield filesizeHandler(UInt64(self, "dir_len"))
if 3 <= self["version"].value:
yield UInt64(self, "data_offset")
class PMGL_Entry(FieldSet):
def createFields(self):
yield CWord(self, "name_len")
yield String(self, "name", self["name_len"].value, charset="UTF-8")
yield CWord(self, "space")
yield CWord(self, "start")
yield filesizeHandler(CWord(self, "length"))
def createDescription(self):
return "%s (%s)" % (self["name"].value, self["length"].display)
class PMGL(FieldSet):
def createFields(self):
# Header
yield String(self, "magic", 4, "PMGL", charset="ASCII")
yield filesizeHandler(Int32(self, "free_space",
"Length of free space and/or quickref area at end of directory chunk"))
yield Int32(self, "unknown")
yield Int32(self, "previous", "Chunk number of previous listing chunk")
yield Int32(self, "next", "Chunk number of previous listing chunk")
# Entries
stop = self.size - self["free_space"].value * 8
while self.current_size < stop:
yield PMGL_Entry(self, "entry[]")
# Padding
padding = (self.size - self.current_size) // 8
if padding:
yield PaddingBytes(self, "padding", padding)
class PMGI_Entry(FieldSet):
def createFields(self):
yield CWord(self, "name_len")
yield String(self, "name", self["name_len"].value, charset="UTF-8")
yield CWord(self, "page")
def createDescription(self):
return "%s (page #%u)" % (self["name"].value, self["page"].value)
class PMGI(FieldSet):
def createFields(self):
yield String(self, "magic", 4, "PMGI", charset="ASCII")
yield filesizeHandler(UInt32(self, "free_space",
"Length of free space and/or quickref area at end of directory chunk"))
stop = self.size - self["free_space"].value * 8
while self.current_size < stop:
yield PMGI_Entry(self, "entry[]")
padding = (self.size - self.current_size) // 8
if padding:
yield PaddingBytes(self, "padding", padding)
class Directory(FieldSet):
def createFields(self):
yield ITSP(self, "itsp")
block_size = self["itsp/block_size"].value * 8
nb_dir = self["itsp/nb_dir"].value
if nb_dir < 0:
nb_dir = 1
for index in xrange(nb_dir):
yield PMGL(self, "pmgl[]", size=block_size)
if self.current_size < self.size:
yield PMGI(self, "pmgi", size=block_size)
class ChmFile(Parser):
PARSER_TAGS = {
"id": "chm",
"category": "misc",
"file_ext": ("chm",),
"min_size": 4*8,
"magic": (("ITSF\3\0\0\0", 0),),
"description": "Microsoft's HTML Help (.chm)",
}
endian = LITTLE_ENDIAN
def validate(self):
if self.stream.readBytes(0, 4) != "ITSF":
return "Invalid magic"
if self["itsf/version"].value != 3:
return "Invalid version"
return True
def createFields(self):
yield ITSF(self, "itsf")
yield Filesize_Header(self, "file_size", size=self["itsf/filesize_len"].value*8)
padding = self.seekByte(self["itsf/dir_offset"].value)
if padding:
yield padding
yield Directory(self, "dir", size=self["itsf/dir_len"].value*8)
size = (self.size - self.current_size) // 8
if size:
yield RawBytes(self, "raw_end", size)
def createContentSize(self):
return self["file_size/file_size"].value * 8
+13
View File
@@ -0,0 +1,13 @@
from hachoir_core.field import StaticFieldSet, Float32
class Vertex(StaticFieldSet):
format = ((Float32, "x"), (Float32, "y"), (Float32, "z"))
def createValue(self):
return (self["x"].value, self["y"].value, self["z"].value)
class MapUV(StaticFieldSet):
format = ((Float32, "u"), (Float32, "v"))
def createValue(self):
return (self["u"].value, self["v"].value)
+214
View File
@@ -0,0 +1,214 @@
# -*- coding: utf-8 -*-
"""
3do model parser.
Author: Cyril Zorin
Creation date: 28 september 2006
"""
from hachoir_parser import Parser
from hachoir_core.field import (FieldSet,
UInt32, Int32, String, Float32,
RawBytes, PaddingBytes)
from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
from hachoir_parser.misc.common import Vertex, MapUV
class Vector(FieldSet):
def __init__(self, parent, name,
count, type, ename, edesc=None, description=None):
FieldSet.__init__(self, parent, name, description)
self.count = count
self.type = type
self.ename = ename+"[]"
self.edesc = edesc
try:
item_size = self.type.static_size(self.ename, self.edesc)
except TypeError:
item_size = self.type.static_size
if item_size:
self._size = item_size * self.count
def createFields(self):
for index in xrange(self.count):
yield self.type(self, self.ename, self.edesc)
class Face(FieldSet):
def createFields(self):
yield UInt32(self, "id")
yield UInt32(self, "type")
yield UInt32(self, "geometry_mode")
yield UInt32(self, "lighting_mode")
yield UInt32(self, "texture_mode")
yield UInt32(self, "nvertices")
yield Float32(self, "unknown[]", "unknown")
yield UInt32(self, "has_texture", "Has texture?")
yield UInt32(self, "has_material", "Has material?")
yield Vertex(self, "unknown[]")
yield Float32(self, "extra_light")
yield Vertex(self, "unknown[]")
yield Vertex(self, "normal")
if self["nvertices"].value:
yield Vector(self, "vertex_indices",
self["nvertices"].value, UInt32, "vertex")
if self["has_texture"].value:
yield Vector(self, "texture_vertex_indices",
self["nvertices"].value, UInt32, "texture_vertex")
if self["has_material"].value:
yield UInt32(self, "material_index", "material index")
def createDescription(self):
return "Face: id=%s" % self["id"].value
class Mesh(FieldSet):
def __init__(self, *args):
FieldSet.__init__(self, *args)
def createFields(self):
yield String(self, "name", 32, strip="\0")
yield UInt32(self, "id")
yield UInt32(self, "geometry_mode")
yield UInt32(self, "lighting_mode")
yield UInt32(self, "texture_mode")
yield UInt32(self, "nmesh_vertices")
yield UInt32(self, "ntexture_vertices")
yield UInt32(self, "nfaces")
nb_vert = self["nmesh_vertices"].value
if nb_vert:
yield Vector(self, "vertices",
nb_vert, Vertex, "vertex")
if self["ntexture_vertices"].value:
yield Vector(self, "texture vertices",
self["ntexture_vertices"].value, MapUV, "texture_vertex")
if nb_vert:
yield Vector(self, "light vertices",
nb_vert, Float32, "extra_light")
yield Vector(self, "unknown[]",
nb_vert, Float32, "unknown")
if self["nfaces"].value:
yield Vector(self, "faces", self["nfaces"].value, Face, "face")
if nb_vert:
yield Vector(self, "vertex normals",
nb_vert, Vertex, "normal")
yield UInt32(self, "has_shadow")
yield Float32(self, "unknown[]")
yield Float32(self, "radius")
yield Vertex(self, "unknown[]")
yield Vertex(self, "unknown[]")
def createDescription(self):
return 'Mesh "%s" (id %s)' % (self["name"].value, self["id"].value)
class Geoset(FieldSet):
def createFields(self):
yield UInt32(self, "count")
for index in xrange(self["count"].value):
yield Mesh(self, "mesh[]")
def createDescription(self):
return "Set of %s meshes" % self["count"].value
class Node(FieldSet):
def __init__(self, *args):
FieldSet.__init__(self, *args)
size = (188-4)*8
if self["parent_offset"].value != 0:
size += 32
if self["first_child_offset"].value != 0:
size += 32
if self["next_sibling_offset"].value != 0:
size += 32
self._size = size
def createFields(self):
yield String(self, "name", 32, strip="\0")
yield PaddingBytes(self, "unknown[]", 32, pattern="\xCC")
yield UInt32(self, "flags")
yield UInt32(self, "id")
yield UInt32(self, "type")
yield Int32(self, "mesh_id")
yield UInt32(self, "depth")
yield Int32(self, "parent_offset")
yield UInt32(self, "nchildren")
yield UInt32(self, "first_child_offset")
yield UInt32(self, "next_sibling_offset")
yield Vertex(self, "pivot")
yield Vertex(self, "position")
yield Float32(self, "pitch")
yield Float32(self, "yaw")
yield Float32(self, "roll")
for index in xrange(4):
yield Vertex(self, "unknown_vertex[]")
if self["parent_offset"].value != 0:
yield UInt32(self, "parent_id")
if self["first_child_offset"].value != 0:
yield UInt32(self, "first_child_id")
if self["next_sibling_offset"].value != 0:
yield UInt32(self, "next_sibling_id")
def createDescription(self):
return 'Node "%s"' % self["name"].value
class Nodes(FieldSet):
def createFields(self):
yield UInt32(self, "count")
for index in xrange(self["count"].value):
yield Node(self, "node[]")
def createDescription(self):
return 'Nodes (%s)' % self["count"].value
class Materials(FieldSet):
def __init__(self, *args):
FieldSet.__init__(self, *args)
count = self["count"]
self._size = count.size + count.value * (32*8)
def createFields(self):
yield UInt32(self, "count")
for index in xrange(self["count"].value):
yield String(self, "filename[]", 32, "Material file name", strip="\0")
def createDescription(self):
return 'Material file names (%s)' % self["count"].value
class File3do(Parser):
PARSER_TAGS = {
"id": "3do",
"category": "misc",
"file_ext": ("3do",),
"mime": (u"image/x-3do",),
"min_size": 8*4,
"description": "renderdroid 3d model."
}
endian = LITTLE_ENDIAN
def validate(self):
signature = self.stream.readBytes(0, 4)
return signature in ('LDOM', 'MODL') # lazy endian-safe hack =D
def createFields(self):
# Read file signature, and fix endian if needed
yield String(self, "file_sig", 4, "File signature", charset="ASCII")
if self["file_sig"].value == "MODL":
self.endian = BIG_ENDIAN
# Read file content
yield Materials(self, "materials")
yield String(self, "model_name", 32, "model file name", strip="\0")
yield RawBytes(self, "unknown[]", 4)
yield UInt32(self, "ngeosets")
for index in xrange(self["ngeosets"].value):
yield Geoset(self, "geoset[]")
yield RawBytes(self, "unknown[]", 4)
yield Nodes(self, "nodes")
yield Float32(self, "model_radius")
yield Vertex(self, "insertion_offset")
# Read the end of the file
if self.current_size < self._size:
yield self.seekBit(self._size, "end")
+177
View File
@@ -0,0 +1,177 @@
"""
3D Studio Max file (.3ds) parser.
Author: Victor Stinner
"""
from hachoir_parser import Parser
from hachoir_core.field import (StaticFieldSet, FieldSet,
UInt16, UInt32, RawBytes, Enum, CString)
from hachoir_parser.image.common import RGB
from hachoir_core.endian import LITTLE_ENDIAN
from hachoir_core.text_handler import textHandler, hexadecimal
from hachoir_parser.misc.common import Vertex, MapUV
def readObject(parent):
yield CString(parent, "name", "Object name")
size = parent["size"].value * 8
while parent.current_size < size:
yield Chunk(parent, "chunk[]")
def readTextureFilename(parent):
yield CString(parent, "filename", "Texture filename")
def readVersion(parent):
yield UInt32(parent, "version", "3DS file format version")
def readMaterialName(parent):
yield CString(parent, "name", "Material name")
class Polygon(StaticFieldSet):
format = (
(UInt16, "a", "Vertex A"),
(UInt16, "b", "Vertex B"),
(UInt16, "c", "Vertex C"),
(UInt16, "flags", "Flags"))
def readMapList(parent):
yield UInt16(parent, "count", "Map count")
for index in xrange(parent["count"].value):
yield MapUV(parent, "map_uv[]", "Mapping UV")
def readColor(parent):
yield RGB(parent, "color")
def readVertexList(parent):
yield UInt16(parent, "count", "Vertex count")
for index in range(0, parent["count"].value):
yield Vertex(parent, "vertex[]", "Vertex")
def readPolygonList(parent):
count = UInt16(parent, "count", "Vertex count")
yield count
for i in range(0, count.value):
yield Polygon(parent, "polygon[]")
size = parent["size"].value * 8
while parent.current_size < size:
yield Chunk(parent, "chunk[]")
class Chunk(FieldSet):
# List of chunk type name
type_name = {
0x0011: "Color",
0x4D4D: "Main chunk",
0x0002: "File version",
0x3D3D: "Materials and objects",
0x4000: "Object",
0x4100: "Mesh (triangular)",
0x4110: "Vertices list",
0x4120: "Polygon (faces) list",
0x4140: "Map UV list",
0x4130: "Object material",
0xAFFF: "New material",
0xA000: "Material name",
0xA010: "Material ambient",
0xA020: "Material diffuse",
0xA030: "Texture specular",
0xA200: "Texture",
0xA300: "Texture filename",
# Key frames
0xB000: "Keyframes",
0xB002: "Object node tag",
0xB006: "Light target node tag",
0xB007: "Spot light node tag",
0xB00A: "Keyframes header",
0xB009: "Keyframe current time",
0xB030: "Node identifier",
0xB010: "Node header",
0x7001: "Viewport layout"
}
chunk_id_by_type = {
0x4d4d: "main",
0x0002: "version",
0x3d3d: "obj_mat",
0xb000: "keyframes",
0xafff: "material[]",
0x4000: "object[]",
0x4110: "vertices_list",
0x4120: "polygon_list",
0x4140: "mapuv_list",
0x4100: "mesh"
}
# List of chunks which contains other chunks
sub_chunks = \
(0x4D4D, 0x4100, 0x3D3D, 0xAFFF, 0xA200,
0xB002, 0xB006, 0xB007,
0xA010, 0xA030, 0xA020, 0xB000)
# List of chunk type handlers
handlers = {
0xA000: readMaterialName,
0x4000: readObject,
0xA300: readTextureFilename,
0x0011: readColor,
0x0002: readVersion,
0x4110: readVertexList,
0x4120: readPolygonList,
0x4140: readMapList
}
def __init__(self, *args):
FieldSet.__init__(self, *args)
# Set description
self._description = "Chunk: %s" % self["type"].display
# Set name based on type field
type = self["type"].value
if type in Chunk.chunk_id_by_type:
self._name = Chunk.chunk_id_by_type[type]
else:
self._name = "chunk_%04x" % type
# Guess chunk size
self._size = self["size"].value * 8
def createFields(self):
yield Enum(textHandler(UInt16(self, "type", "Chunk type"), hexadecimal), Chunk.type_name)
yield UInt32(self, "size", "Chunk size (in bytes)")
content_size = self["size"].value - 6
if content_size == 0:
return
type = self["type"].value
if type in Chunk.sub_chunks:
while self.current_size < self.size:
yield Chunk(self, "chunk[]")
else:
if type in Chunk.handlers:
fields = Chunk.handlers[type] (self)
for field in fields:
yield field
else:
yield RawBytes(self, "data", content_size)
class File3ds(Parser):
endian = LITTLE_ENDIAN
PARSER_TAGS = {
"id": "3ds",
"category": "misc",
"file_ext": ("3ds",),
"mime": (u"image/x-3ds",),
"min_size": 16*8,
"description": "3D Studio Max model"
}
def validate(self):
if self.stream.readBytes(0, 2) != "MM":
return "Wrong signature"
if self["main/version/version"].value not in (2, 3):
return "Unknown format version"
return True
def createFields(self):
while not self.eof:
yield Chunk(self, "chunk[]")
+200
View File
@@ -0,0 +1,200 @@
"""
Gnome keyring parser.
Sources:
- Gnome Keyring source code,
function generate_file() in keyrings/gkr-keyring.c,
Author: Victor Stinner
Creation date: 2008-04-09
"""
from hachoir_core.tools import paddingSize
from hachoir_parser import Parser
from hachoir_core.field import (FieldSet,
Bit, NullBits, NullBytes,
UInt8, UInt32, String, RawBytes, Enum,
TimestampUnix64, CompressedField,
SubFile)
from hachoir_core.endian import BIG_ENDIAN
try:
import hashlib
def sha256(data):
hash = hashlib.new('sha256')
hash.update(data)
return hash.digest()
except ImportError:
def sha256(data):
raise ImportError("hashlib module is missing")
try:
from Crypto.Cipher import AES
class DeflateStream:
def __init__(self, stream):
hash_iterations = 1234
password = "x" * 8
salt = "\0" * 8
key, iv = generate_key(password, salt, hash_iterations)
self.cipher = AES.new(key, AES.MODE_CBC, iv)
def __call__(self, size, data=None):
if data is None:
return ''
return self.cipher.decrypt(data)
def Deflate(field):
CompressedField(field, DeflateStream)
return field
except ImportError:
def Deflate(field):
return field
class KeyringString(FieldSet):
def createFields(self):
yield UInt32(self, "length")
length = self["length"].value
if length == 0xffffffff:
return
yield String(self, "text", length, charset="UTF-8")
def createValue(self):
if "text" in self:
return self["text"].value
else:
return u''
def createDescription(self):
if "text" in self:
return self["text"].value
else:
return u"(empty string)"
class Attribute(FieldSet):
def createFields(self):
yield KeyringString(self, "name")
yield UInt32(self, "type")
type = self["type"].value
if type == 0:
yield KeyringString(self, "value")
elif type == 1:
yield UInt32(self, "value")
else:
raise TypeError("Unknown attribute type (%s)" % type)
def createDescription(self):
return 'Attribute "%s"' % self["name"].value
class ACL(FieldSet):
def createFields(self):
yield UInt32(self, "types_allowed")
yield KeyringString(self, "display_name")
yield KeyringString(self, "pathname")
yield KeyringString(self, "reserved[]")
yield UInt32(self, "reserved[]")
class Item(FieldSet):
def createFields(self):
yield UInt32(self, "id")
yield UInt32(self, "type")
yield UInt32(self, "attr_count")
for index in xrange(self["attr_count"].value):
yield Attribute(self, "attr[]")
def createDescription(self):
return "Item #%s: %s attributes" % (self["id"].value, self["attr_count"].value)
class Items(FieldSet):
def createFields(self):
yield UInt32(self, "count")
for index in xrange(self["count"].value):
yield Item(self, "item[]")
class EncryptedItem(FieldSet):
def createFields(self):
yield KeyringString(self, "display_name")
yield KeyringString(self, "secret")
yield TimestampUnix64(self, "mtime")
yield TimestampUnix64(self, "ctime")
yield KeyringString(self, "reserved[]")
for index in xrange(4):
yield UInt32(self, "reserved[]")
yield UInt32(self, "attr_count")
for index in xrange(self["attr_count"].value):
yield Attribute(self, "attr[]")
yield UInt32(self, "acl_count")
for index in xrange(self["acl_count"].value):
yield ACL(self, "acl[]")
# size = 8 # paddingSize((self.stream.size - self.current_size) // 8, 16)
# if size:
# yield NullBytes(self, "hash_padding", size, "16 bytes alignment")
class EncryptedData(Parser):
PARSER_TAGS = {
"id": "gnomeencryptedkeyring",
"min_size": 16*8,
"description": u"Gnome encrypted keyring",
}
endian = BIG_ENDIAN
def validate(self):
return True
def createFields(self):
yield RawBytes(self, "md5", 16)
while True:
size = (self.size - self.current_size) // 8
if size < 77:
break
yield EncryptedItem(self, "item[]")
size = paddingSize(self.current_size // 8, 16)
if size:
yield NullBytes(self, "padding_align", size)
class GnomeKeyring(Parser):
MAGIC = "GnomeKeyring\n\r\0\n"
PARSER_TAGS = {
"id": "gnomekeyring",
"category": "misc",
"magic": ((MAGIC, 0),),
"min_size": 47*8,
"description": u"Gnome keyring",
}
CRYPTO_NAMES = {
0: u"AEL",
}
HASH_NAMES = {
0: u"MD5",
}
endian = BIG_ENDIAN
def validate(self):
if self.stream.readBytes(0, len(self.MAGIC)) != self.MAGIC:
return u"Invalid magic string"
return True
def createFields(self):
yield String(self, "magic", len(self.MAGIC), 'Magic string (%r)' % self.MAGIC, charset="ASCII")
yield UInt8(self, "major_version")
yield UInt8(self, "minor_version")
yield Enum(UInt8(self, "crypto"), self.CRYPTO_NAMES)
yield Enum(UInt8(self, "hash"), self.HASH_NAMES)
yield KeyringString(self, "keyring_name")
yield TimestampUnix64(self, "mtime")
yield TimestampUnix64(self, "ctime")
yield Bit(self, "lock_on_idle")
yield NullBits(self, "reserved[]", 31, "Reserved for future flags")
yield UInt32(self, "lock_timeout")
yield UInt32(self, "hash_iterations")
yield RawBytes(self, "salt", 8)
yield NullBytes(self, "reserved[]", 16)
yield Items(self, "items")
yield UInt32(self, "encrypted_size")
yield Deflate(SubFile(self, "encrypted", self["encrypted_size"].value, "AES128 CBC", parser_class=EncryptedData))
def generate_key(password, salt, hash_iterations):
sha = sha256(password+salt)
for index in xrange(hash_iterations-1):
sha = sha256(sha)
return sha[:16], sha[16:]
+76
View File
@@ -0,0 +1,76 @@
"""
Microsoft Windows Help (HLP) parser for Hachoir project.
Documents:
- Windows Help File Format / Annotation File Format / SHG and MRB File Format
written by M. Winterhoff (100326.2776@compuserve.com)
found on http://www.wotsit.org/
Author: Victor Stinner
Creation date: 2007-09-03
"""
from hachoir_parser import Parser
from hachoir_core.field import (FieldSet,
Bits, Int32, UInt16, UInt32,
NullBytes, RawBytes, PaddingBytes, String)
from hachoir_core.endian import LITTLE_ENDIAN
from hachoir_core.text_handler import (textHandler, hexadecimal,
displayHandler, humanFilesize)
class FileEntry(FieldSet):
def __init__(self, *args, **kw):
FieldSet.__init__(self, *args, **kw)
self._size = self["res_space"].value * 8
def createFields(self):
yield displayHandler(UInt32(self, "res_space", "Reserved space"), humanFilesize)
yield displayHandler(UInt32(self, "used_space", "Used space"), humanFilesize)
yield Bits(self, "file_flags", 8, "(=4)")
yield textHandler(UInt16(self, "magic"), hexadecimal)
yield Bits(self, "flags", 16)
yield displayHandler(UInt16(self, "page_size", "Page size in bytes"), humanFilesize)
yield String(self, "structure", 16, strip="\0", charset="ASCII")
yield NullBytes(self, "zero", 2)
yield UInt16(self, "nb_page_splits", "Number of page splits B+ tree has suffered")
yield UInt16(self, "root_page", "Page number of B+ tree root page")
yield PaddingBytes(self, "one", 2, pattern="\xFF")
yield UInt16(self, "nb_page", "Number of B+ tree pages")
yield UInt16(self, "nb_level", "Number of levels of B+ tree")
yield UInt16(self, "nb_entry", "Number of entries in B+ tree")
size = (self.size - self.current_size)//8
if size:
yield PaddingBytes(self, "reserved_space", size)
class HlpFile(Parser):
PARSER_TAGS = {
"id": "hlp",
"category": "misc",
"file_ext": ("hlp",),
"min_size": 32,
"description": "Microsoft Windows Help (HLP)",
}
endian = LITTLE_ENDIAN
def validate(self):
if self["magic"].value != 0x00035F3F:
return "Invalid magic"
if self["filesize"].value != self.stream.size//8:
return "Invalid magic"
return True
def createFields(self):
yield textHandler(UInt32(self, "magic"), hexadecimal)
yield UInt32(self, "dir_start", "Directory start")
yield Int32(self, "first_free_block", "First free block")
yield UInt32(self, "filesize", "File size in bytes")
yield self.seekByte(self["dir_start"].value)
yield FileEntry(self, "file[]")
size = (self.size - self.current_size)//8
if size:
yield RawBytes(self, "end", size)
+613
View File
@@ -0,0 +1,613 @@
"""
Windows Shortcut (.lnk) parser.
Documents:
- The Windows Shortcut File Format (document version 1.0)
Reverse-engineered by Jesse Hager
http://www.i2s-lab.com/Papers/The_Windows_Shortcut_File_Format.pdf
- Wine source code:
http://source.winehq.org/source/include/shlobj.h (SHELL_LINK_DATA_FLAGS enum)
http://source.winehq.org/source/dlls/shell32/pidl.h
- Microsoft:
http://msdn2.microsoft.com/en-us/library/ms538128.aspx
Author: Robert Xiao, Victor Stinner
Changes:
2007-06-27 - Robert Xiao
* Fixes to FileLocationInfo to correctly handle Unicode paths
2007-06-13 - Robert Xiao
* ItemID, FileLocationInfo and ExtraInfo structs, correct Unicode string handling
2007-03-15 - Victor Stinner
* Creation of the parser
"""
from hachoir_parser import Parser
from hachoir_core.field import (FieldSet,
CString, String,
UInt32, UInt16, UInt8,
Bit, Bits, PaddingBits,
TimestampWin64, DateTimeMSDOS32,
NullBytes, PaddingBytes, RawBytes, Enum)
from hachoir_core.endian import LITTLE_ENDIAN
from hachoir_core.text_handler import textHandler, hexadecimal
from hachoir_parser.common.win32 import GUID
from hachoir_parser.common.msdos import MSDOSFileAttr16, MSDOSFileAttr32
from hachoir_core.text_handler import filesizeHandler
from hachoir_core.tools import paddingSize
class ItemIdList(FieldSet):
def __init__(self, *args, **kw):
FieldSet.__init__(self, *args, **kw)
self._size = (self["size"].value+2) * 8
def createFields(self):
yield UInt16(self, "size", "Size of item ID list")
while True:
item = ItemId(self, "itemid[]")
yield item
if not item["length"].value:
break
class ItemId(FieldSet):
ITEM_TYPE = {
0x1F: "GUID",
0x23: "Drive",
0x25: "Drive",
0x29: "Drive",
0x2E: "Shell Extension",
0x2F: "Drive",
0x30: "Dir/File",
0x31: "Directory",
0x32: "File",
0x34: "File [Unicode Name]",
0x41: "Workgroup",
0x42: "Computer",
0x46: "Net Provider",
0x47: "Whole Network",
0x4C: "Web Folder",
0x61: "MSITStore",
0x70: "Printer/RAS Connection",
0xB1: "History/Favorite",
0xC3: "Network Share",
}
def __init__(self, *args, **kw):
FieldSet.__init__(self, *args, **kw)
if self["length"].value:
self._size = self["length"].value * 8
else:
self._size = 16
def createFields(self):
yield UInt16(self, "length", "Length of Item ID Entry")
if not self["length"].value:
return
yield Enum(UInt8(self, "type"),self.ITEM_TYPE)
entrytype=self["type"].value
if entrytype in (0x1F, 0x70):
# GUID
yield RawBytes(self, "dummy", 1, "should be 0x50")
yield GUID(self, "guid")
elif entrytype == 0x2E:
# Shell extension
yield RawBytes(self, "dummy", 1, "should be 0x50")
if self["dummy"].value == '\0':
yield UInt16(self, "length_data", "Length of shell extension-specific data")
if self["length_data"].value:
yield RawBytes(self, "data", self["length_data"].value, "Shell extension-specific data")
yield GUID(self, "handler_guid")
yield GUID(self, "guid")
elif entrytype in (0x23, 0x25, 0x29, 0x2F):
# Drive
yield String(self, "drive", self["length"].value-3, strip="\0")
elif entrytype in (0x30, 0x31, 0x32, 0x61, 0xb1):
yield RawBytes(self, "dummy", 1, "should be 0x00")
yield UInt32(self, "size", "size of file; 0 for folders")
yield DateTimeMSDOS32(self, "date_time", "File/folder date and time")
yield MSDOSFileAttr16(self, "attribs", "File/folder attributes")
yield CString(self, "name", "File/folder name")
if self.root.hasUnicodeNames():
# Align to 2-bytes
n = paddingSize(self.current_size//8, 2)
if n:
yield PaddingBytes(self, "pad", n)
yield UInt16(self, "length_w", "Length of wide struct member")
yield RawBytes(self, "unknown[]", 6)
yield DateTimeMSDOS32(self, "creation_date_time", "File/folder creation date and time")
yield DateTimeMSDOS32(self, "access_date_time", "File/folder last access date and time")
yield RawBytes(self, "unknown[]", 2)
yield UInt16(self, "length_next", "Length of next two strings (if zero, ignore this field)")
yield CString(self, "unicode_name", "File/folder name", charset="UTF-16-LE")
if self["length_next"].value:
yield CString(self, "localized_name", "Localized name")
yield RawBytes(self, "unknown[]", 2)
else:
yield CString(self, "name_short", "File/folder short name")
elif entrytype in (0x41, 0x42, 0x46):
yield RawBytes(self, "unknown[]", 2)
yield CString(self, "name")
yield CString(self, "protocol")
yield RawBytes(self, "unknown[]", 2)
elif entrytype == 0x47:
# Whole Network
yield RawBytes(self, "unknown[]", 2)
yield CString(self, "name")
elif entrytype == 0xC3:
# Network Share
yield RawBytes(self, "unknown[]", 2)
yield CString(self, "name")
yield CString(self, "protocol")
yield CString(self, "description")
yield RawBytes(self, "unknown[]", 2)
elif entrytype == 0x4C:
# Web Folder
yield RawBytes(self, "unknown[]", 5)
yield TimestampWin64(self, "modification_time")
yield UInt32(self, "unknown[]")
yield UInt32(self, "unknown[]")
yield UInt32(self, "unknown[]")
yield LnkString(self, "name")
yield RawBytes(self, "padding[]", 2)
yield LnkString(self, "address")
if self["address/length"].value:
yield RawBytes(self, "padding[]", 2)
else:
yield RawBytes(self, "raw", self["length"].value-3)
def createDescription(self):
if self["length"].value:
return "Item ID Entry: "+self.ITEM_TYPE.get(self["type"].value,"Unknown")
else:
return "End of Item ID List"
def formatVolumeSerial(field):
val = field.value
return '%04X-%04X'%(val>>16, val&0xFFFF)
class LocalVolumeTable(FieldSet):
VOLUME_TYPE={
1: "No root directory",
2: "Removable (Floppy, Zip, etc.)",
3: "Fixed (Hard disk)",
4: "Remote (Network drive)",
5: "CD-ROM",
6: "Ram drive",
}
def createFields(self):
yield UInt32(self, "length", "Length of this structure")
yield Enum(UInt32(self, "volume_type", "Volume Type"),self.VOLUME_TYPE)
yield textHandler(UInt32(self, "volume_serial", "Volume Serial Number"), formatVolumeSerial)
yield UInt32(self, "label_offset", "Offset to volume label")
padding = self.seekByte(self["label_offset"].value)
if padding:
yield padding
yield CString(self, "drive")
def hasValue(self):
return bool(self["drive"].value)
def createValue(self):
return self["drive"].value
class NetworkVolumeTable(FieldSet):
def createFields(self):
yield UInt32(self, "length", "Length of this structure")
yield UInt32(self, "unknown[]")
yield UInt32(self, "share_name_offset", "Offset to share name")
yield UInt32(self, "unknown[]")
yield UInt32(self, "unknown[]")
padding = self.seekByte(self["share_name_offset"].value)
if padding:
yield padding
yield CString(self, "share_name")
def createValue(self):
return self["share_name"].value
class FileLocationInfo(FieldSet):
def createFields(self):
yield UInt32(self, "length", "Length of this structure")
if not self["length"].value:
return
yield UInt32(self, "first_offset_pos", "Position of first offset")
has_unicode_paths = (self["first_offset_pos"].value == 0x24)
yield Bit(self, "on_local_volume")
yield Bit(self, "on_network_volume")
yield PaddingBits(self, "reserved[]", 30)
yield UInt32(self, "local_info_offset", "Offset to local volume table; only meaningful if on_local_volume = 1")
yield UInt32(self, "local_pathname_offset", "Offset to local base pathname; only meaningful if on_local_volume = 1")
yield UInt32(self, "remote_info_offset", "Offset to network volume table; only meaningful if on_network_volume = 1")
yield UInt32(self, "pathname_offset", "Offset of remaining pathname")
if has_unicode_paths:
yield UInt32(self, "local_pathname_unicode_offset", "Offset to Unicode version of local base pathname; only meaningful if on_local_volume = 1")
yield UInt32(self, "pathname_unicode_offset", "Offset to Unicode version of remaining pathname")
if self["on_local_volume"].value:
padding = self.seekByte(self["local_info_offset"].value)
if padding:
yield padding
yield LocalVolumeTable(self, "local_volume_table", "Local Volume Table")
padding = self.seekByte(self["local_pathname_offset"].value)
if padding:
yield padding
yield CString(self, "local_base_pathname", "Local Base Pathname")
if has_unicode_paths:
padding = self.seekByte(self["local_pathname_unicode_offset"].value)
if padding:
yield padding
yield CString(self, "local_base_pathname_unicode", "Local Base Pathname in Unicode", charset="UTF-16-LE")
if self["on_network_volume"].value:
padding = self.seekByte(self["remote_info_offset"].value)
if padding:
yield padding
yield NetworkVolumeTable(self, "network_volume_table")
padding = self.seekByte(self["pathname_offset"].value)
if padding:
yield padding
yield CString(self, "final_pathname", "Final component of the pathname")
if has_unicode_paths:
padding = self.seekByte(self["pathname_unicode_offset"].value)
if padding:
yield padding
yield CString(self, "final_pathname_unicode", "Final component of the pathname in Unicode", charset="UTF-16-LE")
padding=self.seekByte(self["length"].value)
if padding:
yield padding
class LnkString(FieldSet):
def createFields(self):
yield UInt16(self, "length", "Length of this string")
if self["length"].value:
if self.root.hasUnicodeNames():
yield String(self, "data", self["length"].value*2, charset="UTF-16-LE")
else:
yield String(self, "data", self["length"].value, charset="ASCII")
def createValue(self):
if self["length"].value:
return self["data"].value
else:
return ""
class ColorRef(FieldSet):
''' COLORREF struct, 0x00bbggrr '''
static_size=32
def createFields(self):
yield UInt8(self, "red", "Red")
yield UInt8(self, "green", "Green")
yield UInt8(self, "blue", "Blue")
yield PaddingBytes(self, "pad", 1, "Padding (must be 0)")
def createDescription(self):
rgb = self["red"].value, self["green"].value, self["blue"].value
return "RGB Color: #%02X%02X%02X" % rgb
class ColorTableIndex(Bits):
def __init__(self, parent, name, size, description=None):
Bits.__init__(self, parent, name, size, None)
self.desc=description
def createDescription(self):
assert hasattr(self, 'parent') and hasattr(self, 'value')
return "%s: %s"%(self.desc,
self.parent["color[%i]"%self.value].description)
class ExtraInfo(FieldSet):
INFO_TYPE={
0xA0000001: "Link Target Information", # EXP_SZ_LINK_SIG
0xA0000002: "Console Window Properties", # NT_CONSOLE_PROPS_SIG
0xA0000003: "Hostname and Other Stuff",
0xA0000004: "Console Codepage Information", # NT_FE_CONSOLE_PROPS_SIG
0xA0000005: "Special Folder Info", # EXP_SPECIAL_FOLDER_SIG
0xA0000006: "DarwinID (Windows Installer ID) Information", # EXP_DARWIN_ID_SIG
0xA0000007: "Custom Icon Details", # EXP_LOGO3_ID_SIG or EXP_SZ_ICON_SIG
}
SPECIAL_FOLDER = {
0: "DESKTOP",
1: "INTERNET",
2: "PROGRAMS",
3: "CONTROLS",
4: "PRINTERS",
5: "PERSONAL",
6: "FAVORITES",
7: "STARTUP",
8: "RECENT",
9: "SENDTO",
10: "BITBUCKET",
11: "STARTMENU",
16: "DESKTOPDIRECTORY",
17: "DRIVES",
18: "NETWORK",
19: "NETHOOD",
20: "FONTS",
21: "TEMPLATES",
22: "COMMON_STARTMENU",
23: "COMMON_PROGRAMS",
24: "COMMON_STARTUP",
25: "COMMON_DESKTOPDIRECTORY",
26: "APPDATA",
27: "PRINTHOOD",
28: "LOCAL_APPDATA",
29: "ALTSTARTUP",
30: "COMMON_ALTSTARTUP",
31: "COMMON_FAVORITES",
32: "INTERNET_CACHE",
33: "COOKIES",
34: "HISTORY",
35: "COMMON_APPDATA",
36: "WINDOWS",
37: "SYSTEM",
38: "PROGRAM_FILES",
39: "MYPICTURES",
40: "PROFILE",
41: "SYSTEMX86",
42: "PROGRAM_FILESX86",
43: "PROGRAM_FILES_COMMON",
44: "PROGRAM_FILES_COMMONX86",
45: "COMMON_TEMPLATES",
46: "COMMON_DOCUMENTS",
47: "COMMON_ADMINTOOLS",
48: "ADMINTOOLS",
49: "CONNECTIONS",
53: "COMMON_MUSIC",
54: "COMMON_PICTURES",
55: "COMMON_VIDEO",
56: "RESOURCES",
57: "RESOURCES_LOCALIZED",
58: "COMMON_OEM_LINKS",
59: "CDBURN_AREA",
61: "COMPUTERSNEARME",
}
BOOL_ENUM = {
0: "False",
1: "True",
}
def __init__(self, *args, **kw):
FieldSet.__init__(self, *args, **kw)
if self["length"].value:
self._size = self["length"].value * 8
else:
self._size = 32
def createFields(self):
yield UInt32(self, "length", "Length of this structure")
if not self["length"].value:
return
yield Enum(textHandler(UInt32(self, "signature", "Signature determining the function of this structure"),hexadecimal),self.INFO_TYPE)
if self["signature"].value == 0xA0000003:
# Hostname and Other Stuff
yield UInt32(self, "remaining_length")
yield UInt32(self, "unknown[]")
yield String(self, "hostname", 16, "Computer hostname on which shortcut was last modified", strip="\0")
yield RawBytes(self, "unknown[]", 32)
yield RawBytes(self, "unknown[]", 32)
elif self["signature"].value == 0xA0000005:
# Special Folder Info
yield Enum(UInt32(self, "special_folder_id", "ID of the special folder"),self.SPECIAL_FOLDER)
yield UInt32(self, "offset", "Offset to Item ID entry")
elif self["signature"].value in (0xA0000001, 0xA0000006, 0xA0000007):
if self["signature"].value == 0xA0000001: # Link Target Information
object_name="target"
elif self["signature"].value == 0xA0000006: # DarwinID (Windows Installer ID) Information
object_name="darwinID"
else: # Custom Icon Details
object_name="icon_path"
yield CString(self, object_name, "Data (ASCII format)", charset="ASCII")
remaining = self["length"].value - self.current_size/8 - 260*2 # 260*2 = size of next part
if remaining:
yield RawBytes(self, "slack_space[]", remaining, "Data beyond end of string")
yield CString(self, object_name+'_unicode', "Data (Unicode format)", charset="UTF-16-LE", truncate="\0")
remaining = self["length"].value - self.current_size/8
if remaining:
yield RawBytes(self, "slack_space[]", remaining, "Data beyond end of string")
elif self["signature"].value == 0xA0000002:
# Console Window Properties
yield ColorTableIndex(self, "color_text", 4, "Screen text color index")
yield ColorTableIndex(self, "color_bg", 4, "Screen background color index")
yield NullBytes(self, "reserved[]", 1)
yield ColorTableIndex(self, "color_popup_text", 4, "Pop-up text color index")
yield ColorTableIndex(self, "color_popup_bg", 4, "Pop-up background color index")
yield NullBytes(self, "reserved[]", 1)
yield UInt16(self, "buffer_width", "Screen buffer width (character cells)")
yield UInt16(self, "buffer_height", "Screen buffer height (character cells)")
yield UInt16(self, "window_width", "Window width (character cells)")
yield UInt16(self, "window_height", "Window height (character cells)")
yield UInt16(self, "position_left", "Window distance from left edge (screen coords)")
yield UInt16(self, "position_top", "Window distance from top edge (screen coords)")
yield UInt32(self, "font_number")
yield UInt32(self, "input_buffer_size")
yield UInt16(self, "font_width", "Font width in pixels; 0 for a non-raster font")
yield UInt16(self, "font_height", "Font height in pixels; equal to the font size for non-raster fonts")
yield UInt32(self, "font_family")
yield UInt32(self, "font_weight")
yield String(self, "font_name_unicode", 64, "Font Name (Unicode format)", charset="UTF-16-LE", truncate="\0")
yield UInt32(self, "cursor_size", "Relative size of cursor (% of character size)")
yield Enum(UInt32(self, "full_screen", "Run console in full screen?"), self.BOOL_ENUM)
yield Enum(UInt32(self, "quick_edit", "Console uses quick-edit feature (using mouse to cut & paste)?"), self.BOOL_ENUM)
yield Enum(UInt32(self, "insert_mode", "Console uses insertion mode?"), self.BOOL_ENUM)
yield Enum(UInt32(self, "auto_position", "System automatically positions window?"), self.BOOL_ENUM)
yield UInt32(self, "history_size", "Size of the history buffer (in lines)")
yield UInt32(self, "history_count", "Number of history buffers (each process gets one up to this limit)")
yield Enum(UInt32(self, "history_no_dup", "Automatically eliminate duplicate lines in the history buffer?"), self.BOOL_ENUM)
for index in xrange(16):
yield ColorRef(self, "color[]")
elif self["signature"].value == 0xA0000004:
# Console Codepage Information
yield UInt32(self, "codepage", "Console's code page")
else:
yield RawBytes(self, "raw", self["length"].value-self.current_size/8)
def createDescription(self):
if self["length"].value:
return "Extra Info Entry: "+self["signature"].display
else:
return "End of Extra Info"
HOT_KEYS = {
0x00: u'None',
0x13: u'Pause',
0x14: u'Caps Lock',
0x21: u'Page Up',
0x22: u'Page Down',
0x23: u'End',
0x24: u'Home',
0x25: u'Left',
0x26: u'Up',
0x27: u'Right',
0x28: u'Down',
0x2d: u'Insert',
0x2e: u'Delete',
0x6a: u'Num *',
0x6b: u'Num +',
0x6d: u'Num -',
0x6e: u'Num .',
0x6f: u'Num /',
0x90: u'Num Lock',
0x91: u'Scroll Lock',
0xba: u';',
0xbb: u'=',
0xbc: u',',
0xbd: u'-',
0xbe: u'.',
0xbf: u'/',
0xc0: u'`',
0xdb: u'[',
0xdc: u'\\',
0xdd: u']',
0xde: u"'",
}
def text_hot_key(field):
assert hasattr(field, "value")
val=field.value
if 0x30 <= val <= 0x39:
return unichr(val)
elif 0x41 <= val <= 0x5A:
return unichr(val)
elif 0x60 <= val <= 0x69:
return u'Numpad %c' % unichr(val-0x30)
elif 0x70 <= val <= 0x87:
return 'F%i'%(val-0x6F)
elif val in HOT_KEYS:
return HOT_KEYS[val]
return str(val)
class LnkFile(Parser):
MAGIC = "\x4C\0\0\0\x01\x14\x02\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46"
PARSER_TAGS = {
"id": "lnk",
"category": "misc",
"file_ext": ("lnk",),
"mime": (u"application/x-ms-shortcut",),
"magic": ((MAGIC, 0),),
"min_size": len(MAGIC)*8, # signature + guid = 20 bytes
"description": "Windows Shortcut (.lnk)",
}
endian = LITTLE_ENDIAN
SHOW_WINDOW_STATE = {
0: u"Hide",
1: u"Show Normal",
2: u"Show Minimized",
3: u"Show Maximized",
4: u"Show Normal, not activated",
5: u"Show",
6: u"Minimize",
7: u"Show Minimized, not activated",
8: u"Show, not activated",
9: u"Restore",
10: u"Show Default",
}
def validate(self):
if self["signature"].value != 0x0000004C:
return "Invalid signature"
if self["guid"].value != "00021401-0000-0000-C000-000000000046":
return "Invalid GUID"
return True
def hasUnicodeNames(self):
return self["has_unicode_names"].value
def createFields(self):
yield UInt32(self, "signature", "Shortcut signature (0x0000004C)")
yield GUID(self, "guid", "Shortcut GUID (00021401-0000-0000-C000-000000000046)")
yield Bit(self, "has_shell_id", "Is the Item ID List present?")
yield Bit(self, "target_is_file", "Is a file or a directory?")
yield Bit(self, "has_description", "Is the Description field present?")
yield Bit(self, "has_rel_path", "Is the relative path to the target available?")
yield Bit(self, "has_working_dir", "Is there a working directory?")
yield Bit(self, "has_cmd_line_args", "Are there any command line arguments?")
yield Bit(self, "has_custom_icon", "Is there a custom icon?")
yield Bit(self, "has_unicode_names", "Are Unicode names used?")
yield Bit(self, "force_no_linkinfo")
yield Bit(self, "has_exp_sz")
yield Bit(self, "run_in_separate")
yield Bit(self, "has_logo3id", "Is LOGO3 ID info present?")
yield Bit(self, "has_darwinid", "Is the DarwinID info present?")
yield Bit(self, "runas_user", "Is the target run as another user?")
yield Bit(self, "has_exp_icon_sz", "Is custom icon information available?")
yield Bit(self, "no_pidl_alias")
yield Bit(self, "force_unc_name")
yield Bit(self, "run_with_shim_layer")
yield PaddingBits(self, "reserved[]", 14, "Flag bits reserved for future use")
yield MSDOSFileAttr32(self, "target_attr")
yield TimestampWin64(self, "creation_time")
yield TimestampWin64(self, "modification_time")
yield TimestampWin64(self, "last_access_time")
yield filesizeHandler(UInt32(self, "target_filesize"))
yield UInt32(self, "icon_number")
yield Enum(UInt32(self, "show_window"), self.SHOW_WINDOW_STATE)
yield textHandler(UInt8(self, "hot_key", "Hot key used for quick access"),text_hot_key)
yield Bit(self, "hot_key_shift", "Hot key: is Shift used?")
yield Bit(self, "hot_key_ctrl", "Hot key: is Ctrl used?")
yield Bit(self, "hot_key_alt", "Hot key: is Alt used?")
yield PaddingBits(self, "hot_key_reserved", 21, "Hot key: (reserved)")
yield NullBytes(self, "reserved[]", 8)
if self["has_shell_id"].value:
yield ItemIdList(self, "item_idlist", "Item ID List")
if self["target_is_file"].value:
yield FileLocationInfo(self, "file_location_info", "File Location Info")
if self["has_description"].value:
yield LnkString(self, "description")
if self["has_rel_path"].value:
yield LnkString(self, "relative_path", "Relative path to target")
if self["has_working_dir"].value:
yield LnkString(self, "working_dir", "Working directory (dir to start target in)")
if self["has_cmd_line_args"].value:
yield LnkString(self, "cmd_line_args", "Command Line Arguments")
if self["has_custom_icon"].value:
yield LnkString(self, "custom_icon", "Custom Icon Path")
while not self.eof:
yield ExtraInfo(self, "extra_info[]")
+131
View File
@@ -0,0 +1,131 @@
"""
Parsers for the different streams and fragments found in an OLE2 file.
Documents:
- goffice source code
Author: Robert Xiao, Victor Stinner
Creation: 2006-04-23
"""
from hachoir_parser import HachoirParser
from hachoir_core.field import FieldSet, RootSeekableFieldSet, RawBytes
from hachoir_core.endian import LITTLE_ENDIAN
from hachoir_core.stream import StringInputStream
from hachoir_parser.misc.msoffice_summary import SummaryFieldSet, CompObj
from hachoir_parser.misc.word_doc import WordDocumentFieldSet
PROPERTY_NAME = {
u"\5DocumentSummaryInformation": "doc_summary",
u"\5SummaryInformation": "summary",
u"WordDocument": "word_doc",
}
class OfficeRootEntry(HachoirParser, RootSeekableFieldSet):
PARSER_TAGS = {
"description": "Microsoft Office document subfragments",
}
endian = LITTLE_ENDIAN
def __init__(self, stream, **args):
RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
HachoirParser.__init__(self, stream, **args)
def validate(self):
return True
def createFields(self):
for index, property in enumerate(self.ole2.properties):
if index == 0:
continue
try:
name = PROPERTY_NAME[property["name"].value]
except LookupError:
name = property.name+"content"
for field in self.parseProperty(index, property, name):
yield field
def parseProperty(self, property_index, property, name_prefix):
ole2 = self.ole2
if not property["size"].value:
return
if property["size"].value >= ole2["header/threshold"].value:
return
name = "%s[]" % name_prefix
first = None
previous = None
size = 0
start = property["start"].value
chain = ole2.getChain(start, True)
blocksize = ole2.ss_size
desc_format = "Small blocks %s..%s (%s)"
while True:
try:
block = chain.next()
contiguous = False
if not first:
first = block
contiguous = True
if previous and block == (previous+1):
contiguous = True
if contiguous:
previous = block
size += blocksize
continue
except StopIteration:
block = None
self.seekSBlock(first)
desc = desc_format % (first, previous, previous-first+1)
size = min(size, property["size"].value*8)
if name_prefix in ("summary", "doc_summary"):
yield SummaryFieldSet(self, name, desc, size=size)
elif name_prefix == "word_doc":
yield WordDocumentFieldSet(self, name, desc, size=size)
elif property_index == 1:
yield CompObj(self, "comp_obj", desc, size=size)
else:
yield RawBytes(self, name, size//8, desc)
if block is None:
break
first = block
previous = block
size = ole2.sector_size
def seekSBlock(self, block):
self.seekBit(block * self.ole2.ss_size)
class FragmentGroup:
def __init__(self, parser):
self.items = []
self.parser = parser
def add(self, item):
self.items.append(item)
def createInputStream(self):
# FIXME: Use lazy stream creation
data = []
for item in self.items:
data.append( item["rawdata"].value )
data = "".join(data)
# FIXME: Use smarter code to send arguments
args = {"ole2": self.items[0].root}
tags = {"class": self.parser, "args": args}
tags = tags.iteritems()
return StringInputStream(data, "<fragment group>", tags=tags)
class CustomFragment(FieldSet):
def __init__(self, parent, name, size, parser, description=None, group=None):
FieldSet.__init__(self, parent, name, description, size=size)
if not group:
group = FragmentGroup(parser)
self.group = group
self.group.add(self)
def createFields(self):
yield RawBytes(self, "rawdata", self.size//8)
def _createInputStream(self, **args):
return self.group.createInputStream()
@@ -0,0 +1,377 @@
"""
Microsoft Document summaries structures.
Documents
---------
- Apache POI (HPSF Internals):
http://poi.apache.org/hpsf/internals.html
"""
from hachoir_parser import HachoirParser
from hachoir_core.field import (FieldSet, ParserError,
RootSeekableFieldSet, SeekableFieldSet,
Bit, Bits, NullBits,
UInt8, UInt16, UInt32, TimestampWin64, TimedeltaWin64, Enum,
Bytes, RawBytes, NullBytes, String,
Int8, Int32, Float32, Float64, PascalString32)
from hachoir_core.text_handler import textHandler, hexadecimal, filesizeHandler
from hachoir_core.tools import createDict
from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
from hachoir_parser.common.win32 import GUID, PascalStringWin32, CODEPAGE_CHARSET
from hachoir_parser.image.bmp import BmpHeader, parseImageData
MAX_SECTION_COUNT = 100
OS_MAC = 1
OS_NAME = {
0: "Windows 16-bit",
1: "Macintosh",
2: "Windows 32-bit",
}
class OSConfig:
def __init__(self, big_endian):
if big_endian:
self.charset = "MacRoman"
self.utf16 = "UTF-16-BE"
else:
# FIXME: Don't guess the charset, use ISO-8859-1 or UTF-8
#self.charset = "ISO-8859-1"
self.charset = None
self.utf16 = "UTF-16-LE"
class PropertyIndex(FieldSet):
TAG_CODEPAGE = 1
COMMON_PROPERTY = {
0: "Dictionary",
1: "CodePage",
0x80000000: "LOCALE_SYSTEM_DEFAULT",
0x80000003: "CASE_SENSITIVE",
}
DOCUMENT_PROPERTY = {
2: "Category",
3: "PresentationFormat",
4: "NumBytes",
5: "NumLines",
6: "NumParagraphs",
7: "NumSlides",
8: "NumNotes",
9: "NumHiddenSlides",
10: "NumMMClips",
11: "Scale",
12: "HeadingPairs",
13: "DocumentParts",
14: "Manager",
15: "Company",
16: "LinksDirty",
17: "DocSumInfo_17",
18: "DocSumInfo_18",
19: "DocSumInfo_19",
20: "DocSumInfo_20",
21: "DocSumInfo_21",
22: "DocSumInfo_22",
23: "DocSumInfo_23",
}
DOCUMENT_PROPERTY.update(COMMON_PROPERTY)
COMPONENT_PROPERTY = {
2: "Title",
3: "Subject",
4: "Author",
5: "Keywords",
6: "Comments",
7: "Template",
8: "LastSavedBy",
9: "RevisionNumber",
10: "TotalEditingTime",
11: "LastPrinted",
12: "CreateTime",
13: "LastSavedTime",
14: "NumPages",
15: "NumWords",
16: "NumCharacters",
17: "Thumbnail",
18: "AppName",
19: "Security",
}
COMPONENT_PROPERTY.update(COMMON_PROPERTY)
def createFields(self):
if self["../.."].name.startswith("doc_summary"):
enum = self.DOCUMENT_PROPERTY
else:
enum = self.COMPONENT_PROPERTY
yield Enum(UInt32(self, "id"), enum)
yield UInt32(self, "offset")
def createDescription(self):
return "Property: %s" % self["id"].display
class Bool(Int8):
def createValue(self):
value = Int8.createValue(self)
return (value == -1)
class Thumbnail(FieldSet):
"""
Thumbnail.
Documents:
- See Jakarta POI
http://jakarta.apache.org/poi/hpsf/thumbnails.html
http://www.penguin-soft.com/penguin/developer/poi/
org/apache/poi/hpsf/Thumbnail.html#CF_BITMAP
- How To Extract Thumbnail Images
http://sparks.discreet.com/knowledgebase/public/
solutions/ExtractThumbnailImg.htm
"""
FORMAT_CLIPBOARD = -1
FORMAT_NAME = {
-1: "Windows clipboard",
-2: "Macintosh clipboard",
-3: "GUID that contains format identifier",
0: "No data",
2: "Bitmap",
3: "Windows metafile format",
8: "Device Independent Bitmap (DIB)",
14: "Enhanced Windows metafile",
}
DIB_BMP = 8
DIB_FORMAT = {
2: "Bitmap Obsolete (old BMP)",
3: "Windows metafile format (WMF)",
8: "Device Independent Bitmap (BMP)",
14: "Enhanced Windows metafile (EMF)",
}
def __init__(self, *args):
FieldSet.__init__(self, *args)
self._size = self["size"].value * 8
def createFields(self):
yield filesizeHandler(UInt32(self, "size"))
yield Enum(Int32(self, "format"), self.FORMAT_NAME)
if self["format"].value == self.FORMAT_CLIPBOARD:
yield Enum(UInt32(self, "dib_format"), self.DIB_FORMAT)
if self["dib_format"].value == self.DIB_BMP:
yield BmpHeader(self, "bmp_header")
size = (self.size - self.current_size) // 8
yield parseImageData(self, "pixels", size, self["bmp_header"])
return
size = (self.size - self.current_size) // 8
if size:
yield RawBytes(self, "data", size)
class PropertyContent(FieldSet):
TYPE_LPSTR = 30
TYPE_INFO = {
0: ("EMPTY", None),
1: ("NULL", None),
2: ("UInt16", UInt16),
3: ("UInt32", UInt32),
4: ("Float32", Float32),
5: ("Float64", Float64),
6: ("CY", None),
7: ("DATE", None),
8: ("BSTR", None),
9: ("DISPATCH", None),
10: ("ERROR", None),
11: ("BOOL", Bool),
12: ("VARIANT", None),
13: ("UNKNOWN", None),
14: ("DECIMAL", None),
16: ("I1", None),
17: ("UI1", None),
18: ("UI2", None),
19: ("UI4", None),
20: ("I8", None),
21: ("UI8", None),
22: ("INT", None),
23: ("UINT", None),
24: ("VOID", None),
25: ("HRESULT", None),
26: ("PTR", None),
27: ("SAFEARRAY", None),
28: ("CARRAY", None),
29: ("USERDEFINED", None),
30: ("LPSTR", PascalString32),
31: ("LPWSTR", PascalString32),
64: ("FILETIME", TimestampWin64),
65: ("BLOB", None),
66: ("STREAM", None),
67: ("STORAGE", None),
68: ("STREAMED_OBJECT", None),
69: ("STORED_OBJECT", None),
70: ("BLOB_OBJECT", None),
71: ("THUMBNAIL", Thumbnail),
72: ("CLSID", None),
0x1000: ("Vector", None),
}
TYPE_NAME = createDict(TYPE_INFO, 0)
def createFields(self):
self.osconfig = self.parent.osconfig
if True:
yield Enum(Bits(self, "type", 12), self.TYPE_NAME)
yield Bit(self, "is_vector")
yield NullBits(self, "padding", 32-12-1)
else:
yield Enum(Bits(self, "type", 32), self.TYPE_NAME)
tag = self["type"].value
kw = {}
try:
handler = self.TYPE_INFO[tag][1]
if handler == PascalString32:
osconfig = self.osconfig
if tag == self.TYPE_LPSTR:
kw["charset"] = osconfig.charset
else:
kw["charset"] = osconfig.utf16
elif handler == TimestampWin64:
if self.description == "TotalEditingTime":
handler = TimedeltaWin64
except LookupError:
handler = None
if not handler:
raise ParserError("OLE2: Unable to parse property of type %s" \
% self["type"].display)
if self["is_vector"].value:
yield UInt32(self, "count")
for index in xrange(self["count"].value):
yield handler(self, "item[]", **kw)
else:
yield handler(self, "value", **kw)
self.createValue = lambda: self["value"].value
PropertyContent.TYPE_INFO[12] = ("VARIANT", PropertyContent)
class SummarySection(SeekableFieldSet):
def __init__(self, *args):
SeekableFieldSet.__init__(self, *args)
self._size = self["size"].value * 8
def createFields(self):
self.osconfig = self.parent.osconfig
yield UInt32(self, "size")
yield UInt32(self, "property_count")
for index in xrange(self["property_count"].value):
yield PropertyIndex(self, "property_index[]")
for index in xrange(self["property_count"].value):
findex = self["property_index[%u]" % index]
self.seekByte(findex["offset"].value)
field = PropertyContent(self, "property[]", findex["id"].display)
yield field
if not self.osconfig.charset \
and findex['id'].value == PropertyIndex.TAG_CODEPAGE:
codepage = field['value'].value
if codepage in CODEPAGE_CHARSET:
self.osconfig.charset = CODEPAGE_CHARSET[codepage]
else:
self.warning("Unknown codepage: %r" % codepage)
class SummaryIndex(FieldSet):
static_size = 20*8
def createFields(self):
yield String(self, "name", 16)
yield UInt32(self, "offset")
class BaseSummary:
endian = LITTLE_ENDIAN
def __init__(self):
if self["endian"].value == "\xFF\xFE":
self.endian = BIG_ENDIAN
elif self["endian"].value == "\xFE\xFF":
self.endian = LITTLE_ENDIAN
else:
raise ParserError("OLE2: Invalid endian value")
self.osconfig = OSConfig(self["os_type"].value == OS_MAC)
def createFields(self):
yield Bytes(self, "endian", 2, "Endian (0xFF 0xFE for Intel)")
yield UInt16(self, "format", "Format (0)")
yield UInt8(self, "os_version")
yield UInt8(self, "os_revision")
yield Enum(UInt16(self, "os_type"), OS_NAME)
yield GUID(self, "format_id")
yield UInt32(self, "section_count")
if MAX_SECTION_COUNT < self["section_count"].value:
raise ParserError("OLE2: Too much sections (%s)" % self["section_count"].value)
section_indexes = []
for index in xrange(self["section_count"].value):
section_index = SummaryIndex(self, "section_index[]")
yield section_index
section_indexes.append(section_index)
for section_index in section_indexes:
self.seekByte(section_index["offset"].value)
yield SummarySection(self, "section[]")
size = (self.size - self.current_size) // 8
if 0 < size:
yield NullBytes(self, "end_padding", size)
class SummaryParser(BaseSummary, HachoirParser, RootSeekableFieldSet):
PARSER_TAGS = {
"description": "Microsoft Office summary",
}
def __init__(self, stream, **kw):
RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
HachoirParser.__init__(self, stream, **kw)
BaseSummary.__init__(self)
def validate(self):
return True
class SummaryFieldSet(BaseSummary, FieldSet):
def __init__(self, parent, name, description=None, size=None):
FieldSet.__init__(self, parent, name, description=description, size=size)
BaseSummary.__init__(self)
class CompObj(FieldSet):
OS_VERSION = {
0x0a03: "Windows 3.1",
}
def createFields(self):
# Header
yield UInt16(self, "version", "Version (=1)")
yield textHandler(UInt16(self, "endian", "Endian (0xFF 0xFE for Intel)"), hexadecimal)
yield UInt8(self, "os_version")
yield UInt8(self, "os_revision")
yield Enum(UInt16(self, "os_type"), OS_NAME)
yield Int32(self, "unused", "(=-1)")
yield GUID(self, "clsid")
# User type
yield PascalString32(self, "user_type", strip="\0")
# Clipboard format
if self["os_type"].value == OS_MAC:
yield Int32(self, "unused[]", "(=-2)")
yield String(self, "clipboard_format", 4)
else:
yield PascalString32(self, "clipboard_format", strip="\0")
if self.current_size == self.size:
return
#-- OLE 2.01 ---
# Program ID
yield PascalString32(self, "prog_id", strip="\0")
if self["os_type"].value != OS_MAC:
# Magic number
yield textHandler(UInt32(self, "magic", "Magic number (0x71B239F4)"), hexadecimal)
# Unicode version
yield PascalStringWin32(self, "user_type_unicode", strip="\0")
yield PascalStringWin32(self, "clipboard_format_unicode", strip="\0")
yield PascalStringWin32(self, "prog_id_unicode", strip="\0")
size = (self.size - self.current_size) // 8
if size:
yield NullBytes(self, "end_padding", size)
+367
View File
@@ -0,0 +1,367 @@
"""
Microsoft Office documents parser.
Informations:
* wordole.c of AntiWord program (v0.35)
Copyright (C) 1998-2003 A.J. van Os
Released under GNU GPL
http://www.winfield.demon.nl/
* File gsf-infile-msole.c of libgsf library (v1.14.0)
Copyright (C) 2002-2004 Jody Goldberg (jody@gnome.org)
Released under GNU LGPL 2.1
http://freshmeat.net/projects/libgsf/
* PDF from AAF Association
Copyright (C) 2004 AAF Association
Copyright (C) 1991-2003 Microsoft Corporation
http://www.aafassociation.org/html/specs/aafcontainerspec-v1.0.1.pdf
Author: Victor Stinner
Creation: 2006-04-23
"""
from hachoir_parser import HachoirParser
from hachoir_core.field import (
FieldSet, ParserError, SeekableFieldSet, RootSeekableFieldSet,
UInt8, UInt16, UInt32, UInt64, TimestampWin64, Enum,
Bytes, RawBytes, NullBytes, String)
from hachoir_core.text_handler import filesizeHandler
from hachoir_core.endian import LITTLE_ENDIAN
from hachoir_parser.common.win32 import GUID
from hachoir_parser.misc.msoffice import CustomFragment, OfficeRootEntry, PROPERTY_NAME
from hachoir_parser.misc.word_doc import WordDocumentParser
from hachoir_parser.misc.msoffice_summary import SummaryParser
MIN_BIG_BLOCK_LOG2 = 6 # 512 bytes
MAX_BIG_BLOCK_LOG2 = 14 # 64 kB
# Number of items in DIFAT
NB_DIFAT = 109
class SECT(UInt32):
UNUSED = 0xFFFFFFFF # -1
END_OF_CHAIN = 0xFFFFFFFE # -2
BFAT_SECTOR = 0xFFFFFFFD # -3
DIFAT_SECTOR = 0xFFFFFFFC # -4
SPECIALS = set((END_OF_CHAIN, UNUSED, BFAT_SECTOR, DIFAT_SECTOR))
special_value_name = {
UNUSED: "unused",
END_OF_CHAIN: "end of a chain",
BFAT_SECTOR: "BFAT sector (in a FAT)",
DIFAT_SECTOR: "DIFAT sector (in a FAT)",
}
def __init__(self, parent, name, description=None):
UInt32.__init__(self, parent, name, description)
def createDisplay(self):
val = self.value
return SECT.special_value_name.get(val, str(val))
class Property(FieldSet):
TYPE_ROOT = 5
TYPE_NAME = {
1: "storage",
2: "stream",
3: "ILockBytes",
4: "IPropertyStorage",
5: "root"
}
DECORATOR_NAME = {
0: "red",
1: "black",
}
static_size = 128 * 8
def createFields(self):
bytes = self.stream.readBytes(self.absolute_address, 4)
if bytes == "\0R\0\0":
charset = "UTF-16-BE"
else:
charset = "UTF-16-LE"
yield String(self, "name", 64, charset=charset, truncate="\0")
yield UInt16(self, "namelen", "Length of the name")
yield Enum(UInt8(self, "type", "Property type"), self.TYPE_NAME)
yield Enum(UInt8(self, "decorator", "Decorator"), self.DECORATOR_NAME)
yield SECT(self, "left")
yield SECT(self, "right")
yield SECT(self, "child", "Child node (valid for storage and root types)")
yield GUID(self, "clsid", "CLSID of this storage (valid for storage and root types)")
yield NullBytes(self, "flags", 4, "User flags")
yield TimestampWin64(self, "creation", "Creation timestamp(valid for storage and root types)")
yield TimestampWin64(self, "lastmod", "Modify timestamp (valid for storage and root types)")
yield SECT(self, "start", "Starting SECT of the stream (valid for stream and root types)")
if self["/header/bb_shift"].value == 9:
yield filesizeHandler(UInt32(self, "size", "Size in bytes (valid for stream and root types)"))
yield NullBytes(self, "padding", 4)
else:
yield filesizeHandler(UInt64(self, "size", "Size in bytes (valid for stream and root types)"))
def createDescription(self):
name = self["name"].display
size = self["size"].display
return "Property: %s (%s)" % (name, size)
class DIFat(SeekableFieldSet):
def __init__(self, parent, name, db_start, db_count, description=None):
SeekableFieldSet.__init__(self, parent, name, description)
self.start=db_start
self.count=db_count
def createFields(self):
for index in xrange(NB_DIFAT):
yield SECT(self, "index[%u]" % index)
for index in xrange(self.count):
# this is relative to real DIFAT start
self.seekBit(NB_DIFAT * SECT.static_size+self.parent.sector_size*(self.start+index))
for sect_index in xrange(NB_DIFAT*(index+1),NB_DIFAT*(index+2)):
yield SECT(self, "index[%u]" % sect_index)
class Header(FieldSet):
static_size = 68 * 8
def createFields(self):
yield GUID(self, "clsid", "16 bytes GUID used by some apps")
yield UInt16(self, "ver_min", "Minor version")
yield UInt16(self, "ver_maj", "Minor version")
yield Bytes(self, "endian", 2, "Endian (0xFFFE for Intel)")
yield UInt16(self, "bb_shift", "Log, base 2, of the big block size")
yield UInt16(self, "sb_shift", "Log, base 2, of the small block size")
yield NullBytes(self, "reserved[]", 6, "(reserved)")
yield UInt32(self, "csectdir", "Number of SECTs in directory chain for 4 KB sectors (version 4)")
yield UInt32(self, "bb_count", "Number of Big Block Depot blocks")
yield SECT(self, "bb_start", "Root start block")
yield NullBytes(self, "transaction", 4, "Signature used for transactions (must be zero)")
yield UInt32(self, "threshold", "Maximum size for a mini stream (typically 4096 bytes)")
yield SECT(self, "sb_start", "Small Block Depot start block")
yield UInt32(self, "sb_count")
yield SECT(self, "db_start", "First block of DIFAT")
yield UInt32(self, "db_count", "Number of SECTs in DIFAT")
# Header (ole_id, header, difat) size in bytes
HEADER_SIZE = 64 + Header.static_size + NB_DIFAT * SECT.static_size
class SectFat(FieldSet):
def __init__(self, parent, name, start, count, description=None):
FieldSet.__init__(self, parent, name, description, size=count*32)
self.count = count
self.start = start
def createFields(self):
for i in xrange(self.start, self.start + self.count):
yield SECT(self, "index[%u]" % i)
class OLE2_File(HachoirParser, RootSeekableFieldSet):
PARSER_TAGS = {
"id": "ole2",
"category": "misc",
"file_ext": (
"doc", "dot", # Microsoft Word
"ppt", "ppz", "pps", "pot", # Microsoft Powerpoint
"xls", "xla", # Microsoft Excel
"msi", # Windows installer
),
"mime": (
u"application/msword",
u"application/msexcel",
u"application/mspowerpoint",
),
"min_size": 512*8,
"description": "Microsoft Office document",
"magic": (("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", 0),),
}
endian = LITTLE_ENDIAN
def __init__(self, stream, **args):
RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
HachoirParser.__init__(self, stream, **args)
def validate(self):
if self["ole_id"].value != "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1":
return "Invalid magic"
if self["header/ver_maj"].value not in (3, 4):
return "Unknown major version (%s)" % self["header/ver_maj"].value
if self["header/endian"].value not in ("\xFF\xFE", "\xFE\xFF"):
return "Unknown endian (%s)" % self["header/endian"].raw_display
if not(MIN_BIG_BLOCK_LOG2 <= self["header/bb_shift"].value <= MAX_BIG_BLOCK_LOG2):
return "Invalid (log 2 of) big block size (%s)" % self["header/bb_shift"].value
if self["header/bb_shift"].value < self["header/sb_shift"].value:
return "Small block size (log2=%s) is bigger than big block size (log2=%s)!" \
% (self["header/sb_shift"].value, self["header/bb_shift"].value)
return True
def createFields(self):
# Signature
yield Bytes(self, "ole_id", 8, "OLE object signature")
header = Header(self, "header")
yield header
# Configure values
self.sector_size = (8 << header["bb_shift"].value)
self.fat_count = header["bb_count"].value
self.items_per_bbfat = self.sector_size / SECT.static_size
self.ss_size = (8 << header["sb_shift"].value)
self.items_per_ssfat = self.items_per_bbfat
# Read DIFAT (one level of indirection)
yield DIFat(self, "difat", header["db_start"].value, header["db_count"].value, "Double Indirection FAT")
# Read FAT (one level of indirection)
for field in self.readBFAT():
yield field
# Read SFAT
for field in self.readSFAT():
yield field
# Read properties
chain = self.getChain(self["header/bb_start"].value)
prop_per_sector = self.sector_size // Property.static_size
self.properties = []
for block in chain:
self.seekBlock(block)
for index in xrange(prop_per_sector):
property = Property(self, "property[]")
yield property
self.properties.append(property)
# Parse first property
for index, property in enumerate(self.properties):
if index == 0:
name = "root"
else:
try:
name = PROPERTY_NAME[property["name"].value]
except LookupError:
name = property.name+"content"
for field in self.parseProperty(property, name):
yield field
def parseProperty(self, property, name_prefix):
if not property["size"].value:
return
if property.name != "property[0]" \
and (property["size"].value < self["header/threshold"].value):
# Field is stored in the ministream, skip it
return
name = "%s[]" % name_prefix
first = None
previous = None
size = 0
fragment_group = None
chain = self.getChain(property["start"].value)
while True:
try:
block = chain.next()
contiguous = False
if not first:
first = block
contiguous = True
if previous and block == (previous+1):
contiguous = True
if contiguous:
previous = block
size += self.sector_size
continue
except StopIteration:
block = None
if first is None:
break
self.seekBlock(first)
desc = "Big blocks %s..%s (%s)" % (first, previous, previous-first+1)
desc += " of %s bytes" % (self.sector_size // 8)
if name_prefix in set(("root", "summary", "doc_summary", "word_doc")):
if name_prefix == "root":
parser = OfficeRootEntry
elif name_prefix == "word_doc":
parser = WordDocumentParser
else:
parser = SummaryParser
field = CustomFragment(self, name, size, parser, desc, fragment_group)
yield field
if not fragment_group:
fragment_group = field.group
else:
yield RawBytes(self, name, size//8, desc)
if block is None:
break
first = block
previous = block
size = self.sector_size
def getChain(self, start, use_sfat=False):
if use_sfat:
fat = self.ss_fat
items_per_fat = self.items_per_ssfat
err_prefix = "SFAT chain"
else:
fat = self.bb_fat
items_per_fat = self.items_per_bbfat
err_prefix = "BFAT chain"
block = start
block_set = set()
previous = block
while block != SECT.END_OF_CHAIN:
if block in SECT.SPECIALS:
raise ParserError("%s: Invalid block index (0x%08x), previous=%s" % (err_prefix, block, previous))
if block in block_set:
raise ParserError("%s: Found a loop (%s=>%s)" % (err_prefix, previous, block))
block_set.add(block)
yield block
previous = block
index = block // items_per_fat
try:
block = fat[index]["index[%u]" % block].value
except LookupError:
break
def readBFAT(self):
self.bb_fat = []
start = 0
count = self.items_per_bbfat
for index, block in enumerate(self.array("difat/index")):
block = block.value
if block == SECT.UNUSED:
break
desc = "FAT %u/%u at block %u" % \
(1+index, self["header/bb_count"].value, block)
self.seekBlock(block)
field = SectFat(self, "bbfat[]", start, count, desc)
yield field
self.bb_fat.append(field)
start += count
def readSFAT(self):
chain = self.getChain(self["header/sb_start"].value)
start = 0
self.ss_fat = []
count = self.items_per_ssfat
for index, block in enumerate(chain):
self.seekBlock(block)
field = SectFat(self, "sfat[]", \
start, count, \
"SFAT %u/%u at block %u" % \
(1+index, self["header/sb_count"].value, block))
yield field
self.ss_fat.append(field)
start += count
def createContentSize(self):
max_block = 0
for fat in self.array("bbfat"):
for entry in fat:
block = entry.value
if block not in SECT.SPECIALS:
max_block = max(block, max_block)
if max_block in SECT.SPECIALS:
return None
else:
return HEADER_SIZE + (max_block+1) * self.sector_size
def seekBlock(self, block):
self.seekBit(HEADER_SIZE + block * self.sector_size)
+170
View File
@@ -0,0 +1,170 @@
"""
X11 Portable Compiled Font (pcf) parser.
Documents:
- Format for X11 pcf bitmap font files
http://fontforge.sourceforge.net/pcf-format.html
(file is based on the X11 sources)
Author: Victor Stinner
Creation date: 2007-03-20
"""
from hachoir_parser import Parser
from hachoir_core.field import (FieldSet, Enum,
UInt8, UInt32, Bytes, RawBytes, NullBytes,
Bit, Bits, PaddingBits, CString)
from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
from hachoir_core.text_handler import textHandler, hexadecimal, filesizeHandler
from hachoir_core.tools import paddingSize
class TOC(FieldSet):
TYPE_NAME = {
0x00000001: "Properties",
0x00000002: "Accelerators",
0x00000004: "Metrics",
0x00000008: "Bitmaps",
0x00000010: "Ink metrics",
0x00000020: "BDF encodings",
0x00000040: "SWidths",
0x00000080: "Glyph names",
0x00000100: "BDF accelerators",
}
FORMAT_NAME = {
0x00000000: "Default",
0x00000200: "Ink bounds",
0x00000100: "Accelerator W ink bounds",
# 0x00000200: "Compressed metrics",
}
def createFields(self):
yield Enum(UInt32(self, "type"), self.TYPE_NAME)
yield UInt32(self, "format")
yield filesizeHandler(UInt32(self, "size"))
yield UInt32(self, "offset")
def createDescription(self):
return "%s at %s (%s)" % (
self["type"].display, self["offset"].value, self["size"].display)
class PropertiesFormat(FieldSet):
static_size = 32
endian = LITTLE_ENDIAN
def createFields(self):
yield Bits(self, "reserved[]", 2)
yield Bit(self, "byte_big_endian")
yield Bit(self, "bit_big_endian")
yield Bits(self, "scan_unit", 2)
yield textHandler(PaddingBits(self, "reserved[]", 26), hexadecimal)
class Property(FieldSet):
def createFields(self):
yield UInt32(self, "name_offset")
yield UInt8(self, "is_string")
yield UInt32(self, "value_offset")
def createDescription(self):
# FIXME: Use link or any better way to read name value
name = self["../name[%s]" % (self.index-2)].value
return "Property %s" % name
class GlyphNames(FieldSet):
def __init__(self, parent, name, toc, description, size=None):
FieldSet.__init__(self, parent, name, description, size=size)
self.toc = toc
if self["format/byte_big_endian"].value:
self.endian = BIG_ENDIAN
else:
self.endian = LITTLE_ENDIAN
def createFields(self):
yield PropertiesFormat(self, "format")
yield UInt32(self, "count")
offsets = []
for index in xrange(self["count"].value):
offset = UInt32(self, "offset[]")
yield offset
offsets.append(offset.value)
yield UInt32(self, "total_str_length")
offsets.sort()
offset0 = self.current_size // 8
for offset in offsets:
padding = self.seekByte(offset0+offset)
if padding:
yield padding
yield CString(self, "name[]")
padding = (self.size - self.current_size) // 8
if padding:
yield NullBytes(self, "end_padding", padding)
class Properties(GlyphNames):
def createFields(self):
yield PropertiesFormat(self, "format")
yield UInt32(self, "nb_prop")
properties = []
for index in xrange(self["nb_prop"].value):
property = Property(self, "property[]")
yield property
properties.append(property)
padding = paddingSize(self.current_size//8, 4)
if padding:
yield NullBytes(self, "padding", padding)
yield UInt32(self, "total_str_length")
properties.sort(key=lambda entry: entry["name_offset"].value)
offset0 = self.current_size // 8
for property in properties:
padding = self.seekByte(offset0+property["name_offset"].value)
if padding:
yield padding
yield CString(self, "name[]", "Name of %s" % property.name)
if property["is_string"].value:
yield CString(self, "value[]", "Value of %s" % property.name)
padding = (self.size - self.current_size) // 8
if padding:
yield NullBytes(self, "end_padding", padding)
class PcfFile(Parser):
MAGIC = "\1fcp"
PARSER_TAGS = {
"id": "pcf",
"category": "misc",
"file_ext": ("pcf",),
"magic": ((MAGIC, 0),),
"min_size": 32, # FIXME
"description": "X11 Portable Compiled Font (pcf)",
}
endian = LITTLE_ENDIAN
def validate(self):
if self["signature"].value != self.MAGIC:
return "Invalid signature"
return True
def createFields(self):
yield Bytes(self, "signature", 4, r'File signature ("\1pcf")')
yield UInt32(self, "nb_toc")
entries = []
for index in xrange(self["nb_toc"].value):
entry = TOC(self, "toc[]")
yield entry
entries.append(entry)
entries.sort(key=lambda entry: entry["offset"].value)
for entry in entries:
size = entry["size"].value
padding = self.seekByte(entry["offset"].value)
if padding:
yield padding
maxsize = (self.size-self.current_size)//8
if maxsize < size:
self.warning("Truncate content of %s to %s bytes (was %s)" % (entry.path, maxsize, size))
size = maxsize
if not size:
continue
if entry["type"].value == 1:
yield Properties(self, "properties", entry, "Properties", size=size*8)
elif entry["type"].value == 128:
yield GlyphNames(self, "glyph_names", entry, "Glyph names", size=size*8)
else:
yield RawBytes(self, "data[]", size, "Content of %s" % entry.path)
+442
View File
@@ -0,0 +1,442 @@
"""
Adobe Portable Document Format (PDF) parser.
Author: Christophe Gisquet <christophe.gisquet@free.fr>
"""
from hachoir_parser import Parser
from hachoir_core.field import (
Field, FieldSet,
ParserError,
GenericVector,
UInt8, UInt16, UInt32,
String,
RawBytes)
from hachoir_core.endian import LITTLE_ENDIAN
from hachoir_core.text_handler import textHandler, hexadecimal
MAGIC = "%PDF-"
ENDMAGIC = "%%EOF"
def getLineEnd(s, pos=None):
if pos == None:
pos = (s.absolute_address+s.current_size)//8
end = s.stream.searchBytesLength("\x0D", False, 8*pos)
other_end = s.stream.searchBytesLength("\x0A", False, 8*pos)
if end == None or (other_end != None and other_end < end):
return other_end
return end
# TODO: rewrite to account for all possible terminations: ' ', '/', '\0XD'
# But this probably requires changing *ALL* of the places they are used,
# as ' ' is swallowed but not the others
def getElementEnd(s, limit=' ', offset=0):
addr = s.absolute_address+s.current_size
addr += 8*offset
pos = s.stream.searchBytesLength(limit, True, addr)
if pos == None:
#s.info("Can't find '%s' starting at %u" % (limit, addr))
return None
return pos
class PDFNumber(Field):
LIMITS = ['[', '/', '\x0D', ']']
"""
sprintf("%i") or sprinf("%.?f")
"""
def __init__(self, parent, name, desc=None):
Field.__init__(self, parent, name, description=desc)
# Get size
size = getElementEnd(parent)
for limit in self.LIMITS:
other_size = getElementEnd(parent, limit)
if other_size != None:
other_size -= 1
if size == None or other_size < size:
size = other_size
self._size = 8*size
# Get value
val = parent.stream.readBytes(self.absolute_address, size)
self.info("Number: size=%u value='%s'" % (size, val))
if val.find('.') != -1:
self.createValue = lambda: float(val)
else:
self.createValue = lambda: int(val)
class PDFString(Field):
"""
A string of the shape:
( This string \
uses 3 lines \
with the CR(LF) inhibited )
"""
def __init__(self, parent, name, desc=None):
Field.__init__(self, parent, name, description=desc)
val = ""
count = 1
off = 1
while not parent.eof:
char = parent.stream.readBytes(self.absolute_address+8*off, 1)
# Non-ASCII
if not char.isalpha() or char == '\\':
off += 1
continue
if char == '(':
count += 1
if char == ')':
count -= 1
# Parenthesis block = 0 => end of string
if count == 0:
off += 1
break
# Add it to the string
val += char
self._size = 8*off
self.createValue = lambda: val
class PDFName(Field):
LIMITS = ['[', '/', '<', ']']
"""
String starting with '/', where characters may be written using their
ASCII code (exemple: '#20' would be ' '
' ', ']' and '\0' are supposed not to be part of the name
"""
def __init__(self, parent, name, desc=None):
Field.__init__(self, parent, name, description=desc)
if parent.stream.readBytes(self.absolute_address, 1) != '/':
raise ParserError("Unknown PDFName '%s'" %
parent.stream.readBytes(self.absolute_address, 10))
size = getElementEnd(parent, offset=1)
#other_size = getElementEnd(parent, '[')-1
#if size == None or (other_size != None and other_size < size):
# size = other_size
for limit in self.LIMITS:
other_size = getElementEnd(parent, limit, 1)
if other_size != None:
other_size -= 1
if size == None or other_size < size:
#self.info("New size: %u" % other_size)
size = other_size
self._size = 8*(size+1)
# Value should be without the initial '/' and final ' '
self.createValue = lambda: parent.stream.readBytes(self.absolute_address+8, size).strip(' ')
class PDFID(Field):
"""
Not described as an object, but let's do as it was.
This ID has the shape <hexadecimal ASCII string>
"""
def __init__(self, parent, name, desc=None):
Field.__init__(self, parent, name, description=desc)
self._size = 8*getElementEnd(parent, '>')
self.createValue = lambda: parent.stream.readBytes(self.absolute_address+8, (self._size//8)-1)
class NotABool(Exception): pass
class PDFBool(Field):
"""
"true" or "false" string standing for the boolean value
"""
def __init__(self, parent, name, desc=None):
Field.__init__(self, parent, name, description=desc)
if parent.stream.readBytes(self.absolute_address, 4) == "true":
self._size = 4
self.createValue = lambda: True
elif parent.stream.readBytes(self.absolute_address, 5) == "false":
self._size = 5
self.createValue = lambda: False
raise NotABool
class LineEnd(FieldSet):
"""
Made of 0x0A, 0x0D (we may include several line ends)
"""
def createFields(self):
while not self.eof:
addr = self.absolute_address+self.current_size
char = self.stream.readBytes(addr, 1)
if char == '\x0A':
yield UInt8(self, "lf", "Line feed")
elif char == '\x0D':
yield UInt8(self, "cr", "Line feed")
else:
self.info("Line ends at %u/%u, len %u" %
(addr, self.stream._size, self.current_size))
break
class PDFDictionaryPair(FieldSet):
def createFields(self):
yield PDFName(self, "name", getElementEnd(self))
for field in parsePDFType(self):
yield field
class PDFDictionary(FieldSet):
def createFields(self):
yield String(self, "dict_start", 2)
while not self.eof:
addr = self.absolute_address+self.current_size
if self.stream.readBytes(addr, 2) != '>>':
for field in parsePDFType(self):
yield field
else:
break
yield String(self, "dict_end", 2)
class PDFArray(FieldSet):
"""
Array of possibly non-homogeneous elements, starting with '[' and ending
with ']'
"""
def createFields(self):
yield String(self, "array_start", 1)
while self.stream.readBytes(self.absolute_address+self.current_size, 1) != ']':
for field in parsePDFType(self):
yield field
yield String(self, "array_end", 1)
def parsePDFType(s):
addr = s.absolute_address+s.current_size
char = s.stream.readBytes(addr, 1)
if char == '/':
yield PDFName(s, "type[]", getElementEnd(s))
elif char == '<':
if s.stream.readBytes(addr+8, 1) == '<':
yield PDFDictionary(s, "dict[]")
else:
yield PDFID(s, "id[]")
elif char == '(':
yield PDFString(s, "string[]")
elif char == '[':
yield PDFArray(s, "array[]")
else:
# First parse size
size = getElementEnd(s)
for limit in ['/', '>', '<']:
other_size = getElementEnd(s, limit)
if other_size != None:
other_size -= 1
if size == None or (other_size>0 and other_size < size):
size = other_size
# Get element
name = s.stream.readBytes(addr, size)
char = s.stream.readBytes(addr+8*size+8, 1)
if name.count(' ') > 1 and char == '<':
# Probably a catalog
yield Catalog(s, "catalog[]")
elif name[0] in ('.','-','+', '0', '1', '2', '3', \
'4', '5', '6', '7', '8', '9'):
s.info("Not a catalog: %u spaces and end='%s'" % (name.count(' '), char))
yield PDFNumber(s, "integer[]")
else:
s.info("Trying to parse '%s': %u bytes" % \
(s.stream.readBytes(s.absolute_address+s.current_size, 4), size))
yield String(s, "unknown[]", size)
class Header(FieldSet):
def createFields(self):
yield String(self, "marker", 5, MAGIC)
length = getLineEnd(self, 4)
if length != None:
#self.info("Found at position %08X" % len)
yield String(self, "version", length-1)
yield LineEnd(self, "line_end")
else:
self.warning("Can't determine version!")
def createDescription(self):
return "PDF version %s" % self["version"].display
class Body(FieldSet):
def __init__(self, parent, name, desc=None):
FieldSet.__init__(self, parent, name, desc)
pos = self.stream.searchBytesLength(CrossReferenceTable.MAGIC, False)
if pos == None:
raise ParserError("Can't find xref starting at %u" %
(self.absolute_address//8))
self._size = 8*pos-self.absolute_address
def createFields(self):
while self.stream.readBytes(self.absolute_address+self.current_size, 1) == '%':
size = getLineEnd(self, 4)
if size == 2:
yield textHandler(UInt16(self, "crc32"), hexadecimal)
elif size == 4:
yield textHandler(UInt32(self, "crc32"), hexadecimal)
elif self.stream.readBytes(self.absolute_address+self.current_size, size).isalpha():
yield String(self, "comment[]", size)
else:
RawBytes(self, "unknown_data[]", size)
yield LineEnd(self, "line_end[]")
#abs_offset = self.current_size//8
# TODO: yield objects that read offsets and deduce size from
# "/cross_ref_table/sub_section[]/entries/item[]"
offsets = []
for subsection in self.array("/cross_ref_table/sub_section"):
for obj in subsection.array("entries/item"):
if "byte_offset" in obj:
# Could be inserted already sorted
offsets.append(obj["byte_offset"].value)
offsets.append(self["/cross_ref_table"].absolute_address//8)
offsets.sort()
for index in xrange(len(offsets)-1):
yield Catalog(self, "object[]", size=offsets[index+1]-offsets[index])
class Entry(FieldSet):
static_size = 20*8
def createFields(self):
typ = self.stream.readBytes(self.absolute_address+17*8, 1)
if typ == 'n':
yield PDFNumber(self, "byte_offset")
elif typ == 'f':
yield PDFNumber(self, "next_free_object_number")
else:
yield PDFNumber(self, "unknown_string")
yield PDFNumber(self, "generation_number")
yield UInt8(self, "type")
yield LineEnd(self, "line_end")
def createDescription(self):
if self["type"].value == 'n':
return "In-use entry at offset %u" % int(self["byte_offset"].value)
elif self["type"].value == 'f':
return "Free entry before in-use object %u" % \
int(self["next_free_object_number"].value)
else:
return "unknown %s" % self["unknown_string"].value
class SubSection(FieldSet):
def __init__(self, parent, name, desc=None):
FieldSet.__init__(self, parent, name, desc)
self.info("Got entry count: '%s'" % self["entry_count"].value)
self._size = self.current_size + 8*20*int(self["entry_count"].value) \
+ self["line_end"].size
def createFields(self):
yield PDFNumber(self, "start_number",
"Object number of first entry in subsection")
self.info("start_number = %i" % self["start_number"].value)
yield PDFNumber(self, "entry_count", "Number of entries in subsection")
self.info("entry_count = %i" % self["entry_count"].value)
yield LineEnd(self, "line_end")
yield GenericVector(self, "entries", int(self["entry_count"].value),
Entry)
#yield LineEnd(self, "line_end[]")
def createDescription(self):
return "Subsection with %s elements, starting at %s" % \
(self["entry_count"].value, self["start_number"])
class CrossReferenceTable(FieldSet):
MAGIC = "xref"
def __init__(self, parent, name, desc=None):
FieldSet.__init__(self, parent, name, description=desc)
pos = self.stream.searchBytesLength(Trailer.MAGIC, False)
if pos == None:
raise ParserError("Can't find '%s' starting at %u" \
(Trailer.MAGIC, self.absolute_address//8))
self._size = 8*pos-self.absolute_address
def createFields(self):
yield RawBytes(self, "marker", len(self.MAGIC))
yield LineEnd(self, "line_end[]")
while not self.eof:
yield SubSection(self, "sub_section[]")
class Catalog(FieldSet):
END_NAME = ['<', '/', '[']
def __init__(self, parent, name, size=None, desc=None):
FieldSet.__init__(self, parent, name, description=desc)
if size != None:
self._size = 8*size
# object catalogs are ended with "obj"
elif self["object"].value == "obj":
size = self.stream.searchBytesLength("endobj", False)
if size != None:
self._size = 8*(size+2)
def createFields(self):
yield PDFNumber(self, "index")
yield PDFNumber(self, "unknown[]")
length = getElementEnd(self)
for limit in self.END_NAME:
new_length = getElementEnd(self, limit)-len(limit)
if length == None or (new_length != None and new_length < length):
length = new_length
yield String(self, "object", length, strip=' ')
if self.stream.readBytes(self.absolute_address+self.current_size, 2) == '<<':
yield PDFDictionary(self, "key_list")
# End of catalog: this one has "endobj"
if self["object"].value == "obj":
yield LineEnd(self, "line_end[]")
yield String(self, "end_object", len("endobj"))
yield LineEnd(self, "line_end[]")
class Trailer(FieldSet):
MAGIC = "trailer"
def createFields(self):
yield RawBytes(self, "marker", len(self.MAGIC))
yield LineEnd(self, "line_end[]")
yield String(self, "start_attribute_marker", 2)
addr = self.absolute_address + self.current_size
while self.stream.readBytes(addr, 2) != '>>':
t = PDFName(self, "type[]")
yield t
name = t.value
self.info("Parsing PDFName '%s'" % name)
if name == "Size":
yield PDFNumber(self, "size", "Entries in the file cross-reference section")
elif name == "Prev":
yield PDFNumber(self, "offset")
elif name == "Root":
yield Catalog(self, "object_catalog")
elif name == "Info":
yield Catalog(self, "info")
elif name == "ID":
yield PDFArray(self, "id")
elif name == "Encrypt":
yield PDFDictionary(self, "decrypt")
else:
raise ParserError("Don't know trailer type '%s'" % name)
addr = self.absolute_address + self.current_size
yield String(self, "end_attribute_marker", 2)
yield LineEnd(self, "line_end[]")
yield String(self, "start_xref", 9)
yield LineEnd(self, "line_end[]")
yield PDFNumber(self, "cross_ref_table_start_address")
yield LineEnd(self, "line_end[]")
yield String(self, "end_marker", len(ENDMAGIC))
yield LineEnd(self, "line_end[]")
class PDFDocument(Parser):
endian = LITTLE_ENDIAN
PARSER_TAGS = {
"id": "pdf",
"category": "misc",
"file_ext": ("pdf",),
"mime": (u"application/pdf",),
"min_size": (5+4)*8,
"magic": ((MAGIC, 5),),
"description": "Portable Document Format (PDF) document"
}
def validate(self):
if self.stream.readBytes(0, len(MAGIC)) != MAGIC:
return "Invalid magic string"
return True
# Size is not always determined by position of "%%EOF":
# - updated documents have several of those
# - PDF files should be parsed from *end*
# => TODO: find when a document has been updated
def createFields(self):
yield Header(self, "header")
yield Body(self, "body")
yield CrossReferenceTable(self, "cross_ref_table")
yield Trailer(self, "trailer")
+241
View File
@@ -0,0 +1,241 @@
"""
EFI Platform Initialization Firmware Volume parser.
Author: Alexandre Boeglin
Creation date: 08 jul 2007
"""
from hachoir_parser import Parser
from hachoir_core.field import (FieldSet,
UInt8, UInt16, UInt24, UInt32, UInt64, Enum,
CString, String, PaddingBytes, RawBytes, NullBytes)
from hachoir_core.endian import LITTLE_ENDIAN
from hachoir_core.tools import paddingSize, humanFilesize
from hachoir_parser.common.win32 import GUID
EFI_SECTION_COMPRESSION = 0x1
EFI_SECTION_GUID_DEFINED = 0x2
EFI_SECTION_PE32 = 0x10
EFI_SECTION_PIC = 0x11
EFI_SECTION_TE = 0x12
EFI_SECTION_DXE_DEPEX = 0x13
EFI_SECTION_VERSION = 0x14
EFI_SECTION_USER_INTERFACE = 0x15
EFI_SECTION_COMPATIBILITY16 = 0x16
EFI_SECTION_FIRMWARE_VOLUME_IMAGE = 0x17
EFI_SECTION_FREEFORM_SUBTYPE_GUID = 0x18
EFI_SECTION_RAW = 0x19
EFI_SECTION_PEI_DEPEX = 0x1b
EFI_SECTION_TYPE = {
EFI_SECTION_COMPRESSION: "Encapsulation section where other sections" \
+ " are compressed",
EFI_SECTION_GUID_DEFINED: "Encapsulation section where other sections" \
+ " have format defined by a GUID",
EFI_SECTION_PE32: "PE32+ Executable image",
EFI_SECTION_PIC: "Position-Independent Code",
EFI_SECTION_TE: "Terse Executable image",
EFI_SECTION_DXE_DEPEX: "DXE Dependency Expression",
EFI_SECTION_VERSION: "Version, Text and Numeric",
EFI_SECTION_USER_INTERFACE: "User-Friendly name of the driver",
EFI_SECTION_COMPATIBILITY16: "DOS-style 16-bit EXE",
EFI_SECTION_FIRMWARE_VOLUME_IMAGE: "PI Firmware Volume image",
EFI_SECTION_FREEFORM_SUBTYPE_GUID: "Raw data with GUID in header to" \
+ " define format",
EFI_SECTION_RAW: "Raw data",
EFI_SECTION_PEI_DEPEX: "PEI Dependency Expression",
}
EFI_FV_FILETYPE_RAW = 0x1
EFI_FV_FILETYPE_FREEFORM = 0x2
EFI_FV_FILETYPE_SECURITY_CORE = 0x3
EFI_FV_FILETYPE_PEI_CORE = 0x4
EFI_FV_FILETYPE_DXE_CORE = 0x5
EFI_FV_FILETYPE_PEIM = 0x6
EFI_FV_FILETYPE_DRIVER = 0x7
EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER = 0x8
EFI_FV_FILETYPE_APPLICATION = 0x9
EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE = 0xb
EFI_FV_FILETYPE_FFS_PAD = 0xf0
EFI_FV_FILETYPE = {
EFI_FV_FILETYPE_RAW: "Binary data",
EFI_FV_FILETYPE_FREEFORM: "Sectioned data",
EFI_FV_FILETYPE_SECURITY_CORE: "Platform core code used during the SEC" \
+ " phase",
EFI_FV_FILETYPE_PEI_CORE: "PEI Foundation",
EFI_FV_FILETYPE_DXE_CORE: "DXE Foundation",
EFI_FV_FILETYPE_PEIM: "PEI module (PEIM)",
EFI_FV_FILETYPE_DRIVER: "DXE driver",
EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER: "Combined PEIM/DXE driver",
EFI_FV_FILETYPE_APPLICATION: "Application",
EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: "Firmware volume image",
EFI_FV_FILETYPE_FFS_PAD: "Pad File For FFS",
}
for x in xrange(0xc0, 0xe0):
EFI_FV_FILETYPE[x] = "OEM File"
for x in xrange(0xe0, 0xf0):
EFI_FV_FILETYPE[x] = "Debug/Test File"
for x in xrange(0xf1, 0x100):
EFI_FV_FILETYPE[x] = "Firmware File System Specific File"
class BlockMap(FieldSet):
static_size = 8*8
def createFields(self):
yield UInt32(self, "num_blocks")
yield UInt32(self, "len")
def createDescription(self):
return "%d blocks of %s" % (
self["num_blocks"].value, humanFilesize(self["len"].value))
class FileSection(FieldSet):
COMPRESSION_TYPE = {
0: 'Not Compressed',
1: 'Standard Compression',
}
def __init__(self, *args, **kw):
FieldSet.__init__(self, *args, **kw)
self._size = self["size"].value * 8
section_type = self["type"].value
if section_type in (EFI_SECTION_DXE_DEPEX, EFI_SECTION_PEI_DEPEX):
# These sections can sometimes be longer than what their size
# claims! It's so nice to have so detailled specs and not follow
# them ...
if self.stream.readBytes(self.absolute_address +
self._size, 1) == '\0':
self._size = self._size + 16
def createFields(self):
# Header
yield UInt24(self, "size")
yield Enum(UInt8(self, "type"), EFI_SECTION_TYPE)
section_type = self["type"].value
if section_type == EFI_SECTION_COMPRESSION:
yield UInt32(self, "uncomp_len")
yield Enum(UInt8(self, "comp_type"), self.COMPRESSION_TYPE)
elif section_type == EFI_SECTION_FREEFORM_SUBTYPE_GUID:
yield GUID(self, "sub_type_guid")
elif section_type == EFI_SECTION_GUID_DEFINED:
yield GUID(self, "section_definition_guid")
yield UInt16(self, "data_offset")
yield UInt16(self, "attributes")
elif section_type == EFI_SECTION_USER_INTERFACE:
yield CString(self, "file_name", charset="UTF-16-LE")
elif section_type == EFI_SECTION_VERSION:
yield UInt16(self, "build_number")
yield CString(self, "version", charset="UTF-16-LE")
# Content
content_size = (self.size - self.current_size) // 8
if content_size == 0:
return
if section_type == EFI_SECTION_COMPRESSION:
compression_type = self["comp_type"].value
if compression_type == 1:
while not self.eof:
yield RawBytes(self, "compressed_content", content_size)
else:
while not self.eof:
yield FileSection(self, "section[]")
elif section_type == EFI_SECTION_FIRMWARE_VOLUME_IMAGE:
yield FirmwareVolume(self, "firmware_volume")
else:
yield RawBytes(self, "content", content_size,
EFI_SECTION_TYPE.get(self["type"].value,
"Unknown Section Type"))
def createDescription(self):
return EFI_SECTION_TYPE.get(self["type"].value,
"Unknown Section Type")
class File(FieldSet):
def __init__(self, *args, **kw):
FieldSet.__init__(self, *args, **kw)
self._size = self["size"].value * 8
def createFields(self):
# Header
yield GUID(self, "name")
yield UInt16(self, "integrity_check")
yield Enum(UInt8(self, "type"), EFI_FV_FILETYPE)
yield UInt8(self, "attributes")
yield UInt24(self, "size")
yield UInt8(self, "state")
# Content
while not self.eof:
yield FileSection(self, "section[]")
def createDescription(self):
return "%s: %s containing %d section(s)" % (
self["name"].value,
self["type"].display,
len(self.array("section")))
class FirmwareVolume(FieldSet):
def __init__(self, *args, **kw):
FieldSet.__init__(self, *args, **kw)
if not self._size:
self._size = self["volume_len"].value * 8
def createFields(self):
# Header
yield NullBytes(self, "zero_vector", 16)
yield GUID(self, "fs_guid")
yield UInt64(self, "volume_len")
yield String(self, "signature", 4)
yield UInt32(self, "attributes")
yield UInt16(self, "header_len")
yield UInt16(self, "checksum")
yield UInt16(self, "ext_header_offset")
yield UInt8(self, "reserved")
yield UInt8(self, "revision")
while True:
bm = BlockMap(self, "block_map[]")
yield bm
if bm['num_blocks'].value == 0 and bm['len'].value == 0:
break
# TODO must handle extended header
# Content
while not self.eof:
padding = paddingSize(self.current_size // 8, 8)
if padding:
yield PaddingBytes(self, "padding[]", padding)
yield File(self, "file[]")
def createDescription(self):
return "Firmware Volume containing %d file(s)" % len(self.array("file"))
class PIFVFile(Parser):
endian = LITTLE_ENDIAN
MAGIC = '_FVH'
PARSER_TAGS = {
"id": "pifv",
"category": "program",
"file_ext": ("bin", ""),
"min_size": 64*8, # smallest possible header
"magic_regex": (("\0{16}.{24}%s" % MAGIC, 0), ),
"description": "EFI Platform Initialization Firmware Volume",
}
def validate(self):
if self.stream.readBytes(40*8, 4) != self.MAGIC:
return "Invalid magic number"
if self.stream.readBytes(0, 16) != "\0"*16:
return "Invalid zero vector"
return True
def createFields(self):
while not self.eof:
yield FirmwareVolume(self, "firmware_volume[]")
+163
View File
@@ -0,0 +1,163 @@
"""
.torrent metainfo file parser
http://wiki.theory.org/BitTorrentSpecification#Metainfo_File_Structure
Status: To statufy
Author: Christophe Gisquet <christophe.gisquet@free.fr>
"""
from hachoir_parser import Parser
from hachoir_core.field import (FieldSet, ParserError,
String, RawBytes)
from hachoir_core.endian import LITTLE_ENDIAN
from hachoir_core.tools import makePrintable, timestampUNIX, humanFilesize
# Maximum number of bytes for string length
MAX_STRING_LENGTH = 6 # length in 0..999999
# Maximum number of bytes for integer value
MAX_INTEGER_SIZE = 21 # 21 decimal digits (or "-" sign and 20 digits)
class Integer(FieldSet):
# i<integer encoded in base ten ASCII>e
def createFields(self):
yield String(self, "start", 1, "Integer start delimiter (i)", charset="ASCII")
# Find integer end
addr = self.absolute_address+self.current_size
len = self.stream.searchBytesLength('e', False, addr, addr+(MAX_INTEGER_SIZE+1)*8)
if len is None:
raise ParserError("Torrent: Unable to find integer end delimiter (e)!")
if not len:
raise ParserError("Torrent: error, empty integer!")
yield String(self, "value", len, "Integer value", charset="ASCII")
yield String(self, "end", 1, "Integer end delimiter")
def createValue(self):
"""Read integer value (may raise ValueError)"""
return int(self["value"].value)
class TorrentString(FieldSet):
# <string length encoded in base ten ASCII>:<string data>
def createFields(self):
addr = self.absolute_address
len = self.stream.searchBytesLength(':', False, addr, addr+(MAX_STRING_LENGTH+1)*8)
if len is None:
raise ParserError("Torrent: unable to find string separator (':')")
if not len:
raise ParserError("Torrent: error: no string length!")
val = String(self, "length", len, "String length")
yield val
try:
len = int(val.value)
except ValueError:
len = -1
if len < 0:
raise ParserError("Invalid string length (%s)" % makePrintable(val.value, "ASCII", to_unicode=True))
yield String(self, "separator", 1, "String length/value separator")
if not len:
self.info("Empty string: len=%i" % len)
return
if len<512:
yield String(self, "value", len, "String value", charset="ISO-8859-1")
else:
# Probably raw data
yield RawBytes(self, "value", len, "Raw data")
def createValue(self):
if "value" in self:
field = self["value"]
if field.__class__ != RawBytes:
return field.value
else:
return None
else:
return None
class Dictionary(FieldSet):
# d<bencoded string><bencoded element>e
def createFields(self):
yield String(self, "start", 1, "Dictionary start delimiter (d)", charset="ASCII")
while self.stream.readBytes(self.absolute_address+self.current_size, 1) != "e":
yield DictionaryItem(self, "item[]")
yield String(self, "end", 1, "Dictionary end delimiter")
class List(FieldSet):
# l<bencoded values>e
def createFields(self):
yield String(self, "start", 1, "List start delimiter")
while self.stream.readBytes(self.absolute_address+self.current_size, 1) != "e":
yield Entry(self, "item[]")
yield String(self, "end", 1, "List end delimiter")
class DictionaryItem(FieldSet):
def __init__(self, *args):
FieldSet.__init__(self, *args)
# TODO: Remove this because it's not lazy?
key = self["key"]
if not key.hasValue():
return
key = key.value
self._name = str(key).replace(" ", "_")
def createDisplay(self):
if not self["value"].hasValue():
return None
if self._name in ("length", "piece_length"):
return humanFilesize(self.value)
return FieldSet.createDisplay(self)
def createValue(self):
if not self["value"].hasValue():
return None
if self._name == "creation_date":
return self.createTimestampValue()
else:
return self["value"].value
def createFields(self):
yield Entry(self, "key")
yield Entry(self, "value")
def createTimestampValue(self):
return timestampUNIX(self["value"].value)
# Map first chunk byte => type
TAGS = {'d': Dictionary, 'i': Integer, 'l': List}
for index in xrange(1, 9+1):
TAGS[str(index)] = TorrentString
# Create an entry
def Entry(parent, name):
addr = parent.absolute_address + parent.current_size
tag = parent.stream.readBytes(addr, 1)
if tag not in TAGS:
raise ParserError("Torrent: Entry of type %r not handled" % type)
cls = TAGS[tag]
return cls(parent, name)
class TorrentFile(Parser):
endian = LITTLE_ENDIAN
MAGIC = "d8:announce"
PARSER_TAGS = {
"id": "torrent",
"category": "misc",
"file_ext": ("torrent",),
"min_size": 50*8,
"mime": (u"application/x-bittorrent",),
"magic": ((MAGIC, 0),),
"description": "Torrent metainfo file"
}
def validate(self):
if self.stream.readBytes(0, len(self.MAGIC)) != self.MAGIC:
return "Invalid magic"
return True
def createFields(self):
yield Dictionary(self, "root", size=self.size)
+277
View File
@@ -0,0 +1,277 @@
"""
TrueType Font parser.
Documents:
- "An Introduction to TrueType Fonts: A look inside the TTF format"
written by "NRSI: Computers & Writing Systems"
http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-Chapter08
Author: Victor Stinner
Creation date: 2007-02-08
"""
from hachoir_parser import Parser
from hachoir_core.field import (FieldSet, ParserError,
UInt16, UInt32, Bit, Bits,
PaddingBits, NullBytes,
String, RawBytes, Bytes, Enum,
TimestampMac32)
from hachoir_core.endian import BIG_ENDIAN
from hachoir_core.text_handler import textHandler, hexadecimal, filesizeHandler
MAX_NAME_COUNT = 300
MIN_NB_TABLE = 3
MAX_NB_TABLE = 30
DIRECTION_NAME = {
0: u"Mixed directional",
1: u"Left to right",
2: u"Left to right + neutrals",
-1: u"Right to left",
-2: u"Right to left + neutrals",
}
NAMEID_NAME = {
0: u"Copyright notice",
1: u"Font family name",
2: u"Font subfamily name",
3: u"Unique font identifier",
4: u"Full font name",
5: u"Version string",
6: u"Postscript name",
7: u"Trademark",
8: u"Manufacturer name",
9: u"Designer",
10: u"Description",
11: u"URL Vendor",
12: u"URL Designer",
13: u"License Description",
14: u"License info URL",
16: u"Preferred Family",
17: u"Preferred Subfamily",
18: u"Compatible Full",
19: u"Sample text",
20: u"PostScript CID findfont name",
}
PLATFORM_NAME = {
0: "Unicode",
1: "Macintosh",
2: "ISO",
3: "Microsoft",
4: "Custom",
}
CHARSET_MAP = {
# (platform, encoding) => charset
0: {3: "UTF-16-BE"},
1: {0: "MacRoman"},
3: {1: "UTF-16-BE"},
}
class TableHeader(FieldSet):
def createFields(self):
yield String(self, "tag", 4)
yield textHandler(UInt32(self, "checksum"), hexadecimal)
yield UInt32(self, "offset")
yield filesizeHandler(UInt32(self, "size"))
def createDescription(self):
return "Table entry: %s (%s)" % (self["tag"].display, self["size"].display)
class NameHeader(FieldSet):
def createFields(self):
yield Enum(UInt16(self, "platformID"), PLATFORM_NAME)
yield UInt16(self, "encodingID")
yield UInt16(self, "languageID")
yield Enum(UInt16(self, "nameID"), NAMEID_NAME)
yield UInt16(self, "length")
yield UInt16(self, "offset")
def getCharset(self):
platform = self["platformID"].value
encoding = self["encodingID"].value
try:
return CHARSET_MAP[platform][encoding]
except KeyError:
self.warning("TTF: Unknown charset (%s,%s)" % (platform, encoding))
return "ISO-8859-1"
def createDescription(self):
platform = self["platformID"].display
name = self["nameID"].display
return "Name record: %s (%s)" % (name, platform)
def parseFontHeader(self):
yield UInt16(self, "maj_ver", "Major version")
yield UInt16(self, "min_ver", "Minor version")
yield UInt16(self, "font_maj_ver", "Font major version")
yield UInt16(self, "font_min_ver", "Font minor version")
yield textHandler(UInt32(self, "checksum"), hexadecimal)
yield Bytes(self, "magic", 4, r"Magic string (\x5F\x0F\x3C\xF5)")
if self["magic"].value != "\x5F\x0F\x3C\xF5":
raise ParserError("TTF: invalid magic of font header")
# Flags
yield Bit(self, "y0", "Baseline at y=0")
yield Bit(self, "x0", "Left sidebearing point at x=0")
yield Bit(self, "instr_point", "Instructions may depend on point size")
yield Bit(self, "ppem", "Force PPEM to integer values for all")
yield Bit(self, "instr_width", "Instructions may alter advance width")
yield Bit(self, "vertical", "e laid out vertically?")
yield PaddingBits(self, "reserved[]", 1)
yield Bit(self, "linguistic", "Requires layout for correct linguistic rendering?")
yield Bit(self, "gx", "Metamorphosis effects?")
yield Bit(self, "strong", "Contains strong right-to-left glyphs?")
yield Bit(self, "indic", "contains Indic-style rearrangement effects?")
yield Bit(self, "lossless", "Data is lossless (Agfa MicroType compression)")
yield Bit(self, "converted", "Font converted (produce compatible metrics)")
yield Bit(self, "cleartype", "Optimised for ClearType")
yield Bits(self, "adobe", 2, "(used by Adobe)")
yield UInt16(self, "unit_per_em", "Units per em")
if not(16 <= self["unit_per_em"].value <= 16384):
raise ParserError("TTF: Invalid unit/em value")
yield UInt32(self, "created_high")
yield TimestampMac32(self, "created")
yield UInt32(self, "modified_high")
yield TimestampMac32(self, "modified")
yield UInt16(self, "xmin")
yield UInt16(self, "ymin")
yield UInt16(self, "xmax")
yield UInt16(self, "ymax")
# Mac style
yield Bit(self, "bold")
yield Bit(self, "italic")
yield Bit(self, "underline")
yield Bit(self, "outline")
yield Bit(self, "shadow")
yield Bit(self, "condensed", "(narrow)")
yield Bit(self, "expanded")
yield PaddingBits(self, "reserved[]", 9)
yield UInt16(self, "lowest", "Smallest readable size in pixels")
yield Enum(UInt16(self, "font_dir", "Font direction hint"), DIRECTION_NAME)
yield Enum(UInt16(self, "ofst_format"), {0: "short offsets", 1: "long"})
yield UInt16(self, "glyph_format", "(=0)")
def parseNames(self):
# Read header
yield UInt16(self, "format")
if self["format"].value != 0:
raise ParserError("TTF (names): Invalid format (%u)" % self["format"].value)
yield UInt16(self, "count")
yield UInt16(self, "offset")
if MAX_NAME_COUNT < self["count"].value:
raise ParserError("Invalid number of names (%s)"
% self["count"].value)
# Read name index
entries = []
for index in xrange(self["count"].value):
entry = NameHeader(self, "header[]")
yield entry
entries.append(entry)
# Sort names by their offset
entries.sort(key=lambda field: field["offset"].value)
# Read name value
last = None
for entry in entries:
# Skip duplicates values
new = (entry["offset"].value, entry["length"].value)
if last and last == new:
self.warning("Skip duplicate %s %s" % (entry.name, new))
continue
last = (entry["offset"].value, entry["length"].value)
# Skip negative offset
offset = entry["offset"].value + self["offset"].value
if offset < self.current_size//8:
self.warning("Skip value %s (negative offset)" % entry.name)
continue
# Add padding if any
padding = self.seekByte(offset, relative=True, null=True)
if padding:
yield padding
# Read value
size = entry["length"].value
if size:
yield String(self, "value[]", size, entry.description, charset=entry.getCharset())
padding = (self.size - self.current_size) // 8
if padding:
yield NullBytes(self, "padding_end", padding)
class Table(FieldSet):
TAG_INFO = {
"head": ("header", "Font header", parseFontHeader),
"name": ("names", "Names", parseNames),
}
def __init__(self, parent, name, table, **kw):
FieldSet.__init__(self, parent, name, **kw)
self.table = table
tag = table["tag"].value
if tag in self.TAG_INFO:
self._name, self._description, self.parser = self.TAG_INFO[tag]
else:
self.parser = None
def createFields(self):
if self.parser:
for field in self.parser(self):
yield field
else:
yield RawBytes(self, "content", self.size//8)
def createDescription(self):
return "Table %s (%s)" % (self.table["tag"].value, self.table.path)
class TrueTypeFontFile(Parser):
endian = BIG_ENDIAN
PARSER_TAGS = {
"id": "ttf",
"category": "misc",
"file_ext": ("ttf",),
"min_size": 10*8, # FIXME
"description": "TrueType font",
}
def validate(self):
if self["maj_ver"].value != 1:
return "Invalid major version (%u)" % self["maj_ver"].value
if self["min_ver"].value != 0:
return "Invalid minor version (%u)" % self["min_ver"].value
if not (MIN_NB_TABLE <= self["nb_table"].value <= MAX_NB_TABLE):
return "Invalid number of table (%u)" % self["nb_table"].value
return True
def createFields(self):
yield UInt16(self, "maj_ver", "Major version")
yield UInt16(self, "min_ver", "Minor version")
yield UInt16(self, "nb_table")
yield UInt16(self, "search_range")
yield UInt16(self, "entry_selector")
yield UInt16(self, "range_shift")
tables = []
for index in xrange(self["nb_table"].value):
table = TableHeader(self, "table_hdr[]")
yield table
tables.append(table)
tables.sort(key=lambda field: field["offset"].value)
for table in tables:
padding = self.seekByte(table["offset"].value, null=True)
if padding:
yield padding
size = table["size"].value
if size:
yield Table(self, "table[]", table, size=size*8)
padding = self.seekBit(self.size, null=True)
if padding:
yield padding
+299
View File
@@ -0,0 +1,299 @@
"""
Documents:
* libwx source code: see fib.c source code
* "Microsoft Word 97 Binary File Format"
http://bio.gsi.de/DOCS/AIX/wword8.html
Microsoft Word 97 (aka Version 8) for Windows and Macintosh. From the Office
book, found in the Microsoft Office Development section in the MSDN Online
Library. HTMLified June 1998. Revised Aug 1 1998, added missing Definitions
section. Revised Dec 21 1998, added missing Document Properties (section).
"""
from hachoir_parser import Parser
from hachoir_core.field import (FieldSet,
Bit, Bits,
UInt8, Int16, UInt16, UInt32, Int32,
NullBytes, RawBytes, PascalString16,
DateTimeMSDOS32)
from hachoir_core.endian import LITTLE_ENDIAN
TIMESTAMP = DateTimeMSDOS32
class BaseWordDocument:
def createFields(self):
yield UInt16(self, "wIdent", 2)
yield UInt16(self, "nFib")
yield UInt16(self, "nProduct")
yield UInt16(self, "lid")
yield Int16(self, "pnNext")
yield Bit(self, "fDot")
yield Bit(self, "fGlsy")
yield Bit(self, "fComplex")
yield Bit(self, "fHasPic")
yield Bits(self, "cQuickSaves", 4)
yield Bit(self, "fEncrypted")
yield Bit(self, "fWhichTblStm")
yield Bit(self, "fReadOnlyRecommanded")
yield Bit(self, "fWriteReservation")
yield Bit(self, "fExtChar")
yield Bit(self, "fLoadOverride")
yield Bit(self, "fFarEeast")
yield Bit(self, "fCrypto")
yield UInt16(self, "nFibBack")
yield UInt32(self, "lKey")
yield UInt8(self, "envr")
yield Bit(self, "fMac")
yield Bit(self, "fEmptySpecial")
yield Bit(self, "fLoadOverridePage")
yield Bit(self, "fFutureSavedUndo")
yield Bit(self, "fWord97Save")
yield Bits(self, "fSpare0", 3)
yield UInt16(self, "chse")
yield UInt16(self, "chsTables")
yield UInt32(self, "fcMin")
yield UInt32(self, "fcMac")
yield PascalString16(self, "file_creator", strip="\0")
yield NullBytes(self, "reserved[]", 12)
yield Int16(self, "lidFE")
yield UInt16(self, "clw")
yield Int32(self, "cbMac")
yield UInt32(self, "lProductCreated")
yield TIMESTAMP(self, "lProductRevised")
yield UInt32(self, "ccpText")
yield Int32(self, "ccpFtn")
yield Int32(self, "ccpHdr")
yield Int32(self, "ccpMcr")
yield Int32(self, "ccpAtn")
yield Int32(self, "ccpEdn")
yield Int32(self, "ccpTxbx")
yield Int32(self, "ccpHdrTxbx")
yield Int32(self, "pnFbpChpFirst")
yield Int32(self, "pnChpFirst")
yield Int32(self, "cpnBteChp")
yield Int32(self, "pnFbpPapFirst")
yield Int32(self, "pnPapFirst")
yield Int32(self, "cpnBtePap")
yield Int32(self, "pnFbpLvcFirst")
yield Int32(self, "pnLvcFirst")
yield Int32(self, "cpnBteLvc")
yield Int32(self, "fcIslandFirst")
yield Int32(self, "fcIslandLim")
yield UInt16(self, "cfclcb")
yield Int32(self, "fcStshfOrig")
yield UInt32(self, "lcbStshfOrig")
yield Int32(self, "fcStshf")
yield UInt32(self, "lcbStshf")
yield Int32(self, "fcPlcffndRef")
yield UInt32(self, "lcbPlcffndRef")
yield Int32(self, "fcPlcffndTxt")
yield UInt32(self, "lcbPlcffndTxt")
yield Int32(self, "fcPlcfandRef")
yield UInt32(self, "lcbPlcfandRef")
yield Int32(self, "fcPlcfandTxt")
yield UInt32(self, "lcbPlcfandTxt")
yield Int32(self, "fcPlcfsed")
yield UInt32(self, "lcbPlcfsed")
yield Int32(self, "fcPlcpad")
yield UInt32(self, "lcbPlcpad")
yield Int32(self, "fcPlcfphe")
yield UInt32(self, "lcbPlcfphe")
yield Int32(self, "fcSttbfglsy")
yield UInt32(self, "lcbSttbfglsy")
yield Int32(self, "fcPlcfglsy")
yield UInt32(self, "lcbPlcfglsy")
yield Int32(self, "fcPlcfhdd")
yield UInt32(self, "lcbPlcfhdd")
yield Int32(self, "fcPlcfbteChpx")
yield UInt32(self, "lcbPlcfbteChpx")
yield Int32(self, "fcPlcfbtePapx")
yield UInt32(self, "lcbPlcfbtePapx")
yield Int32(self, "fcPlcfsea")
yield UInt32(self, "lcbPlcfsea")
yield Int32(self, "fcSttbfffn")
yield UInt32(self, "lcbSttbfffn")
yield Int32(self, "fcPlcffldMom")
yield UInt32(self, "lcbPlcffldMom")
yield Int32(self, "fcPlcffldHdr")
yield UInt32(self, "lcbPlcffldHdr")
yield Int32(self, "fcPlcffldFtn")
yield UInt32(self, "lcbPlcffldFtn")
yield Int32(self, "fcPlcffldAtn")
yield UInt32(self, "lcbPlcffldAtn")
yield Int32(self, "fcPlcffldMcr")
yield UInt32(self, "lcbPlcffldMcr")
yield Int32(self, "fcSttbfbkmk")
yield UInt32(self, "lcbSttbfbkmk")
yield Int32(self, "fcPlcfbkf")
yield UInt32(self, "lcbPlcfbkf")
yield Int32(self, "fcPlcfbkl")
yield UInt32(self, "lcbPlcfbkl")
yield Int32(self, "fcCmds")
yield UInt32(self, "lcbCmds")
yield Int32(self, "fcPlcmcr")
yield UInt32(self, "lcbPlcmcr")
yield Int32(self, "fcSttbfmcr")
yield UInt32(self, "lcbSttbfmcr")
yield Int32(self, "fcPrDrvr")
yield UInt32(self, "lcbPrDrvr")
yield Int32(self, "fcPrEnvPort")
yield UInt32(self, "lcbPrEnvPort")
yield Int32(self, "fcPrEnvLand")
yield UInt32(self, "lcbPrEnvLand")
yield Int32(self, "fcWss")
yield UInt32(self, "lcbWss")
yield Int32(self, "fcDop")
yield UInt32(self, "lcbDop")
yield Int32(self, "fcSttbfAssoc")
yield UInt32(self, "lcbSttbfAssoc")
yield Int32(self, "fcClx")
yield UInt32(self, "lcbClx")
yield Int32(self, "fcPlcfpgdFtn")
yield UInt32(self, "lcbPlcfpgdFtn")
yield Int32(self, "fcAutosaveSource")
yield UInt32(self, "lcbAutosaveSource")
yield Int32(self, "fcGrpXstAtnOwners")
yield UInt32(self, "lcbGrpXstAtnOwners")
yield Int32(self, "fcSttbfAtnbkmk")
yield UInt32(self, "lcbSttbfAtnbkmk")
yield Int32(self, "fcPlcdoaMom")
yield UInt32(self, "lcbPlcdoaMom")
yield Int32(self, "fcPlcdoaHdr")
yield UInt32(self, "lcbPlcdoaHdr")
yield Int32(self, "fcPlcspaMom")
yield UInt32(self, "lcbPlcspaMom")
yield Int32(self, "fcPlcspaHdr")
yield UInt32(self, "lcbPlcspaHdr")
yield Int32(self, "fcPlcfAtnbkf")
yield UInt32(self, "lcbPlcfAtnbkf")
yield Int32(self, "fcPlcfAtnbkl")
yield UInt32(self, "lcbPlcfAtnbkl")
yield Int32(self, "fcPms")
yield UInt32(self, "lcbPms")
yield Int32(self, "fcFormFldSttbs")
yield UInt32(self, "lcbFormFldSttbs")
yield Int32(self, "fcPlcfendRef")
yield UInt32(self, "lcbPlcfendRef")
yield Int32(self, "fcPlcfendTxt")
yield UInt32(self, "lcbPlcfendTxt")
yield Int32(self, "fcPlcffldEdn")
yield UInt32(self, "lcbPlcffldEdn")
yield Int32(self, "fcPlcfpgdEdn")
yield UInt32(self, "lcbPlcfpgdEdn")
yield Int32(self, "fcDggInfo")
yield UInt32(self, "lcbDggInfo")
yield Int32(self, "fcSttbfRMark")
yield UInt32(self, "lcbSttbfRMark")
yield Int32(self, "fcSttbCaption")
yield UInt32(self, "lcbSttbCaption")
yield Int32(self, "fcSttbAutoCaption")
yield UInt32(self, "lcbSttbAutoCaption")
yield Int32(self, "fcPlcfwkb")
yield UInt32(self, "lcbPlcfwkb")
yield Int32(self, "fcPlcfspl")
yield UInt32(self, "lcbPlcfspl")
yield Int32(self, "fcPlcftxbxTxt")
yield UInt32(self, "lcbPlcftxbxTxt")
yield Int32(self, "fcPlcffldTxbx")
yield UInt32(self, "lcbPlcffldTxbx")
yield Int32(self, "fcPlcfhdrtxbxTxt")
yield UInt32(self, "lcbPlcfhdrtxbxTxt")
yield Int32(self, "fcPlcffldHdrTxbx")
yield UInt32(self, "lcbPlcffldHdrTxbx")
yield Int32(self, "fcStwUser")
yield UInt32(self, "lcbStwUser")
yield Int32(self, "fcSttbttmbd")
yield UInt32(self, "cbSttbttmbd")
yield Int32(self, "fcUnused")
yield UInt32(self, "lcbUnused")
yield Int32(self, "fcPgdMother")
yield UInt32(self, "lcbPgdMother")
yield Int32(self, "fcBkdMother")
yield UInt32(self, "lcbBkdMother")
yield Int32(self, "fcPgdFtn")
yield UInt32(self, "lcbPgdFtn")
yield Int32(self, "fcBkdFtn")
yield UInt32(self, "lcbBkdFtn")
yield Int32(self, "fcPgdEdn")
yield UInt32(self, "lcbPgdEdn")
yield Int32(self, "fcBkdEdn")
yield UInt32(self, "lcbBkdEdn")
yield Int32(self, "fcSttbfIntlFld")
yield UInt32(self, "lcbSttbfIntlFld")
yield Int32(self, "fcRouteSlip")
yield UInt32(self, "lcbRouteSlip")
yield Int32(self, "fcSttbSavedBy")
yield UInt32(self, "lcbSttbSavedBy")
yield Int32(self, "fcSttbFnm")
yield UInt32(self, "lcbSttbFnm")
yield Int32(self, "fcPlcfLst")
yield UInt32(self, "lcbPlcfLst")
yield Int32(self, "fcPlfLfo")
yield UInt32(self, "lcbPlfLfo")
yield Int32(self, "fcPlcftxbxBkd")
yield UInt32(self, "lcbPlcftxbxBkd")
yield Int32(self, "fcPlcftxbxHdrBkd")
yield UInt32(self, "lcbPlcftxbxHdrBkd")
yield Int32(self, "fcDocUndo")
yield UInt32(self, "lcbDocUndo")
yield Int32(self, "fcRgbuse")
yield UInt32(self, "lcbRgbuse")
yield Int32(self, "fcUsp")
yield UInt32(self, "lcbUsp")
yield Int32(self, "fcUskf")
yield UInt32(self, "lcbUskf")
yield Int32(self, "fcPlcupcRgbuse")
yield UInt32(self, "lcbPlcupcRgbuse")
yield Int32(self, "fcPlcupcUsp")
yield UInt32(self, "lcbPlcupcUsp")
yield Int32(self, "fcSttbGlsyStyle")
yield UInt32(self, "lcbSttbGlsyStyle")
yield Int32(self, "fcPlgosl")
yield UInt32(self, "lcbPlgosl")
yield Int32(self, "fcPlcocx")
yield UInt32(self, "lcbPlcocx")
yield Int32(self, "fcPlcfbteLvc")
yield UInt32(self, "lcbPlcfbteLvc")
yield TIMESTAMP(self, "ftModified")
yield Int32(self, "fcPlcflvc")
yield UInt32(self, "lcbPlcflvc")
yield Int32(self, "fcPlcasumy")
yield UInt32(self, "lcbPlcasumy")
yield Int32(self, "fcPlcfgram")
yield UInt32(self, "lcbPlcfgram")
yield Int32(self, "fcSttbListNames")
yield UInt32(self, "lcbSttbListNames")
yield Int32(self, "fcSttbfUssr")
yield UInt32(self, "lcbSttbfUssr")
tail = (self.size - self.current_size) // 8
if tail:
yield RawBytes(self, "tail", tail)
class WordDocumentFieldSet(BaseWordDocument, FieldSet):
pass
class WordDocumentParser(BaseWordDocument, Parser):
PARSER_TAGS = {
"id": "word_document",
"min_size": 8,
"description": "Microsoft Office Word document",
}
endian = LITTLE_ENDIAN
def __init__(self, stream, **kw):
Parser.__init__(self, stream, **kw)
def validate(self):
return True