#include <stdio.h>
#include <string.h>
#include <conio.h>
#define BUFFER_SIZE (1024 * 1024 * 256)
/**
* BIG-SIZE buffer
**/
unsigned char *buff1;
unsigned char *buff2;
/**
* SONNORI Lz77 Xor Key
**/
unsigned short lz77_customkey[] = {
	0xFF21, 0x834F, 0x675F, 0x0034, 0xF237, 0x815F, 0x4765, 0x0233
}; //65313 33615 26463 52 62007 33119 18277 563
//
// NOP file unpack~~~ *^^*
//
void nop_unpack(const char *nopFile)
{
	enum e_noptype {
		NOP_DATA_RAW = 0x00,
		NOP_DATA_LZ77 = 0x01,
		NOP_DATA_DIRECTORY = 0x02,
		NOP_DATA_SONNORI_LZ77 = 0x03
	};
	FILE *fp, *out_fp;
	int off, num, i, j;
	unsigned char key;
	fopen_s(&fp, nopFile, "rb");
	if (!fp)
		return;
	/* the last byte value of NOP file must be 0x12 */
	fseek(fp, -1, SEEK_END);
	if (fgetc(fp) != 0x12) {
		printf_s("* %-15s : 손상된 NOP 파일입니다.\n", nopFile);
		fclose(fp);
		return;
	}
	/* NOP file header */
	fseek(fp, -9, SEEK_END);
	fread(&off, 4, 1, fp);
	fread(&num, 4, 1, fp);
	printf_s("* %-15s : 총 %d개의 데이터를 포함하고 있습니다.\n", nopFile, num);
	for (i = 0; i < num; ++i) {
		unsigned char ? name[256];
		unsigned char ? name_size;
		unsigned char ? type;
		int ? ? ? ? ? ? offset;
		int ? ? ? ? ? ? encode_size;
		int ? ? ? ? ? ? decode_size;
		/* File infomation */
		fseek(fp, off, SEEK_SET);
		fread(&name_size, 1, 1, fp);
		fread(&type, 1, 1, fp);
		fread(&offset, 4, 1, fp);
		fread(&encode_size, 4, 1, fp);
		fread(&decode_size, 4, 1, fp);
		fread(name, 1, name_size + 1, fp);
		off += name_size + 15;
		/**
		* [ Data Type ]
		* 0x00 : Raw
		* 0x01 : Lz77 Compressed
		* 0x02 : Directory
		* 0x03 : SONNORI Lz77 Compressed
		**/
		/* In directory, this value used as file path decrypt key */
		if (type == NOP_DATA_DIRECTORY) {
			key = (char)decode_size;
		}
		else {
			decode_size ^= key;
		}
		/* File path decrypt */
		for (j = 0; j < name_size; ++j)
			name[j] ^= key;
		/* we doesn't have enough buffer size. so we skip this file. */
		if (decode_size > BUFFER_SIZE) {
			printf_s(" -> 실패 : %s ? ? ?\n", name);
			continue;
		}
		/* show current status with percent^^ */
		printf_s("* %0.1f%% : %-68s\r", ((float)i / (float)num * (float)100.0), name);
		switch (type) {
		case NOP_DATA_RAW:
		{
							 fseek(fp, offset, SEEK_SET);
							 fread(buff1, 1, encode_size, fp);
							 fopen_s(&out_fp, name, "wb");
							 if (!out_fp) {
								 printf_s(" -> 실패 : %s ? ? ?\n", name);
								 break;
							 }
							 fwrite(buff1, 1, decode_size, out_fp);
							 fclose(out_fp);
							 break;
		}
		case NOP_DATA_LZ77:
		{
							 ?int bmask, bcnt = 0, size = 0, off, len;
							 ?unsigned short Lz77Info;
							 ?fseek(fp, offset, SEEK_SET);
							 ?fread(buff1, 1, encode_size, fp);
							 ?/* Lz77 uncompress */
							 ?for (j = 0; j < encode_size; bcnt = (bcnt + 1) & 0x07)
							 ?{
								 ?if (!bcnt) {
									 ?bmask = buff1[j++];
								 ?}
								 ?else {
									 ?bmask >>= 1;
								 ?}
								 ?if (bmask & 0x01) {
									 ?Lz77Info = *(unsigned short *)&buff1[j], j += 2;
									 ?off = Lz77Info & 0x0FFF;
									 ?len = (Lz77Info >> 12) + 2;
									 ?memcpy(&buff2[size], &buff2[size - off], len);
									 ?size += len;
								 ?}
								 ?else {
									 ?buff2[size++] = buff1[j++];
								 ?}
							 ?}
							 ?if (size != decode_size) {
								 ?printf_s(" -> 실패 : %s ? ? ?%d != %d\n", name, size, decode_size);
								 ?break;
							 ?}
							 ?fopen_s(&out_fp, name, "wb");
							 ?if (!out_fp) {
								 ?printf_s(" -> 실패 : %s ? ? ?\n", name);
								 ?break;
							 ?}
							 ?fwrite(buff2, 1, decode_size, out_fp);
							 ?fclose(out_fp);
							 ?break;
		}
		case NOP_DATA_DIRECTORY:
		{
								 ? _mkdir(name);
								 ? break;
		}
		case NOP_DATA_SONNORI_LZ77:
		{
									 ?int bmask, bsrcmask, bcnt = 0, size = 0, off, len;
									 ?unsigned short Lz77Info;
									 ?fseek(fp, offset, SEEK_SET);
									 ?fread(buff1, 1, encode_size, fp);
									 ?/* SONNORI Lz77 uncompress */
									 ?for (j = 0; j < encode_size; bcnt = (bcnt + 1) & 0x07)
									 ?{
										 ?if (!bcnt) {
											 ?bmask = bsrcmask = buff1[j++];
											 ?bmask ^= 0xC8;
										 ?}
										 ?else {
											 ?bmask >>= 1;
										 ?}
										 ?if (bmask & 0x01) {
											 ?Lz77Info = *(unsigned short *)&buff1[j], j += 2;
											 ?Lz77Info ^= lz77_customkey[(bsrcmask >> 3) & 0x07];
											 ?off = Lz77Info & 0x0FFF;
											 ?len = (Lz77Info >> 12) + 2;
											 ?memcpy(&buff2[size], &buff2[size - off], len);
											 ?size += len;
										 ?}
										 ?else {
											 ?buff2[size++] = buff1[j++];
										 ?}
									 ?}
									 ?if (size != decode_size) {
										 ?printf_s(" -> 실패 : %s ? ? ?%d != %d\n", name, size, decode_size);
										 ?break;
									 ?}
									 ?fopen_s(&out_fp, name, "wb");
									 ?if (!out_fp) {
										 ?printf_s(" -> 실패 : %s ? ? ?\n", name);
										 ?break;
									 ?}
									 ?fwrite(buff2, 1, decode_size, out_fp);
									 ?fclose(out_fp);
									 ?break;
		}
		default: // unknown type
			printf_s(" -> 실패 : %s ? ? ?\n", name);
			break;
		}
	}
	fclose(fp);
	return;
}
int main(int argc, char *argv[])
{
	/* 콘솔 속성 변경 */
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
	/* 인트로 표시 */
	SetConsoleTitle("NOPUnpack - http://c...content-available-to-author-only...r.com/wdlabyrinth");
	printf_s("o------------------------------------------------------------------------------o");
	printf_s("| W h i t e D a y :: A Labyrinth named school ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|");
	printf_s("o------------------------------------------------------------------------------o");
	printf_s(" 네이버 카페 [화이트데이 게임 - 미궁의 열쇠] http://c...content-available-to-author-only...r.com/wdlabyrinth\n");
	printf_s(" 제작자: 다솜(Naver ID - love947345, MSN - eunju...@hotmail.com)\n");
	printf_s("o------------------------------------------------------------------------------o");
	printf_s(" NOP파일을 풀어줍니다..^^\n");
	printf_s(" 화이트데이가 설치된 폴더에서 실행하시면, 게임실행에 문제가 생길 수 있습니다.\n");
	printf_s(" 계속 하실려면 아무키나 누르세요.\n");
	printf_s("o------------------------------------------------------------------------------o");
	_getch();
	/* 버퍼 메모리 할당 */
	if (!(buff1 = (unsigned char *)malloc(BUFFER_SIZE)) || !(buff2 = (unsigned char *)malloc(BUFFER_SIZE))) {
		printf_s("* 메모리 할당에 실패하였습니다.\n");
		getchar();
		return 0;
	}
	{
		char path[256];
		int i;
		/* 데모버전 NOP 파일 */
		nop_unpack("whiteday.nop");
		/* 데모버전 NOP 패치 파일 */
		for (i = 100; i < 999; ++i) {
			sprintf_s(path, sizeof(path), "patch%02d.nop", i);
			nop_unpack(path);
		}
		/* 정식버전 NOP 패치 파일 */
		for (i = 100; i < 999; ++i) {
			sprintf_s(path, sizeof(path), "whiteday%03d.nop", i);
			nop_unpack(path);
		}
		/* 오!재미 NOP 파일*/
		for (i = 99; i < 999; ++i) {
			sprintf_s(path, sizeof(path), "mod_beanbag%03d.nop", i);
			nop_unpack(path);
		}
	}
	/* 메모리 해제 */
	free(buff2);
	free(buff1);
	printf_s("o------------------------------------------------------------------------------o");
	printf_s(" 모든 작업이 끝났습니다. 종료하실려면 엔터키를 누르세요.\n");
	getchar();
	return 0;