import os
import sys
import zlib
from ctypes import *
from elftools.elf.elffile import ELFFile

class setcion_t(Structure): # 24 bytes
    _fields_ = [("length", c_uint),
                ("load_addr", c_uint), 
                ("name", c_ubyte*12),
                ("crc32", c_uint)] 
                    
class image_header(Structure): # 512 bytes
    _fields_ = [("magic", c_uint),
                ("length", c_uint), 
                ("entry_point", c_uint), 
                ("vect_table_addr", c_uint), 
                ("sections", c_ubyte),
                ("rsv0", c_ubyte*7),
                ("section", setcion_t*20),
                ("rsv1", c_ubyte*4),
                ("crc32", c_uint)]

def makeimage(elf, output_file):

    header = image_header() 
    memset(byref(header), 0, sizeof(header))

    header.magic = 0xAA55AA55
    header.entry_point = elf.header.e_entry
    
    offset = sizeof(header)
    imgfile = open(output_file, 'wb')

    for segment in elf.iter_segments():
        if segment.header.p_type != 'PT_LOAD':
            continue
        if segment.header.p_filesz == 0:
            continue

        for section in elf.iter_sections():
            if section['sh_offset'] >= segment.header.p_offset and section['sh_offset'] < segment.header.p_offset + segment.header.p_filesz:
                print(f"process section \"{section.name}\", addr: {hex(section['sh_addr'])}, size: {section['sh_size']} / {hex(section['sh_size'])}")

                if section.name == '.init':
                    header.vect_table_addr = section['sh_addr']

                info = header.section[header.sections]

                #info.length = section['sh_size']
                info.length = (section['sh_size'] + 3) & ~(3)

                if (section['sh_size'] & 3) :
                    print(f"4bytes align, section \"{section.name}\" actual size: {info.length} / {hex(info.length)}")

                info.load_addr = section['sh_addr']
                name_len = min(len(info.name),len(section.name))
                for i in range(0, name_len):
                    info.name[i] = ord(section.name[i])

                section_data = section.data()

                info.crc32 = zlib.crc32(section_data) & 0xffffffff

                imgfile.seek(offset, 0)                
                imgfile.write(section_data)
                #offset += section['sh_size']
                offset += info.length

                header.sections += 1
                if header.sections >= 20:
                    print("--- Too many sections, abort! ---")
                    break

    header.length = offset - sizeof(header)
    header.crc32 = zlib.crc32(header) & 0xffffffff

    imgfile.seek(0,0)
    imgfile.write(header)
    imgfile.flush()
    imgfile.close

    print(f"image file size: {offset} / {hex(offset)}")

if __name__ == '__main__':
    if len(sys.argv) < 3:
        print("usage: python elf2img.py input_elf_file output_image_file\n")
        print("usage: elf2img.exe input_elf_file output_image_file\n")
        sys.exit()

    elffile = sys.argv[1]
    imgfile = sys.argv[2]

    # version 1.2 4bytes align
    print('------------->>> Create image v1.2: %s <<<---------------' %(os.path.abspath(imgfile)))
    print('Input file: %s.'%(os.path.abspath(elffile)))
    
    if os.path.exists(imgfile):
        os.remove(imgfile)
        
    if not os.path.exists(elffile):
        print("input file not exist, failed.");
        sys.exit();
    
    with open(elffile, 'rb') as f:
        elf = ELFFile(f)
        makeimage(elf, imgfile)


            
