/*
 * (C) Copyright 2000-2009
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */
/*
 * Boot support
 */
#include "./ziplib/bzlib.h"
#include "./ziplib/zlib.h"
#include "./ziplib/image.h"
#include "string.h"
#include "stdlib.h"

 #define ntohl(x)    (((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8)| ((x & 0xFF000000) >> 24))
 
int  gunzip (void *, int, unsigned char *, unsigned long *);

static void *zalloc(void *, unsigned, unsigned);
static void zfree(void *, void *, unsigned);

#define ulong unsigned long
#define uchar unsigned char

#define CFG_MALLOC_LEN 8192 * 1024 // 192*1024


#ifndef CFG_BOOTM_LEN
#define CFG_BOOTM_LEN    0x800000    /* use 8MByte as default max gunzip size */
#endif

image_header_t header;

int do_bootm (UINT32 u32SrcAddress,UINT32 u32DestAddress, UINT32 u32Mode)
{
    unsigned long    addr;
    unsigned long    data, len;
    unsigned int    unc_len = CFG_BOOTM_LEN;
    image_header_t *hdr = &header;

    addr  =u32SrcAddress;

    sysprintf ("## Booting image at 0x%08x ...\n", addr);

    /* Copy header so we can blank CRC field for re-calculation */
    memcpy (&header, (char *)addr, sizeof(image_header_t));

    if (ntohl(hdr->ih_magic) != IH_MAGIC) {
        {
        sysprintf ("Bad Magic Number\n");
        return 1;
        }
    }
    else
    {
        sysprintf ("Get Magic Number\n");
        if(u32Mode)
            return 0;    
    }

    data = (ulong)&header;
    len  = sizeof(image_header_t);

    hdr->ih_hcrc = 0;

    data = addr + sizeof(image_header_t);
    len  = ntohl(hdr->ih_size);
   
    /*
     * We have reached the point of no return: we are going to
     * overwrite all exception vector code, so we cannot easily
     * recover from any failures any more...
     */
    if(hdr->ih_comp ==IH_COMP_GZIP) {
        sysprintf ("   Gzip Uncompressing to 0x%X ... ",u32DestAddress);
        if (gunzip ((void *)u32DestAddress, unc_len,
                (uchar *)data, &len) != 0) {
            sysprintf ("GUNZIP ERROR\n");
        }    
    }
    else    
    {
        sysprintf ("Unimplemented compression type %d\n", hdr->ih_comp);
        return 1;
    }
    sysprintf ("OK\n");
    
    return 1;
}

#define    ZALLOC_ALIGNMENT    16

static void *zalloc(void *x, unsigned items, unsigned size)
{
    void *p;

    size *= items;
    size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1);

    p = (void *)malloc (size);

    return (p);
}

static void zfree(void *x, void *addr, unsigned nb)
{
    free (addr);
}

#define HEAD_CRC    2
#define EXTRA_FIELD    4
#define ORIG_NAME    8
#define COMMENT        0x10
#define RESERVED    0xe0

#define DEFLATED    8

int gunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp)
{
    z_stream s;
    int r, i, flags;

    /* skip header */
    i = 10;
    flags = src[3];
    if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
        sysprintf ("Error: Bad gzipped data\n");
        return (-1);
    }
    if ((flags & EXTRA_FIELD) != 0)
        i = 12 + src[10] + (src[11] << 8);
    if ((flags & ORIG_NAME) != 0)
        while (src[i++] != 0)
            ;
    if ((flags & COMMENT) != 0)
        while (src[i++] != 0)
            ;
    if ((flags & HEAD_CRC) != 0)
        i += 2;
    if (i >= *lenp) {
        sysprintf ("Error: gunzip out of data in header\n");
        return (-1);
    }

    s.zalloc = zalloc;
    s.zfree = zfree;

    s.outcb = Z_NULL;

    r = inflateInit2(&s, -MAX_WBITS);
    if (r != Z_OK) {
        sysprintf ("Error: inflateInit2() returned %d\n", r);
        return (-1);
    }
    s.next_in = src + i;
    s.avail_in = *lenp - i;
    s.next_out = dst;
    s.avail_out = dstlen;
    r = inflate(&s, Z_FINISH);
    if (r != Z_OK && r != Z_STREAM_END) {
        sysprintf ("Error: inflate() returned %d\n", r);
        return (-1);
    }
    *lenp = s.next_out - (unsigned char *) dst;
    inflateEnd(&s);

    return (0);
}


#ifdef CONFIG_BZIP2
void bz_internal_error(int errcode)
{
    sysprintf ("BZIP2 internal error %d\n", errcode);
}
#endif /* CONFIG_BZIP2 */
