{{
import malcat
import datetime
from filetypes.CFB import toprintable, CFB_CLSIDs


def pval(key, val=None, fmt="{}", va=False, fa=False, rva=False, kalign=30, valign=0, formated=False):
    r = ""
    if key:
        r = "[normal]{}[/normal]:".format(key)
        lr = len(r) - 17
        if lr < kalign:
            r += (" "*(kalign-lr))
    if isinstance(val, malcat.StructAccess):
        if val.has_enum and val.enum:
            d = "[color6]{}[/color6]([dim]{}[/dim])".format(val.enum, fmt.format(val.value))
            formated = True
            ld = len(d) - len("[color3][/color3][dim][/dim]")
        else:
            d = fmt.format(val.value)
            ld = len(d)
        val = val.value
    else:
        d = fmt.format(val)
        ld = len(d)
    if not formated:
        grp = "normal"
        if type(val) == int: 
            if val == 0:
                grp = "dim"
            elif fa:
                grp = "fa"
            elif rva:
                grp = "rva"
            elif va:
                grp = "va"
        elif isinstance(val, datetime.datetime):
            if val.year <= 1970:
                grp = "dim"
            d = val.strftime("%Y-%m-%d %H:%M")
            ld = len(d)
        d = "[{}]{}[/{}]".format(grp,d,grp)
    if ld < valign:
        d += (" "*(valign-ld))
    write(r+d)  

def bitfieldpp(fa):
    s = []
    for i in range(fa.count - 1, -1, -1):
        bit = fa.at(i)
        if bit.value:
            s.append("[color5]{}[/color5]([dim]{:X}[/dim])".format(bit.name, 1 << i))
    if s:
        return  " + ".join(s)
    else:
        return "[dim]0[/dim]"

def pbitfield(key, fa, kalign=30):    
    r = "[normal]{}[/normal]:".format(key)
    lr = len(r) - 17
    if lr < kalign:
        r += (" "*(kalign-lr))
    write(r + bitfieldpp(fa))

def sections_rights(rights):
    s = ""
    for e in [
        ("MemRead", "R"),
        ("MemWrite", "W"),
        ("MemExecute", "X"),
        ("ScnAlign1", "1"),
        ("ScnAlign2", "2"),
        ("ScnAlign8", "8"),
        ("MemShared", "S"),
        ("MemNotPaged", "P"),
        ("MemNotCached", "H"),
        ("MemDiscardable", "D"),
        ("SctCntCode", "C"),
        ("SctCntInitializedData", "I"),
        ("SctCntUnititializedData", "U"),
        ]:
        if rights[e[0]]:
            s += "[color5]{}[/color5]".format(e[1])
        else:
            s += "[dim]∙[/dim]"
    return s

def is_encrypted():
    return hasattr(analysis, "anomalies") and analysis.anomalies["EncryptedRegion"]

def normalize_stream_size(sz):
    if analysis.parser.sector_size == 512: 
        if sz == 0xffffffff:
            return 0
    elif sz == 0xffffffffffffffff:
        return 0
    return sz

UNSET_CLSID = frozenset([
"00000000-0000-0000-0000-000000000000",
"FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF",
])
}}
[color1]Header[/color1] 
  {{ pval("Version", "{}.{}".format(analysis.struct.FileHeader["MajorVersion"], analysis.struct.FileHeader["MinorVersion"]), valign=24) }} {{ pval("Byte order", analysis.struct.FileHeader["ByteOrder"] == 0xFFFE and "little endian" or "big endian (unsupported)") }}
  {{ pval("Sector size", 2 ** analysis.struct.FileHeader["SectorShift"], valign=24) }} {{ pval("Mini sector size", 2 ** analysis.struct.FileHeader["MiniSectorShift"]) }}
  {{ pval("First directory sector", analysis.struct.FileHeader.FirstDirectorySector, valign=24) }} {{ pval("Number of directory sectors", analysis.struct.FileHeader.NumberOfDirectorySectors) }}
  {{ pval("First MiniFAT sector", analysis.struct.FileHeader["FirstMiniFATSector"] == 0xFFFFFFFE and "-" or analysis.struct.FileHeader.FirstMiniFATSector, valign=24) }} {{ pval("Number of MiniFAT sectors", analysis.struct.FileHeader.NumberOfMiniFATSectors) }}
  {{ pval("First DIFAT sector", analysis.struct.FileHeader["FirstDIFATSector"] == 0xFFFFFFFE and "-" or analysis.struct.FileHeader.FirstDIFATSector, valign=24) }} {{ pval("Number of DIFAT sectors", analysis.struct.FileHeader.NumberOfDIFATSectors) }}

[color1]Content[/color1] 
  {{ pval("File type", analysis.parser.type) }}
  {{ pval("Encrypted", is_encrypted() and "yes [dim](use decrypt script)[/dim]" or "no") }}
  {{ pval("Fragmented", analysis.parser.fragmented and "yes   [block3]Be careful when carving files, use the VFS instead![/block3]" or "no") }}
  {{ pval("Contains VBA", analysis.parser.vba_modules and "yes   [block3]Press F4 to decompile![/block3]" or "no") }}
  {{ pval("Contains MSI database", analysis.parser.msi_db and "yes   [block3]Press F4 to view tables![/block3]" or "no") }}

[color1]Directory[/color1]
{{ for e in filter(lambda x: x["ObjectType"], analysis.parser.directory):}}
{{ 
is_mini =  normalize_stream_size(e["StreamSize"]) and e["StreamSize"] < analysis.struct.FileHeader["MiniStreamCutoffSize"] and "MiniFAT" in analysis.struct
name = toprintable(analysis.parser.decode_stream_name(e["Name"]))
sz = normalize_stream_size(e["StreamSize"])
}}
  \[[color3]{{ write({5: "R", 1:"D", 2:"S"}.get(e["ObjectType"], e["ObjectType"]))}}{{write(is_mini and "m" or "∙")}}[/color3]] [color2]{{ "{:s}".format(name) }}[/color2] {{if e["CLSID"] not in UNSET_CLSID:}}[dim]({{write(CFB_CLSIDs.get(e["CLSID"], e["CLSID"]))}})[/dim]{{:endif}}
{{ if e["ObjectType"] == 2 and sz:}}
      {{ pval("First sector", analysis.parser.sector2offset(e["StartingSector"], sz), "{:x}", fa=True, valign=24) }} {{ pval("Stream size", sz) }} bytes
{{ :else: }}
      {{ pval("First sector", 0, valign=24) }} {{ pval("Stream size", sz) }} bytes
{{:endif}}
      {{ pval("Creation time", e["CreationTime"], valign=24) }} {{ pval("Modification time", e["ModificationTime"]) }}
{{:endif}}

[dim]
Legend:
  \[R] = Root directory
  \[S] = Stream
  \[D] = Storage (directory)
  \[m] = Stream lies in MiniFAT
[/dim]
