/*
 * This code is released under the Microsoft Public License (MS-PL). See License.txt, below.
 */
#include "stdafx.h"
#include "struct.h"
#include "create.h"
#include "decorder.h"
#include "get.h"
#include "uncompress.h"

/*
	Crime Crackers 2(SCPS-10037)
	LOVE & DESTROY(SCPS-10124)
	Leading Jockey Hybrid(SLPS-00348)
	WILD ARMS(SCPS-10028)
	WILD ARMS 2nd IGNITION(SCPS-10089)
	<<フォーマット>>
	DWORD	圧縮前のサイズ
	BYTE	圧縮フラグ（０＝圧縮、１＝未圧縮）
	BYTE	データ[未圧縮の数+圧縮の数*2]
	BYTE	圧縮フラグ（０＝圧縮、１＝未圧縮）
	BYTE	データ[未圧縮の数+圧縮の数*2]
		:
		:

	Eberouge(SLPS-00844)
	<<フォーマット>>
	DWORD	圧縮前のサイズ
	DWORD	LZ00(String)
	BYTE	圧縮フラグ（１＝圧縮、０＝未圧縮）
	BYTE	データ[未圧縮の数+圧縮の数*2]
	BYTE	圧縮フラグ（１＝圧縮、０＝未圧縮）
	BYTE	データ[未圧縮の数+圧縮の数*2]
		:
		:

	Meitantei Conan - 3nin no Meisuiri(SLPS-02804)
	Meitantei Conan - Saikou no Partner(SLPS-03425)
	<<フォーマット>>
	DWORD	圧縮後のサイズ
	BYTE	圧縮フラグ（０＝圧縮、１＝未圧縮）
	BYTE	データ[未圧縮の数+圧縮の数*2]
	BYTE	圧縮フラグ（０＝圧縮、１＝未圧縮）
	BYTE	データ[未圧縮の数+圧縮の数*2]
		:
		:

	<<圧縮仕様>>
	　Bit  0〜11	… 辞書のアドレス（辞書幅＝4096[Byte]）
	　Bit 13〜15	… データ長
*/
BOOL uncompressLzss(
	LPBYTE sbuf,
	_TCHAR* pszFilenameWithFullpath,
	DWORD ofsOfSize,
	DWORD ofsOfPos,
	COMPRESS_FLAG_TYPE compressFlag,
	DWORD encondingBit,
	COMPRESS_SIZE_TYPE compressSizeType,
	LZSS_TYPE lzssType,
	_TCHAR* ext,
	INT specialFix,
	PINTERNAL_MUSIC im
	)
{
	DWORD dataIdx = 0;
	LPDWORD ofs = 0;
	if (!_tcscmp(ext, _T(".seq")) ||
		!_tcscmp(ext, _T(".sssq"))) {
		dataIdx = im->nSeqIdx;
		ofs = im->nSeqOfs;
	}
	for (DWORD i = 0; i < dataIdx; i++) {
		DWORD dlen = 0;
		if (compressSizeType == Uncompress) {
			dlen = getlong(sbuf + (ofs[i] - ofsOfSize));
		}
		else {
			DWORD tmpsize = 0;
			DWORD sl=0;
			BYTE bits = 0;
			DWORD bitscnt = 0;
			DWORD tmpbitscnt = 0;
			DWORD readsize = 0;

			for (;;) {
				LPBYTE data = sbuf + (ofs[i] - ofsOfPos + readsize);
				bits = getbyte(data);
				bitscnt = getBitCount(bits);
				tmpbitscnt = (8 - bitscnt) * 2 + bitscnt;
				// 圧縮されたデータとされていないデータと圧縮フラグのサイズの合計
				readsize += tmpbitscnt + 1;
				DWORD cnt = 0;
				for (DWORD idx = 0; idx < tmpbitscnt + 1; idx++) {
					if (getbyte(data + idx) == 0x00) {
						cnt++;
					}
					else {
						break;
					}
				}
				if (cnt == tmpbitscnt + 1) {
					// 圧縮前のサイズ
					readsize = tmpsize + sl;
					break;
				}
				// 圧縮されていないデータのサイズ(圧縮フラグは含まず)
				tmpsize += bitscnt;
				DWORD sp = 1;
				for (int fc = 0; fc < 8; fc++){
					if ((bits & 1)==0){
						// 圧縮されたデータの伸張後のサイズ
						sl += 3 + (0x0f & *(data + sp + 1));
						sp += 2;
					}
					else {
						sp++;
					}
					bits >>= 1;
				}
			}
			dlen = readsize;
		}
		LPBYTE dbuf = (LPBYTE)malloc(dlen);
		if (dbuf) {
			DWORD rlen = 0;
			if (lzssType == LZSS_TYPE1) {
				ps2_lzss(sbuf + (ofs[i] - ofsOfPos),
					dbuf, dlen, compressFlag, encondingBit);
			}
			else if (lzssType == LZSS_TYPE2) {
				ps2_lzss2(sbuf + (ofs[i] - ofsOfPos),
					dbuf, dlen, compressFlag, encondingBit);
			}
			else if (lzssType == LZSS_TYPE3) {
#if 0
				ps2_lzss3(sbuf + (ofs[i] - ofsOfPos),
					dbuf, dlen, compressFlag, encondingBit);
#endif
			}
			if (compressSizeType == Compress) {
				for (int k = 0; k < 65535; k++) {
					DWORD longByte = getlong(dbuf + k);
					// end of track
					if ((longByte & 0xFFFFFF00) == 0x002FFF00) {
						dlen = k + sizeof(longByte);
						break;
					}
				}
			}
			if (rlen != dlen) {
//				OutputErrorLog(" expect uncompress size %d, real uncompress size %d\n", dlen, rlen);
			}
			FILE* outFp = CreateOrOpenFile(pszFilenameWithFullpath,
				NULL, NULL, NULL, ext, _T("wb"), i, dataIdx);
			if (!outFp) {
				OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
				return FALSE;
			}
			if (dlen > 8 && specialFix == 1) {
				dbuf[7] = 0x01;
			}
			fwrite(dbuf, sizeof(BYTE), dlen, outFp);
			im->isSeq[i] = TRUE;
			FcloseAndNull(outFp);
			FreeAndNull(dbuf);
		}
		OutputString(_T("\rUncompress %4u/%4u"), i + 1, dataIdx);
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL uncompressBrightis(
	LPBYTE sbuf,
	_TCHAR* pszFilenameWithFullpath,
	PINTERNAL_MUSIC im
	)
{
	for (DWORD a = 0; a < im->nSeqIdx; a++) {
		FILE* outfp = CreateOrOpenFile(pszFilenameWithFullpath, 
			NULL, NULL, NULL, _T(".seq"), _T("wb"), a, im->nSeqIdx);
		if (!outfp) {
			OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
			return FALSE;
		}
#ifdef _DEBUG
		FILE* logfp = CreateOrOpenFile(pszFilenameWithFullpath, 
			NULL, NULL, NULL, _T(".txt"), _T("wb"), a, im->nSeqIdx);
		if (!logfp) {
			OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
			return FALSE;
		}
#endif
		BYTE val1 = getbyte(sbuf + im->nSeqOfs[a] - 2);
		BYTE val2 = getbyte(sbuf + im->nSeqOfs[a] - 1);
		size_t srcIdx = size_t(((val1 << 8) & 0x0F00) | val2);
		size_t dstIdx = srcIdx;
		size_t srcFilesize = 0;
		for (int z = 1; ; z++) {
			srcFilesize++;
			if (getlong(sbuf + im->nSeqOfs[a] + z) == 0x002FFF00) {
				break;
			}
		}
		LPBYTE dbuf = (LPBYTE)calloc(srcFilesize * 10, sizeof(BYTE));
		if (!dbuf) {
			OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
			return FALSE;
		}
		memcpy(dbuf, sbuf + im->nSeqOfs[a], srcIdx);

		while (srcIdx < srcFilesize) {
			BYTE byte = getbyte(sbuf + im->nSeqOfs[a] + srcIdx);
			BYTE secondByte = getbyte(sbuf + im->nSeqOfs[a] + srcIdx + 1);
			BYTE thirdByte = getbyte(sbuf + im->nSeqOfs[a] + srcIdx + 2);
			BYTE fourthByte = getbyte(sbuf + im->nSeqOfs[a] + srcIdx + 3);
			if (byte == 0 && secondByte == 0 && thirdByte == 0 && fourthByte == 0) {
				break;
			}
			WORD ofs = (WORD)(((byte & 0x1F) << 8) + secondByte);
			size_t tmpdstIdx = dstIdx - ofs;
			DWORD dstCopySize = 0;
			DWORD srcSize = 0;
			BOOL bRet = FALSE;

			if (tmpdstIdx > 0 && ofs > 0) {
				switch(byte & 0xF0) {
				case 0x80:
				case 0x90:
					dstCopySize = 4;
					bRet = TRUE;
					break;

				case 0xA0:
				case 0xB0:
					dstCopySize = 5;
					bRet = TRUE;
					break;

				case 0xC0:
				case 0xD0:
					dstCopySize = 6;
					bRet = TRUE;
					break;

				case 0xE0:
				case 0xF0:
					dstCopySize = 7;
					for (;;) {
						BYTE tmpByte = 
							getbyte(sbuf + im->nSeqOfs[a] + srcIdx + 2 + srcSize);
						if (0x60 < tmpByte && tmpByte < 0x80) {
							dstCopySize += tmpByte - 0x60;
							srcSize++;
						}
						else {
							break;
						}
					}
					bRet = TRUE;
					break;

				default:
					dbuf[dstIdx++] = byte;
					srcIdx++;
					break;
				}
			}
			else {
				dbuf[dstIdx++] = byte;
				srcIdx++;
			}

			if (bRet == TRUE) {
				for (;;) {
					if (dstCopySize > ofs) {
						memcpy(dbuf + dstIdx, dbuf + tmpdstIdx, ofs);
						dstIdx += ofs;
						dstCopySize -= ofs;
					}
					else {
						memcpy(dbuf + dstIdx, dbuf + tmpdstIdx, dstCopySize);
						break;
					}
				}
#ifdef _DEBUG
				_ftprintf(logfp, 
					_T("src: %04x, %02x %02x %02x %02x, dst: %04x, "), 
					srcIdx, byte, secondByte, thirdByte, fourthByte, dstIdx);
				for (DWORD i = 0; i < dstCopySize; i++) {
					_ftprintf(logfp, _T("%02x "), getbyte(dbuf + tmpdstIdx + i));
				}
				_ftprintf(logfp, _T("\n"));
#endif
				srcIdx += 2 + srcSize;
				dstIdx += dstCopySize;
				BYTE tmpByte = getbyte(sbuf + im->nSeqOfs[a] + srcIdx);
				if ((tmpByte & 0xF0) == 0x20) {
					srcIdx++;
					WORD ofs2 = 
						(WORD)(((tmpByte & 0x1F) << 8) + getbyte(sbuf + im->nSeqOfs[a] + srcIdx));
					memcpy(dbuf + dstIdx, sbuf + im->nSeqOfs[a] + srcIdx + 1, ofs2);
					srcIdx += ofs2 + 1;
					dstIdx += ofs2;
				}
				else if (0 < tmpByte && tmpByte < 0x20) {
					memcpy(dbuf + dstIdx, sbuf + im->nSeqOfs[a] + srcIdx + 1, tmpByte);
					srcIdx += tmpByte + 1;
					dstIdx += tmpByte;
				}
			}
		}
		fwrite(dbuf, sizeof(BYTE), dstIdx, outfp);
		im->isSeq[a] = TRUE;
		FreeAndNull(dbuf);
#ifdef _DEBUG
		FcloseAndNull(logfp);
#endif
		FcloseAndNull(outfp);
		OutputString(_T("\rUncompress %4u/%4u"), a + 1, im->nSeqIdx);
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL uncompressGust(
	LPBYTE sbuf,
	DWORD filesize,
	_TCHAR* pszFilenameWithFullpath,
	PINTERNAL_MUSIC im
)
{
	LPBYTE dbuf = (LPBYTE)calloc(filesize * 10, sizeof(BYTE));
	if (!dbuf) {
		OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
		return FALSE;
	}
	FILE* outfp = CreateOrOpenFile(pszFilenameWithFullpath, 
		NULL, NULL, NULL, _T(".dec"), _T("wb"), 0, im->nSeqIdx);
	if (!outfp) {
		OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
		return FALSE;
	}
#ifdef _DEBUG
	FILE* logfp = CreateOrOpenFile(pszFilenameWithFullpath, 
		NULL, NULL, NULL, _T(".txt"), _T("wb"), 0, im->nSeqIdx);
	if (!logfp) {
		OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
		return FALSE;
	}
#endif
	BYTE byte = 0;
	size_t srcIdx = 0;
	LONG dstIdx = 0;
	while (srcIdx < filesize) {
#ifdef _DEBUG
		_ftprintf(logfp, _T("srcIdx: %08X, dstIdx: %08X\r\n"), srcIdx, dstIdx);
#endif
		byte = getbyte(sbuf + srcIdx);
		if (byte == 0xD0) {
			BYTE secondByte = getbyte(sbuf + srcIdx + 1);
			dbuf[dstIdx++] = secondByte;
			srcIdx += 2;
		}
		else if (byte == 0xD4) {
			BYTE secondByte = getbyte(sbuf + srcIdx + 1);
			BYTE thirdByte = getbyte(sbuf + srcIdx + 2);
			BYTE fourthByte = getbyte(sbuf + srcIdx + 3);
			dbuf[dstIdx++] = secondByte;
			dbuf[dstIdx++] = thirdByte;
			dbuf[dstIdx++] = fourthByte;
			dbuf[dstIdx++] = secondByte;
			dbuf[dstIdx++] = thirdByte;
			dbuf[dstIdx++] = fourthByte;
			srcIdx += 5;
		}
		else if (byte == 0xD7) {
			BYTE secondByte = getbyte(sbuf + srcIdx + 1);
			LONG tmp = dstIdx - 0x10;
			if (tmp >= 0) {
				memcpy(dbuf + dstIdx, dbuf + tmp, (size_t)(secondByte + 3));
			}
			else {
				memset(dbuf + dstIdx, 0, (size_t)(secondByte + 3));
			}
			dstIdx += secondByte + 3;
			srcIdx += 2;
		}
		else if (byte == 0xD8) {
			BYTE secondByte = getbyte(sbuf + srcIdx + 1);
			LONG tmp = dstIdx - 0x11;
			if (tmp >= 0) {
				memcpy(dbuf + dstIdx, dbuf + tmp, (size_t)(secondByte + 3));
			}
			else {
				memset(dbuf + dstIdx, 0, (size_t)(secondByte + 3));
			}
			dstIdx += secondByte + 3;
			srcIdx += 2;
		}
		else if (byte == 0xE2) {
			BYTE secondByte = getbyte(sbuf + srcIdx + 1);
			LONG tmp = dstIdx - 0x0F;
			if (tmp >= 0) {
				memcpy(dbuf + dstIdx, dbuf + tmp, (size_t)(secondByte + 3));
			}
			else {
				memset(dbuf + dstIdx, 0, (size_t)(secondByte + 3));
			}
			dstIdx += secondByte + 3;
			srcIdx += 2;
		}
		else if (byte == 0xE3) {
			BYTE secondByte = getbyte(sbuf + srcIdx + 1);
			BYTE thirdByte = getbyte(sbuf + srcIdx + 2);
			LONG tmp = dstIdx - secondByte - 4;
			if (tmp >= 0) {
				memcpy(dbuf + dstIdx, dbuf + tmp, (size_t)(thirdByte + 4));
			}
			else {
				memset(dbuf + dstIdx, 0, (size_t)(thirdByte + 4));
			}
			dstIdx += thirdByte + 4;
			srcIdx += 3;
		}
		else if (byte == 0xE6) {
			WORD secondShort = getword(sbuf + srcIdx + 1);
			BYTE fourthByte = getbyte(sbuf + srcIdx + 3);
			LONG tmp = dstIdx - secondShort - 5;
			if (tmp >= 0) {
				memcpy(dbuf + dstIdx, dbuf + tmp, (size_t)(fourthByte + 5));
			}
			else {
				memset(dbuf + dstIdx, 0, (size_t)(fourthByte + 5));
			}
			dstIdx += fourthByte + 5;
			srcIdx += 4;
		}
		else {
			dbuf[dstIdx++] = byte;
			srcIdx++;
		}
	}
	fwrite(dbuf, (size_t)(dstIdx), 1, outfp);
	FreeAndNull(sbuf);
	FreeAndNull(dbuf);
#ifdef _DEBUG
	FcloseAndNull(logfp);
#endif
	FcloseAndNull(outfp);
	return TRUE;
}
