from filetypes.base import *
import malcat 
import struct


EmfTypeField = UInt32(name="Type", values=[
   ("EMR_HEADER", 0x00000001),
   ("EMR_POLYBEZIER", 0x00000002),
   ("EMR_POLYGON", 0x00000003),
   ("EMR_POLYLINE", 0x00000004),
   ("EMR_POLYBEZIERTO", 0x00000005),
   ("EMR_POLYLINETO", 0x00000006),
   ("EMR_POLYPOLYLINE", 0x00000007),
   ("EMR_POLYPOLYGON", 0x00000008),
   ("EMR_SETWINDOWEXTEX", 0x00000009),
   ("EMR_SETWINDOWORGEX", 0x0000000A),
   ("EMR_SETVIEWPORTEXTEX", 0x0000000B),
   ("EMR_SETVIEWPORTORGEX", 0x0000000C),
   ("EMR_SETBRUSHORGEX", 0x0000000D),
   ("EMR_EOF", 0x0000000E),
   ("EMR_SETPIXELV", 0x0000000F),
   ("EMR_SETMAPPERFLAGS", 0x00000010),
   ("EMR_SETMAPMODE", 0x00000011),
   ("EMR_SETBKMODE", 0x00000012),
   ("EMR_SETPOLYFILLMODE", 0x00000013),
   ("EMR_SETROP2", 0x00000014),
   ("EMR_SETSTRETCHBLTMODE", 0x00000015),
   ("EMR_SETTEXTALIGN", 0x00000016),
   ("EMR_SETCOLORADJUSTMENT", 0x00000017),
   ("EMR_SETTEXTCOLOR", 0x00000018),
   ("EMR_SETBKCOLOR", 0x00000019),
   ("EMR_OFFSETCLIPRGN", 0x0000001A),
   ("EMR_MOVETOEX", 0x0000001B),
   ("EMR_SETMETARGN", 0x0000001C),
   ("EMR_EXCLUDECLIPRECT", 0x0000001D),
   ("EMR_INTERSECTCLIPRECT", 0x0000001E),
   ("EMR_SCALEVIEWPORTEXTEX", 0x0000001F),
   ("EMR_SCALEWINDOWEXTEX", 0x00000020),
   ("EMR_SAVEDC", 0x00000021),
   ("EMR_RESTOREDC", 0x00000022),
   ("EMR_SETWORLDTRANSFORM", 0x00000023),
   ("EMR_MODIFYWORLDTRANSFORM", 0x00000024),
   ("EMR_SELECTOBJECT", 0x00000025),
   ("EMR_CREATEPEN", 0x00000026),
   ("EMR_CREATEBRUSHINDIRECT", 0x00000027),
   ("EMR_DELETEOBJECT", 0x00000028),
   ("EMR_ANGLEARC", 0x00000029),
   ("EMR_ELLIPSE", 0x0000002A),
   ("EMR_RECTANGLE", 0x0000002B),
   ("EMR_ROUNDRECT", 0x0000002C),
   ("EMR_ARC", 0x0000002D),
   ("EMR_CHORD", 0x0000002E),
   ("EMR_PIE", 0x0000002F),
   ("EMR_SELECTPALETTE", 0x00000030),
   ("EMR_CREATEPALETTE", 0x00000031),
   ("EMR_SETPALETTEENTRIES", 0x00000032),
   ("EMR_RESIZEPALETTE", 0x00000033),
   ("EMR_REALIZEPALETTE", 0x00000034),
   ("EMR_EXTFLOODFILL", 0x00000035),
   ("EMR_LINETO", 0x00000036),
   ("EMR_ARCTO", 0x00000037),
   ("EMR_POLYDRAW", 0x00000038),
   ("EMR_SETARCDIRECTION", 0x00000039),
   ("EMR_SETMITERLIMIT", 0x0000003A),
   ("EMR_BEGINPATH", 0x0000003B),
   ("EMR_ENDPATH", 0x0000003C),
   ("EMR_CLOSEFIGURE", 0x0000003D),
   ("EMR_FILLPATH", 0x0000003E),
   ("EMR_STROKEANDFILLPATH", 0x0000003F),
   ("EMR_STROKEPATH", 0x00000040),
   ("EMR_FLATTENPATH", 0x00000041),
   ("EMR_WIDENPATH", 0x00000042),
   ("EMR_SELECTCLIPPATH", 0x00000043),
   ("EMR_ABORTPATH", 0x00000044),
   ("EMR_COMMENT", 0x00000046),
   ("EMR_FILLRGN", 0x00000047),
   ("EMR_FRAMERGN", 0x00000048),
   ("EMR_INVERTRGN", 0x00000049),
   ("EMR_PAINTRGN", 0x0000004A),
   ("EMR_EXTSELECTCLIPRGN", 0x0000004B),
   ("EMR_BITBLT", 0x0000004C),
   ("EMR_STRETCHBLT", 0x0000004D),
   ("EMR_MASKBLT", 0x0000004E),
   ("EMR_PLGBLT", 0x0000004F),
   ("EMR_SETDIBITSTODEVICE", 0x00000050),
   ("EMR_STRETCHDIBITS", 0x00000051),
   ("EMR_EXTCREATEFONTINDIRECTW", 0x00000052),
   ("EMR_EXTTEXTOUTA", 0x00000053),
   ("EMR_EXTTEXTOUTW", 0x00000054),
   ("EMR_POLYBEZIER16", 0x00000055),
   ("EMR_POLYGON16", 0x00000056),
   ("EMR_POLYLINE16", 0x00000057),
   ("EMR_POLYBEZIERTO16", 0x00000058),
   ("EMR_POLYLINETO16", 0x00000059),
   ("EMR_POLYPOLYLINE16", 0x0000005A),
   ("EMR_POLYPOLYGON16", 0x0000005B),
   ("EMR_POLYDRAW16", 0x0000005C),
   ("EMR_CREATEMONOBRUSH", 0x0000005D),
   ("EMR_CREATEDIBPATTERNBRUSHPT", 0x0000005E),
   ("EMR_EXTCREATEPEN", 0x0000005F),
   ("EMR_POLYTEXTOUTA", 0x00000060),
   ("EMR_POLYTEXTOUTW", 0x00000061),
   ("EMR_SETICMMODE", 0x00000062),
   ("EMR_CREATECOLORSPACE", 0x00000063),
   ("EMR_SETCOLORSPACE", 0x00000064),
   ("EMR_DELETECOLORSPACE", 0x00000065),
   ("EMR_GLSRECORD", 0x00000066),
   ("EMR_GLSBOUNDEDRECORD", 0x00000067),
   ("EMR_PIXELFORMAT", 0x00000068),
   ("EMR_DRAWESCAPE", 0x00000069),
   ("EMR_EXTESCAPE", 0x0000006A),
   ("EMR_SMALLTEXTOUT", 0x0000006C),
   ("EMR_FORCEUFIMAPPING", 0x0000006D),
   ("EMR_NAMEDESCAPE", 0x0000006E),
   ("EMR_COLORCORRECTPALETTE", 0x0000006F),
   ("EMR_SETICMPROFILEA", 0x00000070),
   ("EMR_SETICMPROFILEW", 0x00000071),
   ("EMR_ALPHABLEND", 0x00000072),
   ("EMR_SETLAYOUT", 0x00000073),
   ("EMR_TRANSPARENTBLT", 0x00000074),
   ("EMR_GRADIENTFILL", 0x00000076),
   ("EMR_SETLINKEDUFIS", 0x00000077),
   ("EMR_SETTEXTJUSTIFICATION", 0x00000078),
   ("EMR_COLORMATCHTOTARGETW", 0x00000079),
   ("EMR_CREATECOLORSPACEW", 0x0000007A),
    ])

class EmfRecord(Struct):

    def parse(self):
        type = yield EmfTypeField
        size = yield UInt32(name="Size", comment="size of data")
        if size > 8:
            yield Bytes(size - 8, name="Data")

class HeaderRecord(Struct):

    def parse(self):
        type = yield EmfTypeField
        size = yield UInt32(name="Size", comment="size of data")
        yield Rect(name="Bounds")
        yield Rect(name="Frame")
        yield String(4, zero_terminated=False, name="Signature")
        yield UInt16(name="VersionMinor")
        yield UInt16(name="VersionMajor")
        yield UInt32(name="FileSize")
        yield UInt32(name="Records")
        yield UInt16(name="Handles")
        yield UInt16(name="Reserved")
        yield UInt32(name="DescriptionSize")
        yield Offset32(name="DescriptionOffset")
        yield UInt32(name="PaletteEntries")
        yield Dimension(name="SurfaceSize")
        yield Dimension(name="SurfaceSizeMilli")
        yield UInt32(name="PixelFormatCount")
        yield Offset32(name="PixelFormatOffset")
        yield UInt32(name="OpenGL")
        yield Dimension(name="SurfaceSizeMicro")
        if len(self) < size:
            yield Bytes(size - len(self), name="Data")            


class ObjectRecord(Struct):

     def parse(self):
        start = self.parser.tell()
        type = yield EmfTypeField
        size = yield UInt32(name="Size", comment="size of data")
        yield Rect(name="Bounds")
        yield Point(name="Destination")
        yield Rect(name="Source")
        yield Offset32(name="BmiHeaderOffset", base=start)
        yield UInt32(name="BmiHeaderSize")
        yield Offset32(name="BufferOffset", base=start)
        yield UInt32(name="BufferSize")
        yield UInt32(name="UsageSource")
        yield UInt32(name="RasterOperation")
        yield Point(name="cDestination")
        if len(self) < size:
            yield Bytes(size - len(self), name="Data")            


class CommentRecord(Struct):

     def parse(self):
        type = yield EmfTypeField
        size = yield UInt32(name="Size", comment="size of data")
        yield UInt32(name="DataSize")
        sig = yield String(4, zero_terminated=False, name="Signature")
        if sig == "EMF+":
            while len(self) + 2 < size:
                tag, = struct.unpack("<H", self.look_ahead(2))
                cls = RECORDS_PLUS.get(tag, EmfPlusRecord)
                yield cls()
        if len(self) < size:
            yield Bytes(size - len(self), name="Data")            


class ExtCreateFontIndirectRecord(Struct):

     def parse(self):
        start = self.parser.tell()
        type = yield EmfTypeField
        size = yield UInt32(name="Size", comment="size of data")
        yield UInt32(name="FontIndex", comment="index of the logical font object in the EMF object table")
        
        left = size - len(self)
        if left == 0x140:
            # logpanose object
            yield LogFont()
            yield StringUtf16le(64, name="FullName", zero_terminated=True)
            yield StringUtf16le(32, name="Style", zero_terminated=True)
            yield UInt32(name="Version")
            yield UInt32(name="StyleSize")
            yield UInt32(name="Match")
            yield UInt32(name="Reserved")
            yield UInt32(name="VendorId")
            yield UInt32(name="Culture")
            yield Bytes(10, name="Panose")
            yield Unused(2)
        else:
            # logfontdv object
            yield LogFont()
            yield StringUtf16le(64, name="FullName", zero_terminated=True)
            yield StringUtf16le(32, name="Style", zero_terminated=True)
            yield StringUtf16le(32, name="Script", zero_terminated=True)
            yield DesignVector()

        if len(self) < size:
            yield Bytes(size - len(self), name="Data")                        


class LogFont(StaticStruct):

    @classmethod
    def parse(cls):
        yield Int32(name="Height")
        yield Int32(name="Width")
        yield Int32(name="Escapement")
        yield Int32(name="Orientation")
        yield Int32(name="Weight")
        yield UInt8(name="Italic")
        yield UInt8(name="Underline")
        yield UInt8(name="StrikeOut")
        yield UInt8(name="CharSet")
        yield UInt8(name="OutPrecision")
        yield UInt8(name="ClipPrecision")
        yield UInt8(name="Quality")
        yield UInt8(name="PitchAndFamily")
        yield StringUtf16le(32, name="FontName", zero_terminated=True)


class DesignVector (Struct):

     def parse(self):
         magic = yield UInt32(name="Signature")
         if magic != 0x08007664:
             raise FatalError("Invalid DesignVector signature")
         num = yield UInt32(name="NumAxes")
         if num:
             yield Array(num, UInt32(), name="Values")


class Point(Struct):

    def parse(self):
        yield UInt32(name="X")
        yield UInt32(name="Y")


class Dimension(Struct):

    def parse(self):
        yield UInt32(name="Width")
        yield UInt32(name="Height")        


class Rect(Struct):

    def parse(self):
        yield Point(name="TopLeft")
        yield Point(name="BottomRight")        




RECORDS = {
    1: (HeaderRecord, Type.HEADER),
    0x46: (CommentRecord, Type.DEBUG),
    0x51: (ObjectRecord, Type.DATA),
    0x52: (ExtCreateFontIndirectRecord, Type.DATA),
}
   

############################################### EMF+

class EmfPlusRecord(Struct):

    def parse(self):
        type = yield UInt16(name="Type")
        flags = yield UInt16(name="Flags")
        size = yield UInt32(name="Size", comment="size of data")
        if size > 8:
            yield Bytes(size - 8, name="Data")


class ObjectPlusImageRecord(Struct):

    def parse(self):
        yield UInt32(name="Version")
        tp = yield UInt32(name="Type", values = [
            ("BITMAP", 1),
            ("METAFILE", 2),
            ])
        if tp == 1:
            yield UInt32(name="Width")
            yield UInt32(name="Height")
            yield UInt32(name="Stride")
            yield UInt32(name="PixelFormat")
            yield UInt32(name="BitmapType", values = [
            ("PIXEL", 0),
            ("COMPRESSED", 1),
            ])

class ObjectPlusRecord(Struct):

    def parse(self):
        type = yield UInt16(name="Type")
        yield UInt8(name="ObjectId")
        object_type = yield UInt8(name="ObjectType", values = [
            ("BRUSH", 0x1),
            ("PEN", 0x2),
            ("PATH", 0x3),
            ("REGION", 0x4),
            ("IMAGE", 0x5),
            ("FONT", 0x6),
            ("STRING_FORMAT", 0x7),
            ("IMAGE_ATTRIBUTES", 0x8),
            ("CUSTOM_LINE_CAP", 0x9),
            ("BRUSH", 0x81),
            ("PEN", 0x82),
            ("PATH", 0x83),
            ("REGION", 0x84),
            ("IMAGE", 0x85),
            ("FONT", 0x86),
            ("STRING_FORMAT", 0x87),
            ("IMAGE_ATTRIBUTES", 0x88),            
            ("CUSTOM_LINE_CAP", 0x89),
            ])
        size = yield UInt32(name="Size", comment="size of data")
        if object_type & 0x80:
            yield UInt32(name="TotalSize", comment="Once TotalObjectSize number of bytes has been read, the next EMF+ record will not be treated as part of the continuing object")
        size = yield UInt32(name="DataSize", comment="32-bit-aligned number of bytes of data in the record-specific data that follows")
        if object_type & 0x7f == 5:
            yield ObjectPlusImageRecord(name="DataHeader")
        if len(self) < size:
            yield Bytes(size - len(self), name="Data")            
        

RECORDS_PLUS = {
    0x4008: ObjectPlusRecord,
}
               
############################################### Analyzer


class EMFAnalyzer(FileTypeAnalyzer):
    category = malcat.FileType.IMAGE
    name = "EMF"
    regexp = r"\x01\x00\x00\x00.{36} EMF\x00\x00\x01\x00"



    def parse(self, hint):
        hdr = yield HeaderRecord(name="Header", category=Type.HEADER)
        self.add_metadata("Version", "{}.{}".format(hdr["VersionMajor"], hdr["VersionMinor"]))
        self.add_metadata("Surface", "{}cm x {}cm".format(hdr["SurfaceSizeMilli"]["Width"] / 10, hdr["SurfaceSizeMilli"]["Height"] / 10))
        filesize = hdr["FileSize"]
        self.set_eof(filesize)
        self.confirm()
        while self.remaining() >= 8 and self.tell() < filesize:
            tag, = struct.unpack("<I", self.read(self.tell(), 4))
            cls, typ = RECORDS.get(tag, (EmfRecord, Type.DATA))
            record = yield cls(category=typ)
            if tag == 14:
                # EOF
                self.confirm()
                break
            if not record.Type.enum:
                raise FatalError("Unrecognized record type: {:x}".format(record["Type"]))

       
        

