/******************************************************************************
 * hiperiso.h
 *
 * Copyright (c) 2020, longpanda <admin@hiperiso.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 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; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef __HIPERISO_H__
#define __HIPERISO_H__

#define COMPILE_ASSERT(a, expr)  extern char __compile_assert##a[(expr) ? 1 : -1]

#define HIPERISO_COMPATIBLE_STR      "HIPERISO COMPATIBLE"
#define HIPERISO_COMPATIBLE_STR_LEN  17

#define HIPERISO_GUID { 0x77772020, 0x2e77, 0x6576, { 0x6e, 0x74, 0x6f, 0x79, 0x2e, 0x6e, 0x65, 0x74 }}

typedef enum hiperiso_fs_type
{
    hiperiso_fs_exfat = 0, /* 0: exfat */
    hiperiso_fs_ntfs,      /* 1: NTFS */
    hiperiso_fs_ext,       /* 2: ext2/ext3/ext4 */
    hiperiso_fs_xfs,       /* 3: XFS */
    hiperiso_fs_udf,       /* 4: UDF */
    hiperiso_fs_fat,       /* 5: FAT */
    hiperiso_fs_btrfs,     /* 6: BTRFS */

    hiperiso_fs_max
}hiperiso_fs_type;

typedef enum hiperiso_chain_type
{
    hiperiso_chain_linux = 0, /* 0: linux */
    hiperiso_chain_windows,   /* 1: windows */
    hiperiso_chain_wim,       /* 2: wim */

    hiperiso_chain_max
}hiperiso_chain_type;

#pragma pack(1)

typedef struct hiperiso_guid
{
    grub_uint32_t   data1;
    grub_uint16_t   data2;
    grub_uint16_t   data3;
    grub_uint8_t    data4[8];
}hiperiso_guid;


typedef struct hiperiso_image_disk_region
{
    grub_uint32_t   image_sector_count; /* image sectors contained in this region (in 2048) */
    grub_uint32_t   image_start_sector; /* image sector start (in 2048) */
    grub_uint64_t   disk_start_sector;  /* disk sector start (in 512) */
}hiperiso_image_disk_region;

typedef struct hiperiso_image_location
{
    hiperiso_guid  guid;

    /* image sector size, 2048/512 */
    grub_uint32_t   image_sector_size;

    /* disk sector size, normally the value is 512 */
    grub_uint32_t   disk_sector_size;

    grub_uint32_t   region_count;

    /*
     * disk region data (region_count)
     * If the image file has more than one fragments in disk,
     * there will be more than one region data here.
     *
     */
    hiperiso_image_disk_region regions[1];

    /* hiperiso_image_disk_region regions[2~region_count-1] */
}hiperiso_image_location;


typedef struct hiperiso_os_param
{
    hiperiso_guid    guid;                  // HIPERISO_GUID
    grub_uint8_t   chksum;                // checksum

    grub_uint8_t   hiso_disk_guid[16];
    grub_uint64_t  hiso_disk_size;       // disk size in bytes
    grub_uint16_t  hiso_disk_part_id;    // begin with 1
    grub_uint16_t  hiso_disk_part_type;  // 0:exfat   1:ntfs  other: reserved
    char           hiso_img_path[384];   // It seems to be enough, utf-8 format
    grub_uint64_t  hiso_img_size;        // image file size in bytes

    /*
     * Hiperiso will write a copy of hiperiso_image_location data into runtime memory
     * this is the physically address and length of that memory.
     * Address 0 means no such data exist.
     * Address will be aligned by 4KB.
     *
     */
    grub_uint64_t  hiso_img_location_addr;
    grub_uint32_t  hiso_img_location_len;

    /*
     * These 32 bytes are reserved by hiperiso.
     *
     * hiso_reserved[0]: hiso_break_level
     * hiso_reserved[1]: hiso_debug_level
     * hiso_reserved[2]: hiso_chain_type     0:Linux    1:Windows  2:wimfile
     * hiso_reserved[3]: hiso_iso_format     0:iso9660  1:udf
     * hiso_reserved[4]: hiso_windows_cd_prompt
     * hiso_reserved[5]: hiso_linux_remount
     * hiso_reserved[6]: hiso_hlnk
     * hiso_reserved[7~10]: hiso_disk_sig[4] used for hlnk
     * hiso_reserved[11]: hiso_win_uefi_max_res
     *
     */
    grub_uint8_t   hiso_reserved[32];    // Internal use by hiperiso

    grub_uint8_t   hiso_disk_signature[4];

    grub_uint8_t   reserved[27];
}hiperiso_os_param;


typedef struct hiperiso_windows_data
{
    char auto_install_script[384];
    char injection_archive[384];
    grub_uint8_t windows11_bypass_check;

    grub_uint32_t auto_install_len;

    grub_uint8_t windows11_bypass_nro;

    grub_uint8_t reserved[255 - 5];

    /* auto_intall file buf */
    /* ...... + auto_install_len */
}hiperiso_windows_data;


typedef struct hiperiso_secure_data
{
    grub_uint8_t magic1[16];     /* HIPERISO_GUID */
    grub_uint8_t diskuuid[16];
    grub_uint8_t Checksum[16];
    grub_uint8_t adminSHA256[32];
    grub_uint8_t reserved[4000];
    grub_uint8_t magic2[16];     /* HIPERISO_GUID */
}hiperiso_secure_data;


typedef struct hiperiso_hlnk
{
    hiperiso_guid   guid;         // HIPERISO_GUID
    grub_uint32_t crc32;        // crc32
    grub_uint32_t disk_signature;
    grub_uint64_t part_offset; // in bytes
    char filepath[384];
    grub_uint8_t reserved[96];
}hiperiso_hlnk;

#pragma pack()

// compile assert check : sizeof(hiperiso_os_param) must be 512
COMPILE_ASSERT(1,sizeof(hiperiso_os_param) == 512);
COMPILE_ASSERT(2,sizeof(hiperiso_secure_data) == 4096);
COMPILE_ASSERT(3,sizeof(hiperiso_hlnk) == 512);







#pragma pack(4)

typedef struct hiperiso_chain_head
{
    hiperiso_os_param os_param;

    grub_uint32_t disk_drive;
    grub_uint32_t drive_map;
    grub_uint32_t disk_sector_size;

    grub_uint64_t real_img_size_in_bytes;
    grub_uint64_t virt_img_size_in_bytes;
    grub_uint32_t boot_catalog;
    grub_uint8_t  boot_catalog_sector[2048];

    grub_uint32_t img_chunk_offset;
    grub_uint32_t img_chunk_num;

    grub_uint32_t override_chunk_offset;
    grub_uint32_t override_chunk_num;

    grub_uint32_t virt_chunk_offset;
    grub_uint32_t virt_chunk_num;
}hiperiso_chain_head;

typedef struct hiperiso_image_desc
{
    grub_uint64_t disk_size;
    grub_uint64_t part1_size;
    grub_uint8_t  disk_uuid[16];
    grub_uint8_t  disk_signature[4];
    grub_uint32_t img_chunk_count;
    /* hiperiso_img_chunk list */
}hiperiso_image_desc;



typedef struct hiperiso_img_chunk
{
    grub_uint32_t img_start_sector; // sector size: 2KB
    grub_uint32_t img_end_sector;   // included

    grub_uint64_t disk_start_sector; // in disk_sector_size
    grub_uint64_t disk_end_sector;   // included
}hiperiso_img_chunk;


typedef struct hiperiso_override_chunk
{
    grub_uint64_t img_offset;
    grub_uint32_t override_size;
    grub_uint8_t  override_data[512];
}hiperiso_override_chunk;

typedef struct hiperiso_virt_chunk
{
    grub_uint32_t mem_sector_start;
    grub_uint32_t mem_sector_end;
    grub_uint32_t mem_sector_offset;
    grub_uint32_t remap_sector_start;
    grub_uint32_t remap_sector_end;
    grub_uint32_t org_sector_start;
}hiperiso_virt_chunk;

#define DEFAULT_CHUNK_NUM   1024
typedef struct hiperiso_img_chunk_list
{
    char *buf;
    grub_uint32_t last_off;
    grub_uint32_t err_code;

    grub_uint32_t max_chunk;
    grub_uint32_t cur_chunk;
    hiperiso_img_chunk *chunk;
}hiperiso_img_chunk_list;

#pragma pack()

#define hiperiso_filt_register grub_file_filter_register

#pragma pack(1)

#define HISO_MAX_CONF_REPLACE    2
#define GRUB_FILE_REPLACE_MAGIC  0x1258BEEF
#define GRUB_IMG_REPLACE_MAGIC   0x1259BEEF

typedef const char * (*grub_env_get_pf)(const char *name);
typedef int (*grub_env_set_pf)(const char *name, const char *val);
typedef int (*grub_env_printf_pf)(const char *fmt, ...);

typedef struct hiperiso_grub_param_file_replace
{
    grub_uint32_t magic;
    char old_file_name[4][256];
    grub_uint32_t old_name_cnt;
    grub_uint32_t new_file_virtual_id;
}hiperiso_grub_param_file_replace;

typedef struct hiperiso_grub_param
{
    grub_env_get_pf grub_env_get;
    grub_env_set_pf grub_env_set;
    hiperiso_grub_param_file_replace file_replace;
    hiperiso_grub_param_file_replace img_replace[HISO_MAX_CONF_REPLACE];
    grub_env_printf_pf grub_env_printf;
}hiperiso_grub_param;

#pragma pack()

#define HISO_CHUNK_BUF_SIZE          (4 * 1024 * 1024)

typedef enum hiso_chunk_err
{
    HISO_CHUNK_ERR_NONE = 0,
    HISO_CHUNK_ERR_MULTI_DEV,
    HISO_CHUNK_ERR_RAID,
    HISO_CHUNK_ERR_COMPRESS,
    HISO_CHUNK_ERR_NOT_FLAT,
    HISO_CHUNK_ERR_OVER_FLOW,
    HISO_CHUNK_ERR_MAX
}hiso_chunk_err;

int grub_ext_get_file_chunk(grub_uint64_t part_start, grub_file_t file, hiperiso_img_chunk_list *chunk_list);
int grub_btrfs_get_file_chunk(grub_uint64_t part_start, grub_file_t file, hiperiso_img_chunk_list *chunk_list);
int grub_fat_get_file_chunk(grub_uint64_t part_start, grub_file_t file, hiperiso_img_chunk_list *chunk_list);
void grub_iso9660_set_nojoliet(int nojoliet);
int grub_iso9660_is_joliet(void);
grub_uint64_t grub_iso9660_get_last_read_pos(grub_file_t file);
grub_uint64_t grub_iso9660_get_last_file_dirent_pos(grub_file_t file);
grub_uint64_t grub_udf_get_file_offset(grub_file_t file);
grub_uint64_t grub_udf_get_last_pd_size_offset(void);
grub_uint64_t grub_udf_get_last_file_attr_offset
(
    grub_file_t file,
    grub_uint32_t *startBlock,
    grub_uint64_t *fe_entry_size_offset
);
int hiperiso_is_efi_os(void);
void hiperiso_memfile_env_set(const char *prefix, const void *buf, unsigned long long len);

#endif /* __HIPERISO_H__ */

