#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 */
printf_s("* %-15s : 손상된 NOP 파일입니다.\n", nopFile);
return;
}
/* NOP file header */
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(&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
); 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
); ?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
); ?break;
}
default: // unknown type
printf_s(" -> 실패 : %s ? ? ?\n", name);
break;
}
}
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");
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);
}
}
/* 메모리 해제 */
printf_s("o------------------------------------------------------------------------------o");
printf_s(" 모든 작업이 끝났습니다. 종료하실려면 엔터키를 누르세요.\n");
return 0;
I2luY2x1ZGUgPHN0ZGlvLmg+CiNpbmNsdWRlIDxzdHJpbmcuaD4KI2luY2x1ZGUgPGNvbmlvLmg+CiNkZWZpbmUgQlVGRkVSX1NJWkUgKDEwMjQgKiAxMDI0ICogMjU2KQovKioKKiBCSUctU0laRSBidWZmZXIKKiovCnVuc2lnbmVkIGNoYXIgKmJ1ZmYxOwp1bnNpZ25lZCBjaGFyICpidWZmMjsKLyoqCiogU09OTk9SSSBMejc3IFhvciBLZXkKKiovCnVuc2lnbmVkIHNob3J0IGx6NzdfY3VzdG9ta2V5W10gPSB7CgkweEZGMjEsIDB4ODM0RiwgMHg2NzVGLCAweDAwMzQsIDB4RjIzNywgMHg4MTVGLCAweDQ3NjUsIDB4MDIzMwp9OyAvLzY1MzEzIDMzNjE1IDI2NDYzIDUyIDYyMDA3IDMzMTE5IDE4Mjc3IDU2MwovLwovLyBOT1AgZmlsZSB1bnBhY2t+fn4gKl5eKgovLwp2b2lkIG5vcF91bnBhY2soY29uc3QgY2hhciAqbm9wRmlsZSkKewoJZW51bSBlX25vcHR5cGUgewoJCU5PUF9EQVRBX1JBVyA9IDB4MDAsCgkJTk9QX0RBVEFfTFo3NyA9IDB4MDEsCgkJTk9QX0RBVEFfRElSRUNUT1JZID0gMHgwMiwKCQlOT1BfREFUQV9TT05OT1JJX0xaNzcgPSAweDAzCgl9OwoJRklMRSAqZnAsICpvdXRfZnA7CglpbnQgb2ZmLCBudW0sIGksIGo7Cgl1bnNpZ25lZCBjaGFyIGtleTsKCWZvcGVuX3MoJmZwLCBub3BGaWxlLCAicmIiKTsKCWlmICghZnApCgkJcmV0dXJuOwoJLyogdGhlIGxhc3QgYnl0ZSB2YWx1ZSBvZiBOT1AgZmlsZSBtdXN0IGJlIDB4MTIgKi8KCWZzZWVrKGZwLCAtMSwgU0VFS19FTkQpOwoJaWYgKGZnZXRjKGZwKSAhPSAweDEyKSB7CgkJcHJpbnRmX3MoIiogJS0xNXMgOiDshpDsg4HrkJwgTk9QIO2MjOydvOyeheuLiOuLpC5cbiIsIG5vcEZpbGUpOwoJCWZjbG9zZShmcCk7CgkJcmV0dXJuOwoJfQoJLyogTk9QIGZpbGUgaGVhZGVyICovCglmc2VlayhmcCwgLTksIFNFRUtfRU5EKTsKCWZyZWFkKCZvZmYsIDQsIDEsIGZwKTsKCWZyZWFkKCZudW0sIDQsIDEsIGZwKTsKCXByaW50Zl9zKCIqICUtMTVzIDog7LSdICVk6rCc7J2YIOuNsOydtO2EsOulvCDtj6ztlajtlZjqs6Ag7J6I7Iq164uI64ukLlxuIiwgbm9wRmlsZSwgbnVtKTsKCWZvciAoaSA9IDA7IGkgPCBudW07ICsraSkgewoJCXVuc2lnbmVkIGNoYXIgPyBuYW1lWzI1Nl07CgkJdW5zaWduZWQgY2hhciA/IG5hbWVfc2l6ZTsKCQl1bnNpZ25lZCBjaGFyID8gdHlwZTsKCQlpbnQgPyA/ID8gPyA/ID8gb2Zmc2V0OwoJCWludCA/ID8gPyA/ID8gPyBlbmNvZGVfc2l6ZTsKCQlpbnQgPyA/ID8gPyA/ID8gZGVjb2RlX3NpemU7CgkJLyogRmlsZSBpbmZvbWF0aW9uICovCgkJZnNlZWsoZnAsIG9mZiwgU0VFS19TRVQpOwoJCWZyZWFkKCZuYW1lX3NpemUsIDEsIDEsIGZwKTsKCQlmcmVhZCgmdHlwZSwgMSwgMSwgZnApOwoJCWZyZWFkKCZvZmZzZXQsIDQsIDEsIGZwKTsKCQlmcmVhZCgmZW5jb2RlX3NpemUsIDQsIDEsIGZwKTsKCQlmcmVhZCgmZGVjb2RlX3NpemUsIDQsIDEsIGZwKTsKCQlmcmVhZChuYW1lLCAxLCBuYW1lX3NpemUgKyAxLCBmcCk7CgkJb2ZmICs9IG5hbWVfc2l6ZSArIDE1OwoJCS8qKgoJCSogWyBEYXRhIFR5cGUgXQoJCSogMHgwMCA6IFJhdwoJCSogMHgwMSA6IEx6NzcgQ29tcHJlc3NlZAoJCSogMHgwMiA6IERpcmVjdG9yeQoJCSogMHgwMyA6IFNPTk5PUkkgTHo3NyBDb21wcmVzc2VkCgkJKiovCgkJLyogSW4gZGlyZWN0b3J5LCB0aGlzIHZhbHVlIHVzZWQgYXMgZmlsZSBwYXRoIGRlY3J5cHQga2V5ICovCgkJaWYgKHR5cGUgPT0gTk9QX0RBVEFfRElSRUNUT1JZKSB7CgkJCWtleSA9IChjaGFyKWRlY29kZV9zaXplOwoJCX0KCQllbHNlIHsKCQkJZGVjb2RlX3NpemUgXj0ga2V5OwoJCX0KCQkvKiBGaWxlIHBhdGggZGVjcnlwdCAqLwoJCWZvciAoaiA9IDA7IGogPCBuYW1lX3NpemU7ICsraikKCQkJbmFtZVtqXSBePSBrZXk7CgkJLyogd2UgZG9lc24ndCBoYXZlIGVub3VnaCBidWZmZXIgc2l6ZS4gc28gd2Ugc2tpcCB0aGlzIGZpbGUuICovCgkJaWYgKGRlY29kZV9zaXplID4gQlVGRkVSX1NJWkUpIHsKCQkJcHJpbnRmX3MoIiAtPiDsi6TtjKggOiAlcyA/ID8gP1xuIiwgbmFtZSk7CgkJCWNvbnRpbnVlOwoJCX0KCQkvKiBzaG93IGN1cnJlbnQgc3RhdHVzIHdpdGggcGVyY2VudF5eICovCgkJcHJpbnRmX3MoIiogJTAuMWYlJSA6ICUtNjhzXHIiLCAoKGZsb2F0KWkgLyAoZmxvYXQpbnVtICogKGZsb2F0KTEwMC4wKSwgbmFtZSk7CgkJc3dpdGNoICh0eXBlKSB7CgkJY2FzZSBOT1BfREFUQV9SQVc6CgkJewoJCQkJCQkJIGZzZWVrKGZwLCBvZmZzZXQsIFNFRUtfU0VUKTsKCQkJCQkJCSBmcmVhZChidWZmMSwgMSwgZW5jb2RlX3NpemUsIGZwKTsKCQkJCQkJCSBmb3Blbl9zKCZvdXRfZnAsIG5hbWUsICJ3YiIpOwoJCQkJCQkJIGlmICghb3V0X2ZwKSB7CgkJCQkJCQkJIHByaW50Zl9zKCIgLT4g7Iuk7YyoIDogJXMgPyA/ID9cbiIsIG5hbWUpOwoJCQkJCQkJCSBicmVhazsKCQkJCQkJCSB9CgkJCQkJCQkgZndyaXRlKGJ1ZmYxLCAxLCBkZWNvZGVfc2l6ZSwgb3V0X2ZwKTsKCQkJCQkJCSBmY2xvc2Uob3V0X2ZwKTsKCQkJCQkJCSBicmVhazsKCQl9CgkJY2FzZSBOT1BfREFUQV9MWjc3OgoJCXsKCQkJCQkJCSA/aW50IGJtYXNrLCBiY250ID0gMCwgc2l6ZSA9IDAsIG9mZiwgbGVuOwoJCQkJCQkJID91bnNpZ25lZCBzaG9ydCBMejc3SW5mbzsKCQkJCQkJCSA/ZnNlZWsoZnAsIG9mZnNldCwgU0VFS19TRVQpOwoJCQkJCQkJID9mcmVhZChidWZmMSwgMSwgZW5jb2RlX3NpemUsIGZwKTsKCQkJCQkJCSA/LyogTHo3NyB1bmNvbXByZXNzICovCgkJCQkJCQkgP2ZvciAoaiA9IDA7IGogPCBlbmNvZGVfc2l6ZTsgYmNudCA9IChiY250ICsgMSkgJiAweDA3KQoJCQkJCQkJID97CgkJCQkJCQkJID9pZiAoIWJjbnQpIHsKCQkJCQkJCQkJID9ibWFzayA9IGJ1ZmYxW2orK107CgkJCQkJCQkJID99CgkJCQkJCQkJID9lbHNlIHsKCQkJCQkJCQkJID9ibWFzayA+Pj0gMTsKCQkJCQkJCQkgP30KCQkJCQkJCQkgP2lmIChibWFzayAmIDB4MDEpIHsKCQkJCQkJCQkJID9Mejc3SW5mbyA9ICoodW5zaWduZWQgc2hvcnQgKikmYnVmZjFbal0sIGogKz0gMjsKCQkJCQkJCQkJID9vZmYgPSBMejc3SW5mbyAmIDB4MEZGRjsKCQkJCQkJCQkJID9sZW4gPSAoTHo3N0luZm8gPj4gMTIpICsgMjsKCQkJCQkJCQkJID9tZW1jcHkoJmJ1ZmYyW3NpemVdLCAmYnVmZjJbc2l6ZSAtIG9mZl0sIGxlbik7CgkJCQkJCQkJCSA/c2l6ZSArPSBsZW47CgkJCQkJCQkJID99CgkJCQkJCQkJID9lbHNlIHsKCQkJCQkJCQkJID9idWZmMltzaXplKytdID0gYnVmZjFbaisrXTsKCQkJCQkJCQkgP30KCQkJCQkJCSA/fQoJCQkJCQkJID9pZiAoc2l6ZSAhPSBkZWNvZGVfc2l6ZSkgewoJCQkJCQkJCSA/cHJpbnRmX3MoIiAtPiDsi6TtjKggOiAlcyA/ID8gPyVkICE9ICVkXG4iLCBuYW1lLCBzaXplLCBkZWNvZGVfc2l6ZSk7CgkJCQkJCQkJID9icmVhazsKCQkJCQkJCSA/fQoJCQkJCQkJID9mb3Blbl9zKCZvdXRfZnAsIG5hbWUsICJ3YiIpOwoJCQkJCQkJID9pZiAoIW91dF9mcCkgewoJCQkJCQkJCSA/cHJpbnRmX3MoIiAtPiDsi6TtjKggOiAlcyA/ID8gP1xuIiwgbmFtZSk7CgkJCQkJCQkJID9icmVhazsKCQkJCQkJCSA/fQoJCQkJCQkJID9md3JpdGUoYnVmZjIsIDEsIGRlY29kZV9zaXplLCBvdXRfZnApOwoJCQkJCQkJID9mY2xvc2Uob3V0X2ZwKTsKCQkJCQkJCSA/YnJlYWs7CgkJfQoJCWNhc2UgTk9QX0RBVEFfRElSRUNUT1JZOgoJCXsKCQkJCQkJCQkgPyBfbWtkaXIobmFtZSk7CgkJCQkJCQkJID8gYnJlYWs7CgkJfQoJCWNhc2UgTk9QX0RBVEFfU09OTk9SSV9MWjc3OgoJCXsKCQkJCQkJCQkJID9pbnQgYm1hc2ssIGJzcmNtYXNrLCBiY250ID0gMCwgc2l6ZSA9IDAsIG9mZiwgbGVuOwoJCQkJCQkJCQkgP3Vuc2lnbmVkIHNob3J0IEx6NzdJbmZvOwoJCQkJCQkJCQkgP2ZzZWVrKGZwLCBvZmZzZXQsIFNFRUtfU0VUKTsKCQkJCQkJCQkJID9mcmVhZChidWZmMSwgMSwgZW5jb2RlX3NpemUsIGZwKTsKCQkJCQkJCQkJID8vKiBTT05OT1JJIEx6NzcgdW5jb21wcmVzcyAqLwoJCQkJCQkJCQkgP2ZvciAoaiA9IDA7IGogPCBlbmNvZGVfc2l6ZTsgYmNudCA9IChiY250ICsgMSkgJiAweDA3KQoJCQkJCQkJCQkgP3sKCQkJCQkJCQkJCSA/aWYgKCFiY250KSB7CgkJCQkJCQkJCQkJID9ibWFzayA9IGJzcmNtYXNrID0gYnVmZjFbaisrXTsKCQkJCQkJCQkJCQkgP2JtYXNrIF49IDB4Qzg7CgkJCQkJCQkJCQkgP30KCQkJCQkJCQkJCSA/ZWxzZSB7CgkJCQkJCQkJCQkJID9ibWFzayA+Pj0gMTsKCQkJCQkJCQkJCSA/fQoJCQkJCQkJCQkJID9pZiAoYm1hc2sgJiAweDAxKSB7CgkJCQkJCQkJCQkJID9Mejc3SW5mbyA9ICoodW5zaWduZWQgc2hvcnQgKikmYnVmZjFbal0sIGogKz0gMjsKCQkJCQkJCQkJCQkgP0x6NzdJbmZvIF49IGx6NzdfY3VzdG9ta2V5Wyhic3JjbWFzayA+PiAzKSAmIDB4MDddOwoJCQkJCQkJCQkJCSA/b2ZmID0gTHo3N0luZm8gJiAweDBGRkY7CgkJCQkJCQkJCQkJID9sZW4gPSAoTHo3N0luZm8gPj4gMTIpICsgMjsKCQkJCQkJCQkJCQkgP21lbWNweSgmYnVmZjJbc2l6ZV0sICZidWZmMltzaXplIC0gb2ZmXSwgbGVuKTsKCQkJCQkJCQkJCQkgP3NpemUgKz0gbGVuOwoJCQkJCQkJCQkJID99CgkJCQkJCQkJCQkgP2Vsc2UgewoJCQkJCQkJCQkJCSA/YnVmZjJbc2l6ZSsrXSA9IGJ1ZmYxW2orK107CgkJCQkJCQkJCQkgP30KCQkJCQkJCQkJID99CgkJCQkJCQkJCSA/aWYgKHNpemUgIT0gZGVjb2RlX3NpemUpIHsKCQkJCQkJCQkJCSA/cHJpbnRmX3MoIiAtPiDsi6TtjKggOiAlcyA/ID8gPyVkICE9ICVkXG4iLCBuYW1lLCBzaXplLCBkZWNvZGVfc2l6ZSk7CgkJCQkJCQkJCQkgP2JyZWFrOwoJCQkJCQkJCQkgP30KCQkJCQkJCQkJID9mb3Blbl9zKCZvdXRfZnAsIG5hbWUsICJ3YiIpOwoJCQkJCQkJCQkgP2lmICghb3V0X2ZwKSB7CgkJCQkJCQkJCQkgP3ByaW50Zl9zKCIgLT4g7Iuk7YyoIDogJXMgPyA/ID9cbiIsIG5hbWUpOwoJCQkJCQkJCQkJID9icmVhazsKCQkJCQkJCQkJID99CgkJCQkJCQkJCSA/ZndyaXRlKGJ1ZmYyLCAxLCBkZWNvZGVfc2l6ZSwgb3V0X2ZwKTsKCQkJCQkJCQkJID9mY2xvc2Uob3V0X2ZwKTsKCQkJCQkJCQkJID9icmVhazsKCQl9CgkJZGVmYXVsdDogLy8gdW5rbm93biB0eXBlCgkJCXByaW50Zl9zKCIgLT4g7Iuk7YyoIDogJXMgPyA/ID9cbiIsIG5hbWUpOwoJCQlicmVhazsKCQl9Cgl9CglmY2xvc2UoZnApOwoJcmV0dXJuOwp9CmludCBtYWluKGludCBhcmdjLCBjaGFyICphcmd2W10pCnsKCS8qIOy9mOyGlCDsho3shLEg67OA6rK9ICovCglTZXRDb25zb2xlVGV4dEF0dHJpYnV0ZShHZXRTdGRIYW5kbGUoU1REX09VVFBVVF9IQU5ETEUpLCBGT1JFR1JPVU5EX1JFRCB8IEZPUkVHUk9VTkRfR1JFRU4gfCBGT1JFR1JPVU5EX0JMVUUgfCBGT1JFR1JPVU5EX0lOVEVOU0lUWSk7CgkvKiDsnbjtirjroZwg7ZGc7IucICovCglTZXRDb25zb2xlVGl0bGUoIk5PUFVucGFjayAtIGh0dHA6Ly9jLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5yLmNvbS93ZGxhYnlyaW50aCIpOwoJcHJpbnRmX3MoIm8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1vIik7CglwcmludGZfcygifCBXIGggaSB0IGUgRCBhIHkgOjogQSBMYWJ5cmludGggbmFtZWQgc2Nob29sID8gPyA/ID8gPyA/ID8gPyA/ID8gPyA/ID8gPyA/ID8gP3wiKTsKCXByaW50Zl9zKCJvLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tbyIpOwoJcHJpbnRmX3MoIiDrhKTsnbTrsoQg7Lm07Y6YIFvtmZTsnbTtirjrjbDsnbQg6rKM7J6EIC0g66+46raB7J2YIOyXtOyHoF0gaHR0cDovL2MuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLnIuY29tL3dkbGFieXJpbnRoXG4iKTsKCXByaW50Zl9zKCIg7KCc7J6R7J6QOiDri6TshpwoTmF2ZXIgSUQgLSBsb3ZlOTQ3MzQ1LCBNU04gLSBldW5qdS4uLkBob3RtYWlsLmNvbSlcbiIpOwoJcHJpbnRmX3MoIm8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1vIik7CglwcmludGZfcygiIE5PUO2MjOydvOydhCDtkoDslrTspI3ri4jri6QuLl5eXG4iKTsKCXByaW50Zl9zKCIg7ZmU7J207Yq4642w7J206rCAIOyEpOy5mOuQnCDtj7TrjZTsl5DshJwg7Iuk7ZaJ7ZWY7Iuc66m0LCDqsozsnoTsi6Ttlonsl5Ag66y47KCc6rCAIOyDneq4uCDsiJgg7J6I7Iq164uI64ukLlxuIik7CglwcmludGZfcygiIOqzhOyGjSDtlZjsi6TroKTrqbQg7JWE66y07YKk64KYIOuIhOultOyEuOyalC5cbiIpOwoJcHJpbnRmX3MoIm8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1vIik7CglfZ2V0Y2goKTsKCS8qIOuyhO2NvCDrqZTrqqjrpqwg7ZWg64u5ICovCglpZiAoIShidWZmMSA9ICh1bnNpZ25lZCBjaGFyICopbWFsbG9jKEJVRkZFUl9TSVpFKSkgfHwgIShidWZmMiA9ICh1bnNpZ25lZCBjaGFyICopbWFsbG9jKEJVRkZFUl9TSVpFKSkpIHsKCQlwcmludGZfcygiKiDrqZTrqqjrpqwg7ZWg64u57JeQIOyLpO2MqO2VmOyYgOyKteuLiOuLpC5cbiIpOwoJCWdldGNoYXIoKTsKCQlyZXR1cm4gMDsKCX0KCXsKCQljaGFyIHBhdGhbMjU2XTsKCQlpbnQgaTsKCQkvKiDrjbDrqqjrsoTsoIQgTk9QIO2MjOydvCAqLwoJCW5vcF91bnBhY2soIndoaXRlZGF5Lm5vcCIpOwoJCS8qIOuNsOuqqOuyhOyghCBOT1Ag7Yyo7LmYIO2MjOydvCAqLwoJCWZvciAoaSA9IDEwMDsgaSA8IDk5OTsgKytpKSB7CgkJCXNwcmludGZfcyhwYXRoLCBzaXplb2YocGF0aCksICJwYXRjaCUwMmQubm9wIiwgaSk7CgkJCW5vcF91bnBhY2socGF0aCk7CgkJfQoJCS8qIOygleyLneuyhOyghCBOT1Ag7Yyo7LmYIO2MjOydvCAqLwoJCWZvciAoaSA9IDEwMDsgaSA8IDk5OTsgKytpKSB7CgkJCXNwcmludGZfcyhwYXRoLCBzaXplb2YocGF0aCksICJ3aGl0ZWRheSUwM2Qubm9wIiwgaSk7CgkJCW5vcF91bnBhY2socGF0aCk7CgkJfQoJCS8qIOyYpCHsnqzrr7ggTk9QIO2MjOydvCovCgkJZm9yIChpID0gOTk7IGkgPCA5OTk7ICsraSkgewoJCQlzcHJpbnRmX3MocGF0aCwgc2l6ZW9mKHBhdGgpLCAibW9kX2JlYW5iYWclMDNkLm5vcCIsIGkpOwoJCQlub3BfdW5wYWNrKHBhdGgpOwoJCX0KCX0KCS8qIOuplOuqqOumrCDtlbTsoJwgKi8KCWZyZWUoYnVmZjIpOwoJZnJlZShidWZmMSk7CglwcmludGZfcygiby0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLW8iKTsKCXByaW50Zl9zKCIg66qo65OgIOyekeyXheydtCDrgZ3rgqzsirXri4jri6QuIOyiheujjO2VmOyLpOugpOuptCDsl5TthLDtgqTrpbwg64iE66W07IS47JqULlxuIik7CglnZXRjaGFyKCk7CglyZXR1cm4gMDs=