/***************************************************************************
 * Copyright (C) 2010
 * by Dimok
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you
 * must not claim that you wrote the original software. If you use
 * this software in a product, an acknowledgment in the product
 * documentation would be appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and
 * must not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 * distribution.
 *
 * for WiiXplorer 2010
 ***************************************************************************/
#include <malloc.h>
#include <string.h>

#include "uncompress.h"
#include "lz77.h"

u8 * uncompressLZ77(const u8 *inBuf, u32 inLength, u32 * size)
{
	if(!inBuf)
		return NULL;

	u8 *buffer = NULL;
	if (*((const u32 *)inBuf) != 0x4C5A3737 /*"LZ77"*/)
		return NULL;

	decompressLZ77content( (u8*)inBuf + 4, inLength - 4, &buffer, size);

	return buffer;
}

//Thanks to _demo_ for this function
//src points to the yaz0 source data (to the "real" source data, not at the header!)
//dst points to a buffer uncompressedSize bytes large (you get uncompressedSize from
//the second 4 bytes in the Yaz0 header).
void uncompressYaz0(const u8* srcBuf, u8* dst, int uncompressedSize)
{
	const u8 * src = srcBuf;

	if(memcmp(src, "Yaz0", 4) == 0)
	{
		src += sizeof(Yaz0_Header);
	}

	int srcPlace = 0, dstPlace = 0; //current read/write positions

	u32 validBitCount = 0; //number of valid bits left in "code" byte
	u8 currCodeByte = 0;

	while(dstPlace < uncompressedSize)
	{
		//read new "code" byte if the current one is used up
		if(validBitCount == 0)
		{
			currCodeByte = src[srcPlace];
			++srcPlace;
			validBitCount = 8;
		}

		if((currCodeByte & 0x80) != 0)
		{
			//straight copy
			dst[dstPlace] = src[srcPlace];
			dstPlace++;
			srcPlace++;
		}
		else
		{
			//RLE part
			u8 byte1 = src[srcPlace];
			u8 byte2 = src[srcPlace + 1];
			srcPlace += 2;

			u32 dist = ((byte1 & 0xF) << 8) | byte2;
			u32 copySource = dstPlace - (dist + 1);

			u32 numBytes = byte1 >> 4;
			if(numBytes == 0)
			{
				numBytes = src[srcPlace] + 0x12;
				srcPlace++;
			}
			else
				numBytes += 2;

			//copy run
			u32 i = 0;
			for(i = 0; i < numBytes; ++i)
			{
				dst[dstPlace] = dst[copySource];
				copySource++;
				dstPlace++;
			}
		}

		//use next bit from "code" byte
		currCodeByte <<= 1;
		validBitCount-=1;
	}
}


u32 CheckIMD5Type(const u8 * buffer, int length)
{
	if(*((u32 *) buffer) != 'IMD5')
	{
		return *((u32 *) buffer);
	}

	const u8 * file = buffer+32;

	if(*((u32 *) file) != 'LZ77')
	{
		return *((u32 *) file);
	}

	u32 uncSize = 0;
	u8 * uncompressed_data = uncompressLZ77(file, length-32, &uncSize);
	if(!uncompressed_data)
		return 0;

	u32 * magic = (u32 *) uncompressed_data;
	u32 Type = magic[0];
	free(uncompressed_data);

	return Type;
}
