/*
 * This code is released under the Microsoft Public License (MS-PL). See license.txt, below.
 */
#include "stdafx.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)

	<<フォーマット>>
	unsigned long	圧縮前のサイズ
	unsigned char	圧縮フラグ（０＝圧縮、１＝未圧縮）
	unsigned char	データ[未圧縮の数+圧縮の数*2]
	unsigned char	圧縮フラグ（０＝圧縮、１＝未圧縮）
	unsigned char	データ[未圧縮の数+圧縮の数*2]
		:
		:

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

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

	<<圧縮仕様>>
	　Bit  0〜11	… 辞書のアドレス（辞書幅＝4096[Byte]）
	　Bit 13〜15	… データ長
*/
void uncompressLzss(
	unsigned char* sbuf,
	_TCHAR* pszFilenameWithFullpath,
	unsigned long offsetOfSize,
	unsigned long offsetOfPos,
	CompressFlagType compressFlag,
	unsigned long encondingBit,
	CompressSizeType compressSizeType,
	LzssType lzssType,
	_TCHAR* ext,
	int specialFix,
	InternalMusic* im
	)
{
	unsigned long dataIdx = 0;
	unsigned long* offset = 0;
	if(!_tcscmp(ext, _T(".seq")) ||
		!_tcscmp(ext, _T(".sssq"))) {
		dataIdx = im->nSeqIdx;
		offset = im->nSeqOffset;
	}
	for(unsigned long i = 0; i < dataIdx; i++) {
		unsigned long dlen = 0;
		if(compressSizeType == Uncompress) {
			dlen = getlong(sbuf + (offset[i] - offsetOfSize));
		}
		else {
			unsigned long tmpsize = 0;
			unsigned long sl=0;
			unsigned char bits = 0;
			unsigned long bitscnt = 0;
			unsigned long tmpbitscnt = 0;
			unsigned long readsize = 0;

			for(;;) {
				unsigned char* data = sbuf + (offset[i] - offsetOfPos + readsize);
				bits = getbyte(data);
				bitscnt = getBitCount(bits);
				tmpbitscnt = (8 - bitscnt) * 2 + bitscnt;
				// 圧縮されたデータとされていないデータと圧縮フラグのサイズの合計
				readsize += tmpbitscnt + 1;
				unsigned long cnt = 0;
				for(unsigned long idx = 0; idx < tmpbitscnt + 1; idx++) {
					if(getbyte(data + idx) == 0x00) {
						cnt++;
					}
					else {
						break;
					}
				}
				if(cnt == tmpbitscnt + 1) {
					// 圧縮前のサイズ
					readsize = tmpsize + sl;
					break;
				}
				// 圧縮されていないデータのサイズ(圧縮フラグは含まず)
				tmpsize += bitscnt;
				unsigned long 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;
		}
		unsigned char* dbuf = (unsigned char*)malloc(dlen);
		if(dbuf) {
			unsigned long rlen = 0;
			if(lzssType == LzssType1) {
				ps2_lzss(sbuf + (offset[i] - offsetOfPos),
					dbuf, dlen, compressFlag, encondingBit);
			}
			else if(lzssType == LzssType2) {
				ps2_lzss2(sbuf + (offset[i] - offsetOfPos),
					dbuf, dlen, compressFlag, encondingBit);
			}
			if(compressSizeType == Compress) {
				for(int k = 0; k < 65535; k++) {
					unsigned long longbyte = getlong(dbuf + k);
					//トラック終端
					if((longbyte & 0xFFFFFF00) == 0x002FFF00) {
						dlen = k + sizeof(longbyte);
						break;
					}
				}
			}
			if(rlen != dlen) {
//				OutputErrorString(" expect uncompress size %d, real uncompress size %d\n", dlen, rlen);
			}
			FILE* wfp = CreateOrOpenFile(pszFilenameWithFullpath,
				NULL, NULL, ext, _T("wb"), i, dataIdx);
			if(wfp) {
				if(dlen > 8 && specialFix == 1) {
					dbuf[7] = 0x01;
				}
				fwrite(dbuf, dlen, 1, wfp);
				FcloseAndNull(wfp);
			}
			FreeAndNull(dbuf);
		}
		_tprintf(_T("\runcompress %d/%d"), i + 1, dataIdx);
	}
	_tprintf(_T("\n"));
}

void uncompressBrightis(
	unsigned char* sbuf,
	_TCHAR* pszFilenameWithFullpath,
	InternalMusic* im
	)
{
	for(unsigned long a = 0; a < im->nSeqIdx; a++) {
		FILE* outfp = CreateOrOpenFile(pszFilenameWithFullpath, 
			NULL, NULL, _T(".seq"), _T("wb"), a, im->nSeqIdx);
		if(!outfp) {
			_tprintf(_T("No file or already opened:%s\r\n"), pszFilenameWithFullpath);
			return;
		}
#ifdef _DEBUG
		FILE* logfp = CreateOrOpenFile(pszFilenameWithFullpath, 
			NULL, NULL, _T(".txt"), _T("wb"), a, im->nSeqIdx);
		if(!logfp) {
			_tprintf(_T("No file or already opened : %s\r\n"), pszFilenameWithFullpath);
			return;
		}
		_TCHAR logbuf[128];
		memset(logbuf, 0, sizeof(logbuf));
#endif
		unsigned char val1 = getbyte(sbuf + im->nSeqOffset[a] - 2);
		unsigned char val2 = getbyte(sbuf + im->nSeqOffset[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->nSeqOffset[a] + z) == 0x002FFF00) {
				break;
			}
		}
		unsigned char* dbuf = (unsigned char*)calloc(srcFilesize * 10, sizeof(unsigned char));
		if(!dbuf) {
			_tprintf(_T("Could not alloc memory [L:%d]\r\n"), __LINE__);
			return;
		}
		memcpy(dbuf, sbuf + im->nSeqOffset[a], srcIdx);

		while(srcIdx < srcFilesize) {
			unsigned char byte = getbyte(sbuf + im->nSeqOffset[a] + srcIdx);
			unsigned char secondByte = getbyte(sbuf + im->nSeqOffset[a] + srcIdx + 1);
			unsigned char thirdByte = getbyte(sbuf + im->nSeqOffset[a] + srcIdx + 2);
			unsigned char fourthByte = getbyte(sbuf + im->nSeqOffset[a] + srcIdx + 3);
			if(byte == 0 && secondByte == 0 && thirdByte == 0 && fourthByte == 0) {
				break;
			}
			unsigned short offset = (unsigned short)(((byte & 0x1F) << 8) + secondByte);
			size_t tmpdstIdx = dstIdx - offset;
			unsigned long dstCopySize = 0;
			unsigned long srcSize = 0;
			bool bRet = false;

			if(tmpdstIdx > 0 && offset > 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(;;) {
						unsigned char tmpByte = 
							getbyte(sbuf + im->nSeqOffset[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 > offset) {
						memcpy(dbuf + dstIdx, dbuf + tmpdstIdx, offset);
						dstIdx += offset;
						dstCopySize -= offset;
					}
					else {
						memcpy(dbuf + dstIdx, dbuf + tmpdstIdx, dstCopySize);
						break;
					}
				}
#ifdef _DEBUG
				_sntprintf(logbuf, sizeof(logbuf) / sizeof(_TCHAR), 
					_T("src:%04X, %02X %02X %02X %02X, dst:%04X, "), 
					srcIdx, byte, secondByte, thirdByte, fourthByte, dstIdx);
				logbuf[128-1] = 0;
				fwrite(logbuf, _tcslen(logbuf), 2, logfp);
				for(unsigned long i = 0; i < dstCopySize; i++) {
					_sntprintf(logbuf, sizeof(logbuf) / sizeof(_TCHAR), 
						_T("%02X "), getbyte(dbuf + tmpdstIdx + i));
					logbuf[128-1] = 0;
					fwrite(logbuf, sizeof(_TCHAR), _tcslen(logbuf), logfp);
				}
				fwrite(_T("\r\n"), sizeof(_TCHAR), 2, logfp);
#endif
				srcIdx += 2 + srcSize;
				dstIdx += dstCopySize;
				unsigned char tmpByte = getbyte(sbuf + im->nSeqOffset[a] + srcIdx);
				if((tmpByte & 0xF0) == 0x20) {
					srcIdx++;
					unsigned short offset2 = 
						(unsigned short)(((tmpByte & 0x1F) << 8) + getbyte(sbuf + im->nSeqOffset[a] + srcIdx));
					memcpy(dbuf + dstIdx, sbuf + im->nSeqOffset[a] + srcIdx + 1, offset2);
					srcIdx += offset2 + 1;
					dstIdx += offset2;
				}
				else if(0 < tmpByte && tmpByte < 0x20) {
					memcpy(dbuf + dstIdx, sbuf + im->nSeqOffset[a] + srcIdx + 1, tmpByte);
					srcIdx += tmpByte + 1;
					dstIdx += tmpByte;
				}
			}
		}
		fwrite(dbuf, sizeof(unsigned char), dstIdx, outfp);
		FreeAndNull(dbuf);
#ifdef _DEBUG
		FcloseAndNull(logfp);
#endif
		FcloseAndNull(outfp);
		_tprintf(_T("\runcompress %d/%d"), a + 1, im->nSeqIdx);
	}
	_tprintf(_T("\n"));
}

void uncompressGust(
	unsigned char* sbuf,
	unsigned long filesize,
	_TCHAR* pszFilenameWithFullpath,
	InternalMusic* im
)
{
	unsigned char* dbuf = (unsigned char*)calloc(filesize * 10, sizeof(unsigned char));
	if(!dbuf) {
		_tprintf(_T("Could not alloc memory [L:%d]\r\n"), __LINE__);
		return;
	}
	FILE* outfp = CreateOrOpenFile(pszFilenameWithFullpath, 
		NULL, NULL, _T(".dec"), _T("wb"), 0, im->nSeqIdx);
	if(!outfp) {
		_tprintf(_T("No file or already opened : %s\r\n"), pszFilenameWithFullpath);
		return;
	}
#ifdef _DEBUG
	FILE* logfp = CreateOrOpenFile(pszFilenameWithFullpath, 
		NULL, NULL, _T(".txt"), _T("wb"), 0, im->nSeqIdx);
	if(!logfp) {
		_tprintf(_T("No file or already opened : %s\r\n"), pszFilenameWithFullpath);
		return;
	}
	_TCHAR logbuf[128];
	memset(logbuf, 0, sizeof(logbuf));
#endif
	unsigned char byte = 0;
	size_t srcIdx = 0;
	long dstIdx = 0;
	while(srcIdx < filesize) {
#ifdef _DEBUG
		_sntprintf(logbuf, sizeof(logbuf) / sizeof(_TCHAR), 
			_T("srcIdx:%08X, dstIdx:%08X\r\n"), srcIdx, dstIdx);
		logbuf[128-1] = 0;
		fwrite(logbuf, _tcslen(logbuf), 2, logfp);
#endif
		byte = getbyte(sbuf + srcIdx);
		if(byte == 0xD0) {
			unsigned char secondByte = getbyte(sbuf + srcIdx + 1);
			dbuf[dstIdx++] = secondByte;
			srcIdx += 2;
		}
		else if(byte == 0xD4) {
			unsigned char secondByte = getbyte(sbuf + srcIdx + 1);
			unsigned char thirdByte = getbyte(sbuf + srcIdx + 2);
			unsigned char 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) {
			unsigned char 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) {
			unsigned char 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) {
			unsigned char 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) {
			unsigned char secondByte = getbyte(sbuf + srcIdx + 1);
			unsigned char 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) {
			unsigned short secondShort = getword(sbuf + srcIdx + 1);
			unsigned char 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);
}
