#include <std/mem.pat>
#include <std/io.pat>

u8 langCount = 1; // default, will be overwritten
u8 failed = 0;

fn failUnknownOp(char op) {
	std::print("Failed! unknown op 0x{:02X} at 0x{:08X}", op, $);
	// stop processing
	failed = 1;
};

// \0 terminated string
struct cstr {
  char string[];
};


struct Header {
  char unknown_5[5];

  u32 someOffset_1; // deflated file data size?
  u32 someOffset_2;

  char unknown_4[4];

  // Creation of this WiseScript.bin in seconds since UNIX epoch
  u32 datetime;

  char unknown_22[22];
};

struct InstStruct1 {
  cstr url;
  cstr log_path;
  cstr font;

  char unknown_6[6];
  u8 languageCount;

  // NOTE: NOT part of the struct just set global for later use
  langCount = languageCount;

  char unknown_7[7];

  // NOTE: langCount of 0 is invalid..
  if (langCount == 1) {
    cstr ui_strings_1[56];
  }
  else {
    cstr ui_strings_1[((55 * languageCount) + languageCount * 2) + 2];
  }
};

// 0x00 Wise deflated file header
struct FileHeader {
  char unknown_2[2]; // 0x8000, 0x8100, 0x0000, 0x9800 0xA100
  u32 deflateStart;
  u32 deflateEnd;
  u16 date;
  u16 time;
  u32 inflatedSize;

  char unknown_20[20];

  u32 crc32;
  cstr filePath;
  cstr fileText[langCount];
  cstr unknownString;
};

struct Unknown_0x03 {
  char unknown_1;
  cstr lang_strings[(langCount * 2)];
};

struct FormItem_0x04 {
  u8 no;
  cstr command_strings[langCount];
  // read this struct again until 'no' == 0 ???
};

struct Unknown_0x05 {
  // write .ini file?
  cstr file;
  cstr heading;
  cstr values;
};


struct DeflateInfo {
  u32 deflateStart;
  u32 deflateEnd;
  u32 inflatedSize;
};


struct Unknown_0x06 {
  char unknown_2[2];
  u32 unknown;
  DeflateInfo deflateInfoPerLang[langCount];
  char terminator;
};

struct Unknown_0x07 {
  char unknown_1;
  cstr string_1;
  cstr string_2;
  cstr string_3;
};

struct Unknown_0x08 {
  char unknown_1;
};

struct Unknown_0x09 {
  char unknown_1;
  cstr string_1;
  cstr string_2;
  cstr string_3;
  cstr string_4;
  cstr lang_strs[langCount];
};

struct Unknown_0x0A {
  char unknown_2[2]; // 0x0200
  cstr string_1;
  cstr string_2;
  cstr string_3;
};

struct Unknown_0x0B {
  char unknown_1;
  cstr string_1;
};

struct Unknown_0x0C {
  char unknown_1;
  cstr string_1;
  cstr string_2;
};

struct Unknown_0x11 {
  cstr string_1;
};

// Existing file on install medium (CD/DVD)
struct ExistingFile_0x12 {
  char unknown_1; // 0C
  char unknown_41[41];
  cstr from_path;
  cstr unknown_string_1;
  cstr lang_strings[langCount];
  cstr to_path;
};

struct Unknown_0x14 {
  DeflateInfo deflateInfo;
  cstr name;
  cstr message;
};

struct Unknown_0x15 {
  char unknown_1;
  cstr string_1;
  cstr string_2;
};

struct TempFile_0x16 {
  cstr name;
};

struct Unknown_0x17 {
  char unknown_1;
  char unknown_4[4];
  cstr string_1;
};

struct Unknown_0x18 {
  std::print("0x18 at 0x{:08X}", $);
  if (($ + 1) > std::mem::size()) {
    return 0;
  }

  char testChar;
  if (testChar == 0x00) {
    $ = $ + 5; // skip 6 bytes in total
  }
  else {
    $ = $ - 1; // skip none
  }
};

struct Unknown_0x19 {
  char unknown_1;
  cstr string_1;
  cstr string_2;
  cstr string_3;
};

struct Unknown_0x1A {
  char unknown_1;
  cstr string_1;
  cstr string_2;
};

struct Unknown_0x1C {
  cstr string_1;
};


struct Unknown_0x1D {
  cstr string_1;
  cstr string_2;
};

struct Unknown_0x1E {
  char unknown;
  cstr unknownString;
};

struct Unknown_0x20 {
  char unknown;
};

struct Unknown_0x23 { // else if
  char unknown_1;
  cstr string_1;
  cstr string_2;
};

struct Unknown_0x30 {
  char unknown;
  cstr string_1;
  cstr string_2;
};

struct Container {
  char op;

  match (op) {
    (0x00): FileHeader fileHeader;
    (0x03): Unknown_0x03 unknown_0x03;
    (0x04): FormItem_0x04 formItem;
    (0x05): Unknown_0x05 unknown_0x05;
    (0x06): Unknown_0x06 unknown_0x06;
    (0x07): Unknown_0x07 unknown_0x07;
    (0x08): Unknown_0x08 unknown_0x08; // end statements block
    (0x09): Unknown_0x09 unknown_0x09;
    (0x0F): continue; // start form data?
    (0x10): continue; // end form data?
    (0x11): Unknown_0x11 unknown_0x11;
    (0x12): ExistingFile_0x12 existingFile_0x12;
    (0x14): Unknown_0x14 unknown_0x14;
    (0x15): Unknown_0x15 unknown_0x15;
    (0x16): TempFile_0x16 tempFile_0x16;
    (0x17): Unknown_0x17 unknown_0x17;
    (0x18): Unknown_0x18 unknown_0x18;
    (0x19): Unknown_0x19 unknown_0x19;
    (0x1A): Unknown_0x1A unknown_0x1A;
    (0x23): Unknown_0x23 unknown_0x23; // else if
    (0x0A): Unknown_0x0A unknown_0x0A;
    (0x0B): Unknown_0x0B unknown_0x0B;
    (0x0C): Unknown_0x0C unknown_0x0C; // if
    (0x0D): continue; // else / default
    (0x1B): continue;
    (0x20): Unknown_0x20 unknown_0x20;
    (0x1C): Unknown_0x1C unknown_0x1C;
    (0x1D): Unknown_0x1D unknown_0x1D;
    (0x1E): Unknown_0x1E unknown_0x1E;
    (0x30): Unknown_0x30 unknown_0x30;
    (_): failUnknownOp(op);
  }
};


struct MainStruct {
  Header header;
  InstStruct1 install_struct_1;
  Container items[while($ < std::mem::size() && failed == 0)];
};


/* Example:

// NOTE: Set absolute path to WISEINSTALL.hexpat
#include </home/user/code/WISESCRIPT.hexpat>

MainStruct mainStruct @0x0;
*/


MainStruct mainStruct @0x0;
