/****************************************************************************
 * GC Zip Extension
 *
 * GC DVD Zip File Loader.
 *
 * The idea here is not to support every zip file on the planet!
 * The unzip routine will simply unzip the first file in the zip archive.
 *
 * For maximum compression, I'd recommend using 7Zip,
 *	7za a -tzip -mx=9 rom.zip rom.smc
 ****************************************************************************/

#include <gccore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>

#define PKZIPID 0x504b0304
typedef struct {
	unsigned int zipid __attribute__ ((__packed__));       // 0x04034b50
	unsigned short zipversion __attribute__ ((__packed__));
	unsigned short zipflags __attribute__ ((__packed__));
	unsigned short compressionMethod __attribute__ ((__packed__));
	unsigned short lastmodtime __attribute__ ((__packed__));
	unsigned short lastmoddate __attribute__ ((__packed__));
	unsigned int crc32 __attribute__ ((__packed__));
	unsigned int compressedSize __attribute__ ((__packed__));
	unsigned int uncompressedSize __attribute__ ((__packed__));
	unsigned short filenameLength __attribute__ ((__packed__));
	unsigned short extraDataLength __attribute__ ((__packed__));
} PKZIPHEADER;

static inline u32 FLIP32(u32 b)
{
	unsigned int c;

	c = ( b & 0xff000000 ) >> 24;
	c |= ( b & 0xff0000 ) >> 8;
	c |= ( b & 0xff00 ) << 8;
	c |= ( b & 0xff ) << 24;

	return c;
}

static inline u16 FLIP16(u16 b)
{
	u16 c;

	c = ( b & 0xff00 ) >> 8;
	c |= ( b &0xff ) << 8;

	return c;
}

int unzipfile(unsigned char *out, unsigned char *in, int size, int max)
{
	PKZIPHEADER pkzip;
	int zipoffset=0;
	int r;
	int outsize;
	z_stream zs;
	
	
	memcpy(&pkzip, in, sizeof(PKZIPHEADER));
	
	memset(&zs, 0, sizeof(z_stream));
	zs.zalloc = Z_NULL;
	zs.zfree = Z_NULL;
	zs.opaque = Z_NULL;
	zs.avail_in = 0;
	zs.next_in = Z_NULL;
	r = inflateInit2(&zs, -MAX_WBITS);
	
	if ( r != Z_OK )
		return 0;

	zipoffset = sizeof(PKZIPHEADER) + FLIP16(pkzip.filenameLength) + FLIP16(pkzip.extraDataLength);
	
	zs.avail_out = max;
	zs.next_out = out;
	zs.avail_in = sizeof(in) - zipoffset;
	zs.next_in = in + zipoffset;
	
	for(;;){
		r = inflate(&zs, Z_NO_FLUSH);
		
		if (r == Z_STREAM_END){
			outsize = max - zs.avail_out;
			break;
		}
		else if (r!=Z_OK || zs.avail_out==0) {
			outsize = 0;
			break;
		}
		
		zs.avail_in = size;
		zs.next_in = in;
		if(zs.avail_in == 0){
			outsize = 0;
			break;
		}
	}
	inflateEnd(&zs);
	
	return outsize;
}
