// File: DimitriFourny[P]unxip/unxip.cpp
// Function: main
int main(int argc, char* argv[]) {
  if (argc < 2) {
    fprintf(stderr, "Usage: %s <file.xip>\n", argv[0]);
    return 1;
  }
  char* filepath = argv[1];

  FILE* file = fopen(filepath, "rb");
  if (!file) {
    fprintf(stderr, "Error: File \"%s\" can't be opened\n", filepath);
    return 1;
  }

  std::string outdir = CreateXipDirectory(filepath);

  // Check the XIP headers
  struct XipHeader header;
  memset(&header, 0, sizeof(header));
  size_t nb_read = 0;
  nb_read = fread(&header, 1, sizeof(header), file);

  if (nb_read < 4 || memcmp(&header.signature, "xar!", 4)) {
    fprintf(stderr, "Invalid XAR signature\n");
    return InvalidXipFile(file);
  }

  // Big endian to little endian
  header.header_size = __bswap_16(header.header_size);
  header.xar_version = __bswap_16(header.xar_version);
  header.toc_size_compressed = __bswap_64(header.toc_size_compressed);
  header.toc_size_uncompressed = __bswap_64(header.toc_size_uncompressed);
  header.checksum_algo = __bswap_32(header.checksum_algo);

  if (fseek(file, header.header_size, SEEK_SET)) {
    fprintf(stderr, "Header size is invalid\n");
    return InvalidXipFile(file);
  }

  if (!header.toc_size_compressed ||
      header.toc_size_compressed > MAX_TOC_SIZE ||
      header.toc_size_uncompressed > MAX_TOC_SIZE) {
    fprintf(stderr, "Table of Contents invalid size\n");
    return InvalidXipFile(file);
  }

  // Read the Table of Contents (TOC)
  uint8_t* toc_compressed = new uint8_t[header.toc_size_compressed];
  nb_read = fread(toc_compressed, 1, header.toc_size_compressed, file);
  if (nb_read < header.toc_size_compressed) {
    fprintf(stderr, "Can't read the Table of Contents\n");
    return InvalidXipFile(file);
  }

  const size_t toc_size = header.toc_size_uncompressed;
  uint8_t* toc = new uint8_t[toc_size];
  bool success =
      ZlibUncompress(toc_compressed, header.toc_size_compressed, toc, toc_size);
  if (!success) {
    fprintf(stderr, "Can't uncompress the Table of Contents\n");
    return InvalidXipFile(file);
  }
  delete[] toc_compressed;

  SaveToFile(outdir + "xip_toc.xml", toc, toc_size);

  // Parse the TOC to get the files
  auto files = ParseTableOfContents(toc, toc_size);
  std::string savepath;
  for (const auto& xar_file : files) {
    savepath = outdir + xar_file.name;
    printf("%s\t", savepath.c_str());
    fflush(stdout);

    const size_t offset =
        header.header_size + header.toc_size_compressed + xar_file.offset;
    success = SaveFileStreamToFile(savepath, file, offset, xar_file.size);

    if (success) {
      printf("done\n");
    } else {
      printf("error\n");
    }
  }

  fclose(file);
  return 0;
}

// Assembly:
subq $0x1b8, %rsp            # imm = 0x1B8
movl $0x0, 0x1b4(%rsp)
movl %edi, 0x1b0(%rsp)
movq %rsi, 0x1a8(%rsp)
cmpl $0x2, 0x1b0(%rsp)
jge 0x2e4e
movq 0x51be(%rip), %rax      # 0x7fe0
movq (%rax), %rdi
movq 0x1a8(%rsp), %rax
movq (%rax), %rdx
leaq 0x228c(%rip), %rsi      # 0x50c3
movb $0x0, %al
callq 0x22c0
movl $0x1, 0x1b4(%rsp)
jmp 0x3553
movq 0x1a8(%rsp), %rax
movq 0x8(%rax), %rax
movq %rax, 0x1a0(%rsp)
movq 0x1a0(%rsp), %rdi
leaq 0x2268(%rip), %rsi      # 0x50d9
callq 0x2250
movq %rax, 0x198(%rsp)
cmpq $0x0, 0x198(%rsp)
jne 0x2eb9
movq 0x5150(%rip), %rax      # 0x7fe0
movq (%rax), %rdi
movq 0x1a0(%rsp), %rdx
leaq 0x223a(%rip), %rsi      # 0x50dc
movb $0x0, %al
callq 0x22c0
movl $0x1, 0x1b4(%rsp)
jmp 0x3553
movq 0x1a0(%rsp), %rsi
leaq 0x178(%rsp), %rdi
callq 0x2580
xorps %xmm0, %xmm0
movups %xmm0, 0x15c(%rsp)
movaps %xmm0, 0x150(%rsp)
movq $0x0, 0x148(%rsp)
movq 0x198(%rsp), %rcx
leaq 0x150(%rsp), %rdi
movl $0x1, %esi
movl $0x1c, %edx
callq 0x22f0
movq %rax, 0x70(%rsp)
jmp 0x2f13
movq 0x70(%rsp), %rax
movq %rax, 0x148(%rsp)
cmpq $0x4, 0x148(%rsp)
jb 0x2f45
leaq 0x150(%rsp), %rax
movl (%rax), %eax
subl $0x21726178, %eax       # imm = 0x21726178
setne %al
movzbl %al, %eax
cmpl $0x0, %eax
je 0x2fa4
movq 0x5094(%rip), %rax      # 0x7fe0
movq (%rax), %rdi
leaq 0x21ad(%rip), %rsi      # 0x5103
xorl %eax, %eax
callq 0x22c0
movq 0x198(%rsp), %rdi
callq 0x2dc0
movl %eax, 0x6c(%rsp)
jmp 0x2f70
movl 0x6c(%rsp), %eax
movl %eax, 0x1b4(%rsp)
movl $0x1, 0x138(%rsp)
jmp 0x3535
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x140(%rsp)
movl %eax, 0x13c(%rsp)
jmp 0x3544
movzwl 0x154(%rsp), %edi
callq 0x3570
movw %ax, 0x6a(%rsp)
jmp 0x2fb8
movw 0x6a(%rsp), %ax
movw %ax, 0x154(%rsp)
movzwl 0x156(%rsp), %edi
callq 0x3570
movw %ax, 0x68(%rsp)
jmp 0x2fd9
movw 0x68(%rsp), %ax
movw %ax, 0x156(%rsp)
movq 0x158(%rsp), %rdi
callq 0x35a0
movq %rax, 0x60(%rsp)
jmp 0x2ffa
movq 0x60(%rsp), %rax
movq %rax, 0x158(%rsp)
movq 0x160(%rsp), %rdi
callq 0x35a0
movq %rax, 0x58(%rsp)
jmp 0x301b
movq 0x58(%rsp), %rax
movq %rax, 0x160(%rsp)
movl 0x168(%rsp), %edi
callq 0x3650
movl %eax, 0x54(%rsp)
jmp 0x303a
movl 0x54(%rsp), %eax
movl %eax, 0x168(%rsp)
movq 0x198(%rsp), %rdi
movzwl 0x154(%rsp), %eax
movl %eax, %esi
xorl %edx, %edx
callq 0x2230
movl %eax, 0x50(%rsp)
jmp 0x3064
movl 0x50(%rsp), %eax
cmpl $0x0, %eax
je 0x30b3
movq 0x4f6c(%rip), %rax      # 0x7fe0
movq (%rax), %rdi
leaq 0x209c(%rip), %rsi      # 0x511a
xorl %eax, %eax
callq 0x22c0
movq 0x198(%rsp), %rdi
callq 0x2dc0
movl %eax, 0x4c(%rsp)
jmp 0x3098
movl 0x4c(%rsp), %eax
movl %eax, 0x1b4(%rsp)
movl $0x1, 0x138(%rsp)
jmp 0x3535
cmpq $0x0, 0x158(%rsp)
je 0x30da
cmpq $0xffff, 0x158(%rsp) # imm = 0xFFFF
ja 0x30da
cmpq $0xffff, 0x160(%rsp) # imm = 0xFFFF
jbe 0x3120
movq 0x4eff(%rip), %rax      # 0x7fe0
movq (%rax), %rdi
leaq 0x2047(%rip), %rsi      # 0x5132
xorl %eax, %eax
callq 0x22c0
movq 0x198(%rsp), %rdi
callq 0x2dc0
movl %eax, 0x48(%rsp)
jmp 0x3105
movl 0x48(%rsp), %eax
movl %eax, 0x1b4(%rsp)
movl $0x1, 0x138(%rsp)
jmp 0x3535
movq 0x158(%rsp), %rdi
callq 0x2030
movq %rax, 0x40(%rsp)
jmp 0x3134
movq 0x40(%rsp), %rax
movq %rax, 0x130(%rsp)
movq 0x130(%rsp), %rdi
movq 0x158(%rsp), %rdx
movq 0x198(%rsp), %rcx
movl $0x1, %esi
callq 0x22f0
movq %rax, 0x38(%rsp)
jmp 0x316a
movq 0x38(%rsp), %rax
movq %rax, 0x148(%rsp)
movq 0x148(%rsp), %rax
cmpq 0x158(%rsp), %rax
jae 0x31cf
movq 0x4e50(%rip), %rax      # 0x7fe0
movq (%rax), %rdi
leaq 0x1fb8(%rip), %rsi      # 0x5152
xorl %eax, %eax
callq 0x22c0
movq 0x198(%rsp), %rdi
callq 0x2dc0
movl %eax, 0x34(%rsp)
jmp 0x31b4
movl 0x34(%rsp), %eax
movl %eax, 0x1b4(%rsp)
movl $0x1, 0x138(%rsp)
jmp 0x3535
movq 0x160(%rsp), %rax
movq %rax, 0x128(%rsp)
movq 0x128(%rsp), %rdi
callq 0x2030
movq %rax, 0x28(%rsp)
jmp 0x31f3
movq 0x28(%rsp), %rax
movq %rax, 0x120(%rsp)
movq 0x130(%rsp), %rdi
movq 0x158(%rsp), %rsi
movq 0x120(%rsp), %rdx
movq 0x128(%rsp), %rcx
callq 0x2490
movb %al, 0x27(%rsp)
jmp 0x322b
movb 0x27(%rsp), %al
andb $0x1, %al
movb %al, 0x11f(%rsp)
testb $0x1, 0x11f(%rsp)
jne 0x3288
movq 0x4d97(%rip), %rax      # 0x7fe0
movq (%rax), %rdi
leaq 0x1f21(%rip), %rsi      # 0x5174
xorl %eax, %eax
callq 0x22c0
movq 0x198(%rsp), %rdi
callq 0x2dc0
movl %eax, 0x20(%rsp)
jmp 0x326d
movl 0x20(%rsp), %eax
movl %eax, 0x1b4(%rsp)
movl $0x1, 0x138(%rsp)
jmp 0x3535
movq 0x130(%rsp), %rax
movq %rax, 0x18(%rsp)
cmpq $0x0, %rax
je 0x32a5
movq 0x18(%rsp), %rdi
callq 0x2290
leaq 0x1ef0(%rip), %rdx      # 0x519c
leaq 0xf8(%rsp), %rdi
leaq 0x178(%rsp), %rsi
callq 0x3850
jmp 0x32c3
movq 0x120(%rsp), %rsi
movq 0x128(%rsp), %rdx
leaq 0xf8(%rsp), %rdi
callq 0x26e0
jmp 0x32e2
leaq 0xf8(%rsp), %rdi
callq 0x2100
movq 0x120(%rsp), %rsi
movq 0x128(%rsp), %rdx
leaq 0xe0(%rsp), %rdi
callq 0x2be0
jmp 0x330e
leaq 0xc0(%rsp), %rdi
callq 0x22a0
leaq 0xe0(%rsp), %rax
movq %rax, 0xb8(%rsp)
movq 0xb8(%rsp), %rdi
callq 0x38e0
movq %rax, 0xb0(%rsp)
movq 0xb8(%rsp), %rdi
callq 0x3910
movq %rax, 0xa8(%rsp)
leaq 0xb0(%rsp), %rdi
leaq 0xa8(%rsp), %rsi
callq 0x3940
testb $0x1, %al
jne 0x3373
jmp 0x34f6
leaq 0xb0(%rsp), %rdi
callq 0x3980
movq %rax, 0xa0(%rsp)
movq 0xa0(%rsp), %rdx
leaq 0x80(%rsp), %rdi
leaq 0x178(%rsp), %rsi
callq 0x3990
jmp 0x33a7
leaq 0xc0(%rsp), %rdi
movq %rdi, 0x10(%rsp)
leaq 0x80(%rsp), %rsi
movq %rsi, 0x8(%rsp)
callq 0x2270
movq 0x8(%rsp), %rdi
callq 0x2100
movq 0x10(%rsp), %rdi
callq 0x20a0
movq %rax, %rsi
leaq 0x1dc4(%rip), %rdi      # 0x51a8
xorl %eax, %eax
callq 0x2050
jmp 0x33ed
movq 0x4bd4(%rip), %rax      # 0x7fc8
movq (%rax), %rdi
callq 0x2240
jmp 0x33fe
movzwl 0x154(%rsp), %eax
movq 0x158(%rsp), %rcx
addq %rcx, %rax
movq 0xa0(%rsp), %rcx
movq 0x20(%rcx), %rcx
addq %rcx, %rax
movq %rax, 0x78(%rsp)
movq 0x198(%rsp), %rsi
movq 0x78(%rsp), %rdx
movq 0xa0(%rsp), %rax
movq 0x28(%rax), %rcx
leaq 0xc0(%rsp), %rdi
callq 0x2790
movb %al, 0x7(%rsp)
jmp 0x3451
movb 0x7(%rsp), %al
andb $0x1, %al
movb %al, 0x11f(%rsp)
testb $0x1, 0x11f(%rsp)
je 0x34d0
leaq 0x1d3d(%rip), %rdi      # 0x51ac
xorl %eax, %eax
callq 0x2050
jmp 0x3478
jmp 0x34e2
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x140(%rsp)
movl %eax, 0x13c(%rsp)
leaq 0xf8(%rsp), %rdi
callq 0x2100
jmp 0x3544
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x140(%rsp)
movl %eax, 0x13c(%rsp)
leaq 0xc0(%rsp), %rdi
callq 0x2100
leaq 0xe0(%rsp), %rdi
callq 0x37f0
jmp 0x3544
leaq 0x1cdb(%rip), %rdi      # 0x51b2
xorl %eax, %eax
callq 0x2050
jmp 0x34e0
jmp 0x34e2
jmp 0x34e4
leaq 0xb0(%rsp), %rdi
callq 0x3a20
jmp 0x3355
movq 0x198(%rsp), %rdi
callq 0x21a0
jmp 0x3505
movl $0x0, 0x1b4(%rsp)
movl $0x1, 0x138(%rsp)
leaq 0xc0(%rsp), %rdi
callq 0x2100
leaq 0xe0(%rsp), %rdi
callq 0x37f0
leaq 0x178(%rsp), %rdi
callq 0x2100
jmp 0x3553
leaq 0x178(%rsp), %rdi
callq 0x2100
jmp 0x3562
movl 0x1b4(%rsp), %eax
addq $0x1b8, %rsp            # imm = 0x1B8
retq
movq 0x140(%rsp), %rdi
callq 0x2310
nop