#include <windows.h>
#include <tchar.h>
#include <crass_types.h>
#include <acui.h>
#include <cui.h>
#include <package.h>
#include <resource.h>
#include <cui_error.h>
#include <utility.h>
#include <stdio.h>
#include <zlib.h>
#include "blowfish.h"

/* TODO
 * 1. 加入更多游戏的支持
 * 2. 加入前导xor解密
 * 3. mov的xor解密key来自安装时的cd-key，看似需要读取注册表获得key，实际上可以probe出来（"RIFF" xor encrypt dword）
 */

/* 接口数据结构: 表示cui插件的一般信息 */
struct acui_information Musica_cui_information = {
	_T("minori"),			/* copyright */
	_T("Media Unification System Interpreter and Compiler Authorization"),	/* system */
	_T(".paz system.dat"),	/* package */
	_T("0.3.4"),			/* revision */
	_T("痴漢公賊"),			/* author */
	_T("2008-5-31 13:27"),	/* date */
	NULL,					/* notion */
	ACUI_ATTRIBUTE_LEVEL_DEVELOP
};

/* 所有的封包特定的数据结构都要放在这个#pragma段里 */
#pragma pack (1)
typedef struct {
	u8 byte[32];
} paz_header_t;

typedef struct {
	s8 *name;
	u64 offset;
	u32 uncomprlen;
	u32 comprlen;
	u32 decrypt_length;	/* 适应有对齐要求的加密算法（bf要求8字节对齐） */
	u16 is_compressed;	/* 0 - 无压缩; 1 - zlib压缩；etc - 未定义 */
	u16 reserved;
} paz_entry_t;
#pragma pack ()

typedef struct {
	s8 name[MAX_PATH];
	u32 offset;
	u32 comprlen;
	u32 uncomprlen;
	u32 decrypt_length;
	u16 is_compressed;
	u16 reserved;
} my_paz_entry_t;

struct paz_blowfish_list {
	const char *name;
	struct paz_blowfish_key *key;
	int no_header;
	int do_xor;
	BYTE xor_code;
};

struct paz_blowfish_key {
	const char *paz_name;
	DWORD key_len;
	unsigned char key[2][56];	/* 第一组用作解密索引段；第二组用于解密资源数据 */
	struct paz_blowfish_list *list;
};

static BYTE ef_latter_mov_rc4table[256];
static struct paz_blowfish_key ef_latter_paz_blowfish_key[] = {
	{
		"scr", 
		32,
		{
			{
				0x07, 0x81, 0x2b, 0x53, 0xaf, 0xa3, 0x2b, 0x3b, 0x2d, 0xa8, 0x5a, 0xbb, 0x5c, 0x50, 0x50, 0x8b, 
				0xf3, 0xcd, 0xc3, 0x23, 0x84, 0xf4, 0x9f, 0xe8, 0x24, 0xfb, 0x74, 0xa7, 0x46, 0xd9, 0x1b, 0x1b, 
			},
			{
				0xd3, 0xe0, 0x5d, 0x33, 0x9f, 0x4e, 0xc7, 0x9d, 0xcb, 0xb4, 0x18, 0xfe, 0xbd, 0xca, 0x7d, 0x1b, 
				0x36, 0x45, 0xcd, 0x47, 0x31, 0xe3, 0x2a, 0x2c, 0xf1, 0x04, 0x5a, 0xe5, 0x28, 0x09, 0xab, 0xa0, 
			}
		},
	},
	{
		"bg",
		32,
		{
			{
				0x1a, 0x59, 0x52, 0xd9, 0x1e, 0xe4, 0xda, 0x31, 0xcf, 0xc6, 0xa0, 0xa7, 0x94, 0xe5, 0x41, 0xa6, 
				0xbd, 0x37, 0xbb, 0xdf, 0xd0, 0xed, 0x4b, 0x3e, 0xab, 0x8d, 0x4e, 0x1a, 0x9b, 0xf6, 0xc5, 0x8b, 
			},
			{
				0x9d, 0xfd, 0xe2, 0x56, 0xe0, 0x70, 0x79, 0x4e, 0x3f, 0x46, 0x8d, 0x49, 0xc6, 0x68, 0x22, 0x32, 
				0x00, 0x2d, 0x7b, 0x0a, 0x47, 0x2c, 0x90, 0x17, 0x9b, 0x44, 0x0b, 0x61, 0xe1, 0xd9, 0x98, 0x1f, 
			}
		},
	},
	{
		"unknown0", 
		32,
		{
			{
				0x56, 0x2b, 0xe5, 0x8d, 0x06, 0x51, 0xfc, 0xeb, 0x4e, 0x5d, 0xa8, 0xf5, 0x36, 0x6c, 0xb0, 0xc4, 
				0xd2, 0xf8, 0xfe, 0x19, 0xa1, 0x57, 0x93, 0x25, 0x07, 0xd6, 0xb1, 0xaa, 0x3f, 0xb1, 0xe2, 0x87, 
			},
			{
				0x49, 0xfb, 0xb7, 0x7a, 0x16, 0xdc, 0x51, 0x5a, 0x4a, 0xe8, 0x48, 0xea, 0xa0, 0x69, 0xe5, 0xbf, 
				0xd6, 0xcc, 0x3d, 0xb3, 0x6a, 0xea, 0xfa, 0x09, 0x02, 0x82, 0xd5, 0xff, 0xb6, 0x59, 0x3b, 0x4c, 
			}
		},
	},
	{		
		"st",
		32,
		{
			{
				0x09, 0x55, 0x97, 0x79, 0xd4, 0xe7, 0x0d, 0x93, 0x02, 0xe1, 0x9c, 0x47, 0x97, 0x81, 0xb5, 0x50, 
				0xae, 0x9a, 0x56, 0xde, 0xfc, 0x83, 0xe7, 0x4d, 0xfd, 0x71, 0xbe, 0x14, 0xfd, 0x27, 0x9b, 0xe1, 
			},
			{
				0xa2, 0xcc, 0xd4, 0xf8, 0xb6, 0xf7, 0xc9, 0x2f, 0x91, 0x28, 0xe1, 0x2d, 0x91, 0x8f, 0x71, 0xc7, 
				0x75, 0x01, 0x04, 0x47, 0xc8, 0x26, 0x2f, 0x3a, 0xe7, 0x9d, 0x06, 0xda, 0x32, 0x16, 0xf6, 0x1e, 
			},
		},
	},
	{		
		"sys",
		32,
		{
			{
				0xdf, 0x74, 0xcf, 0x44, 0x3a, 0xd5, 0x54, 0x0e, 0xb9, 0x09, 0x52, 0xe4, 0x0f, 0xb0, 0x88, 0x83, 
				0x0a, 0x40, 0x26, 0x06, 0x2e, 0xa0, 0x66, 0x56, 0x51, 0xc9, 0x16, 0x54, 0xd1, 0xdb, 0xe5, 0xc3, 
			},
			{
				0x7a, 0x87, 0x77, 0x86, 0x8f, 0xb0, 0xa2, 0xc3, 0x9e, 0xcd, 0x85, 0x16, 0xa3, 0x06, 0xf3, 0xd1, 
				0x58, 0x23, 0x57, 0xb4, 0x34, 0x90, 0xed, 0x41, 0xf0, 0x5f, 0x40, 0xb8, 0x3b, 0x0c, 0x81, 0x1c, 
			}
		},
	},
	{
		"bgm",
		32,
		{
			{
				0xfc, 0xa7, 0x93, 0x36, 0x69, 0x34, 0x61, 0x41, 0x0f, 0x10, 0x88, 0xba, 0x11, 0x9e, 0x50, 0x3e, 
				0xc8, 0x5f, 0x91, 0x89, 0xb4, 0xa2, 0x7e, 0x67, 0x33, 0xa5, 0x5d, 0x2f, 0xa0, 0x07, 0xf7, 0x9a, 
			},
			{
				0x73, 0x84, 0x77, 0x7f, 0x4d, 0xfc, 0x9a, 0x95, 0xf6, 0x0a, 0xe6, 0xcd, 0x50, 0x9a, 0xaf, 0x8f, 
				0x5e, 0xda, 0x8d, 0x02, 0x12, 0x0a, 0x6c, 0xb7, 0x40, 0xc5, 0x23, 0xf5, 0x07, 0x40, 0x28, 0x58, 
			}
		},
	},
	{
		"voice",
		32,
		{
			{
				0x00, 0x95, 0xfe, 0xf8, 0x45, 0x42, 0x0d, 0xcb, 0xff, 0x3f, 0x56, 0x01, 0x17, 0xd4, 0x26, 0x32, 
				0xd1, 0x2c, 0x70, 0xa7, 0xfe, 0x0d, 0xb8, 0xc4, 0x4d, 0xa2, 0x63, 0x97, 0x6e, 0xed, 0xe2, 0x27, 
			},
			{
				0x7d, 0xa1, 0x2b, 0xe2, 0x3c, 0x15, 0x3d, 0x2d, 0x10, 0x7c, 0x22, 0xa4, 0x73, 0x69, 0x05, 0x4a, 
				0xa0, 0x18, 0xa0, 0x3c, 0xf5, 0x83, 0xea, 0x85, 0x22, 0xae, 0x9b, 0x3b, 0x63, 0xef, 0xf8, 0xaf, 
			}
		},
	},
	{
		"se",
		32,
		{
			{
				0xd2, 0x30, 0xe2, 0xec, 0xbb, 0x90, 0x2a, 0x53, 0xab, 0x1d, 0xa5, 0xe6, 0xf5, 0xb4, 0xd9, 0x62, 
				0xee, 0xb6, 0x6e, 0x83, 0xfa, 0x6b, 0xdd, 0x54, 0x86, 0x3c, 0xa5, 0xad, 0x06, 0x01, 0xf1, 0x11, 
			},
			{
				0x83, 0x56, 0xe5, 0xb1, 0x83, 0x64, 0x38, 0x7d, 0x97, 0x1c, 0x44, 0xfc, 0x17, 0x34, 0xc2, 0x93, 
				0xf3, 0x7a, 0x43, 0x6d, 0x90, 0xf5, 0xce, 0x9e, 0xcd, 0x12, 0x23, 0xf6, 0x79, 0x91, 0x2c, 0x51, 
			}
		},
	},
	{
		"unknown1",
		32,
		{
			{
				0x5e, 0x47, 0x42, 0xa1, 0xca, 0xed, 0x4f, 0xef, 0xbb, 0x8a, 0xe1, 0xc3, 0x8d, 0x2a, 0xd2, 0x0a, 
				0x28, 0xd6, 0x4a, 0x0c, 0xc9, 0x14, 0x6c, 0x5a, 0xaf, 0x67, 0xd5, 0x4f, 0xed, 0x76, 0xa9, 0xb5, 
			},
			{
				0x14, 0x73, 0x7f, 0x42, 0xeb, 0x74, 0x70, 0x63, 0x81, 0x79, 0xc1, 0xca, 0x83, 0x6e, 0x57, 0x8e, 
				0x2c, 0x6f, 0xc5, 0x76, 0xce, 0xdd, 0x5a, 0x4f, 0xde, 0xcb, 0xe4, 0x0f, 0xd9, 0x88, 0xb0, 0xc2, 
			}
		},
	},
	{
		"mov",
		32,
		{
			{
				0xc2, 0xea, 0xff, 0xab, 0x99, 0xce, 0xe2, 0x4b, 0x9a, 0xa5, 0xfd, 0xee, 0x5a, 0xca, 0x77, 0xca, 
				0x6a, 0xf3, 0x71, 0x15, 0xd9, 0x14, 0x48, 0x37, 0xd3, 0x22, 0x43, 0xd1, 0xbb, 0x36, 0x72, 0x2f, 
			},
			{
				0xa6, 0x11, 0x55, 0x3d, 0xed, 0xdc, 0xde, 0x84, 0x15, 0xce, 0xe6, 0x97, 0x44, 0x7b, 0x6e, 0xb3, 
				0x3f, 0xe5, 0x5f, 0x1e, 0x19, 0x3d, 0x13, 0xaf, 0x88, 0x2e, 0x55, 0xff, 0xa1, 0x86, 0x70, 0xcb, 
			}
		},
	},
	{		
		NULL,
		0,
	}
};

static struct paz_blowfish_key ef_lttg_paz_blowfish_key[] = {
	{
		"scr", 
		32,
		{
			{
				0x45, 0x65, 0xce, 0xc0, 0x80, 0x34, 0xc3, 0x20, 0x00, 0x83, 0x07, 0xce, 0x4f, 0x4f, 0xe3, 0x7f, 
				0x39, 0xd7, 0xd3, 0x17, 0xd0, 0x43, 0xa3, 0xab, 0x5c, 0x96, 0x67, 0x04, 0x71, 0x9a, 0x53, 0x55, 
			},
			{
				0xed, 0x8c, 0x25, 0xf4, 0xfc, 0xcf, 0x86, 0x79, 0x9b, 0x2a, 0x4f, 0x13, 0x1a, 0xb1, 0xd2, 0xc2, 
				0xae, 0x5b, 0x52, 0xe2, 0xfb, 0xc4, 0x0e, 0x41, 0x07, 0x9f, 0x1b, 0xb7, 0x30, 0x62, 0xf7, 0xb9, 
			}
		},
	},
	{
		"bg",
		32,
		{
			{
				0xa7, 0xf9, 0xfa, 0x2f, 0xc6, 0xd0, 0xc4, 0xff, 0xee, 0x38, 0xf6, 0x66, 0x40, 0xc8, 0x8e, 0xe7, 
				0xfa, 0xe5, 0x77, 0xa0, 0x2a, 0xb3, 0x8c, 0xb2, 0x20, 0x06, 0x63, 0x7d, 0xe5, 0x12, 0x37, 0x2e, 
			},
			{
				0xb4, 0xa9, 0x8c, 0xac, 0x4b, 0x5e, 0x4e, 0x75, 0x58, 0x7d, 0xc9, 0xc6, 0xe0, 0x02, 0x4d, 0xa3, 
				0xff, 0xcf, 0xb4, 0x93, 0x72, 0x4e, 0xa7, 0xde, 0xfe, 0xe6, 0xea, 0x69, 0x41, 0x12, 0x2e, 0x91, 
			}
		},
	},
	{
		"unknown0", 
		32,
		{
			{
				0x71, 0x1a, 0xdc, 0xd5, 0x65, 0x91, 0x98, 0xa4, 0x58, 0xee, 0xe3, 0x2c, 0x09, 0x43, 0xf3, 0xb8, 
				0x5b, 0xe3, 0x0e, 0x4f, 0x9b, 0x89, 0x3c, 0x74, 0x59, 0xb1, 0xbe, 0xa1, 0xb3, 0x74, 0x7c, 0xab, 
			},
			{
				0x3e, 0xb1, 0x2e, 0x51, 0x0d, 0x03, 0x6d, 0xd1, 0x61, 0x53, 0x12, 0x32, 0x5c, 0xa3, 0x39, 0xff, 
				0x44, 0x37, 0x89, 0xfc, 0x09, 0x3b, 0x5e, 0xe2, 0x24, 0xf2, 0xc2, 0xa0, 0x3e, 0x8d, 0xcf, 0x0d, 
			}
		},
	},
	{		
		"st",
		32,
		{
			{
				0x3a, 0x2e, 0xfa, 0x06, 0x0f, 0x44, 0x7b, 0x75, 0xb4, 0x71, 0x47, 0x2b, 0x38, 0xa5, 0xa2, 0x08, 
				0x47, 0x48, 0x16, 0x9c, 0x7d, 0x5e, 0xff, 0x4a, 0x3b, 0x12, 0x6c, 0xcd, 0xaa, 0x27, 0x32, 0x73, 
			},
			{
				0x66, 0x2c, 0x73, 0xc8, 0x8b, 0x4f, 0xb1, 0x2b, 0x52, 0x72, 0xbd, 0xcf, 0xa8, 0x94, 0x1a, 0x9a, 
				0x37, 0x95, 0x5f, 0xc2, 0x3f, 0xfb, 0xf5, 0xfb, 0x30, 0x93, 0x65, 0x2f, 0x52, 0xed, 0xc2, 0x8c, 
			}
		},
	},
	{		
		"sys",
		32,
		{
			{
				0x79, 0xd1, 0x11, 0x03, 0x8e, 0xbd, 0x16, 0xb2, 0xb5, 0xc1, 0x84, 0xd7, 0x51, 0x23, 0x30, 0x9b, 
				0x25, 0x6d, 0x85, 0xed, 0x60, 0x37, 0x0c, 0x4b, 0xc9, 0x98, 0x71, 0x41, 0x29, 0xfd, 0xef, 0x08, 
			},
			{
				0xc0, 0x1b, 0x4b, 0x8e, 0x32, 0xe8, 0xde, 0x04, 0xae, 0xae, 0xde, 0xb2, 0x1b, 0x99, 0x22, 0xb6, 
				0x03, 0x3b, 0x52, 0xa5, 0x38, 0x91, 0xc8, 0xf5, 0x16, 0x7f, 0x13, 0xfc, 0x1c, 0xa9, 0xa8, 0xf2, 
			}
		},
	},
	{
		"bgm",
		32,
		{
			{
				0x9f, 0x1a, 0xbd, 0x8c, 0xe0, 0x50, 0x54, 0x72, 0xcc, 0xf2, 0x10, 0xa4, 0x9b, 0x67, 0x98, 0xfa, 
				0x44, 0xf6, 0x7f, 0xf7, 0x18, 0xa5, 0x3c, 0xb9, 0xdb, 0x83, 0x25, 0x62, 0x74, 0xbe, 0x11, 0x34, 
			},
			{
				0x98, 0x36, 0x3e, 0x37, 0xef, 0xb4, 0xf5, 0xf1, 0x85, 0x98, 0xd2, 0xb6, 0x27, 0x66, 0x0d, 0xef, 
				0x06, 0x2e, 0x4c, 0xde, 0x28, 0x5e, 0x66, 0x6e, 0x1e, 0x0b, 0x10, 0xd3, 0x48, 0x6f, 0x2a, 0x89, 
			}
		},
	},
	{
		"voice",
		32,
		{
			{
				0xcd, 0x6d, 0x09, 0x59, 0xf7, 0xc9, 0x63, 0x6e, 0xdc, 0x4f, 0xc5, 0xbb, 0x23, 0x53, 0xbc, 0x88, 
				0xf5, 0x6f, 0x28, 0x91, 0x65, 0x94, 0xeb, 0x92, 0xd9, 0x98, 0x45, 0x1c, 0xfa, 0x0a, 0xd8, 0xcc, 
			},
			{
				0x90, 0xf5, 0xf6, 0xc8, 0x50, 0x0b, 0xe9, 0xaa, 0x92, 0xfb, 0xc2, 0x9b, 0x9a, 0x44, 0x62, 0x93, 
				0xf2, 0x92, 0xd7, 0xe6, 0x2b, 0x30, 0x9e, 0x68, 0x4e, 0xa1, 0x1a, 0x6e, 0xcb, 0x82, 0xa3, 0x52, 
			}
		},
	},
	{
		"se",
		32,
		{
			{
				0x7e, 0x2a, 0x5a, 0xfe, 0xc5, 0xb2, 0x41, 0x9a, 0x4a, 0x61, 0x2b, 0x43, 0x6e, 0x80, 0xc7, 0xfd, 
				0xf5, 0x31, 0xcc, 0x0f, 0xb6, 0xec, 0x97, 0x94, 0xa3, 0x25, 0x91, 0xee, 0xa2, 0xf6, 0x6e, 0xcc, 
			},
			{
				0xca, 0x03, 0xf6, 0x6a, 0x49, 0x99, 0x9b, 0xbf, 0xed, 0x81, 0xaf, 0x5a, 0x0b, 0xc5, 0x60, 0x54, 
				0x99, 0x4d, 0xcd, 0xf7, 0xd2, 0x3e, 0x99, 0x3c, 0xf7, 0x9d, 0x91, 0xb2, 0xb1, 0xb7, 0xdf, 0xcf, 
			}
		},
	},
	{
		"unknown1",
		32,
		{
			{
				0x01, 0xf9, 0xe2, 0xed, 0x5c, 0x79, 0x95, 0x1c, 0xf4, 0x3a, 0x31, 0xdb, 0xdc, 0x72, 0x65, 0xee, 
				0x09, 0x61, 0xd7, 0xe6, 0x90, 0xa5, 0x46, 0x6a, 0x7a, 0xe5, 0x40, 0x5d, 0xff, 0xe9, 0x1e, 0xaa, 
			},
			{
				0xfe, 0x7e, 0x04, 0xc2, 0xfb, 0x12, 0x95, 0xf9, 0x1b, 0x61, 0x68, 0x33, 0x8e, 0xc0, 0x33, 0x1c, 
				0x18, 0x52, 0x26, 0x9e, 0xf0, 0xe7, 0x4d, 0x18, 0x8b, 0xa0, 0x4e, 0x27, 0x3e, 0x2b, 0x3e, 0x03, 
			}
		},
	},
	{
		"mov",
		32,
		{
			{
				0xb6, 0x7f, 0x02, 0xf5, 0x8e, 0x06, 0x38, 0x32, 0xa8, 0x47, 0x95, 0x08, 0xb7, 0x79, 0x37, 0x53, 
				0x49, 0xa3, 0x63, 0x30, 0xf9, 0x9e, 0x7d, 0x61, 0x72, 0x77, 0xd1, 0xb2, 0xcc, 0xce, 0x7f, 0x06, 
			},
			{
				0x76, 0x41, 0xf3, 0x79, 0x5a, 0xb4, 0x51, 0x2b, 0xda, 0x0b, 0x75, 0xc3, 0xe8, 0x7a, 0xae, 0x8e, 
				0xc2, 0xdc, 0x3e, 0x48, 0xd9, 0x35, 0x8f, 0x2e, 0xd4, 0x50, 0x79, 0x9d, 0xef, 0x44, 0xe4, 0x40, 
			}
		},
	},
	{		
		NULL,
		0,
	}
};

static struct paz_blowfish_key ef_ltt_trial_paz_blowfish_key[] = {
	{
		"scr", 
		32,
		{
			{
				0xdc, 0x2e, 0x7d, 0x0a, 0xfd, 0x4b, 0x5f, 0x05, 0x11, 0x4a, 0x88, 0x03, 0x8f, 0x2e, 0xc6, 0xc1, 
				0x9c, 0x75, 0x86, 0xc4, 0x55, 0x3a, 0xd9, 0xef, 0x16, 0x65, 0xff, 0xeb, 0xa0, 0x1f, 0x04, 0xd2, 
			},
			{
				0x90, 0xe2, 0xc3, 0xdb, 0xfb, 0x00, 0x27, 0xa5, 0x20, 0x10, 0x07, 0x7b, 0x5a, 0x55, 0x0c, 0x15, 
				0x11, 0x93, 0x83, 0x37, 0xb3, 0x8c, 0xcb, 0x5c, 0xf4, 0x69, 0xba, 0xef, 0x2e, 0xa1, 0xff, 0x90, 
			}
		},
	},
	{
		"bg",
		32,
		{
			{
				0xbc, 0x53, 0x5e, 0x1c, 0xdb, 0x72, 0x77, 0x74, 0x77, 0xd5, 0xa6, 0x22, 0x7e, 0x2c, 0x79, 0x3e, 
				0x83, 0xfb, 0x2a, 0x06, 0xe8, 0x98, 0x1f, 0xb4, 0xf2, 0x96, 0x11, 0xa5, 0xdb, 0x91, 0x60, 0x3d, 
			},
			{
				0x62, 0x39, 0x49, 0x78, 0xc2, 0x64, 0xa7, 0xe6, 0x5f, 0x89, 0x6e, 0xba, 0xef, 0x2f, 0x2b, 0x4a, 
				0x72, 0x68, 0xd0, 0x63, 0x30, 0xac, 0xfa, 0xbc, 0xe5, 0xe9, 0xb3, 0x0c, 0x52, 0xed, 0x5c, 0xac, 
			}
		},
	},
	{
		"unknown0", 
		32,
		{
			{
				0xc4, 0x7b, 0x79, 0x2e, 0x4e, 0xcb, 0x0c, 0xda, 0x24, 0x2d, 0x4b, 0xf5, 0xe4, 0x06, 0xe6, 0x87, 
				0x82, 0xbf, 0x52, 0x28, 0x30, 0x26, 0xf2, 0xc3, 0x79, 0x13, 0xf6, 0x4d, 0x42, 0xfb, 0x0e, 0x60, 
			},
			{
				0xc4, 0x85, 0xea, 0x18, 0x8c, 0x57, 0x8c, 0x65, 0x54, 0x07, 0x25, 0xc2, 0x8b, 0xab, 0x95, 0x08, 
				0xff, 0x96, 0xcb, 0xb8, 0x01, 0x18, 0xc6, 0x42, 0x98, 0x98, 0x25, 0x44, 0xb4, 0x1a, 0x0d, 0x18, 
			},
		},
	},
	{		
		"st",
		32,
		{
			{
				0x94, 0x8c, 0x32, 0xdf, 0xdd, 0xd5, 0xa1, 0x7e, 0x2e, 0xb2, 0x16, 0x13, 0x74, 0x64, 0x28, 0x35, 
				0x6c, 0xe7, 0xd4, 0x47, 0xd8, 0xaf, 0x6d, 0x5d, 0x7d, 0xd3, 0x52, 0x53, 0xd6, 0x7c, 0x13, 0xe5, 
			},
			{
				0x41, 0x7d, 0x96, 0x90, 0x89, 0xf1, 0xed, 0x07, 0x68, 0xe3, 0xec, 0x66, 0xb4, 0xa4, 0x3b, 0xfc, 
				0xa2, 0x80, 0x87, 0x66, 0x42, 0x8d, 0x93, 0x01, 0x1d, 0x70, 0x06, 0x63, 0x07, 0x6e, 0x3a, 0xaf, 
			}
		},
	},
	{		
		"sys",
		32,
		{
			{
				0xa3, 0x53, 0x35, 0x13, 0x5c, 0x06, 0x50, 0x88, 0x4b, 0x6a, 0xe0, 0x8b, 0x8d, 0xf4, 0xab, 0x91, 
				0x57, 0x0c, 0xdc, 0xe8, 0x16, 0x6e, 0xd5, 0x56, 0x75, 0x8d, 0xdb, 0xb9, 0x50, 0x0f, 0xfa, 0xec, 
			},
			{
				0xdc, 0x84, 0xac, 0x26, 0xb7, 0x84, 0x58, 0x27, 0x1e, 0x9d, 0xef, 0xfb, 0x33, 0x58, 0x10, 0xdb, 
				0xca, 0x71, 0xa4, 0x06, 0xb6, 0x94, 0xeb, 0xdc, 0x5b, 0x72, 0xca, 0x94, 0x5b, 0x66, 0x13, 0x72, 
			}
		},
	},
	{
		"bgm",
		32,
		{
			{
				0xff, 0x29, 0x59, 0x91, 0x3a, 0x9c, 0x49, 0xdf, 0xb5, 0x89, 0xaa, 0xaa, 0xac, 0xef, 0x16, 0xd5, 
				0x18, 0x67, 0xb7, 0x68, 0xb5, 0x27, 0xee, 0x25, 0x35, 0x35, 0x1b, 0x9d, 0xc3, 0xb0, 0xcd, 0xe1, 
			},
			{
				0x5d, 0xd5, 0xff, 0xaa, 0x5f, 0x9b, 0xf3, 0x63, 0x9d, 0x46, 0x4f, 0x40, 0xba, 0x90, 0x2b, 0x8f, 
				0xd3, 0x9f, 0xe5, 0x77, 0x50, 0x0a, 0x5c, 0xd9, 0x68, 0x3e, 0xc0, 0xf4, 0x7e, 0x73, 0x03, 0x8a, 
			}
		},
	},
	{
		"voice",
		32,
		{
			{
				0xb6, 0x2a, 0x1a, 0xdc, 0x33, 0x42, 0x37, 0x59, 0x68, 0x43, 0x4a, 0x4c, 0x94, 0x10, 0x7a, 0x4f, 
				0x0c, 0x7b, 0x9f, 0xcf, 0x71, 0x7e, 0x14, 0x8c, 0x6f, 0x9d, 0x5d, 0x78, 0x55, 0xe7, 0x23, 0x0e, 
			},
			{
				0x6c, 0xa7, 0x87, 0x29, 0x59, 0xeb, 0xa4, 0x5d, 0x65, 0x01, 0x40, 0xe4, 0x5a, 0x20, 0x23, 0xf1, 
				0xe3, 0x92, 0x57, 0xda, 0xee, 0x3c, 0x1e, 0x42, 0xd5, 0x00, 0x8a, 0xcd, 0xee, 0x6e, 0xe9, 0x8e, 
			}
		},
	},
	{
		"se",
		32,
		{
			{
				0x20, 0x01, 0x29, 0xc7, 0xab, 0x24, 0x17, 0xaa, 0xa7, 0x43, 0x5b, 0x3b, 0xf8, 0xb2, 0x28, 0x95, 
				0xf0, 0x3f, 0x65, 0x36, 0x50, 0x54, 0x42, 0x09, 0xa5, 0x48, 0x86, 0xe6, 0x23, 0xac, 0x5c, 0x2a, 
			},
			{
				0xca, 0xac, 0x5a, 0x76, 0x7c, 0x61, 0x28, 0xe0, 0x0d, 0x21, 0x9a, 0x8b, 0x2d, 0x1f, 0xf9, 0xa2, 
				0xea, 0x4a, 0x48, 0x73, 0xc7, 0xb4, 0xba, 0x2f, 0x65, 0x41, 0x5f, 0xdc, 0x33, 0x5e, 0xa8, 0xfd, 
			}
		},
	},
	{
		"unknown1",
		32,
		{
			{
				0xf0, 0x85, 0x7c, 0x61, 0x43, 0xa2, 0xb6, 0x7a, 0x5c, 0x39, 0x5c, 0x9f, 0x05, 0xd0, 0x38, 0xdf, 
				0x70, 0x70, 0xb2, 0xa1, 0x9c, 0xe6, 0x0a, 0x7a, 0xaa, 0xe3, 0xf1, 0x30, 0x99, 0xed, 0x08, 0xc5, 
			},
			{
				0x21, 0x22, 0x72, 0x59, 0xad, 0x3c, 0x44, 0x9d, 0x6a, 0x93, 0x25, 0x17, 0x79, 0x06, 0xf2, 0xfb, 
				0x5c, 0xc7, 0xa9, 0x00, 0x32, 0xb0, 0x0b, 0xaa, 0x70, 0xd2, 0xbd, 0xbb, 0xfa, 0x71, 0xe1, 0x8a, 
			}
		},
	},
	{
		"mov",
		32,
		{
			{
				0x35, 0x19, 0xc5, 0x04, 0x7b, 0x85, 0xec, 0xdb, 0x7b, 0x1d, 0xb2, 0x92, 0x4e, 0x93, 0x5f, 0xbd, 
				0xed, 0xb5, 0x75, 0x3f, 0x90, 0x40, 0x19, 0x78, 0xad, 0xda, 0x63, 0x16, 0xcf, 0x62, 0xd8, 0x67, 
			},
			{
				0x62, 0xab, 0x41, 0x5b, 0xe2, 0xa7, 0x15, 0xb7, 0x2b, 0x31, 0x2a, 0xd8, 0x84, 0xb8, 0x41, 0x84, 
				0x74, 0xa8, 0x01, 0xdc, 0x24, 0xc8, 0xc5, 0xed, 0x58, 0x8c, 0xe4, 0x50, 0x1e, 0xde, 0x61, 0xbd, 
			}
		},
	},
	{		
		NULL,
		0,
	}
};

static struct paz_blowfish_key ef_sfm_paz_blowfish_key[] = {
	{
		"scr", 
		32,
		{
			{
				0xa3, 0x5c, 0xf8, 0x00, 0x38, 0x16, 0xbd, 0x7d, 0x1e, 0x59, 0x57, 0x02, 0xed, 0xe4, 0x07, 0x08,
				0x21, 0x45, 0x76, 0x38, 0xf9, 0x04, 0x66, 0x8c, 0xa9, 0xcd, 0x15, 0x4b, 0xf8, 0xdc, 0x34, 0xac
			},
			{
				0x3a, 0x75, 0xbd, 0x84, 0x3f, 0xc8, 0x08, 0x98, 0xf4, 0x41, 0x72, 0xf1, 0xd5, 0x1a, 0x2c, 0xfa, 
				0xfe, 0x1a, 0x2d, 0xba, 0xc7, 0xf6, 0x2e, 0xb0, 0xf2, 0xbd, 0x12, 0xe4, 0x0f, 0x65, 0x16, 0xc1 
			}
		},
	},
	{
		"bg",
		32,
		{
			{
				0xb6, 0xa8, 0xce, 0x49, 0x40, 0x08, 0x93, 0xc1, 0x32, 0xd3, 0xcf, 0x45, 0xe2, 0xf4, 0xa3, 0xd3, 
				0x11, 0x46, 0xf0, 0x26, 0xa2, 0x8f, 0x99, 0xdd, 0x43, 0x04, 0x02, 0x96, 0xbd, 0x51, 0xbc, 0xda 
			},
			{
				0x24, 0x2f, 0x87, 0xdb, 0x7c, 0xa3, 0x78, 0x5b, 0x60, 0xf6, 0xc6, 0xc2, 0xb0, 0x84, 0x1e, 0xc0, 
				0xbc, 0xf6, 0xda, 0x17, 0x1f, 0xe8, 0x39, 0xd4, 0xb7, 0xda, 0x13, 0xed, 0x4a, 0x15, 0xa3, 0x30 
			}
		},
	},

	/* a unknown entry in exe */

	{		
		"st",
		32,
		{
			{
				0x45, 0x8e, 0xf4, 0x24, 0xf9, 0x12, 0x06, 0xcd, 0xa7, 0x70, 0xbd, 0xd7, 0xe0, 0x31, 0x81, 0x3e, 
				0x52, 0x37, 0xd1, 0x0b, 0x4f, 0xa1, 0xec, 0xd1, 0x88, 0x09, 0xbc, 0xe2, 0x5a, 0xb1, 0x78, 0xae 
			},
			{
				0xfa, 0x22, 0x72, 0x09, 0x13, 0x10, 0xc5, 0xe6, 0xb1, 0x60, 0xb3, 0x5c, 0xde, 0x00, 0xae, 0x80, 
				0xe2, 0xa5, 0x13, 0xfa, 0x76, 0x0b, 0xdd, 0xe1, 0xf2, 0x60, 0x14, 0x62, 0x93, 0xf3, 0x4e, 0x20
			}
		},
	},
	{		
		"sys",
		32,
		{
			{
				0x82, 0x9f, 0x2a, 0x25, 0x8a, 0xc3, 0x2a, 0x5e, 0x45, 0x79, 0x0c, 0x64, 0x13, 0x01, 0x60, 0xc4, 
				0x40, 0xa2, 0x2a, 0xe5, 0xfe, 0xfc, 0xe1, 0x6e, 0xca, 0x5a, 0x78, 0x14, 0x22, 0xc6, 0x58, 0x72 
			},
			{
				0x15, 0x7d, 0xdf, 0x83, 0x5f, 0x0c, 0x20, 0xca, 0xb6, 0x29, 0x30, 0xef, 0x7a, 0xf4, 0xf0, 0x1a, 
				0x84, 0xaa, 0x67, 0xb7, 0x2e, 0xd1, 0x5a, 0x21, 0x32, 0x6a, 0x55, 0xbb, 0xf4, 0x3a, 0xe8, 0xa3 
			}
		},
	},
	{		
		"bgm",
		32,
		{
			{
				0x35, 0xd1, 0x71, 0x3d, 0x4b, 0xb2, 0x85, 0xe3, 0x5c, 0x9c, 0x94, 0xb4, 0xae, 0xcd, 0xf0, 0x10, 
				0x9b, 0x58, 0x60, 0x9c, 0x8f, 0xe4, 0xea, 0xe4, 0x40, 0x66, 0xe5, 0x46, 0xa2, 0x3b, 0x5a, 0x62 
			},
			{
				0x04, 0x13, 0x52, 0x26, 0x08, 0xf1, 0xd1, 0xc2, 0x61, 0xb5, 0x79, 0x10, 0xad, 0x4c, 0x9d, 0x66, 
				0xc5, 0x77, 0x78, 0xfa, 0x45, 0x19, 0x89, 0x97, 0x2f, 0x34, 0x6c, 0x53, 0xd0, 0xfd, 0xbe, 0x2b 
			}
		},
	},
	{		
		"voice",
		32,
		{
			{
				0xb3, 0xe1, 0x49, 0xf6, 0xe4, 0xb8, 0xa1, 0xef, 0xd4, 0xcb, 0x26, 0x1b, 0x1c, 0x4e, 0x67, 0xc5, 
				0x81, 0x94, 0x23, 0x5f, 0x8b, 0x20, 0xae, 0xfc, 0x32, 0x54, 0x31, 0x60, 0x8a, 0x86, 0x78, 0x7a 
			},
			{
				0xef, 0x95, 0x16, 0x79, 0xfd, 0x00, 0xca, 0x22, 0x50, 0xed, 0x04, 0x75, 0xa2, 0xd5, 0xeb, 0x69, 
				0x69, 0xe1, 0xb6, 0x4d, 0xdd, 0xfc, 0xa7, 0x24, 0x9d, 0x0d, 0x4e, 0xfc, 0xbe, 0x12, 0x5b, 0x76
			}
		},
	},
	{		
		"se",
		32,
		{
			{
				0x7a, 0x98, 0x10, 0x48, 0x84, 0xbd, 0x1c, 0x10, 0xfd, 0xd8, 0x97, 0xdb, 0x2f, 0x76, 0xe2, 0xbb, 
				0xb4, 0xb0, 0xd6, 0xc9, 0x14, 0xce, 0x83, 0x15, 0xe8, 0xd3, 0x55, 0xd3, 0x52, 0xe7, 0x13, 0xb0 
			},
			{
				0x77, 0xe3, 0xe8, 0xff, 0x18, 0xf1, 0x82, 0xc5, 0x2e, 0x77, 0x3a, 0x0d, 0x89, 0xd1, 0x73, 0x86, 
				0x93, 0x04, 0x6c, 0x17, 0x5a, 0x24, 0xfa, 0xc9, 0xbf, 0xff, 0xd2, 0xe4, 0x58, 0xa1, 0x6d, 0xab 
			}
		},
	},

	/* a unknown entry in exe */

	{		
		"mov",
		32,
		{
			{
				0xb3, 0x25, 0x8d, 0x86, 0x56, 0xff, 0x2f, 0x27, 0xa4, 0x93, 0xb1, 0x4b, 0x97, 0xb9, 0xfb, 0x99, 
				0x94, 0x4b, 0xda, 0x46, 0xf6, 0xb4, 0xcf, 0xc8, 0x15, 0x74, 0x21, 0x50, 0xe3, 0x76, 0x55, 0x79 
			},
			{
				0x2a, 0xca, 0x1a, 0x43, 0xc0, 0x38, 0x86, 0xa1, 0x42, 0x9e, 0x65, 0xac, 0xeb, 0x67, 0x38, 0x1a, 
				0x2a, 0x9f, 0x5f, 0xf5, 0x68, 0x9a, 0xb3, 0xc5, 0xf9, 0x24, 0xf2, 0xbb, 0x16, 0x09, 0xf1, 0xd2
			}
		},
	},
	{		
		NULL,
		0,
	}
};

static struct paz_blowfish_key haru_trial_paz_blowfish_key[] = {
	{		
		NULL,
		0,
	}
};

/*  ef - the latter tale. TG Ver */
static struct paz_blowfish_list ef_lttg_paz_blowfish = {
	"ef_lttg", ef_lttg_paz_blowfish_key, 0, 0, 0, 
};


/*  ef -the latter tale.- 体験版 */
static struct paz_blowfish_list ef_ltt_trial_paz_blowfish = {
	"ef_lttr", ef_ltt_trial_paz_blowfish_key, 0, 0, 0, 
};

/* ef - Second Fan Mix */
static struct paz_blowfish_list ef_sfm_paz_blowfish = {
	"ef_sfm", ef_sfm_paz_blowfish_key, 0, 0, 0, 
};

/* はるのあしおと web trial */
static struct paz_blowfish_list haruoto_trial_paz_blowfish = {
	"haruoto_trial", NULL, 1, 0, 0, 
};

static struct paz_blowfish_list ef_latter_paz_blowfish = {
	"ef_latter", ef_latter_paz_blowfish_key, 0, 1, 0,
};


struct paz_blowfish_list *paz_blowfish_list[] = {
	&haruoto_trial_paz_blowfish,	/* TEST */
//	&haruoto_paz_blowfish,			/* TODO */
//	&haruotoFD_paz_blowfish,		/* TODO */
//	&ef_ft_trial_paz_blowfish,		/* TODO */
//	&ef_ffd_paz_blowfish,			/* TODO */
//	&ef_ft_paz_blowfish,			/* TODO */
	&ef_sfm_paz_blowfish,
	&ef_ltt_trial_paz_blowfish,
	&ef_lttg_paz_blowfish,
	&ef_latter_paz_blowfish,
	NULL
};

static BYTE mov_decode_table[256][256];

static int paz_blowfish_getkey(const char *game_name, const char *name_data, const char *paz_name, struct paz_blowfish_key *ret_key)
{
	struct paz_blowfish_list *list = paz_blowfish_list[0];
	struct paz_blowfish_key *key;

	for (DWORD l = 0; paz_blowfish_list[l]; l++) {
		list = paz_blowfish_list[l];
		if (game_name) {
			if (!strcmp(game_name, name_data) 
					&& !strcmp(list->name, name_data))
				break;
		} else {
			if (!strcmp(list->name, name_data))
				break;
		}

		if (list->do_xor && !list->xor_code) {
			BYTE xor_code = name_data[0] ^ list->name[0];
			int len = strlen(list->name);
			for (int i = 0; i < len; ++i) {
				if ((name_data[i] ^ xor_code) != list->name[i])
					break;
			}
			if (i == len) {
				list->xor_code = xor_code;
				break;
			}
		}
	}
	if (!list)
		return -1;

	if (!list->key) {
		memset(ret_key, 0, sizeof(struct paz_blowfish_key));
		ret_key->list = list;
		return 0;
	}

	for (key = list->key; key->paz_name; key++) {
		char __paz_name[MAX_PATH];
		char *p;
		
		strncpy(__paz_name, paz_name, sizeof(__paz_name));
		p = strstr(__paz_name, ".");
		if (p)
			*p = 0;
		else
			return -1;

		if (!strcmp(key->paz_name, __paz_name))
			break;
	}
	if (!key->paz_name)
		return -1;

	memcpy(ret_key, key, sizeof(struct paz_blowfish_key));
	ret_key->list = list;
	return 0;
}

static BOOL paz_blowfish_valid_key(struct paz_blowfish_key *paz_key)
{
	return paz_key->paz_name && paz_key->key_len;
}

static void paz_decrypt(DWORD *cipher, DWORD cipher_length, unsigned char *key, int key_len)
{
	BLOWFISH_CTX ctx;
	DWORD i;
	
	Blowfish_Init(&ctx, key, key_len);
	for (i = 0; i < cipher_length / 8; i++)
		Blowfish_Decrypt(&ctx, &cipher[i * 2 + 0], &cipher[i * 2 + 1]);
}

static int Musica_paz_read(struct paz_blowfish_key *key,
						   struct package *pkg, void *buf, DWORD len)
{
	int ret = pkg->pio->read(pkg, buf, len);
	if (ret || !key->list->do_xor)
		return ret;

	if (key->list->xor_code) {
		for (DWORD i = 0; i < len; ++i)
			*((u8 *)buf + i) ^= key->list->xor_code;
	}

	return 0;
}

struct ef_latter_crypt {
	BYTE seed[2];
	BYTE stage[256];
};

static void ef_latter_decrypt_init(struct ef_latter_crypt *crypt, 
								   char *key_string, DWORD key_length)
{
	if (!key_length)
		key_length = strlen(key_string);

	crypt->seed[0] = 0;
	crypt->seed[1] = 0;
	for (DWORD i = 0; i < 256; ++i)
		crypt->stage[i] = (BYTE)i;

	DWORD j = 0;
	BYTE idx = 0;
	for (i = 0; i < 256; ++i) {
		BYTE tmp = crypt->stage[i];
		idx += tmp + key_string[j++];
		crypt->stage[i] = crypt->stage[idx];
		crypt->stage[idx] = tmp;
		if (j >= key_length)
			j = 0;
	}
}

static BYTE ef_latter_decrypt_rand(struct ef_latter_crypt *crypt)
{
	BYTE BL, DL;
	++crypt->seed[0];
	DL = crypt->stage[crypt->seed[0]];
	crypt->seed[1] += DL;
	BL = crypt->stage[crypt->seed[1]];
	crypt->stage[crypt->seed[1]] = DL;
	crypt->stage[crypt->seed[0]] = BL;
	BL += DL;
	return crypt->stage[BL];
}

static void ef_latter_decrypt_data(const char *id, BYTE *enc, DWORD enc_length, char *key_string)
{
	struct ef_latter_crypt crypt;
	char init_key = 0;
	char dec_string[256];

	ef_latter_decrypt_init(&crypt, &init_key, 1);
	sprintf(dec_string, "%s %08X %s", key_string, enc_length, id);
	for (DWORD i = 0; i < strlen(dec_string); ++i) {
		if (dec_string[i] == ' ')
			break;
		if (dec_string[i] >= 'A' && dec_string[i] <= 'Z')
			dec_string[i] += 'a' - 'A';
	}
	ef_latter_decrypt_init(&crypt, dec_string, 0);
	for (i = 0; i < enc_length; ++i)
		enc[i] ^= ef_latter_decrypt_rand(&crypt);
}

/********************* paz *********************/

/* 封包匹配回调函数 */
static int Musica_paz_match(struct package *pkg)
{
	struct paz_blowfish_key *key;
	char name[32];
	char paz_name[MAX_PATH];
	const char *game_name;

	if (pkg->pio->open(pkg, IO_READONLY))
		return -CUI_EOPEN;

	if (pkg->pio->read(pkg, name, sizeof(name))) {
		pkg->pio->close(pkg);
		return -CUI_EREAD;
	}

	key = (struct paz_blowfish_key *)malloc(sizeof(struct paz_blowfish_key));
	if (!key) {
		pkg->pio->close(pkg);
		return -CUI_EMEM;
	}
	
	game_name = get_options("game");
	unicode2acp(paz_name, MAX_PATH, pkg->name, -1);
	if (paz_blowfish_getkey(game_name, name, paz_name, key)) {
		free(key);
		pkg->pio->close(pkg);
		return -CUI_EMATCH;
	}

	if (key->list->no_header) {
		if (pkg->pio->seek(pkg, 0, IO_SEEK_SET)) {
			free(key);
			pkg->pio->close(pkg);
			return -CUI_EREAD;
		}
	}
	package_set_private(pkg, key);

	return 0;	
}

/* 封包索引目录提取函数 */
static int Musica_paz_extract_directory(struct package *pkg,
										struct package_directory *pkg_dir)
{
	DWORD *index;
	u32 index_length;
	my_paz_entry_t *index_buffer;
	DWORD index_buffer_length;	
	DWORD i;
	struct paz_blowfish_key *key;

	key = (struct paz_blowfish_key *)package_get_private(pkg);
	if (Musica_paz_read(key, pkg, &index_length, 4))
		return -CUI_EREAD;

	/* 必须8字节对齐 */
	if (index_length & 7)
		return -CUI_EMATCH;

	index = (DWORD *)malloc(index_length);
	if (!index)
		return -CUI_EMEM;

	if (Musica_paz_read(key, pkg, index, index_length)) {
		free(index);
		return -CUI_EREAD;
	}

	if (paz_blowfish_valid_key(key))
		paz_decrypt(index, index_length, key->key[0], key->key_len);

	u32 index_entries = *index;
	index_buffer_length = index_entries * sizeof(my_paz_entry_t);
	index_buffer = (my_paz_entry_t *)malloc(index_buffer_length);
	if (!index_buffer) {
		free(index);
		return -CUI_EMEM;
	}

	BYTE *p = (BYTE *)(index + 1);
	for (i = 0; i < index_entries; ++i) {
		if (!strcmp(key->paz_name, "mov")) {
			if (strcmp(key->list->name, "ef_latter")) {
				for (int j = 0; j < 256; ++j) {
					for (int k = 0; k < 256; ++k) {
						for (int n = 255; n >= 0; --n) {
							if (p[n] == k)
								mov_decode_table[j][k] = n;
						}
					}
					p += 256;
				}
			} else if (!i) {	// 只有1个
				memcpy(ef_latter_mov_rc4table, p, 256);
				p += 256;
			}
		}

		strcpy(index_buffer[i].name, (char *)p);
		p += strlen((char *)p) + 1;
		index_buffer[i].offset = *(u32 *)p;
		p += 8;
		index_buffer[i].uncomprlen = *(u32 *)p;
		p += 4;
		index_buffer[i].comprlen = *(u32 *)p;
		p += 4;
		index_buffer[i].decrypt_length = *(u32 *)p;
		p += 4;	
		if (*(u16 *)p != 1)
			index_buffer[i].uncomprlen = 0;
		index_buffer[i].is_compressed = *(u16 *)p;
		p += 2;
		index_buffer[i].reserved = *(u16 *)p;
		p += 2;
	}
	free(index);

	pkg_dir->index_entries = index_entries;
	pkg_dir->directory = index_buffer;
	pkg_dir->directory_length = index_buffer_length;
	pkg_dir->index_entry_length = sizeof(my_paz_entry_t);

	return 0;
}

/* 封包索引项解析函数 */
static int Musica_paz_parse_resource_info(struct package *pkg,
										  struct package_resource *pkg_res)
{
	my_paz_entry_t *my_paz_entry;

	my_paz_entry = (my_paz_entry_t *)pkg_res->actual_index_entry;
	strcpy(pkg_res->name, my_paz_entry->name);
	pkg_res->name_length = -1;
	pkg_res->raw_data_length = my_paz_entry->comprlen;
	pkg_res->actual_data_length = my_paz_entry->uncomprlen;
	pkg_res->offset = my_paz_entry->offset;

	return 0;
}

/* 封包资源提取函数 */
static int Musica_paz_extract_resource(struct package *pkg,
									   struct package_resource *pkg_res)
{
	BYTE *cipher, *plain, *act_data;
	BYTE *compr, *uncompr;
	DWORD comprlen, uncomprlen, act_data_len;
	my_paz_entry_t *my_paz_entry;
	struct paz_blowfish_key *paz_key;

	my_paz_entry = (my_paz_entry_t *)pkg_res->actual_index_entry;
	cipher = (BYTE *)pkg->pio->readvec_only(pkg, my_paz_entry->decrypt_length, pkg_res->offset, IO_SEEK_SET);
	if (!cipher)
		return -CUI_EREADVECONLY;

	plain = (BYTE *)malloc(my_paz_entry->decrypt_length);
	if (!plain)
		return -CUI_EMEM;

	paz_key = (struct paz_blowfish_key *)package_get_private(pkg);
	if (!strcmp(paz_key->paz_name, "mov")) {
		if (strcmp(paz_key->list->name, "ef_latter")) {
			for (DWORD k = 0; k < my_paz_entry->comprlen; k++)
				plain[k] = mov_decode_table[((k >> 16) << 8) & 0xff][cipher[k] ^ paz_key->list->xor_code];	
		} else {
			char key_string[256];
			DWORD key_len = sprintf(key_string, "%s %08X 8fO1Xj6g", 
				pkg_res->name, my_paz_entry->comprlen);
			BYTE mov_rc4table[256];
			for (DWORD j = 0; j < 256; ++j)
				mov_rc4table[j] = ef_latter_mov_rc4table[j] ^ key_string[j % key_len];

			struct ef_latter_crypt crypt;
			ef_latter_decrypt_init(&crypt, (char *)mov_rc4table, 256);
			for (j = 0; j < 256; ++j)
				for (DWORD k = 0; k < 256; ++k)
					mov_decode_table[j][k] = ef_latter_decrypt_rand(&crypt);

			BYTE *mov_decodeTable = &mov_decode_table[0][0];
			for (DWORD k = 0; k < my_paz_entry->comprlen; k++)
				plain[k] = cipher[k] ^ paz_key->list->xor_code ^ mov_decodeTable[k & 0xffff];	
		} 
		pkg_res->actual_data = plain;
		pkg_res->actual_data_length = my_paz_entry->comprlen;
		return 0;
	}

	memcpy(plain, cipher, my_paz_entry->decrypt_length);
	if (paz_key->list->xor_code) {
		for (DWORD i = 0; i < my_paz_entry->decrypt_length; ++i)
			plain[i] ^= paz_key->list->xor_code;
	}
	if (paz_blowfish_valid_key(paz_key))
		paz_decrypt((DWORD *)plain, my_paz_entry->decrypt_length, paz_key->key[1], paz_key->key_len);

	compr = plain;
	comprlen = pkg_res->raw_data_length;
	uncomprlen = pkg_res->actual_data_length;
	if (uncomprlen) {
		DWORD act_uncomprlen;

		uncompr = (BYTE *)malloc(uncomprlen);
		if (!uncompr) {
			free(compr);
			return -CUI_EMEM;
		}
		act_uncomprlen = uncomprlen;
		uncompress(uncompr, &act_uncomprlen, compr, comprlen);
		act_data = uncompr;
		act_data_len = uncomprlen;
	} else {
		uncompr = NULL;
		act_data = compr;
		act_data_len = comprlen;
	}

	if (!strcmp(paz_key->list->name, "ef_latter")) {
		if (strstr(pkg_res->name, ".png"))
			ef_latter_decrypt_data("957277W1", act_data, act_data_len, pkg_res->name);
		else if (strstr(pkg_res->name, ".sc"))
			ef_latter_decrypt_data("A00U43Mj", act_data, act_data_len, pkg_res->name);
		else if (strstr(pkg_res->name, ".ogg"))
			ef_latter_decrypt_data("m24lX440", act_data, act_data_len, pkg_res->name);
	}

	/* for voice.paz */
	if (!strncmp((char *)act_data, "OggS", 4)) {
		pkg_res->flags |= PKG_RES_FLAG_REEXT;
		pkg_res->replace_extension = _T(".ogg");
	}

	pkg_res->raw_data = compr;
	pkg_res->raw_data_length = comprlen;
	pkg_res->actual_data = uncompr;
	pkg_res->actual_data_length = uncomprlen;

	return 0;
}

/* 资源保存函数 */
static int Musica_paz_save_resource(struct resource *res, 
									struct package_resource *pkg_res)
{
	if (res->rio->create(res))
		return -CUI_ECREATE;

	if (pkg_res->actual_data && pkg_res->actual_data_length) {
		if (res->rio->write(res, pkg_res->actual_data, pkg_res->actual_data_length)) {
			res->rio->close(res);
			return -CUI_EWRITE;
		}
	} else if (pkg_res->raw_data && pkg_res->raw_data_length) {
		if (res->rio->write(res, pkg_res->raw_data, pkg_res->raw_data_length)) {
			res->rio->close(res);
			return -CUI_EWRITE;
		}
	}

	res->rio->close(res);
	
	return 0;
}

/* 封包资源释放函数 */
static void Musica_paz_release_resource(struct package *pkg, 
										struct package_resource *pkg_res)
{
	if (pkg_res->actual_data) {
		free(pkg_res->actual_data);
		pkg_res->actual_data = NULL;
	}
	if (pkg_res->raw_data) {
		free(pkg_res->raw_data);
		pkg_res->raw_data = NULL;
	}
}

/* 封包卸载函数 */
static void Musica_paz_release(struct package *pkg, 
							   struct package_directory *pkg_dir)
{
	void *priv;

	if (pkg_dir->directory) {
		free(pkg_dir->directory);
		pkg_dir->directory = NULL;
	}

	priv = (void *)package_get_private(pkg);
	if (priv) {
		free(priv);
		package_set_private(pkg, NULL);
	}

	pkg->pio->close(pkg);
}

/* 封包处理回调函数集合 */
static cui_ext_operation Musica_paz_operation = {
	Musica_paz_match,					/* match */
	Musica_paz_extract_directory,		/* extract_directory */
	Musica_paz_parse_resource_info,		/* parse_resource_info */
	Musica_paz_extract_resource,		/* extract_resource */
	Musica_paz_save_resource,			/* save_resource */
	Musica_paz_release_resource,		/* release_resource */
	Musica_paz_release					/* release */
}; 

/********************* dat *********************/

/* 封包匹配回调函数 */
static int Musica_dat_match(struct package *pkg)
{
	if (lstrcmp(pkg->name, _T("system.dat")))
		return -CUI_EMATCH;

	if (pkg->pio->open(pkg, IO_READONLY))
		return -CUI_EOPEN;

	return 0;	
}

/* 封包资源提取函数 */
static int Musica_dat_extract_resource(struct package *pkg,
									   struct package_resource *pkg_res)
{
	BYTE *cipher, *plain;
	BYTE *compr, *uncompr;
	DWORD comprlen, uncomprlen;
	u32 dat_size;

	if (pkg->pio->length_of(pkg, &dat_size))
		return -CUI_ELEN;

	cipher = (BYTE *)pkg->pio->readvec_only(pkg, dat_size, 0, IO_SEEK_SET);
	if (!cipher)
		return -CUI_EREADVECONLY;

	plain = (BYTE *)malloc(dat_size);
	if (!plain)
		return -CUI_EMEM;

	memcpy(plain, cipher, dat_size);
	paz_decrypt((DWORD *)plain, dat_size, (unsigned char *)"minori", 6);

	uncomprlen = *(u32 *)plain;
	comprlen = dat_size - 4;
	compr = plain + 4;

	uncompr = (BYTE *)malloc(uncomprlen);
	if (!uncompr) {
		free(plain);
		return -CUI_EMEM;
	}

	if (uncompress(uncompr, &uncomprlen, compr, comprlen) != Z_OK) {
		free(uncompr);
		free(plain);
		return -CUI_EUNCOMPR;
	}
	free(plain);

	pkg_res->raw_data = cipher;
	pkg_res->raw_data_length = dat_size;
	pkg_res->actual_data = uncompr;
	pkg_res->actual_data_length = uncomprlen;

	return 0;
}

/* 资源保存函数 */
static int Musica_dat_save_resource(struct resource *res, 
									struct package_resource *pkg_res)
{
	if (!res || !pkg_res)
		return -CUI_EPARA;
	
	if (res->rio->create(res))
		return -CUI_ECREATE;

	if (pkg_res->actual_data && pkg_res->actual_data_length) {
		if (res->rio->write(res, pkg_res->actual_data, pkg_res->actual_data_length)) {
			res->rio->close(res);
			return -CUI_EWRITE;
		}
	} else if (pkg_res->raw_data && pkg_res->raw_data_length) {
		if (res->rio->write(res, pkg_res->raw_data, pkg_res->raw_data_length)) {
			res->rio->close(res);
			return -CUI_EWRITE;
		}
	}

	res->rio->close(res);
	
	return 0;
}

/* 封包资源释放函数 */
static void Musica_dat_release_resource(struct package *pkg, 
										struct package_resource *pkg_res)
{
	if (!pkg || !pkg_res)
		return;

	if (pkg_res->actual_data) {
		free(pkg_res->actual_data);
		pkg_res->actual_data = NULL;
	}
	if (pkg_res->raw_data)
		pkg_res->raw_data = NULL;
}

/* 封包卸载函数 */
static void Musica_dat_release(struct package *pkg, 
							   struct package_directory *pkg_dir)
{
	if (!pkg)
		return;

	pkg->pio->close(pkg);
}

/* 封包处理回调函数集合 */
static cui_ext_operation Musica_dat_operation = {
	Musica_dat_match,			/* match */
	NULL,						/* extract_directory */
	NULL,						/* parse_resource_info */
	Musica_dat_extract_resource,/* extract_resource */
	Musica_dat_save_resource,	/* save_resource */
	Musica_dat_release_resource,/* release_resource */
	Musica_dat_release			/* release */
};

/* 接口函数: 向cui_core注册支持的封包类型 */
int CALLBACK Musica_register_cui(struct cui_register_callback *callback)
{
	/* 注册cui插件支持的扩展名、资源放入扩展名、处理回调函数和封包属性 */
	if (callback->add_extension(callback->cui, _T(".paz"), NULL, 
		NULL, &Musica_paz_operation, CUI_EXT_FLAG_PKG | CUI_EXT_FLAG_DIR | CUI_EXT_FLAG_WEAK_MAGIC))
			return -1;

	if (callback->add_extension(callback->cui, _T(".dat"), NULL, 
		NULL, &Musica_dat_operation, CUI_EXT_FLAG_PKG | CUI_EXT_FLAG_NO_MAGIC))
			return -1;

	return 0;
}
