/* lefile.h --

   This file is part of the UPX executable compressor.

   Copyright (C) Markus Franz Xaver Johannes Oberhumer
   Copyright (C) Laszlo Molnar
   All Rights Reserved.

   UPX and the UCL library are free software; you can redistribute them
   and/or modify them under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.
   If not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Markus F.X.J. Oberhumer              Laszlo Molnar
   <markus@oberhumer.com>               <ezerotven+github@gmail.com>
 */

#pragma once

#include "util/membuffer.h"

class InputFile;
class OutputFile;

/*************************************************************************
//
**************************************************************************/

class LeFile {
protected:
    explicit LeFile(InputFile *) noexcept;
    virtual ~LeFile() noexcept;

    virtual bool readFileHeader();
    virtual void writeFile(OutputFile *, bool);

protected:
    enum { FIXUP_EXTRA = 3 };

    struct alignas(1) le_header_t {
        // 0x00
        byte _[2];             // signature: 'LE' || 'LX'
        byte byte_order;       // 0 little endian
        byte word_order;       // 0 little endian
        LE32 exe_format_level; // 0
        LE16 cpu_type;         // 1->286..4->586
        LE16 target_os;        // 1->OS2
        byte _0[4];            // module_version = 0
        // 0x10
        LE32 module_type; // 0x200->compatible with PM windowing
        LE32 memory_pages;
        LE32 init_cs_object;
        LE32 init_eip_offset;
        // 0x20
        LE32 init_ss_object;
        LE32 init_esp_offset;
        LE32 memory_page_size;
        LE32 bytes_on_last_page;
        // 0x30
        LE32 fixup_size;
        byte _1[4]; // fixup_checksum = 0
        LE32 loader_size;
        byte _2[4]; // loader_checksum = 0
        // 0x40
        LE32 object_table_offset;
        LE32 object_table_entries;
        LE32 object_pagemap_offset;
        LE32 object_iterate_data_map_offset;
        // 0x50
        byte _3[4]; //  resource_offset
        LE32 resource_entries;
        LE32 resident_names_offset;
        LE32 entry_table_offset;
        // 0x60
        byte _4[4]; //  module_directives_table_offset = 0
        LE32 module_directives_entries;
        LE32 fixup_page_table_offset;
        LE32 fixup_record_table_offset;
        // 0x70
        LE32 imported_modules_name_table_offset;
        LE32 imported_modules_count;
        LE32 imported_procedures_name_table_offset;
        byte _5[4]; // per_page_checksum_table_offset =  0
        // 0x80
        LE32 data_pages_offset;
        byte _6[4]; // preload_page_count = 0
        LE32 non_resident_name_table_offset;
        LE32 non_resident_name_table_length;
        // 0x90
        byte _7[4]; // non_resident_names_checksum
        LE32 automatic_data_object;
#if 1
        byte _8[44];
#else
        LE32 debug_info_offset;
        LE32 debug_info_length;
        // 0xA0
        LE32 preload_instance_pages;
        LE32 demand_instance_pages;
        LE32 extra_heap_alloc;
        byte reserved[12];
        LE32 versioninfo;
        LE32 unknown;
        // 0xC0
        LE16 device_id;
        LE16 ddk_version;
#endif
    };

    struct alignas(1) le_object_table_entry_t {
        LE32 virtual_size;
        LE32 base_address;
        LE32 flags;
        LE32 pagemap_index;
        LE32 npages;
        LE32 reserved;
    };

    struct alignas(1) le_pagemap_entry_t {
        byte h;
        byte m;
        byte l;
        byte type; // 0x00-legal;0x40-iterated;0x80-invalid;0xC0-zeroed
    };

    virtual void readObjectTable();
    virtual void writeObjectTable();
    // virtual void encodeObjectTable(){oobject_table = iobject_table; iobject_table = nullptr;}
    // virtual void decodeObjectTable(){encodeObjectTable();}

    virtual void readFixupPageTable();
    virtual void writeFixupPageTable();
    // virtual void encodeFixupPageTable(){ofpage_table = ifpage_table; ifpage_table = nullptr;}
    // virtual void decodeFixupPageTable(){encodeFixupPageTable();}

    virtual void readPageMap();
    virtual void writePageMap();
    virtual void encodePageMap() {
        opm_entries = ipm_entries;
        ipm_entries = nullptr;
    }
    virtual void decodePageMap() { encodePageMap(); }

    virtual void readResidentNames();
    virtual void writeResidentNames();
    virtual void encodeResidentNames() {
        ores_names = ires_names;
        ires_names = nullptr;
    }
    virtual void decodeResidentNames() { encodeResidentNames(); }

    virtual void readNonResidentNames();
    virtual void writeNonResidentNames();
    virtual void encodeNonResidentNames() {
        ononres_names = inonres_names;
        inonres_names = nullptr;
    }
    virtual void decodeNonResidentNames() { encodeNonResidentNames(); }

    virtual void readEntryTable();
    virtual void writeEntryTable();
    // virtual void encodeEntryTable(){oentries = ientries; ientries = nullptr;}
    // virtual void decodeEntryTable(){encodeEntryTable();}

    virtual void readFixups();
    virtual void writeFixups();
    // virtual void encodeFixups(){ofixups = ifixups; ifixups = nullptr;}
    // virtual void decodeFixups(){encodeFixups();}

    virtual void readImage();
    virtual void writeImage();
    // virtual void encodeImage(){oimage = iimage; iimage = nullptr;}
    // virtual void decodeImage(){encodeImage();}

    void countFixups(unsigned *) const;
    unsigned getImageSize() const;

    InputFile *fif = nullptr;
    OutputFile *fof = nullptr;
    unsigned le_offset = 0;
    unsigned exe_offset = 0;

    le_header_t ih = {}, oh = {};

    le_object_table_entry_t *iobject_table = nullptr;
    le_object_table_entry_t *oobject_table = nullptr;
    unsigned *ifpage_table = nullptr;
    unsigned *ofpage_table = nullptr;
    le_pagemap_entry_t *ipm_entries = nullptr;
    le_pagemap_entry_t *opm_entries = nullptr;
    byte *ires_names = nullptr;
    byte *ores_names = nullptr;
    byte *ifixups = nullptr;
    byte *ofixups = nullptr;
    byte *inonres_names = nullptr;
    byte *ononres_names = nullptr;
    MemBuffer mb_iimage;
    SPAN_0(byte) iimage = nullptr;
    MemBuffer mb_oimage;
    SPAN_0(byte) oimage = nullptr;
    byte *ientries = nullptr;
    byte *oentries = nullptr;

    unsigned soobject_table = 0;
    unsigned sofpage_table = 0;
    unsigned sopm_entries = 0;
    unsigned sores_names = 0;
    unsigned sofixups = 0;
    unsigned sononres_names = 0;
    unsigned soimage = 0;
    unsigned soentries = 0;

private:
    // disable copy and move
    UPX_CXX_DISABLE_COPY_MOVE(LeFile)
};

/* vim:set ts=4 sw=4 et: */
