from construct import *
from zipfile import crc32

GPT_HFS = "005346480000aa11aa1100306543ecac".decode("hex")
GPT_EMF = "00464d450000aa11aa1100306543ecac".decode("hex")

LWVM_partitionRecord = Struct("LWVM_partitionRecord",
                              String("type", 16),
                              String("guid", 16),
                              ULInt64("begin"),
                              ULInt64("end"),
                              ULInt64("attribute"),
                              String("name", 0x48, encoding="utf-16-le", padchar="\x00")
)

LWVM_MAGIC = "6a9088cf8afd630ae351e24887e0b98b".decode("hex")
LWVM_header = Struct("LWVM_header",
                     String("type",16),
                     String("guid", 16),
                     ULInt64("mediaSize"),
                     ULInt32("numPartitions"),
                     ULInt32("crc32"),
                     Padding(464),
                     Array(12, LWVM_partitionRecord),
                     Array(1024, ULInt16("chunks"))
                     )

GPT_header = Struct("GPT_header",
    String("signature", 8),
    ULInt32("revision"),
    ULInt32("header_size"),
    SLInt32("crc"), #hax to match python signed crc
    ULInt32("zero"),
    ULInt64("current_lba"),
    ULInt64("backup_lba"),
    ULInt64("first_usable_lba"),
    ULInt64("last_usable_lba"),
    String("disk_guid", 16),
    ULInt64("partition_entries_lba"),
    ULInt32("num_partition_entries"),
    ULInt32("size_partition_entry"),
    ULInt32("crc_partition_entries")
)

GPT_entry = Struct("GPT_entry",
                   String("partition_type_guid", 16),
                   String("partition_guid", 16),
                   ULInt64("first_lba"),
                   ULInt64("last_lba"),
                   ULInt64("attributes"),
                   String("name", 72, encoding="utf-16-le", padchar="\x00"),
)

GPT_partitions = RepeatUntil(lambda obj, ctx: obj["partition_type_guid"] == "\x00"*16, GPT_entry)

APPLE_ENCRYPTED = 0xAE
MBR_entry = Struct("MBR_entry",
                   Byte("status"),
                   Bytes("chs_start",3),
                   Byte("type"),
                   Bytes("chs_last",3),
                   ULInt32("lba_start"),
                   ULInt32("num_sectors")
)
                   
MBR = Struct("MBR",
             String("code",440),
             ULInt32("signature"),
             ULInt16("zero"),
             Array(4, MBR_entry),
             OneOf(ULInt16("magic"), [0xAA55])
)

def parse_mbr(data):
    try:
        mbr = MBR.parse(data)
        if mbr.MBR_entry[0].type == 0xEE:
            print "Found protective MBR"
            return None
        res = mbr.MBR_entry[:2]
        for p in res:
            p.first_lba = p.lba_start
            p.last_lba = p.lba_start + p.num_sectors
        return res
    except:
        return None

def parse_gpt(data):
    gpt =  GPT_header.parse(data)
    if gpt.signature != "EFI PART":
        return None
    print "Found GPT header current_lba=%d partition_entries_lba=%d" % (gpt.current_lba, gpt.partition_entries_lba)
    assert gpt.partition_entries_lba > gpt.current_lba
    check = gpt.crc
    gpt.crc = 0
    actual = crc32(GPT_header.build(gpt))
    if actual != check:
        print "GPT crc check fail %d vs %d" % (actual, check)
        return None
    return gpt
    
def parse_lwvm(data, pageSize):
    try:
        hdr = LWVM_header.parse(data)
        if hdr.type != LWVM_MAGIC:
            print "LwVM magic mismatch"
            return
        tocheck = data[:44] + "\x00\x00\x00\x00" + data[48:0x1000]
        check = crc32(tocheck) & 0xffffffff
        if check != hdr.crc32:
            return None
        print "LwVM header CRC OK"
        partitions = hdr.LWVM_partitionRecord[:hdr.numPartitions] 
        deviceSize=0
        #XXX: HAAAAAAAX
        for s in [8, 16, 32, 64, 128]:
            if hdr.mediaSize < (s* 1024*1024*1024):
                deviceSize = s
                break
        for i in xrange(len(hdr.chunks)):
            if hdr.chunks[i] == 0x0:
                lba0 = (i * deviceSize*1024*1024) / pageSize
                partitions[0].first_lba = lba0
                partitions[0].last_lba = lba0 + (partitions[0].end - partitions[0].begin) / pageSize
            elif hdr.chunks[i] == 0x1000:
                lbad = (i * deviceSize*1024*1024) / pageSize
                partitions[1].first_lba = lbad
                partitions[1].last_lba = lbad + (partitions[1].end - partitions[1].begin) / pageSize
        return partitions
    except:
        return None

