// File: sbingner[P]kerneldec/kerneldec.cpp
// Function: decompress_kernel
int decompress_kernel(FILE *inputfh, FILE* outputfh, FILE *kppfh, bool quiet)
{
    if (inputfh)
        _input = inputfh;
    if (outputfh)
        _output = outputfh;
#define CHUNK 0x10000

    lzssdecompress lzss;
    uint8_t *ibuf= (uint8_t*)malloc(CHUNK);
    uint8_t *obuf= (uint8_t*)malloc(CHUNK);
    bzero(ibuf, CHUNK);
    size_t nr;

next:
    read_asn1hdr(ibuf);
    if (*ibuf != 0x30) {
        fprintf(stderr, "Invalid input - not IM4P %x\n", *ibuf);
        return 1;
    }

    uint64_t len = read_asn1len(ibuf[1]);

    if (g_debug) fprintf(stderr, "file length: %lld\n", len);

    char *str = read_asn1str();

    if (strcasecmp(str, "IMG4") == 0) {
        // Encapsulated in IMG4
        goto next;
    }

    if (strcasecmp(str, "IM4P")) {
        fprintf(stderr, "Invalid input - not IM4P (%s)\n", str);
        return 1;
    }

    if (g_debug) fprintf(stderr, "Have an %s container\n", str);

    free(str);

    str = read_asn1str();

    if (strcasecmp(str, "krnl")) {
        fprintf(stderr, "Invalid input - not Kernel (0x%02x)\n", *ibuf);
        return 1;
    }

    free(str);

    str = read_asn1str();

    read_asn1hdr(ibuf);

    if (*ibuf != 0x04) {
        fprintf(stderr, "Invalid input - no kernel data\n");
        return 1;
    }

    uint64_t data_len = read_asn1len(ibuf[1]);

    // See if it might be LZFSE
    char peek = fgetc(_input);
    ungetc(peek, _input);

    if (peek == 0x62) {
        // LZFSE?
        if (!quiet) fprintf(stderr, "Writing kernelcache...\n");
        int error;
        size_t decoded = lzfse_decode_file(_output, _input, data_len, &error, NULL);
        if (decoded <= 0 || error) {
            perror("Unable to decode LZFSE kernel cache");
            return 1;
        }
    } else {
        // LZSS?
        struct lzss_hdr hdr;
        nr = fread(&hdr, 1, sizeof(struct lzss_hdr), _input);
        if (nr != sizeof(struct lzss_hdr)) {
            perror("read");
            return 1;
        }

        hdr.magic = be64toh(hdr.magic);
        if (hdr.magic != lzss_magic) {
            fprintf(stderr, "Invalid input - no lzss magic 0x%llx\n", hdr.magic);
            return 1;
        }

        hdr.size = be32toh(hdr.size);
        hdr.src_size = be32toh(hdr.src_size);
        if (g_debug) fprintf(stderr, "Found kernelcache size %u compressed: %u (asn1 size %lld)\n", hdr.size, hdr.src_size, data_len);
        if (hdr.src_size > data_len - sizeof(struct lzss_hdr)) {
            fprintf(stderr, "Invalid input - reports size larger than available\n");
            return 1;
        }

        uint64_t total_written=0;
        uint64_t total_read=0;
        if (!quiet) fprintf(stderr, "Writing kernelcache...\n");
        while (!feof(_input) && total_read < hdr.src_size)
        {
            if (total_read + CHUNK > hdr.src_size) {
                nr = fread(ibuf, 1, hdr.src_size - total_read, _input);
            } else {
                nr = fread(ibuf, 1, CHUNK, _input);
            }
            if (nr==0) {
                perror("input file short read");
                break;
            }

            total_read += nr;
            size_t srcp= 0;
            while (srcp<nr) {
                uint32_t dstused;
                uint32_t srcused;
                lzss.decompress(obuf, CHUNK, &dstused, ibuf+srcp, nr-srcp, &srcused);
                srcp+=srcused;
                if (total_written + dstused > hdr.size) {
                    dstused = hdr.size - total_written;
                }
                size_t nw= fwrite(obuf, 1, dstused, _output);
                if (nw<dstused) {
                    perror("write");
                    return 1;
                }
                total_written += nw;
                if (g_debug) fprintf(stderr, "decompress: 0x%x -> 0x%x\n", srcused, dstused);
            }
        }
    }
    if (!quiet) fprintf(stderr, "... done\n");
    if (kppfh != NULL) {
        while ((nr = fread(ibuf, 1, CHUNK, _input))) {
            if (fwrite(ibuf, 1, nr, kppfh) != nr) {
                perror("write kpp:");
                return 1;
            }
        }
    }
    if (g_debug) fprintf(stderr, "done reading\n");
    uint32_t dstused;
    lzss.flush(obuf, CHUNK, &dstused);
    size_t nw = fwrite(obuf, 1, dstused, _output);
    if (nw<dstused) {
        perror("write");
        return 1;
    }

    if (g_debug) fprintf(stderr, "flush: %d bytes\n", dstused);

    return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x320, %rsp            # imm = 0x320
movb %cl, %al
movq %rdi, -0x10(%rbp)
movq %rsi, -0x18(%rbp)
movq %rdx, -0x20(%rbp)
andb $0x1, %al
movb %al, -0x21(%rbp)
cmpq $0x0, -0x10(%rbp)
je 0x3540
movq -0x10(%rbp), %rax
movq %rax, 0x12498(%rip)     # 0x159d8
cmpq $0x0, -0x18(%rbp)
je 0x3552
movq -0x18(%rbp), %rax
movq %rax, 0x1248e(%rip)     # 0x159e0
leaq -0x88(%rbp), %rdi
callq 0x3ff0
movl $0x10000, %edi          # imm = 0x10000
callq 0x3110
movq %rax, -0x90(%rbp)
movl $0x10000, %edi          # imm = 0x10000
callq 0x3110
movq %rax, -0x98(%rbp)
movq -0x90(%rbp), %rdi
xorl %esi, %esi
movl $0x10000, %edx          # imm = 0x10000
callq 0x3060
movq -0x90(%rbp), %rdi
callq 0x33f0
jmp 0x35a1
movq -0x90(%rbp), %rax
movzbl (%rax), %eax
cmpl $0x30, %eax
je 0x360b
movq 0x11a29(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
movq -0x90(%rbp), %rax
movzbl (%rax), %edx
leaq 0xcab3(%rip), %rsi      # 0x1007e
movb $0x0, %al
callq 0x3120
movl $0x1, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
jmp 0x3ed9
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
leaq -0x88(%rbp), %rdi
callq 0x40e0
jmp 0x3ef1
movq -0x90(%rbp), %rax
movzbl 0x1(%rax), %edi
callq 0x32f0
movq %rax, -0x298(%rbp)
jmp 0x3624
movq -0x298(%rbp), %rax
movq %rax, -0xb8(%rbp)
leaq 0x123af(%rip), %rax     # 0x159e8
cmpl $0x0, (%rax)
je 0x365d
movq 0x1199b(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
movq -0xb8(%rbp), %rdx
leaq 0xca45(%rip), %rsi      # 0x1009b
movb $0x0, %al
callq 0x3120
callq 0x3440
movq %rax, -0x2a0(%rbp)
jmp 0x366b
movq -0x2a0(%rbp), %rax
movq %rax, -0xc0(%rbp)
movq -0xc0(%rbp), %rdi
leaq 0xca27(%rip), %rsi      # 0x100ae
callq 0x30c0
cmpl $0x0, %eax
jne 0x3696
jmp 0x3593
movq -0xc0(%rbp), %rdi
leaq 0xca0f(%rip), %rsi      # 0x100b3
callq 0x30c0
cmpl $0x0, %eax
je 0x36e3
movq 0x1192b(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
movq -0xc0(%rbp), %rdx
leaq 0xc9f2(%rip), %rsi      # 0x100b8
movb $0x0, %al
callq 0x3120
movl $0x1, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
jmp 0x3ed9
leaq 0x122fe(%rip), %rax     # 0x159e8
cmpl $0x0, (%rax)
je 0x370e
movq 0x118ea(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
movq -0xc0(%rbp), %rdx
leaq 0xc9d0(%rip), %rsi      # 0x100d7
movb $0x0, %al
callq 0x3120
movq -0xc0(%rbp), %rdi
callq 0x30e0
callq 0x3440
movq %rax, -0x2a8(%rbp)
jmp 0x3728
movq -0x2a8(%rbp), %rax
movq %rax, -0xc0(%rbp)
movq -0xc0(%rbp), %rdi
leaq 0xc9a9(%rip), %rsi      # 0x100ed
callq 0x30c0
cmpl $0x0, %eax
je 0x3786
movq 0x1188b(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
movq -0x90(%rbp), %rax
movzbl (%rax), %edx
leaq 0xc989(%rip), %rsi      # 0x100f2
movb $0x0, %al
callq 0x3120
movl $0x1, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
jmp 0x3ed9
movq -0xc0(%rbp), %rdi
callq 0x30e0
callq 0x3440
movq %rax, -0x2b0(%rbp)
jmp 0x37a0
movq -0x2b0(%rbp), %rax
movq %rax, -0xc0(%rbp)
movq -0x90(%rbp), %rdi
callq 0x33f0
jmp 0x37bc
movq -0x90(%rbp), %rax
movzbl (%rax), %eax
cmpl $0x4, %eax
je 0x37f9
movq 0x1180e(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
leaq 0xc93b(%rip), %rsi      # 0x10117
movb $0x0, %al
callq 0x3120
movl $0x1, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
jmp 0x3ed9
movq -0x90(%rbp), %rax
movzbl 0x1(%rax), %edi
callq 0x32f0
movq %rax, -0x2b8(%rbp)
jmp 0x3812
movq -0x2b8(%rbp), %rax
movq %rax, -0xc8(%rbp)
movq 0x121b1(%rip), %rdi     # 0x159d8
callq 0x3150
movl %eax, -0x2bc(%rbp)
jmp 0x3834
movl -0x2bc(%rbp), %eax
movb %al, -0xc9(%rbp)
movsbl -0xc9(%rbp), %edi
movq 0x1218a(%rip), %rsi     # 0x159d8
callq 0x3050
jmp 0x3855
movsbl -0xc9(%rbp), %eax
cmpl $0x62, %eax
jne 0x38fc
testb $0x1, -0x21(%rbp)
jne 0x3883
movq 0x1176e(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
leaq 0xc8bb(%rip), %rsi      # 0x10137
movb $0x0, %al
callq 0x3120
movq 0x12156(%rip), %rdi     # 0x159e0
movq 0x12147(%rip), %rsi     # 0x159d8
movq -0xc8(%rbp), %rdx
xorl %eax, %eax
movl %eax, %r8d
leaq -0xd0(%rbp), %rcx
callq 0x54d0
movq %rax, -0x2c8(%rbp)
jmp 0x38b2
movq -0x2c8(%rbp), %rax
movq %rax, -0xd8(%rbp)
cmpq $0x0, -0xd8(%rbp)
jbe 0x38d3
cmpl $0x0, -0xd0(%rbp)
je 0x38f7
leaq 0xc875(%rip), %rdi      # 0x1014f
callq 0x30a0
jmp 0x38e1
movl $0x1, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
jmp 0x3ed9
jmp 0x3d2a
movq 0x120d5(%rip), %rcx     # 0x159d8
leaq -0x258(%rbp), %rdi
movl $0x1, %esi
movl $0x180, %edx            # imm = 0x180
callq 0x3130
movq %rax, -0x2d0(%rbp)
jmp 0x3922
movq -0x2d0(%rbp), %rax
movq %rax, -0xa0(%rbp)
cmpq $0x180, -0xa0(%rbp) # imm = 0x180
je 0x3961
leaq 0xc701(%rip), %rdi      # 0x10045
callq 0x30a0
jmp 0x394b
movl $0x1, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
jmp 0x3ed9
movq -0x258(%rbp), %rdi
callq 0x3f00
movq %rax, -0x2d8(%rbp)
jmp 0x3976
movq -0x2d8(%rbp), %rax
movq %rax, -0x258(%rbp)
movabsq $0x636f6d706c7a7373, %rax # imm = 0x636F6D706C7A7373
cmpq %rax, -0x258(%rbp)
je 0x39cc
movq 0x11642(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
movq -0x258(%rbp), %rdx
leaq 0xc7c4(%rip), %rsi      # 0x10173
movb $0x0, %al
callq 0x3120
movl $0x1, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
jmp 0x3ed9
movl -0x24c(%rbp), %edi
callq 0x3fb0
movl %eax, -0x2dc(%rbp)
jmp 0x39df
movl -0x2dc(%rbp), %eax
movl %eax, -0x24c(%rbp)
movl -0x248(%rbp), %edi
callq 0x3fb0
movl %eax, -0x2e0(%rbp)
jmp 0x39fe
movl -0x2e0(%rbp), %eax
movl %eax, -0x248(%rbp)
leaq 0x11fd7(%rip), %rax     # 0x159e8
cmpl $0x0, (%rax)
je 0x3a41
movq 0x115c3(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
movl -0x24c(%rbp), %edx
movl -0x248(%rbp), %ecx
movq -0xc8(%rbp), %r8
leaq 0xc75f(%rip), %rsi      # 0x10199
movb $0x0, %al
callq 0x3120
movl -0x248(%rbp), %eax
movq -0xc8(%rbp), %rcx
subq $0x180, %rcx            # imm = 0x180
cmpq %rcx, %rax
jbe 0x3a88
movq 0x1157f(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
leaq 0xc769(%rip), %rsi      # 0x101d4
movb $0x0, %al
callq 0x3120
movl $0x1, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
jmp 0x3ed9
movq $0x0, -0x260(%rbp)
movq $0x0, -0x268(%rbp)
testb $0x1, -0x21(%rbp)
jne 0x3abc
movq 0x11535(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
leaq 0xc682(%rip), %rsi      # 0x10137
movb $0x0, %al
callq 0x3120
jmp 0x3abe
movq 0x11f13(%rip), %rdi     # 0x159d8
callq 0x3140
movl %eax, %ecx
xorl %eax, %eax
cmpl $0x0, %ecx
movb %al, -0x2e1(%rbp)
jne 0x3af2
movq -0x268(%rbp), %rax
movl -0x248(%rbp), %ecx
cmpq %rcx, %rax
setb %al
movb %al, -0x2e1(%rbp)
movb -0x2e1(%rbp), %al
testb $0x1, %al
jne 0x3b01
jmp 0x3d28
movq -0x268(%rbp), %rax
addq $0x10000, %rax          # imm = 0x10000
movl -0x248(%rbp), %ecx
cmpq %rcx, %rax
jbe 0x3b5c
movq -0x90(%rbp), %rdi
movl -0x248(%rbp), %eax
movl %eax, %edx
movq -0x268(%rbp), %rax
subq %rax, %rdx
movq 0x11e9f(%rip), %rcx     # 0x159d8
movl $0x1, %esi
callq 0x3130
movq %rax, -0x2f0(%rbp)
jmp 0x3b4c
movq -0x2f0(%rbp), %rax
movq %rax, -0xa0(%rbp)
jmp 0x3b90
movq -0x90(%rbp), %rdi
movq 0x11e6e(%rip), %rcx     # 0x159d8
movl $0x1, %esi
movl $0x10000, %edx          # imm = 0x10000
callq 0x3130
movq %rax, -0x2f8(%rbp)
jmp 0x3b82
movq -0x2f8(%rbp), %rax
movq %rax, -0xa0(%rbp)
cmpq $0x0, -0xa0(%rbp)
jne 0x3bad
leaq 0xc667(%rip), %rdi      # 0x10208
callq 0x30a0
jmp 0x3ba8
jmp 0x3d28
movq -0xa0(%rbp), %rax
addq -0x268(%rbp), %rax
movq %rax, -0x268(%rbp)
movq $0x0, -0x270(%rbp)
movq -0x270(%rbp), %rax
cmpq -0xa0(%rbp), %rax
jae 0x3d23
movq -0x98(%rbp), %rsi
movq -0x90(%rbp), %r8
movq -0x270(%rbp), %rax
addq %rax, %r8
movl -0xa0(%rbp), %r9d
subl %eax, %r9d
movq %rsp, %rax
leaq -0x278(%rbp), %rcx
movq %rcx, (%rax)
leaq -0x88(%rbp), %rdi
movl $0x10000, %edx          # imm = 0x10000
leaq -0x274(%rbp), %rcx
callq 0x4130
jmp 0x3c2a
movl -0x278(%rbp), %eax
addq -0x270(%rbp), %rax
movq %rax, -0x270(%rbp)
movq -0x260(%rbp), %rax
movl -0x274(%rbp), %ecx
addq %rcx, %rax
movl -0x24c(%rbp), %ecx
cmpq %rcx, %rax
jbe 0x3c6c
movl -0x24c(%rbp), %eax
subq -0x260(%rbp), %rax
movl %eax, -0x274(%rbp)
movq -0x98(%rbp), %rdi
movl -0x274(%rbp), %eax
movl %eax, %edx
movq 0x11d5e(%rip), %rcx     # 0x159e0
movl $0x1, %esi
callq 0x3190
movq %rax, -0x300(%rbp)
jmp 0x3c95
movq -0x300(%rbp), %rax
movq %rax, -0x280(%rbp)
movq -0x280(%rbp), %rax
movl -0x274(%rbp), %ecx
cmpq %rcx, %rax
jae 0x3cd9
leaq 0xc562(%rip), %rdi      # 0x1021e
callq 0x30a0
jmp 0x3cc3
movl $0x1, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
jmp 0x3ed9
movq -0x280(%rbp), %rax
addq -0x260(%rbp), %rax
movq %rax, -0x260(%rbp)
leaq 0x11cf3(%rip), %rax     # 0x159e8
cmpl $0x0, (%rax)
je 0x3d1e
movq 0x112df(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
movl -0x278(%rbp), %edx
movl -0x274(%rbp), %ecx
leaq 0xc50d(%rip), %rsi      # 0x10224
movb $0x0, %al
callq 0x3120
jmp 0x3bcd
jmp 0x3abe
jmp 0x3d2a
testb $0x1, -0x21(%rbp)
jne 0x3d48
movq 0x112a9(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
leaq 0xc4fd(%rip), %rsi      # 0x1023e
movb $0x0, %al
callq 0x3120
cmpq $0x0, -0x20(%rbp)
je 0x3def
jmp 0x3d55
movq -0x90(%rbp), %rdi
movq 0x11c75(%rip), %rcx     # 0x159d8
movl $0x1, %esi
movl $0x10000, %edx          # imm = 0x10000
callq 0x3130
movq %rax, -0x308(%rbp)
jmp 0x3d7b
movq -0x308(%rbp), %rax
movq %rax, -0xa0(%rbp)
cmpq $0x0, %rax
je 0x3ded
movq -0x90(%rbp), %rdi
movq -0xa0(%rbp), %rdx
movq -0x20(%rbp), %rcx
movl $0x1, %esi
callq 0x3190
movq %rax, -0x310(%rbp)
jmp 0x3db4
movq -0x310(%rbp), %rax
cmpq -0xa0(%rbp), %rax
je 0x3de8
leaq 0xc47d(%rip), %rdi      # 0x10248
callq 0x30a0
jmp 0x3dd2
movl $0x1, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
jmp 0x3ed9
jmp 0x3d55
jmp 0x3def
leaq 0x11bf2(%rip), %rax     # 0x159e8
cmpl $0x0, (%rax)
je 0x3e13
movq 0x111de(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
leaq 0xc447(%rip), %rsi      # 0x10253
movb $0x0, %al
callq 0x3120
movq -0x98(%rbp), %rsi
leaq -0x88(%rbp), %rdi
movl $0x10000, %edx          # imm = 0x10000
leaq -0x284(%rbp), %rcx
callq 0x4650
jmp 0x3e34
movq -0x98(%rbp), %rdi
movl -0x284(%rbp), %eax
movl %eax, %edx
movq 0x11b96(%rip), %rcx     # 0x159e0
movl $0x1, %esi
callq 0x3190
movq %rax, -0x318(%rbp)
jmp 0x3e5d
movq -0x318(%rbp), %rax
movq %rax, -0x290(%rbp)
movq -0x290(%rbp), %rax
movl -0x284(%rbp), %ecx
cmpq %rcx, %rax
jae 0x3e9e
leaq 0xc39a(%rip), %rdi      # 0x1021e
callq 0x30a0
jmp 0x3e8b
movl $0x1, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
jmp 0x3ed9
leaq 0x11b43(%rip), %rax     # 0x159e8
cmpl $0x0, (%rax)
je 0x3ec8
movq 0x1112f(%rip), %rax     # 0x14fe0
movq (%rax), %rdi
movl -0x284(%rbp), %edx
leaq 0xc3a0(%rip), %rsi      # 0x10261
movb $0x0, %al
callq 0x3120
movl $0x0, -0x4(%rbp)
movl $0x1, -0xb0(%rbp)
leaq -0x88(%rbp), %rdi
callq 0x40e0
movl -0x4(%rbp), %eax
addq $0x320, %rsp            # imm = 0x320
popq %rbp
retq
movq -0xa8(%rbp), %rdi
callq 0x3180
nopl (%rax)