view system/file.cc @ 26:f45da03ca631

* Corrected --disable-vorbis in configure.ac * Added sound configuration * Added misc functions
author thib
date Sun, 01 Mar 2009 19:08:02 +0000 (2009-03-01)
parents b6d6c0ffb423
children 3a6aaeab7b4e
line wrap: on
line source
bool init_end=false;
/*  file.cc  : KANON �ΰ��̥ե����롦PDT �ե�����ʲ����ե�����ˤ�Ÿ����
 *            ����Υ᥽�å�
 *     class ARCFILE : ��˥ե��������Τ򰷤����饹
 *     class ARCINFO : ��˥ե��������Σ��ĤΥե�����򰷤����饹
 *     class PDTCONV : PDT �ե������Ÿ����Ԥ���
 *
 */

/*
 *
 *  Copyright (C) 2000-   Kazunori Ueno(JAGARL) <jagarl@creator.club.ne.jp>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
*/

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#ifdef HAVE_MMAP
#  ifdef MACOSX
#    undef HAVE_MMAP
#  endif /* MACOSX */
#endif /* HAVE_MMAP */

#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <vector>
#include <algorithm>
#if HAVE_MMAP
#include<sys/mman.h>
#endif /* HAVE_MMAP */
#if HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# if HAVE_NDIR_H
#  include <ndir.h>
# endif
#endif

#if HAVE_LIBZ
#include<zlib.h>
#endif
#if HAVE_LIBPNG
#include<png.h>
#endif
#if HAVE_LIBJPEG
extern "C" {
#include<jpeglib.h>
}
#endif

#include "file.h"
#include "file_impl.h"

using namespace std;

FILESEARCH file_searcher;
// #define delete fprintf(stderr,"file.cc: %d.",__LINE__), delete

/* FILESEARCH class �� default ����� */
FILESEARCH::ARCTYPE FILESEARCH::default_is_archived[TYPEMAX] = {
	ATYPE_DIR, ATYPE_DIR, ATYPE_DIR, ATYPE_DIR,
	ATYPE_ARC, ATYPE_ARC, ATYPE_ARC, ATYPE_ARC,
	ATYPE_DIR, ATYPE_DIR, ATYPE_DIR, ATYPE_DIR,
	ATYPE_DIR, ATYPE_DIR
};
char* FILESEARCH::default_dirnames[TYPEMAX] = {
	0, 0, "", "pdt", 
	"seen.txt", "allanm.anl", "allard.ard", "allcur.cur", 
	0, 0, "koe", "bgm", "mov", "gan"};

/*********************************************
**  ARCFILE / DIRFILE:
**	��˥ե����롢���뤤�ϥǥ��쥯�ȥ��
**	���Τ�������륯�饹
**
**	��˥ե����뤫��ե������ȴ���Ф���Find()
**	Find ������Τ�Read��������Ƥ������롣
*/

ARCFILE::ARCFILE(char* aname) {
	struct stat sb;
	/* �ѿ������ */
	arcname = 0;
	list_point = 0;
	filenames_orig = 0;
	next = 0;
	if (aname[0] == '\0') {arcname=new char[1]; arcname[0]='\0';return;} // NULFILE
	/* �ǥ��쥯�ȥ꤫�ݤ��Υ����å� */
	if (stat(aname,&sb) == -1) { /* error */
		perror("stat");
	}
	if ( (sb.st_mode&S_IFMT) == S_IFDIR) {
		int l = strlen(aname);
		arcname = new char[l+2]; strcpy(arcname, aname);
		if (arcname[l-1] != DIR_SPLIT) {
			arcname[l] = DIR_SPLIT;
			arcname[l+1] = 0;
		}
	} else if ( (sb.st_mode&S_IFMT) == S_IFREG) {
		arcname = new char[strlen(aname)+1];
		strcpy(arcname,aname);
		if (arcname[strlen(arcname)-1] == DIR_SPLIT)
			arcname[strlen(arcname)-1] = '\0';
	}
	return;
}

void ARCFILE::Init(void) {
	int i;
	if (! arc_atom.empty()) return;
	if (arcname == 0) return;
	/* �ե������������ */
	int slen = CheckFileDeal();
	/* �ե�����̾�Υ��å� */
	ListupFiles(slen);
	if ( (!arc_atom.empty()) && arc_atom[0].filename) filenames_orig = arc_atom[0].filename;
	sort(arc_atom.begin(), arc_atom.end());
}
ARCFILE::~ARCFILE() {
	if (filenames_orig) delete[] filenames_orig;
	delete[] arcname;
}

ARCFILE::iterator ARCFILE::SearchName(const char* f, const char* ext) {
	char buf[1024]; char buf_ext[1024];
	iterator it;
	Init();
	if (f == 0) return arc_atom.end();
	if (arc_atom.empty()) return arc_atom.end();
	/* ���顼�����å� */
	if (strlen(f)>500) return arc_atom.end();
	if (ext && strlen(ext)>500) return arc_atom.end();

	/* ���� */
	strncpy(buf, f, 1000);
	buf[1000]=0;
	it = lower_bound(arc_atom.begin(), arc_atom.end(), (char*)buf);
	if (it != arc_atom.end() && strcmp(it->filename_lower, buf) == 0) return it;
	// ��ĥ�Ҥ�Ĥ��Ƹ���
	if (ext) {
		strcpy(buf_ext, buf);
		char* ext_pt = strrchr(buf_ext, '.');
		if (ext_pt == 0 || ext_pt == buf_ext) ext_pt = buf_ext + strlen(buf_ext);
		*ext_pt++ = '.';
		while(*ext=='.') ext++;
		strcat(buf_ext, ext);
		it = lower_bound(arc_atom.begin(), arc_atom.end(), (char*)buf_ext);
		if (it != arc_atom.end() && strcmp(it->filename_lower, buf_ext) == 0) return it;
	}

	/* ��ʸ���ˤ��� ���� */
	int i; int l = strlen(f);
	if (l > 500) l = 500;
	for (i=0; i<l; i++)
		buf[i] = tolower(f[i]);
	buf[i++] = 0;
	it = lower_bound(arc_atom.begin(), arc_atom.end(), (char*)buf);
	if (it != arc_atom.end() && strcmp(it->filename_lower, buf) == 0) return it;

	// ��ĥ�Ҥ�Ĥ��Ƹ���
	if (ext == 0) return arc_atom.end();
	strcpy(buf_ext, buf);
	char* ext_pt = strrchr(buf_ext, '.');
	if (ext_pt == 0 || ext_pt == buf_ext) ext_pt = buf_ext + strlen(buf_ext);
	*ext_pt++ = '.';
	/* ��ĥ�Ҥ�Ĺ�������� */
	l = strlen(ext);
	for (i=0; i<l; i++)
		ext_pt[i] = tolower(*ext++);
	ext_pt[i] = 0;
	it = lower_bound(arc_atom.begin(), arc_atom.end(), (char*)buf_ext);
	if (it != arc_atom.end() && strcmp(it->filename_lower, buf_ext) == 0) return it;
	return arc_atom.end();
}

ARCINFO* ARCFILE::Find(const char* fname, const char* ext) {
	Init();
	iterator atom = SearchName(fname,ext);
	if (atom == arc_atom.end()) {
		if (next) return next->Find(fname, ext);
		else return 0;
	}
	return MakeARCINFO(*atom);
}
ARCINFO* ARCFILE::MakeARCINFO(ARCFILE_ATOM& atom) {
	if (atom.arcsize == atom.filesize)
		return new ARCINFO(arcname, atom);
	else // ������
		return new ARCINFO_AVG32(arcname, atom);
}
ARCINFO* NULFILE::MakeARCINFO(ARCFILE_ATOM& atom) {
	fprintf(stderr,"NULFILE::MakeARCINFO is invalid call!\n");
	return 0;
}
ARCINFO* SCN2kFILE::MakeARCINFO(ARCFILE_ATOM& atom) {
	return new ARCINFO2k(arcname, atom);
}
ARCINFO* DIRFILE::MakeARCINFO(ARCFILE_ATOM& atom) {
	char* name = atom.filename;
	char* new_path = new char[strlen(arcname)+strlen(name)+1];
	strcpy(new_path,arcname); strcat(new_path, name);
	ARCINFO* ret = new ARCINFO(new_path, atom);
	delete[] new_path;
	return ret;
}

FILE* DIRFILE::Open(const char* fname) {
	iterator atom = SearchName(fname);
	if (atom == arc_atom.end()) return 0;
	char* name = atom->filename;
	// make FILE*
	char* new_path = new char[strlen(arcname)+strlen(name)+1];
	strcpy(new_path,arcname); strcat(new_path, name);
	FILE* ret = fopen(new_path, "rb+");
	fseek(ret, 0, 0);
	delete[] new_path;
	return ret;
}

char* DIRFILE::SearchFile(const char* fname) {
	iterator atom = SearchName(fname);
	if (atom == arc_atom.end()) return 0;
	char* name = atom->filename;
	char* new_path = new char[strlen(arcname)+strlen(name)+1];
	strcpy(new_path,arcname); strcat(new_path, name);
	struct stat sb;
	if (stat(new_path, &sb) == 0 &&
		( (sb.st_mode&S_IFMT) == S_IFREG ||
		  (sb.st_mode&S_IFMT) == S_IFDIR)) {
		return new_path;
	}
	delete[] new_path;
	return 0;
}

void ARCFILE::ListFiles(FILE* out) {
	int i;
	Init();
	if (arc_atom.empty()) return;
	// list file name...
	fprintf(out,"%16s %10s %10s %10s\n", "Filename", 
		"pointer","arcsize", "filesize");
	vector<ARCFILE_ATOM>::iterator it;
	for (it=arc_atom.begin(); it!=arc_atom.end(); it++) {
		fprintf(out,"%16s %10d %10d %10d\n",
			it->filename,it->offset,it->arcsize,it->filesize);
	}
	return;
}

void ARCFILE::InitList(void) {
	Init();
	list_point = 0;
}
char* ARCFILE::ListItem(void) {
	if (list_point < 0) return 0;
	if (list_point >= arc_atom.size()) return 0;
	char* fname = arc_atom[list_point].filename;
	if (fname == 0) return 0;
	char* ret = new char[strlen(fname)+1];
	strcpy(ret, fname);
	list_point++;
	return ret;
}

int ARCFILE::CheckFileDeal(void) {
	char buf[0x20];
	/* �إå��Υ����å� */
	FILE* stream = fopen(arcname, "rb");
	if (stream == 0) {
		fprintf(stderr, "Cannot open archive file : %s\n",arcname);
		return 0;
	}
	fseek(stream, 0, 2); size_t arc_size = ftell(stream);
	fseek(stream, 0, 0);
	if (arc_size < 0x20) {
		fclose(stream);
		return 0;
	}
	fread(buf, 0x20, 1, stream);
	if (strncmp(buf, "PACL", 4) != 0) {
		fclose(stream);
		return 0;
	}
	int len = read_little_endian_int(buf+0x10);
	if (arc_size < size_t(0x20 + len*0x20)) {
		fclose(stream);
		return 0;
	}
	int i; int slen = 0;
	for (i=0; i<len; i++) {
		fread(buf, 0x20, 1, stream);
		slen += strlen(buf)+1;
	}
	fclose(stream);
	return slen;
}
void ARCFILE::ListupFiles(int fname_len) {
	int i; char fbuf[0x20];
	fname_len *= 2;
	char* buf = new char[fname_len];
	FILE* stream = fopen(arcname, "rb");
	if (stream == 0) {
		fprintf(stderr, "Cannot open archive file : %s\n",arcname);
		return;
	}
	fread(fbuf,0x20,1,stream);
	int len = read_little_endian_int(fbuf+0x10);
	ARCFILE_ATOM atom;
	for (i=0; i<len; i++) {
		fread(fbuf, 0x20, 1, stream);
		int l = strlen(fbuf);
		if (l*2+2 > fname_len) {
			break;
		}
		atom.offset = read_little_endian_int(fbuf+0x10);
		atom.arcsize = read_little_endian_int(fbuf+0x14);
		atom.filesize = read_little_endian_int(fbuf+0x18);
		int j; for (j=0; j<l; j++) {
			buf[j] = fbuf[j];
			buf[j+l+1] = tolower(fbuf[j]);
		}
		buf[j] = buf[j+l+1] = 0;
		atom.filename = buf;
		atom.filename_lower = buf+l+1;
		arc_atom.push_back(atom);
		buf += l*2+2; fname_len -= l*2+2;
	}
	fclose(stream);
	return;
}
int DIRFILE::CheckFileDeal(void) {
	DIR* dir; struct dirent* ent;
	int flen = 0;
	dir = opendir(arcname);
	if (dir == 0) {
		fprintf(stderr, "Cannot open dir file : %s\n",arcname);
		return 0;
	}
	int count = 0;
	while( (ent = readdir(dir)) != NULL) {
		count++;
		flen += strlen(ent->d_name)+1;
	}
	closedir(dir);
	return flen;
}
void DIRFILE::ListupFiles(int fname_len) {
	DIR* dir; int i;
	fname_len *= 2;
	dir = opendir(arcname);
	if (dir == 0) { 
		fprintf(stderr, "Cannot open dir file : %s\n",arcname);
		return;
	}
	/* ���Ū�� arcname �Υǥ��쥯�ȥ�˰�ư���� */
	int old_dir_fd = open(".",O_RDONLY);
	if (old_dir_fd < 0) {
		closedir(dir);
		return;
	}
	if (chdir(arcname) != 0) {
		fprintf(stderr, "Cannot open dir file : %s\n",arcname);
		closedir(dir);
		close(old_dir_fd);
		return;
	};
	
	char* buf = new char[fname_len];
	ARCFILE_ATOM atom;
	struct stat sb;
	struct dirent* ent;
	while( (ent = readdir(dir)) != NULL) {
		if (stat(ent->d_name, &sb) == -1) continue;
		if ( (sb.st_mode & S_IFMT) == S_IFREG) {
			atom.offset = 0;
			atom.arcsize = sb.st_size;
			atom.filesize = sb.st_size;
		} else if ( (sb.st_mode & S_IFMT) == S_IFDIR) {
			atom.offset = 0;
			atom.arcsize = atom.filesize = 0;
		} else {
			continue;
		}
		int l = strlen(ent->d_name);
		if (l*2+2 > fname_len) {
			break;
		}
		int j; for (j=0; j<l+1; j++) {
			buf[j] = ent->d_name[j];
			buf[j+l+1] = tolower(ent->d_name[j]);
		}
		atom.filename = buf; atom.filename_lower = buf+l+1;
		arc_atom.push_back(atom);
		buf += l*2+2; fname_len -= l*2+2;
	}
	/* chdir() �����Τ򸵤���� */
	closedir(dir);
	fchdir(old_dir_fd); close(old_dir_fd);
	return;
}
int NULFILE::CheckFileDeal(void) {
	return 20;
}
void NULFILE::ListupFiles(int fname_len) {
	char* s = new char[40];
	ARCFILE_ATOM atom;
	atom.offset = 0; atom.arcsize = 0; atom.filesize = 0;
	strcpy(s, "** null dummy **");
	atom.filename = s;
	atom.filename_lower = s;
	arc_atom.push_back(atom);
}
int SCN2kFILE::CheckFileDeal(void) {
	/* �إå��Υ����å� */
	FILE* stream = fopen(arcname, "rb");
	if (stream == 0) {
		fprintf(stderr, "Cannot open archive file : %s\n",arcname);
		return 0;
	}
	fseek(stream, 0, 2); size_t arc_size = ftell(stream);
	fseek(stream, 0, 0);
	if (arc_size < 10000*8) {
		fclose(stream);
		return 0;
	}
	char* buf = new char[10000*8];
	fread(buf, 10000, 8, stream);
	/* size == 0 �Υǡ�����¸�ߤ��ʤ� */
	int count = 0;
	int i; for (i=0; i<10000; i++) {
		int tmp_offset = read_little_endian_int(buf+i*8);
		int tmp_size = read_little_endian_int(buf+i*8+4);
		if (tmp_size <= 0 || tmp_offset < 0 || tmp_offset+tmp_size > int(arc_size) ) continue;
		count++;
	}
	fclose(stream);
	delete[] buf;
	return count*13; /* �ե�����̾�� seenXXXX.txt �����顢���12ʸ��+null */
}
void SCN2kFILE::ListupFiles(int fname_len) {
	FILE* stream = fopen(arcname, "rb");
	if (stream == 0) {
		fprintf(stderr, "Cannot open archive file : %s\n",arcname);
		return;
	}
	char* sbuf = new char[fname_len];
	char* buf = new char[10000*8];
	fread(buf, 10000, 8, stream);
	fseek(stream, 0, 2); size_t arc_size = ftell(stream);
	ARCFILE_ATOM atom;
	int i; for (i=0; i<10000; i++) {
		char header[0x200];
		int tmp_offset = read_little_endian_int(buf+i*8);
		int tmp_size = read_little_endian_int(buf+i*8+4);
		if (tmp_size <= 0 || tmp_offset < 0 || tmp_offset+tmp_size > int(arc_size) ) continue;
		/* header �����ư��̷����ʤɤ�Ĵ�٤� */
		fseek(stream, tmp_offset, 0);
		fread(header, 0x200, 1, stream);
		int header_top = read_little_endian_int(header+0);
		int file_version = read_little_endian_int(header+4);

		if (file_version == 0x1adb2) ; // Little Busters!
		else if (file_version != 0x2712) continue; /* system version ���㤦 */

		if (header_top == 0x1cc) { /* �Ť����� : avg2000 */
			int header_size = read_little_endian_int(header+0)+read_little_endian_int(header+0x20)*4;
			int data_size = read_little_endian_int(header+0x24);
			atom.arcsize = data_size + header_size;
			atom.filesize = data_size + header_size;
			atom.private_data = header_size;

		} else if (header_top == 0x1b8) { /* ���븥�� */
			int header_size = read_little_endian_int(header+0)+read_little_endian_int(header+0x08)*4;
			int data_size = read_little_endian_int(header+0x0c);
			int compdata_size = read_little_endian_int(header+0x10);
			atom.arcsize = compdata_size + header_size;
			atom.filesize = data_size + header_size;
			atom.private_data = header_size;
			
		} else if (header_top == 0x1d0) { /* ������������ reallive */
			int header_size = read_little_endian_int(header+0x20);
			int data_size = read_little_endian_int(header+0x24);
			int compdata_size = read_little_endian_int(header+0x28);
			atom.arcsize = compdata_size + header_size;
			atom.filesize = data_size + header_size;
			atom.private_data = header_size;
		} else {
			fprintf(stderr,"invalid header top; %x : not supported\n",header_top);
			continue; /* ���ݡ��Ȥ��ʤ����� */
		}

		atom.offset = tmp_offset;
		atom.filename = sbuf;
		atom.filename_lower = sbuf;
		arc_atom.push_back(atom);
		sprintf(sbuf, "seen%04d.txt",i); sbuf += 13;
	}
	delete[] buf;
	fclose(stream);
	return;
}

/********************************************************
** FILESEARCH ���饹�μ���
*/

FILESEARCH::FILESEARCH(void) {
	int i;
	root_dir = 0; dat_dir = 0;
	for (i=0; i<TYPEMAX; i++) {
		searcher[i] = 0;
		filenames[i] = default_dirnames[i];
		is_archived[i] = default_is_archived[i];
	}
}
FILESEARCH::~FILESEARCH(void) {
	int i;
	for (i=0; i<TYPEMAX; i++) {
		if (filenames[i] != 0 && filenames[i] != default_dirnames[i]) delete[] filenames[i];
		if (searcher[i] && searcher[i] != dat_dir && searcher[i] != root_dir) {
			delete searcher[i];
		}
	}
	if (dat_dir && dat_dir != root_dir) delete dat_dir;
	if (root_dir) delete root_dir;
}

int FILESEARCH::InitRoot(char* root) {
	/* ɬ�פ˱����� ~/ ��Ÿ�� */
	if (root[0] == '~' && root[1] == '/') {
		char* home = getenv("HOME");
		if (home != 0) {
			char* new_root = new char[strlen(home)+strlen(root)];
			strcpy(new_root, home);
			strcat(new_root, root+1);
			root = new_root;
		}
	}
	/* �Ť��ǡ�����ä� */
	int i;
	for (i=0; i<TYPEMAX; i++) {
		if (searcher[i] != 0 &&
			searcher[i] != root_dir &&
			searcher[i] != dat_dir) {
				delete searcher[i];
		}
		searcher[i] = 0;
	}
	if (dat_dir && root_dir != dat_dir) delete dat_dir;
	if (root_dir) delete root_dir;
	dat_dir = 0;

	/* �������ǥ��쥯�ȥ�Τ�Ȥǽ���� */
	root_dir = new DIRFILE(root);
	root_dir->Init();
	/* dat/ �򸡺� */
	char* dat_path = root_dir->SearchFile("dat");
	if (dat_path == 0) {
		/* ���Ĥ���ʤ��ä��� root �� dat ������ˤĤ��� */
		dat_dir = root_dir;
	} else {
		dat_dir = new DIRFILE(dat_path);
		delete[] dat_path;
		dat_dir->Init();
	}
	searcher[ALL] = dat_dir;
	searcher[ROOT] = root_dir;
	return 0;
}

void FILESEARCH::SetFileInformation(FILETYPE tp, ARCTYPE is_arc, char* filename) {
	int type = tp;
	if (type < 0 || type >= TYPEMAX) return;
	ARCFILE* next_arc = 0;
	/* ���Ǥ� searcher ��¸�ߤ���в��� */
	if (searcher[type] != 0 &&
	  searcher[type] != root_dir &&
	  searcher[type] != dat_dir) {
		next_arc = searcher[type]->Next();
		delete searcher[type];
	}
	searcher[type] = 0;
	/* Ŭ���˽���� */
	if (filenames[type] != 0 &&
		filenames[type] != default_dirnames[type]) delete[] filenames[type];
	filenames[type] = new char[strlen(filename)+1];
	strcpy(filenames[type], filename);
	is_archived[type] = is_arc;
	searcher[type] = MakeARCFILE(is_arc, filename);
	if (searcher[type] && next_arc)
		searcher[type]->SetNext(next_arc);
	return;
}
void FILESEARCH::AppendFileInformation(FILETYPE tp, ARCTYPE is_arc, char* filename) {
	int type = tp;
	if (type < 0 || type >= TYPEMAX) return;
	/* searcher ���ޤ�������Ƥ��Ƥʤ���� */
	if (searcher[type] == 0 ||
	  searcher[type] == root_dir ||
	  searcher[type] == dat_dir) {
		searcher[type] = MakeARCFILE(is_archived[type], filenames[type]);
		if (searcher[type] == 0) { /* �����Ǥ��ʤ��ä���� */
			/* ���η������ FileInformation �Ȥ��� */
			SetFileInformation(tp, is_arc, filename);
			return;
		}
	}
	/* ����� */
	ARCFILE* arc = MakeARCFILE(is_arc, filename);
	/* append */
	ARCFILE* cur;
	for (cur=searcher[type]; cur->Next() != 0; cur = cur->Next()) ;
	cur->SetNext(arc);
	return;
}

ARCFILE* FILESEARCH::MakeARCFILE(ARCTYPE tp, char* filename) {
	ARCFILE* arc = 0;
	char* file;
	if (filename == 0) goto err;
	if (tp == ATYPE_DIR) {
		file = root_dir->SearchFile(filename);
	} else {
		file = dat_dir->SearchFile(filename);
		if (file == 0)
			file = root_dir->SearchFile(filename);
	}
	if (file == 0) goto err;
	switch(tp) {
		case ATYPE_DIR: arc = new DIRFILE(file); break;
		case ATYPE_SCN2k:
		case ATYPE_ARC: {
			FILE* f = fopen(file, "rb");
			if (f == 0) goto err;
			char header[32];
			memset(header, 0, 32);
			fread(header, 32, 1, f);
			fclose(f);
			char magic_raf[8] = {'C','A','P','F',1,0,0,0};
			if (strncmp(header, "PACL", 4) == 0) arc = new ARCFILE(file);
			else arc = new SCN2kFILE(file);
			}
			break;
		default: fprintf(stderr,"FILESEARCH::MAKEARCFILE : invalid archive type; type %d name %s\n",tp,filename);
			delete[] file;
			goto err;
	}
	delete[] file;
	return arc;
err:
	arc = new NULFILE;
	return arc;
	
}

ARCINFO* FILESEARCH::Find(FILETYPE type, const char* fname, const char* ext) {
	if (searcher[type] == 0) {
		/* searcher ���� */
		if (filenames[type] == 0) {
			searcher[type] = dat_dir;
		} else {
			searcher[type] = MakeARCFILE(is_archived[type], filenames[type]);
			if (searcher[type] == 0) {
				fprintf(stderr,"FILESEARCH::Find : invalid archive type; type %d name %s\n",type,fname);
				return 0;
			}
		}
	}
	return searcher[type]->Find(fname,ext);
}

char** FILESEARCH::ListAll(FILETYPE type) {
	int i;
	/* �Ȥꤢ���� searcher ������ */
	Find(type, "THIS FILENAME MAY NOT EXIST IN THE FILE SYSTEM !!!");
	if (searcher[type] == 0) return 0;
	/* ���ե�����Υꥹ�ȥ��å� */
	int deal = 0;
	ARCFILE* file;
	for (file = searcher[type]; file != 0; file = file->Next())
		deal += file->Deal();
	if (deal <= 0) return 0;
	char** ret_list = new char*[deal+1];
	int count = 0;
	for (file = searcher[type]; file != 0; file = file->Next()) {
		file->InitList();
		char* f;
		while( (f = file->ListItem() ) != 0) {
			ret_list[count] = new char[strlen(f)+1];
			strcpy(ret_list[count], f);
			count++;
		}
	}
	ret_list[count] = 0;
	return ret_list;
}

ARCINFO::ARCINFO(const char* __arcname, ARCFILE_ATOM& atom) : info(atom) {
	arcfile = new char[strlen(__arcname)+1];
	strcpy(arcfile, __arcname);
	use_mmap = false;
	mmapped_memory = 0;
	data = 0;
	fd = -1;
}

ARCINFO::~ARCINFO() {
#ifdef HAVE_MMAP
	if (mmapped_memory) munmap(mmapped_memory, info.arcsize);
#endif /* HAVE_MMAP */
	if (fd != -1) close(fd);
	if (data != mmapped_memory) delete[] data;
	delete[] arcfile;
}

int ARCINFO::Size(void) const {
	return info.filesize;
}

/* ���ԡ����֤� */
char* ARCINFO::CopyRead(void) {
	const char* d = Read();
	if (d == 0) return 0;
	int s = Size();
	if (s <= 0) return 0;
	char* ret = new char[s]; memcpy(ret, d, s);
	return ret;
}

const char* ARCINFO::Path(void) const {
	if (info.offset != 0) return 0; /* archive file �ʤΤǥѥ��򵢤��ʤ� */
	char* ret = new char[strlen(arcfile)+1];
	strcpy(ret, arcfile);
	return ret;
}
/* �ߴ������� */
FILE* ARCINFO::OpenFile(int* length) const {
	FILE* f = fopen(arcfile, "rb");
	if (info.offset) lseek(fileno(f), info.offset, SEEK_SET);
	if (length) *length = info.arcsize;
	return f;
}

// Ÿ�������Ϥʤ�
bool ARCINFO::ExecExtract(void) {
	return true;
}
/* �ɤ߹��ߤ򳫻Ϥ��� */
const char* ARCINFO::Read(void) {
	// ���Ǥ˥ǡ������ɤ߹��ߺѤߤʤ鲿�⤷�ʤ�
	if (data) return data;

	if (info.offset < 0 || info.arcsize <= 0) {
		return 0;
	}
	/* �ե�����򳫤� */
	fd = open(arcfile, O_RDONLY);
	if (fd < 0) {
		return 0;
	}
	if (lseek(fd, info.offset, 0) != info.offset) {
		close(fd); fd = -1; return 0;
	}
	/* mmap ���ߤ� */
#ifdef HAVE_MMAP
	mmapped_memory = (char*)mmap(0, info.arcsize, PROT_READ, MAP_SHARED, fd, info.offset);
	if (mmapped_memory != MAP_FAILED) {
		use_mmap = true;
		data = (const char*)mmapped_memory;
	} else
#endif /* HAVE_MMAP */
	{
		/* ���ԡ����̤˥ե�������ɤ߹��� */
		char* d = new char[info.arcsize];
		read(fd, d, info.arcsize);
		close(fd);
		fd = -1;
		use_mmap = false;
		data = d;
	}
	/* Ÿ������ */
	if (! ExecExtract()) {
		// ����
#ifdef HAVE_MMAP
		if (use_mmap) {
			munmap(mmapped_memory, info.arcsize);
			if (data == (const char*)mmapped_memory) data = 0;
		}
#endif /* HAVE_MMAP */
		delete[] (char*)data;
		close(fd);
		fd = -1; data = 0;
		return 0;
	}
#ifdef HAVE_MMAP
	if (use_mmap && data != (const char*)mmapped_memory) {
		// ���Ǥ� mmap ��ɬ�פʤ�
		munmap(mmapped_memory, info.arcsize);
		close(fd);
		fd = -1;
		use_mmap = false;
	}
#endif /* HAVE_MMAP */
	return data;
}

/**********************************************
**
**	����Ÿ���ϥ��饹�����������
**
***********************************************
*/
GRPCONV::GRPCONV(void) {
	filename = 0;
	data = 0;
}
GRPCONV::~GRPCONV() {
	if (filename) delete[] filename;
}
void GRPCONV::Init(const char* f, const char* d, int dlen, int w, int h, bool is_m) {
	if (filename) delete[] filename;
	if (f == 0) {
		char* fn = new char[1];
		fn[0] = 0;
		filename = fn;
	} else {
		char* fn = new char[strlen(f)+1];
		strcpy(fn,f);
		filename = fn;
	}

	data = d;
	datalen = dlen;
	width = w;
	height = h;
	is_mask = is_m;
}
class PDTCONV : public GRPCONV {
	bool Read_PDT10(char* image);
	bool Read_PDT11(char* image);
public:
	PDTCONV(const char* _inbuf, int inlen, const char* fname);
	~PDTCONV() {}
	bool Read(char* image);
};
class G00CONV : public GRPCONV {
	struct REGION {
		int x1, y1, x2, y2;
		int Width() { return x2-x1+1;}
		int Height() { return y2-y1+1;}
		void FixVar(int& v, int& w) {
			if (v < 0) v = 0;
			if (v >= w) v = w-1;
		}
		void Fix(int w, int h) {
			FixVar(x1,w);
			FixVar(x2,w);
			FixVar(y1,h);
			FixVar(y2,h);
			if (x1 > x2) x2 = x1;
			if (y1 > y2) y2 = y1;
		}
	};

	void Copy_16bpp(char* image, int x, int y, const char* src, int bpl, int h);
	void Copy_32bpp(char* image, int x, int y, const char* src, int bpl, int h);
	bool Read_Type0(char* image);
	bool Read_Type1(char* image);
	bool Read_Type2(char* image);
public:
	G00CONV(const char* _inbuf, int _inlen, const char* fname);
	~G00CONV() { }
	bool Read(char* image);
};

class BMPCONV : public GRPCONV {
public:
	BMPCONV(const char* _inbuf, int _inlen, const char* fname);
	~BMPCONV() {};
	bool Read(char* image);
};
#if HAVE_LIBPNG
class PNGCONV : public GRPCONV {
	const char* png_data;
	static void png_read(png_structp, png_bytep, png_size_t);

public:
	PNGCONV(const char* _inbuf, int _inlen, const char* fname);
	~PNGCONV() {};
	bool Read(char* image);
};
#endif

#if HAVE_LIBJPEG
class JPEGCONV : public GRPCONV {

public:
	JPEGCONV(const char* _inbuf, int _inlen, const char* fname);
	~JPEGCONV() {};
	bool Read(char* image);
	void SetupSrc(struct jpeg_decompress_struct* cinfo, const char* data, int size);
	static void init_source(j_decompress_ptr cinfo);
	static boolean fill_input_buffer(j_decompress_ptr cinfo);
	static void skip_input_data(j_decompress_ptr cinfo, long num_bytes);
	static boolean resync_to_restart(j_decompress_ptr cinfo, int desired);
	static void term_source(j_decompress_ptr cinf);
};
#endif

GRPCONV* GRPCONV::AssignConverter(const char* inbuf, int inlen, const char* fname) {
	/* �ե���������Ƥ˱���������С������������Ƥ� */
	GRPCONV* conv = 0;
	if (inlen < 10) return 0; /* invalid file */
	if (conv == 0 && strncmp(inbuf, "PDT10", 5) == 0 || strncmp(inbuf, "PDT11", 5) == 0) { /* PDT10 or PDT11 */
		conv = new PDTCONV(inbuf, inlen, fname);
		if (conv->data == 0) { delete conv; conv = 0;}
	}
#if HAVE_LIBPNG
	unsigned char png_magic[4] = {0x89, 'P', 'N', 'G'};
	if (conv == 0 && memcmp(inbuf, png_magic,4) == 0) {
		conv = new PNGCONV(inbuf, inlen, fname);
		if (conv->data == 0) { delete conv; conv = 0;}
	}
#endif
#if HAVE_LIBJPEG
	if ( conv == 0 && *(unsigned char*)inbuf == 0xff && *(unsigned char*)(inbuf+1) == 0xd8 &&
		(strncmp(inbuf+6, "JFIF",4) == 0 || strncmp(inbuf+6,"Exif",4) == 0)) {
		conv = new JPEGCONV(inbuf, inlen, fname);
		if (conv->data == 0) { delete conv; conv = 0;}
	}
#endif
	if (conv == 0 && inbuf[0]=='B' && inbuf[1]=='M' && read_little_endian_int(inbuf+10)==0x36 && read_little_endian_int(inbuf+14) == 0x28) { // Windows BMP
		conv = new BMPCONV(inbuf, inlen, fname);
		if (conv->data == 0) { delete conv; conv = 0;}
	}
	if (conv == 0 && (inbuf[0] == 0 || inbuf[0] == 1 || inbuf[0] == 2)) { /* G00 */
		conv = new G00CONV(inbuf, inlen, fname);
		if (conv->data == 0) { delete conv; conv = 0;}
	}
	return conv;
}

PDTCONV::PDTCONV(const char* _inbuf, int _inlen,const char* filename) {
//	PDT FILE �Υإå�
//	+00 'PDT10'	(PDT11 ��̤�б�)
//	+08 �ե����륵���� (̵��)
//	+0C width (�ۤܤ��٤ơ�640)
//	+10 height(�ۤܤ��٤ơ�480)
//	+14 (mask ��) x ��ɸ (�ºݤ�̵�롦�������ե������ 0 )
//	+1c (mask ��) y��ɸ (�ºݤ�̵�� ���������ե������ 0 )
//	+20 mask ��¸�ߤ���С�mask �ؤΥݥ���

	/* �إå������å� */
	if (_inlen < 0x20) {
		fprintf(stderr, "Invalid PDT file %s : size is too small\n",filename);
		return;
	}
	if (strncmp(_inbuf, "PDT10", 5) != 0 && strncmp(_inbuf, "PDT11", 5) != 0) {
		fprintf(stderr, "Invalid PDT file %s : not 'PDT10 / PDT11' file.\n", filename);
		return;
	}
	if (size_t(_inlen) != size_t(read_little_endian_int(_inbuf+0x08))) {
		fprintf(stderr, "Invalid archive file %s : invalid header.(size)\n",
			filename);
		return;
	}

	int w = read_little_endian_int(_inbuf+0x0c);
	int h = read_little_endian_int(_inbuf+0x10);
	int mask_pt = read_little_endian_int(_inbuf + 0x1c);
	Init(filename, _inbuf, _inlen, w, h, mask_pt ? true : false);

	return;
}


G00CONV::G00CONV(const char* _inbuf, int _inlen, const char* filename) {
//	G00 FILE �Υإå�
//	+00 type (1, 2)
//	+01: width(word)
//	+03: height(word)
//	type 1: (color table �դ� LZ ���� ; PDT11 ���б�)
//		+05: ���̥�����(dword) ; +5 ����ȥǡ������ΤΥ�����
//		+09: Ÿ���奵����(dword)
//	type 2: (�ޥ����ġ����������ΰ��ʬ�䤷�Ƥ��줾�찵��)
//		+05: index size
//		+09: index table(each size is 0x18)
//			+00
//			
//		+09+0x18*size+00: data size
//		+09+0x18*size+04: out size
//		+09+0x18*size+08: (data top)
//

	/* �ǡ�����������ɤ߹��� */
	int type = *_inbuf;

	int w = read_little_endian_short(_inbuf+1);
	int h = read_little_endian_short(_inbuf+3);
	if (w < 0 || h < 0) return;

	if (type == 0 || type == 1) { // color table �դ�����
		if (_inlen < 13) {
			fprintf(stderr, "Invalid G00 file %s : size is too small\n",filename);
			return;
		}
		int data_sz = read_little_endian_int(_inbuf+5);

		if (_inlen != data_sz+5) {
			fprintf(stderr, "Invalid archive file %s : invalid header.(size)\n",
				filename);
			return;
		}
		Init(filename, _inbuf, _inlen, w, h, false);
	} else if (type == 2) { // color table �ʤ����ޥ����դ��Ĥΰ���

		int head_size = read_little_endian_short(_inbuf+5);
		if (head_size < 0 || head_size*24 > _inlen) return;

		const char* data_top = _inbuf + 9 + head_size*24;
		int data_sz = read_little_endian_int(data_top);
		if (_inbuf + _inlen != data_top + data_sz) {
			fprintf(stderr, "Invalid archive file %s : invalid header.(size)\n",
				filename);
			return;
		}
		Init(filename, _inbuf, _inlen, w, h, true);
	}
	return;
}

bool G00CONV::Read(char* image) {
	if (data == 0) return false;
	/* header ���� */
	int type = *data;
	if (type == 0) return Read_Type0(image);
	else if (type == 1) return Read_Type1(image);
	else if (type == 2) return Read_Type2(image);
}

/* ����Ū�� LZ ���̤�Ÿ���롼���� */
/* datasize �ϥǡ������礭����char / short / int ������ */
/* datatype �� Copy1Pixel (1�ǡ����Υ��ԡ�)�ڤ� ExtractData(LZ ���̤ξ��������
** �Ȥ����᥽�åɤ�����������饹 */
static int bitrev_table[256] = {
	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff};
template<class DataType, class DataSize> inline int lzExtract(DataType& datatype,const char*& src, char*& dest, const char* srcend, char* destend) {
	int count = 0;
	const char* lsrcend = srcend; char* ldestend = destend;
	const char* lsrc = src; char* ldest = dest;

	if (lsrc+50 < lsrcend && ldest+1024 < ldestend) {
		/* �ޤ����ϰϥ����å���ˤ����ƹ�®�ʥ롼�����Ȥ� */
		lsrcend -= 50;
		ldestend += 1024;
		while (ldest < ldestend && lsrc < lsrcend) {
			count += 8;
			int flag = int(*(unsigned char*)lsrc++);
			if (datatype.IsRev()) flag = bitrev_table[flag];
			int i; for (i=0; i<8; i++) {
				if (flag & 0x80) {
					datatype.Copy1Pixel(lsrc, ldest);
				} else {
					int data, size;
					datatype.ExtractData(lsrc, data, size);
					DataSize* p_dest = ((DataSize*)ldest) - data;
					int k; for (k=0; k<size; k++) {
						p_dest[data] = *p_dest;
						p_dest++;
					}
					ldest += size*sizeof(DataSize);
				}
				flag <<= 1;
			}
		}
		lsrcend += 50;
		ldestend += 1024;
	}
	/* �Ĥ���Ѵ� */
	while (ldest < ldestend && lsrc < lsrcend) {
		count += 8;
		int flag = int(*(unsigned char*)lsrc++);
		if (datatype.IsRev()) flag = bitrev_table[flag];
		int i; for (i=0; i<8 && ldest < ldestend && lsrc < lsrcend; i++) {
			if (flag & 0x80) {
				datatype.Copy1Pixel(lsrc, ldest);
			} else {
				int data, size;
				datatype.ExtractData(lsrc, data, size);
				DataSize* p_dest = ((DataSize*)ldest) - data;
				int k; for (k=0; k<size; k++) {
					p_dest[data] = *p_dest;
					p_dest++;
				}
				ldest += size*sizeof(DataSize);
			}
			flag <<= 1;
		}
	}
	dest=ldest; src=lsrc;
	return 0;
}
/* �����򸺤餹�����wrapper */
template<class DataType, class DataSize> inline int lzExtract(DataType datatype, DataSize datasize ,const char*& src, char*& dest, const char* srcend, char* destend) {
	return lzExtract<DataType, DataSize>(datatype,src,dest,srcend,destend);
}

/* ���̤� PDT */
class Extract_DataType {
public:
	static void ExtractData(const char*& lsrc, int& data, int& size) {
		data = read_little_endian_short(lsrc) & 0xffff;
		size = (data & 0x0f) + 1;
		data = (data>>4)+1;
		lsrc += 2;
	}
	static void Copy1Pixel(const char*& lsrc, char*& ldest) {
#ifdef WORDS_BIGENDIAN
		ldest[3] = lsrc[0];
		ldest[2] = lsrc[1];
		ldest[1] = lsrc[2];
		ldest[0] = 0;
#else
		*(int*)ldest = read_little_endian_int(lsrc); ldest[3]=0;
#endif
		lsrc += 3; ldest += 4;
	}
	static int IsRev(void) { return 0; }
};

/* PDT11 ������ʳ��Ѵ� */
class Extract_DataType_PDT11 {
	int* index_table;
public:
	Extract_DataType_PDT11(int* it) { index_table = it; }
	void ExtractData(const char*& lsrc, int& data, int& size) {
		data = int(*(const unsigned char*)lsrc);
		size = (data>>4) + 2;
		data = index_table[data&0x0f];
		lsrc++;
	}
	static void Copy1Pixel(const char*& lsrc, char*& ldest) {
		*ldest = *lsrc;
		ldest++; lsrc++;
	}
	static int IsRev(void) { return 0; }
};
/* �ޥ����� */
class Extract_DataType_Mask {
public:
	void ExtractData(const char*& lsrc, int& data, int& size) {
		int d = read_little_endian_short(lsrc) & 0xffff;
		size = (d & 0xff) + 2;
		data = (d>>8)+1;
		lsrc += 2;
	}
	static void Copy1Pixel(const char*& lsrc, char*& ldest) {
		*ldest = *lsrc;
		ldest++; lsrc++;
	}
	static int IsRev(void) { return 0; }
};
/* ����� */
class Extract_DataType_ARC {
public:
	void ExtractData(const char*& lsrc, int& data, int& size) {
		data = read_little_endian_short(lsrc) & 0xffff;
		size = (data&0x0f) + 2;
		data = (data>>4) + 1;
		lsrc+= 2;
	}
	static void Copy1Pixel(const char*& lsrc, char*& ldest) {
		*ldest = *lsrc;
		ldest++; lsrc++;
	}
	static int IsRev(void) { return 0; }
};
/* avg2000 �Υ��ʥꥪ�� */
class Extract_DataType_SCN2k {
public:
	void ExtractData(const char*& lsrc, int& data, int& size) {
		data = read_little_endian_short(lsrc) & 0xffff;
		size = (data&0x0f) + 2;
		data = (data>>4);
		lsrc+= 2;
	}
	static void Copy1Pixel(const char*& lsrc, char*& ldest) {
		*ldest = *lsrc;
		ldest++; lsrc++;
	}
	static int IsRev(void) { return 1; }
};
/* ReadLive �� type0 */
class Extract_DataType_G00Type0 {
public:
	static void ExtractData(const char*& lsrc, int& data, int& size) {
		data = read_little_endian_short(lsrc) & 0xffff;
		size = ((data & 0x0f)+ 1) * 3;
		data = (data>>4) * 3;
		lsrc += 2;
	}
	static void Copy1Pixel(const char*& lsrc, char*& ldest) {
#ifdef WORDS_BIGENDIAN
		ldest[0] = lsrc[0];
		ldest[1] = lsrc[1];
		ldest[2] = lsrc[2];
#else /* LITTLE ENDIAN / intel architecture */
		*(int*)ldest = *(int*)lsrc;
#endif
		lsrc += 3; ldest += 3;
	}
	static int IsRev(void) { return 1; }
};


bool PDTCONV::Read(char* image) {
	if (data == 0) return false;

	if (strncmp(data, "PDT10", 5) == 0) {
		if (! Read_PDT10(image)) return false;
	} else if (strncmp(data, "PDT11", 5) == 0) {
		if (! Read_PDT11(image)) return false;
	}
	if (! is_mask) return true;
	// �ޥ����ɤ߹���
	int mask_pt = read_little_endian_int(data + 0x1c);
	char* buf = new char[width*height+1024];
	const char* src = data + mask_pt;
	const char* srcend = data + datalen;
	char* dest = buf;
	char* destend = buf + width*height;
	while(lzExtract(Extract_DataType_Mask(), char(), src, dest, srcend, destend)) ;
	int i; int len = width*height;
	src = buf; dest = image;
	for (i=0; i<len; i++) {
		*(int*)dest |= int(*(unsigned char*)src) << 24;
		src++;
		dest += 4;
	}
	delete[] buf;
	return true;
}


bool PDTCONV::Read_PDT10(char* image) {
	int mask_pt = read_little_endian_int(data + 0x1c);

	const char* src = data + 0x20;
	const char* srcend;
	if (mask_pt == 0) srcend = data + datalen;
	else srcend = data + mask_pt;

	char* dest = image;
	char* destend;

	destend = image + width*height*4;
	while(lzExtract(Extract_DataType(), int(), src, dest, srcend, destend)) ;
	return true;
}
bool PDTCONV::Read_PDT11(char* image) {
	int index_table[16];
	int color_table[256];
	int i;
	for (i=0; i<16; i++)
		index_table[i] = read_little_endian_int(data + 0x420 + i*4);

	int mask_pt = read_little_endian_int(data + 0x1c);

	const char* src = data + 0x460;
	const char* srcend;
	if (mask_pt == 0) srcend = data + datalen;
	else srcend = data + mask_pt;

	char* dest = image;
	char* destend = image + width*height;

	while(lzExtract(Extract_DataType_PDT11(index_table), char(), src, dest, srcend, destend)) ;

	const char* cur = data + 0x20;
	for (i=0; i<256; i++) {
		color_table[i] =  read_little_endian_int(cur);
		cur += 4;
	}
	src = image + width*height;
	int* desti = (int*)(image + width*height*4);
	while(desti != (int*)image)
		*--desti = color_table[*(unsigned char*)--src];
	return true;
}

/* dest �� dest_end ���� 256 byte �ʾ���ޤ�
** �񤭹��߲�ǽ�Ǥ��뤳�ȡ�
*/
void ARCINFO::Extract(char*& dest_start, char*& src_start, char* dest_end, char* src_end) {
	const char* src = src_start;
	while (lzExtract(Extract_DataType_ARC(), char(), src, dest_start, src_end, dest_end)) ;
	src_start = (char*)src;
	return;
}
void ARCINFO::Extract2k(char*& dest_start, char*& src_start, char* dest_end, char* src_end) {
	const char* src = src_start;
	while (lzExtract(Extract_DataType_SCN2k(), char(), src, dest_start, src_end, dest_end)) ;
	src_start = (char*)src;
	return;
}

bool ARCINFO_AVG32::ExecExtract(void) {
	// �إå��Υ����å�
	if (strncmp(data, "PACK", 4) != 0) return false;
	if (read_little_endian_int(data+8) != info.filesize) return false;
	if (read_little_endian_int(data+12) != info.arcsize) return false;

	// �ե������Ÿ������
	char* ret_data = new char[info.filesize+1024];

	const char* s = data + 0x10;
	const char* send = data + info.arcsize;
	char* d = ret_data;
	char* dend = ret_data + info.filesize;
	while(lzExtract(Extract_DataType_ARC(), char(), s, d, send, dend)) ;
	if (! use_mmap) delete[] data;
	data = ret_data;
	return true;
}

char ARCINFO2k::decode_seed[256] ={
 0x8b ,0xe5 ,0x5d ,0xc3 ,0xa1 ,0xe0 ,0x30 ,0x44 ,0x00 ,0x85 ,0xc0 ,0x74 ,0x09 ,0x5f ,0x5e ,0x33
,0xc0 ,0x5b ,0x8b ,0xe5 ,0x5d ,0xc3 ,0x8b ,0x45 ,0x0c ,0x85 ,0xc0 ,0x75 ,0x14 ,0x8b ,0x55 ,0xec
,0x83 ,0xc2 ,0x20 ,0x52 ,0x6a ,0x00 ,0xe8 ,0xf5 ,0x28 ,0x01 ,0x00 ,0x83 ,0xc4 ,0x08 ,0x89 ,0x45
,0x0c ,0x8b ,0x45 ,0xe4 ,0x6a ,0x00 ,0x6a ,0x00 ,0x50 ,0x53 ,0xff ,0x15 ,0x34 ,0xb1 ,0x43 ,0x00
,0x8b ,0x45 ,0x10 ,0x85 ,0xc0 ,0x74 ,0x05 ,0x8b ,0x4d ,0xec ,0x89 ,0x08 ,0x8a ,0x45 ,0xf0 ,0x84
,0xc0 ,0x75 ,0x78 ,0xa1 ,0xe0 ,0x30 ,0x44 ,0x00 ,0x8b ,0x7d ,0xe8 ,0x8b ,0x75 ,0x0c ,0x85 ,0xc0
,0x75 ,0x44 ,0x8b ,0x1d ,0xd0 ,0xb0 ,0x43 ,0x00 ,0x85 ,0xff ,0x76 ,0x37 ,0x81 ,0xff ,0x00 ,0x00
,0x04 ,0x00 ,0x6a ,0x00 ,0x76 ,0x43 ,0x8b ,0x45 ,0xf8 ,0x8d ,0x55 ,0xfc ,0x52 ,0x68 ,0x00 ,0x00
,0x04 ,0x00 ,0x56 ,0x50 ,0xff ,0x15 ,0x2c ,0xb1 ,0x43 ,0x00 ,0x6a ,0x05 ,0xff ,0xd3 ,0xa1 ,0xe0
,0x30 ,0x44 ,0x00 ,0x81 ,0xef ,0x00 ,0x00 ,0x04 ,0x00 ,0x81 ,0xc6 ,0x00 ,0x00 ,0x04 ,0x00 ,0x85
,0xc0 ,0x74 ,0xc5 ,0x8b ,0x5d ,0xf8 ,0x53 ,0xe8 ,0xf4 ,0xfb ,0xff ,0xff ,0x8b ,0x45 ,0x0c ,0x83
,0xc4 ,0x04 ,0x5f ,0x5e ,0x5b ,0x8b ,0xe5 ,0x5d ,0xc3 ,0x8b ,0x55 ,0xf8 ,0x8d ,0x4d ,0xfc ,0x51
,0x57 ,0x56 ,0x52 ,0xff ,0x15 ,0x2c ,0xb1 ,0x43 ,0x00 ,0xeb ,0xd8 ,0x8b ,0x45 ,0xe8 ,0x83 ,0xc0
,0x20 ,0x50 ,0x6a ,0x00 ,0xe8 ,0x47 ,0x28 ,0x01 ,0x00 ,0x8b ,0x7d ,0xe8 ,0x89 ,0x45 ,0xf4 ,0x8b
,0xf0 ,0xa1 ,0xe0 ,0x30 ,0x44 ,0x00 ,0x83 ,0xc4 ,0x08 ,0x85 ,0xc0 ,0x75 ,0x56 ,0x8b ,0x1d ,0xd0
,0xb0 ,0x43 ,0x00 ,0x85 ,0xff ,0x76 ,0x49 ,0x81 ,0xff ,0x00 ,0x00 ,0x04 ,0x00 ,0x6a ,0x00 ,0x76};
char ARCINFO2k::decode_seed2[16] = {
	0xa8, 0x28, 0xfd, 0x66,
	0xa0, 0x23, 0x77, 0x69,
	0xf9, 0x45, 0xf8, 0x2c,
	0x7c, 0x00, 0xad, 0xf4
};

bool ARCINFO2k::ExecExtract(void) {
	int i;
	char* ret_data = new char[info.filesize + 1024];
	char* decoded_data = new char[info.arcsize + 1024];
	
	/* header �Υ��ԡ� */
	memcpy(ret_data, data, info.private_data);

	/* �ޤ���xor �ΰŹ沽��� */
	const char* s; const char* send;
	char* d; char* dend;

	s = data + info.private_data;
	send = data + info.arcsize;
	d = decoded_data + info.private_data;
	dend = decoded_data + info.arcsize;
	i = 0;
	while(s != send)
		*d++ = *s++ ^ decode_seed[(i++)&0xff];

	if (info.filesize == info.arcsize) {
		memcpy(ret_data+info.private_data, decoded_data + info.private_data + 8, info.arcsize - info.private_data - 8);
	} else {
		/* ���̤���Ƥ���ʤ顢�����Ÿ�� */
		s = (const char*)(decoded_data + info.private_data + 8);
		send = (const char*)(decoded_data + info.arcsize);
		d = ret_data + info.private_data;
		dend = ret_data + info.filesize;
		while(lzExtract(Extract_DataType_SCN2k(), char(), s, d, send, dend)) ;
	}
	if (read_little_endian_int(data+4) == 0x1adb2) { // Little Busters!
		int header_size = info.private_data;
		for (i=0x100; i<=0x200 && header_size+i < info.filesize; i++) {
			ret_data[header_size+i] ^= decode_seed2[i&0x0f];
		}
	}
	delete[] decoded_data;
	if (! use_mmap) delete[] data;
	data = ret_data;
	return true;
}

bool G00CONV::Read_Type0(char* image) {
	int uncompress_size = read_little_endian_int(data+9);
	char* uncompress_data = new char[uncompress_size+1024];

	// �ޤ�Ÿ��
	const char* src = data + 13;
	const char* srcend = data + datalen;
	char* dest = uncompress_data;
	char* dstend = uncompress_data + uncompress_size;
	while(lzExtract(Extract_DataType_G00Type0(), char(), src, dest, srcend, dstend));
	// image �˥��ԡ�
	CopyRGB(image, uncompress_data);
	delete[] uncompress_data;
	return true;
}
bool G00CONV::Read_Type1(char* image) {
	int i;
	int uncompress_size = read_little_endian_int(data+9) + 1;
	char* uncompress_data = new char[uncompress_size + 1024];

	// �ޤ���Ÿ��
	const char* src = data + 13;
	const char* srcend = data + datalen;
	char* dest = uncompress_data;
	char* destend = uncompress_data + uncompress_size;

	while(lzExtract(Extract_DataType_SCN2k(), char(), src, dest, srcend, destend));

	int colortable[256];
	memset(colortable, 0, sizeof(int)*256);
	int colortable_len = read_little_endian_short(uncompress_data);
	if (colortable_len > 256) colortable_len = 256;
	if (colortable_len < 0) colortable_len = 0;
	for (i=0; i<colortable_len; i++) {
		colortable[i] = read_little_endian_int(uncompress_data+2+i*4);
	}
	src = uncompress_data + 2 + read_little_endian_short(uncompress_data)*4;
	srcend = uncompress_data + uncompress_size;
	dest = image; destend = image + width*height*4;
	while(dest < destend && src < srcend) {
		*(int*)dest = colortable[*(unsigned char*)src];
		dest += 4; src ++;
	}
	delete[] uncompress_data;
	return true;
}

bool G00CONV::Read_Type2(char* image) {
	memset(image, 0, width*height*4);
	/* ʬ���ΰ������ */
	int region_deal = read_little_endian_int(data+5);
	REGION* region_table = new REGION[region_deal];

	const char* head = data + 9;
	int i; for (i=0; i<region_deal; i++) {
		region_table[i].x1 = read_little_endian_int(head+0);
		region_table[i].y1 = read_little_endian_int(head+4);
		region_table[i].x2 = read_little_endian_int(head+8);
		region_table[i].y2 = read_little_endian_int(head+12);
		region_table[i].Fix(width, height);
		head += 24;
	}

	// Ÿ��
	int uncompress_size = read_little_endian_int(head+4);
	char* uncompress_data = new char[uncompress_size + 1024];

	const char* src = head + 8;
	const char* srcend = data + datalen;
	char* dest = uncompress_data;
	char* destend = uncompress_data + uncompress_size;
	while(lzExtract(Extract_DataType_SCN2k(), char(), src, dest, srcend, destend));

	/* region_deal2 == region_deal �ΤϤ��ġ�*/
	int region_deal2 = read_little_endian_int(uncompress_data);
	if (region_deal > region_deal2) region_deal = region_deal2;

	for (i=0; i < region_deal; i++) {
		int offset = read_little_endian_int(uncompress_data + i*8 + 4);
		int length = read_little_endian_int(uncompress_data + i*8 + 8);
		src = (const char*)(uncompress_data + offset + 0x74);
		srcend = (const char*)(uncompress_data + offset + length);
		while(src < srcend) {
			int x, y, w, h;
			/* ���ԡ������ΰ������ */
			x = read_little_endian_short(src);
			y = read_little_endian_short(src+2);
			w = read_little_endian_short(src+6);
			h = read_little_endian_short(src+8);
			src += 0x5c;

			x += region_table[i].x1;
			y += region_table[i].y1;

			Copy_32bpp(image, x, y, src, w*4, h);

			src += w*h*4;
		}
	}
	delete[] uncompress_data;
	delete[] region_table;
	return true;
}

void G00CONV::Copy_32bpp(char* image, int x, int y, const char* src, int bpl, int h) {
	int i;
	int* dest = (int*)(image + x*4 + y*4*width);
	int w = bpl / 4;
	for (i=0; i<h; i++) {
		const char* s = src;
		int* d = dest;
		int j; for (j=0; j<w; j++) {
			*d++ = read_little_endian_int(s);
			s += 4;
		}
		src += bpl; dest += width;
	}
}

void GRPCONV::CopyRGBA_rev(char* image, const char* buf) {
	int mask = is_mask ? 0 : 0xff000000;
	/* ���Ѵ���Ԥ� */
	int len = width * height;
	int i;
	unsigned char* s = (unsigned char*)buf;
	int* d = (int*)image;
	for(i=0; i<len; i++) {
		*d = (int(s[2])) | (int(s[1])<<8) | (int(s[0])<<16) | (int(s[3])<<24) | mask;
		d++; s += 4;
	}
	return;
}

void GRPCONV::CopyRGBA(char* image, const char* buf) {
	if (!is_mask) {
		CopyRGB(image, buf);
		return;
	}
	/* ���Ѵ���Ԥ� */
	int len = width * height;
	int i;
	int* outbuf = (int*)image;
	for(i=0; i<len; i++) {
		*outbuf++ =  read_little_endian_int(buf);
		buf += 4;
	}
	return;
}
void GRPCONV::CopyRGB(char* image, const char* buf) {
	/* ���Ѵ���Ԥ� */
	int len = width * height;
	int i;
	unsigned char* s = (unsigned char*)buf;
	int* d = (int*)image;
	for(i=0; i<len; i++) {
		*d = (int(s[0])) | (int(s[1])<<8) | (int(s[2])<<16) | 0xff000000;
		d++; s+=3;
	}
	return;
}

#if HAVE_LIBPNG
PNGCONV::PNGCONV(const char* _inbuf, int _inlen, const char* _filename) {
	int w,h,type;
	png_structp png_ptr = 0;
	png_infop info_ptr = 0;
	png_infop end_info = 0;

	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
	if (!png_ptr) return;

	info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr) goto err;

	end_info = png_create_info_struct(png_ptr);
	if (!end_info) goto err;

        if (setjmp(png_jmpbuf(png_ptr))) {
                /* error occured !! */
		goto err;
        }

	/* initialize I/O */
	png_data = _inbuf;
	png_set_read_fn(png_ptr, (png_voidp)this, &png_read);

	png_read_info(png_ptr, info_ptr);

	w = png_get_image_width(png_ptr, info_ptr);
	h = png_get_image_height(png_ptr, info_ptr);
	type = png_get_color_type(png_ptr, info_ptr);

	if (type == PNG_COLOR_TYPE_GRAY || type == PNG_COLOR_TYPE_GRAY_ALPHA) goto err; // not supported

	Init(filename, _inbuf, _inlen, w, h, type == PNG_COLOR_TYPE_RGB_ALPHA ? true : false);

err:
	if (png_ptr) {
		if (end_info)
			png_destroy_read_struct(&png_ptr, &info_ptr,&end_info);
		else if (info_ptr)
			png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)0);
		else
			png_destroy_read_struct(&png_ptr, (png_infopp) 0,(png_infopp)0);
	}
	return;
}

bool PNGCONV::Read(char* image) {
	if (data == 0) return false;
	bool retcode = false;
	int bpp = is_mask ? 4 : 3;
	int i;
	char* buf;
	png_bytepp row_pointers = 0;

	png_structp png_ptr = 0;
	png_infop info_ptr = 0;
	png_infop end_info = 0;

	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
	if (!png_ptr) goto err;
	info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr) goto err;
	end_info = png_create_info_struct(png_ptr);
	if (!end_info) goto err;

        if (setjmp(png_jmpbuf(png_ptr))) {
                /* error occured !! */
		goto err;
        }

	buf= new char[width*height*4];
	/* initialize I/O */
	png_data = data;
	png_set_read_fn(png_ptr, (png_voidp)this, &png_read);
	
	row_pointers = (png_bytepp)png_malloc(png_ptr, height*sizeof(png_bytep));
	for (i=0; i<height; i++) row_pointers[i] = (png_bytep)(buf + width*bpp*i);
	png_set_rows(png_ptr, info_ptr, row_pointers);

	png_read_png(png_ptr, info_ptr,
           PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_BGR,0);

	if (buf != image) {
		CopyRGBA(image, buf);
		delete[] buf;
	}
	png_free(png_ptr, (png_voidp)row_pointers);

	retcode = true;
err:
	if (png_ptr) {
		if (end_info)
			png_destroy_read_struct(&png_ptr, &info_ptr,&end_info);
		else if (info_ptr)
			png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)0);
		else
			png_destroy_read_struct(&png_ptr, (png_infopp) 0,(png_infopp)0);
	}
	return retcode;
}

void PNGCONV::png_read(png_structp png_ptr, png_bytep d, png_size_t sz) {
	PNGCONV* orig = (PNGCONV*)png_get_io_ptr(png_ptr);
	memcpy(d, orig->png_data, sz);
	orig->png_data += sz;
	return;
}
#endif /* HAVE_LIBPNG */

#if HAVE_LIBJPEG
JPEGCONV::JPEGCONV(const char* _inbuf, int _inlen, const char* _filename) {
	int w,h,type;
	JSAMPARRAY rows, rows_orig; int i;
	char* buf = 0;

	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
	cinfo.src = new jpeg_source_mgr;
	SetupSrc(&cinfo, _inbuf, _inlen);

	if (jpeg_read_header(&cinfo, TRUE) == JPEG_HEADER_OK)  {
		Init(filename, _inbuf, _inlen, cinfo.image_width, cinfo.image_height, false);
	}
	delete cinfo.src;
	cinfo.src = 0;
	jpeg_destroy_decompress(&cinfo);
	return;
}
		
bool JPEGCONV::Read(char* image) {
	if (data == 0) return false;
	bool retcode = false;
	JSAMPARRAY rows, rows_orig; int i;
	char* buf = 0;

	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
	cinfo.src = new jpeg_source_mgr;
	SetupSrc(&cinfo, data, datalen);

	if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) goto err;

	cinfo.out_color_space = JCS_RGB;
	
	jpeg_start_decompress(&cinfo);

	rows = new JSAMPROW[height];
	rows_orig = rows;
	buf = new char[width*height*3];
	for (i=0; i<height; i++) rows[i] = (JSAMPROW)(buf+width*3*i);

	for (i=0; i<height; ) {
		int cnt = jpeg_read_scanlines(&cinfo, rows, height-i);
		rows += cnt;
		i += cnt;
	}
	delete[] rows_orig;
	CopyRGBA_rev(image, buf);
	delete[] buf;
	
	jpeg_finish_decompress(&cinfo);
	retcode = true;
err:
	delete cinfo.src;
	cinfo.src = 0;
	jpeg_destroy_decompress(&cinfo);
	return retcode;
}

void JPEGCONV::init_source(j_decompress_ptr cinfo) {
}
boolean JPEGCONV::fill_input_buffer(j_decompress_ptr cinfo) {
	static char dummy[1024];
	memset(dummy, 0, 1024);
	cinfo->src->next_input_byte = (const JOCTET*)dummy;
	cinfo->src->bytes_in_buffer = 1024;
	fprintf(stderr,"JPEGCONV::fill_input_buffer: warning corrupted jpeg stream\n");
	return TRUE;
}
void JPEGCONV::skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
	if (cinfo->src->bytes_in_buffer > num_bytes) {
		cinfo->src->next_input_byte += num_bytes;
		cinfo->src->bytes_in_buffer -= num_bytes;
	}
}
boolean JPEGCONV::resync_to_restart(j_decompress_ptr cinfo, int desired) {
	return jpeg_resync_to_restart(cinfo, desired);
}
void JPEGCONV::term_source(j_decompress_ptr cinf) {
}

void JPEGCONV::SetupSrc(struct jpeg_decompress_struct* cinfo, const char* data, int size) {
	cinfo->src->next_input_byte = (const JOCTET*) data;
	cinfo->src->bytes_in_buffer = size;
	cinfo->src->init_source = init_source;
	cinfo->src->fill_input_buffer = fill_input_buffer;
	cinfo->src->skip_input_data = skip_input_data;
	cinfo->src->resync_to_restart = resync_to_restart;
	cinfo->src->term_source = term_source;
}
#endif /* HAVE_LIBJPEG */
BMPCONV::BMPCONV(const char* _inbuf, int _inlen, const char* _filename) {
	/* �ǡ�����������ɤ߹��� */
	int w = read_little_endian_int(_inbuf + 0x12);
	int h = read_little_endian_int(_inbuf + 0x16);
	if (h < 0) h = -h;
	int bpp = read_little_endian_short(_inbuf + 0x1c);
	int comp = read_little_endian_int(_inbuf + 0x1e);
	Init(filename, _inbuf, _inlen, w, h, bpp==32 ? true : false);
	return;
}

bool BMPCONV::Read(char* image) {
	if (data == 0) return false;

	/* �ޥ����Υ����å� */
	int bpp = read_little_endian_short(data+0x1c);
	int h = read_little_endian_int(data + 0x16);
	int dsz = read_little_endian_int(data + 0x22);
	bpp /= 8;

	int bpl = dsz / height;
	if (bpl == 0) bpl = bpp*width;
	bpl = bpp * width;
	bpl = (bpl+3) & (~3);
	

	int i;
	char* buf = new char[width*height*bpp+1024];
	const char* src = data + 0x36;
	char* dest = buf;
	if (h < 0) {
		for (i=0; i<height; i++) {
			memcpy(dest+i*width*bpp, src+i*bpl, width*bpp);
		}
	} else {
		for (i=0; i<height; i++) {
			memcpy(dest+i*width*bpp, src+(height-i-1)*bpl, width*bpp);
		}
	}
	if (bpp == 3) CopyRGB(image, buf);
	else /* bpp == 4 */ CopyRGBA(image, buf);
	delete[] buf;
	return true;
}