/*  TA3D, a remake of Total Annihilation
    Copyright (C) 2005  Roland BROCHARD

    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA*/

/*-----------------------------------------------------------------------------------\
|                                         hpi.h                                      |
|  ce fichier contient les structures, classes et fonctions nécessaires à la lecture |
| des fichiers hpi/ufo/... de total annihilation mais sans fournir les fonctions de  |
| lecture des fichiers contenus dans ces archives!                                   |
|                                                                                    |
\-----------------------------------------------------------------------------------*/

#ifndef __HPI_CLASSES
#define __HPI_CLASSES

#include <zlib.h>				// Pour la décompression des archives

typedef unsigned char byte;

/*------------Code expérimental------------------------------------------------*/	// Pour ficher tout les fichiers contenus dans les archives
																					// Afin d'accélérer les temps de chargement
class FILE_NODE		// Arbre pour la gestion de l'arborescence "totale" des archives
{
public:
	bool		dir;		// Indique s'il s'agit d'un dossier
	int			nb_nodes;	// Nombre de noeuds
	FILE_NODE	**node;		// Noeuds
	char		*archive;	// Nom de l'archive contenant le fichier
	int			arch_idx;	// Index de l'archive préouverte (pour accélérer le processus)
	char		*name;		// Nom du fichier/dossier

	inline void init()
	{
		dir=false;
		nb_nodes=0;
		archive=NULL;
		arch_idx=-1;
		name=NULL;
		node=NULL;
	}

	inline void destroy()
	{
		if(node) {
			for(int i=0;i<nb_nodes;i++)
				node[i]->destroy();
			free(node);
			}
		if(archive)	free(archive);
		if(name)	free(name);
		init();
	}

	FILE_NODE()
	{
		init();
	}

	~FILE_NODE()
	{
		destroy();
	}

	void add_file(char *filename,char *src);		// Ajoute un fichier

	FILE_NODE *find_file(char *filename);		// Recherche un fichier un fichier

	void print(char *subdir)
	{
		if(name)
			printf("%s%s\n",subdir,name);
		if(nb_nodes>0) {
			char tmp[1000];
			tmp[0]=0;
			strcat(tmp,subdir);
			if(name)
				strcat(tmp,name);
			strcat(tmp,"\\");
			for(int i=0;i<nb_nodes;i++)
				node[i]->print(tmp);
			}
	}
};

extern FILE_NODE archive_struct;		// Structure des archives

/*-----------------------------------------------------------------------------*/

struct HPIHeader
{
	int HPIMarker;		// 'HAPI'
	int SaveMarker;		// 'BANK'
	int DirectorySize;	// Taille du dossier archivé
	int HeaderKey;		// Clef de décryptage
	int Start;			// Position du dossier dans le fichier
};

HPIHeader read_hpi_header(char *filename);			// Lit l'entête d'un fichier hpi

struct HPIEntry					// Structure des entrées de l'arborescence de l'archive
{
	int			NameOffset;			/* pointe vers le nom du dossier/fichier */
	int			DirDataOffset;		/* position des données du dossier/fichier */
	char		Flag;				/* attributs de l'objet */
};

struct HPIFileData				// Structure des données sur les fichiers
{
	int			DataOffset;			/* position des données du fichier */
	int			FileSize;			/* taille du fichier non comprimé */
	char		Flag;				/* attributs du fichier */
};

struct HPIChunk				// Structure des morceaux de fichiers
{
	int		Marker;				/* toujours 0x48535153 (SQSH) */
	char	Unknown1;
	char	CompMethod;			/* 1=LZ77, 2=ZLib */
	char	Encrypt;			/* indique si le bloc est crypté */          
	int		CompressedSize;		/* taille des données comprimées */
	int		DecompressedSize;	/* taille des données décomprimées */
	int		Checksum;			/* Checksum */
	byte	*data;				/* 'CompressedSize' octets de données */
};

class HPINode
{
public:
	bool		Dir;				/* indique s'il s'agit d'un dossier */
	char		*nom;				/* nom du fichier/dossier */
	HPIEntry	hpientry;			/* données directement lues dans le fichier */
	HPIFileData	hpifile;			/* données concernant le fichier */
	int			nbentry;			/* nombre d'éléments dans le dossirt */
	HPINode		*entry;				/* tableau de sous-éléments */

	inline void init()
	{
		Dir=false;
		nom=NULL;
		hpientry.NameOffset=0;
		hpientry.DirDataOffset=0;
		hpientry.Flag=0;
		hpifile.DataOffset=0;
		hpifile.FileSize=0;
		hpifile.Flag=0;
		nbentry=0;
		entry=NULL;
	}

	inline HPINode()
	{
		init();
	}

	inline void destroy()
	{
		if(Dir && nbentry>0 && entry) {			// Détruit récursivement l'arborescence
			for(int i=0;i<nbentry;i++)
				entry[i].destroy();
			free(entry);
			}
		init();
	}

	inline ~HPINode()
	{
		destroy();
	}

	inline void build(byte *data)					// Procédure de construction récursive des dossiers de l'arborescence
	{
		if(hpientry.Flag==1) {		// Dossier
			Dir=true;
			nom=(char*)(data+hpientry.NameOffset);
			nbentry=*((int*)(data+hpientry.DirDataOffset));		// Nombre d'éléments
			entry=(HPINode*) malloc(sizeof(HPINode)*nbentry);		// Alloue la mémoire nécessaire
			int pointer=*((int*)(data+hpientry.DirDataOffset+4));
			for(int i=0;i<nbentry;i++) {
				entry[i].init();
				entry[i].hpientry=*((HPIEntry*)(data+pointer+i*9));
				entry[i].build(data);
				}
			}
		else {						// Fichier
			Dir=false;
			nom=(char*)(data+hpientry.NameOffset);		// Nom du fichier
			entry=NULL;
			nbentry=0;
			hpifile=*((HPIFileData*)(data+hpientry.DirDataOffset));
			}
	}

	void show(int p=0,int flag=-1,char *dirname=NULL,char *dir=NULL)
	{
		int i;
		if(Dir) {
			if(dir==NULL || strstr(dirname,dir)) {
				printf("%s",dirname);
				printf("%s",nom);
				printf(" ->%d\n",nbentry);
				}
			char *dname=new char[strlen(nom)+strlen(dirname)+3];
			memcpy(dname,dirname,strlen(dirname)+1);
			strcat(dname,nom);
			strcat(dname,"/");
			for(i=0;i<nbentry;i++)
				entry[i].show(p+1,flag,dname,dir);
			delete dname;
			}
		else
			if(dir==NULL || strstr(dirname,dir))
				if(flag==-1 || flag==hpifile.Flag)
					printf("%s%s\n",dirname,nom);
	}

	inline void build_struct(char *dirname,char *src,bool root=false)
	{
		int i;
		if(Dir) {
			char *dname=new char[strlen(nom)+strlen(dirname)+3];
			if(!root) {
				memcpy(dname,dirname,strlen(dirname)+1);
				strcat(dname,nom);
				}
			else
				dname[0]=0;
			strcat(dname,"\\");
			for(i=0;i<nbentry;i++)
				entry[i].build_struct(dname,src);
			delete dname;
			}
		else {
			char *dname=new char[strlen(nom)+strlen(dirname)+3];
			if(!root) {
				memcpy(dname,dirname,strlen(dirname)+1);
				strcat(dname,nom);
				}
			else {
				dname[0]=0;
				strcat(dname,"\\");
				}
			archive_struct.add_file(dname,src);
			delete dname;
			}
	}
};

class HPI_FILE				// Classe pour le chargement de fichiers HPI
{
public:
	HPIHeader	header;		// En-tête
	int			size;		// Taille des données contenues dans le tampon data
	byte		*data;		// Données du fichier HPI décryptées
	HPINode		root;		// Dossier racine de l'archive
	FILE		*hpifile;	// Fichier HPI ouvert

	inline void init()		// Initialise l'objet
	{
		size=0;
		data=NULL;
		root.init();
		hpifile=NULL;
	}

	inline HPI_FILE()
	{
		init();
	}

	inline void destroy()	// Détruit l'objet
	{
		root.destroy();
		if(data)
			free(data);
		if(hpifile)
			fclose(hpifile);
		init();
	}

	inline ~HPI_FILE()
	{
		destroy();
	}

private:			// Quelques fonctions utilisables seulement dans cette classe

	int Decompress(char *out, char *in, int len);

	inline HPIChunk *read_chunk(HPIChunk *chunk,byte *buf)			// Lit une entête de morceau de fichier
	{
		memcpy(&(chunk->Marker),buf,4);
		memcpy(&(chunk->Unknown1),buf+4,1);
		memcpy(&(chunk->CompMethod),buf+5,1);
		memcpy(&(chunk->Encrypt),buf+6,1);
		memcpy(&(chunk->CompressedSize),buf+7,4);
		memcpy(&(chunk->DecompressedSize),buf+11,4);
		memcpy(&(chunk->Checksum),buf+15,4);

		return chunk;
	}

	int read_and_decrypt(FILE *f,byte *dat,int fpos,int size);

public:

	inline int load(char *filename)
	{
		destroy();			// Au cas où il y aurait déjà un fichier chargé

		hpifile=fopen(filename,"rb");			// Ouvre le fichier
		if(hpifile==NULL)
			return 1;			// Echec de l'ouverture du fichier

		fread(&header,sizeof(header),1,hpifile);		// Lit l'entête du fichier

		if(header.HPIMarker!=0x49504148) {
			destroy();
			return 2;
			}

		size=header.DirectorySize;			// Alloue la mémoire tampon nécessaire
		data=(byte*) malloc(size);

		memcpy(data,filename,strlen(filename)+1);

		read_and_decrypt(hpifile,data+header.Start,header.Start,header.DirectorySize-header.Start);		// Lit et décrypte le fichier

		root.hpientry.NameOffset=0;				// Dossier racine de l'archive
		root.hpientry.DirDataOffset=header.Start;
		root.hpientry.Flag=1;
		root.build(data);		// Construit récursivement l'arborescence

		return 0;		// Tout a bien fonctionné
	}

	inline char *find(char *filedir,int n=0)		// Cherche le nième fichier de filedir
	{
		char *subname=strdup(filedir+1);	// Enlève le premier caractère indiquant le dossier racine
//		char *ext=new char[strlen(filedir)];
		char ext[1000];
		int i=0,e=0;

		HPINode *curnode=&root;		// Dossier courant dans l'arborescence de l'archive

		do					// Repère le fichier dans l'arborescence de l'archive
		{
			e=0;
			while(subname[i]!=0 && subname[i]!='\\' && subname[i]!='/')		// Extrait le nom du premier dossier/fichier
				ext[e++]=subname[i++];
			ext[e]=0;
			i++;

			bool found=false;

			for(int p=0;p<curnode->nbentry;p++)
				if(strcasecmp(curnode->entry[p].nom,ext)==0) {
					curnode=&(curnode->entry[p]);
					found=true;
					break;
					}
			if(!found) {			// Echec : dossier non trouvé
//				Console->AddEntry(filedir);
//				delete ext;
				free(subname);
//				Console->AddEntry("erreur: dossier non trouvé");
				return NULL;
				}
		}while(curnode->Dir && subname[i-1]!=0);

		if(n>=curnode->nbentry) {
//			delete ext;
			free(subname);
			return NULL;
			}
//		delete ext;
		free(subname);
		subname=new char[strlen(filedir)+3+strlen(curnode->entry[n].nom)];
		memcpy(subname,filedir,strlen(filedir)+1);
		strcat(subname,"/");
		strcat(subname,curnode->entry[n].nom);
		return subname;
	}

	inline int get_number_of_entry(char *filedir)		// Renvoie le nombre d'entrées du dossier filedir
	{
		char *subname=strdup(filedir+1);	// Enlève le premier caractère indiquant le dossier racine
//		char *ext=new char[strlen(filedir)];
		char ext[1000];
		int i=0,e=0;

		HPINode *curnode=&root;		// Dossier courant dans l'arborescence de l'archive

		do					// Repère le fichier dans l'arborescence de l'archive
		{
			e=0;
			while(subname[i]!=0 && subname[i]!='\\' && subname[i]!='/')		// Extrait le nom du premier dossier/fichier
				ext[e++]=subname[i++];
			ext[e]=0;
			i++;

			bool found=false;

			for(int p=0;p<curnode->nbentry;p++)
				if(strcasecmp(curnode->entry[p].nom,ext)==0) {
					curnode=&(curnode->entry[p]);
					found=true;
					break;
					}
			if(!found) {			// Echec : dossier non trouvé
//				Console->AddEntry(filedir);
//				delete ext;
				free(subname);
//				Console->AddEntry("erreur: dossier non trouvé");
				return -1;
				}
		}while(curnode->Dir && subname[i-1]!=0);

//		delete ext;
		free(subname);
		return curnode->nbentry;
	}

	inline byte *extract_memory(HPINode *node)		// Extrait un fichier dans une mémoire tampon
	{
		byte *buf=NULL;
		int i;
		switch(node->hpifile.Flag)
		{
		case 0:									// Fichier non comprimé
#ifdef HPI_DEBUG_MODE
			Console->AddEntry("fichier non comprimé");
#endif
			{
				buf=(byte*) malloc(node->hpifile.FileSize);			// Alloue de la mémoire tampon
				read_and_decrypt(hpifile,buf,node->hpifile.DataOffset,node->hpifile.FileSize);	// Lit et décrypte le fichier
			}
			break;
		case 1:									// Fichier comprimé avec LZ77
#ifdef HPI_DEBUG_MODE
			Console->AddEntry("fichier comprimé par LZ77");
#endif
			{
				buf=(byte*) malloc(node->hpifile.FileSize);			// Alloue de la mémoire tampon
				int	buf_pos=0;										// Position dans la mémoire tampon
				int f_pos=0;										// Position dans le fichier
				int chunks=node->hpifile.FileSize+0xFFFF>>16;		// Nombre de morceaux composants le fichier
				int chunk_size[chunks];								// Taille des différents morceaux

				f_pos=node->hpifile.DataOffset;
				fseek(hpifile, node->hpifile.DataOffset, SEEK_SET);	// Positionne le curseur de lecture

#ifdef HPI_DEBUG_MODE
				printf("taille du fichier:%d\nmorceaux : %d\n",node->hpifile.FileSize,chunks);
				Console->AddEntry("lit la liste des tailles des morceaux du fichier");
#endif
				read_and_decrypt(hpifile,(byte*)chunk_size,f_pos,4*chunks);

				f_pos+=chunks*4;
				HPIChunk chunk;		// Structure du morceau en cours de décompression

				int max_chunk_size=0;
				for(i=0;i<chunks;i++)
					if(max_chunk_size<chunk_size[i])
						max_chunk_size=chunk_size[i];

				chunk.data=(byte*) malloc(max_chunk_size);			// Alloue la mémoire tampon nécessaire
				byte *ref_data=chunk.data+19;

				for(i=0;i<chunks;i++) {		// Pour chaque morceau
#ifdef HPI_DEBUG_MODE
					Console->AddEntry("lit l'entête d'un morceau");
					printf("taille : %d\n",chunk_size[i]);
#endif
#ifdef HPI_DEBUG_MODE
					Console->AddEntry("lit et decrypte un morceau");
#endif
					read_and_decrypt(hpifile,chunk.data,f_pos,chunk_size[i]);
					f_pos+=chunk_size[i];
					read_chunk(&chunk,chunk.data);		// Lit la structure du morceau

#ifdef HPI_DEBUG_MODE
					printf("Marker=%d\nref=%d\n",chunk.Marker,0x48535153);
					if(chunk.CompMethod==1)
						Console->AddEntry("comprime avec LZ77");
					if(chunk.CompMethod==2)
						Console->AddEntry("comprime avec zlib -> anormal");
#endif
					if(chunk.Encrypt==1) {
#ifdef HPI_DEBUG_MODE
						Console->AddEntry("morceau crypte");
#endif
						for(int x=0;x<chunk.CompressedSize;x++)				// Décrypte le morceau
							ref_data[x] = (ref_data[x] - x) ^ x;
						}
#ifdef HPI_DEBUG_MODE
					printf("taille: %d\n",chunk.CompressedSize);
#endif
					buf_pos+=Decompress((char*)buf+buf_pos,(char*)ref_data,chunk.CompressedSize);		// Décomprime les données
#ifdef HPI_DEBUG_MODE
					printf("position dans le tampon: %d\n",buf_pos);
#endif
					}
				free(chunk.data);				// Libère la mémoire
			}
			break;
		case 2:									// Fichier comprimé avec zlib
#ifdef HPI_DEBUG_MODE
			Console->AddEntry("fichier comprimé par ZLib");
#endif
			{
				buf=(byte*) malloc(node->hpifile.FileSize);			// Alloue de la mémoire tampon
				int	buf_pos=0;										// Position dans la mémoire tampon
				int f_pos=0;										// Position dans le fichier
				int chunks=node->hpifile.FileSize+0xFFFF>>16;		// Nombre de morceaux composants le fichier
				int chunk_size[chunks];								// Taille des différents morceaux

				f_pos=node->hpifile.DataOffset;
				fseek(hpifile, node->hpifile.DataOffset, SEEK_SET);	// Positionne le curseur de lecture

#ifdef HPI_DEBUG_MODE
				printf("taille du fichier:%d\nmorceaux : %d\n",node->hpifile.FileSize,chunks);
				Console->AddEntry("lit la liste des tailles des morceaux du fichier");
#endif
				read_and_decrypt(hpifile,(byte*)chunk_size,f_pos,4*chunks);

				f_pos+=chunks*4;
				HPIChunk chunk;		// Structure du morceau en cours de décompression

				int max_chunk_size=0;
				for(i=0;i<chunks;i++)
					if(max_chunk_size<chunk_size[i])
						max_chunk_size=chunk_size[i];

				chunk.data=(byte*) malloc(max_chunk_size);			// Alloue la mémoire tampon nécessaire
				byte *ref_data=chunk.data+19;

				for(i=0;i<chunks;i++) {		// Pour chaque morceau
#ifdef HPI_DEBUG_MODE
					Console->AddEntry("lit l'entête d'un morceau");
					printf("taille : %d\n",chunk_size[i]);
#endif
#ifdef HPI_DEBUG_MODE
					Console->AddEntry("lit et decrypte un morceau");
#endif
					read_and_decrypt(hpifile,chunk.data,f_pos,chunk_size[i]);
					f_pos+=chunk_size[i];
					read_chunk(&chunk,chunk.data);		// Lit la structure du morceau

#ifdef HPI_DEBUG_MODE
					printf("Marker=%d\nref=%d\n",chunk.Marker,0x48535153);
					if(chunk.CompMethod==1)
						Console->AddEntry("comprime avec LZ77 -> anormal");
					if(chunk.CompMethod==2)
						Console->AddEntry("comprime avec zlib");
#endif
					if(chunk.Encrypt==1) {
#ifdef HPI_DEBUG_MODE
						Console->AddEntry("morceau crypte");
#endif
						for(int x=0;x<chunk.CompressedSize;x++)				// Décrypte le morceau
							ref_data[x] = (ref_data[x] - x) ^ x;
						}
#ifdef HPI_DEBUG_MODE
					printf("taille: %d\n",chunk.CompressedSize);
#endif
					uLongf size=chunk.DecompressedSize;
					uncompress((Bytef*)buf+buf_pos,&size,(Bytef*)ref_data,chunk.CompressedSize);		// Décomprime les données
					buf_pos+=chunk.DecompressedSize;
#ifdef HPI_DEBUG_MODE
					printf("position dans le tampon: %d\n",buf_pos);
#endif
					}
				free(chunk.data);				// Libère la mémoire
			}
			break;
		};

		return buf;		// Retourne l'adresse de la mémoire tampon
	}

	inline int extract(char *filename,char *outputname=NULL)				// Extrait un fichier de l'archive et l'écrit sur le disque
	{
		char *subname=filename+1;	// Enlève le premier caractère indiquant le dossier racine
//		char *ext=new char[strlen(filename)];
		char ext[1000];
		int i=0,e=0;

		HPINode *curnode=&root;		// Dossier courant dans l'arborescence de l'archive

		do					// Repère le fichier dans l'arborescence de l'archive
		{
			e=0;
			while(subname[i]!=0 && subname[i]!='\\' && subname[i]!='/')		// Extrait le nom du premier dossier/fichier
				ext[e++]=subname[i++];
			ext[e]=0;
			i++;

			bool found=false;

			for(int p=0;p<curnode->nbentry;p++)
				if(strcasecmp(curnode->entry[p].nom,ext)==0) {
					curnode=&(curnode->entry[p]);
					found=true;
					break;
					}
			if(!found) {			// Echec : fichier non trouvé
				Console->AddEntry(filename);
//				delete ext;
				Console->AddEntry("erreur: fichier non trouvé");
				return 1;
				}
		}while(curnode->Dir);

		byte *buf=extract_memory(curnode);

		if(buf) {
			FILE *f;
			if(outputname==NULL)
				f=fopen(ext,"wb");
			else
				f=fopen(outputname,"wb");

			fwrite(buf,curnode->hpifile.FileSize,1,f);			// Ecrit les données

			free(buf);			// Libère la mémoire tampon
			fclose(f);			// Ferme le fichier nouvellement créé
			}
		else {
//			delete ext;
			return 2;		// Le fichier n'a pas pu être extrait
			}

//		delete ext;

		return 0;
	}

	inline bool is_dir(char *filename)				// Indique si l'élément filename est un dossier
	{
		char *subname=filename+1;	// Enlève le premier caractère indiquant le dossier racine
//		char *ext=new char[strlen(filename)];
		char ext[1000];
		int i=0,e=0;

		HPINode *curnode=&root;		// Dossier courant dans l'arborescence de l'archive

		do					// Repère le fichier dans l'arborescence de l'archive
		{
			e=0;
			while(subname[i]!=0 && subname[i]!='\\' && subname[i]!='/')		// Extrait le nom du premier dossier/fichier
				ext[e++]=subname[i++];
			ext[e]=0;
			i++;

			bool found=false;

			for(int p=0;p<curnode->nbentry;p++)
				if(strcasecmp(curnode->entry[p].nom,ext)==0) {
					curnode=&(curnode->entry[p]);
					found=true;
					break;
					}
			if(!found) {			// Echec : fichier non trouvé -> il s'agit d'un dossier(on considère que l'élément existe)
//				delete ext;
				return true;
				}
		}while(curnode->Dir);

//		delete ext;

		return false;
	}

	inline byte *extract_memory_file(char *filename,int *size=NULL)				// Extrait un fichier de l'archive dans une mémoire tampon
	{
		char *subname=filename+1;	// Enlève le premier caractère indiquant le dossier racine
//		char *ext=new char[strlen(filename)];
		char ext[1000];
		int i=0,e=0;

		HPINode *curnode=&root;		// Dossier courant dans l'arborescence de l'archive

		do					// Repère le fichier dans l'arborescence de l'archive
		{
			e=0;
			while(subname[i]!=0 && subname[i]!='\\' && subname[i]!='/')		// Extrait le nom du premier dossier/fichier
				ext[e++]=subname[i++];
			ext[e]=0;
			i++;

			bool found=false;

			for(int p=0;p<curnode->nbentry;p++)
				if(strcasecmp(curnode->entry[p].nom,ext)==0) {
					curnode=&(curnode->entry[p]);
					found=true;
					break;
					}
			if(!found) {			// Echec : fichier non trouvé
//				Console->AddEntry(filename);
//				delete ext;
//				Console->AddEntry("erreur: fichier non trouvé");
				return NULL;
				}
		}while(curnode->Dir);

		byte *buf=extract_memory(curnode);			// Extrait le fichier trouvé dans une mémoire tampon

		if(size)					// Copie la taille du fichier
			(*size)=curnode->hpifile.FileSize;

//		delete ext;

		return buf;
	}

	inline bool exist(char *filename)
	{
		char *subname=filename+1;	// Enlève le premier caractère indiquant le dossier racine
//		char *ext=new char[strlen(filename)];
		char ext[1000];
		int i=0,e=0;

		HPINode *curnode=&root;		// Dossier courant dans l'arborescence de l'archive

		do					// Repère le fichier dans l'arborescence de l'archive
		{
			e=0;
			while(subname[i]!=0 && subname[i]!='\\' && subname[i]!='/')		// Extrait le nom du premier dossier/fichier
				ext[e++]=subname[i++];
			ext[e]=0;
			i++;

			bool found=false;

			for(int p=0;p<curnode->nbentry;p++)
				if(strcasecmp(curnode->entry[p].nom,ext)==0) {
					curnode=&(curnode->entry[p]);
					found=true;
					break;
					}
			if(!found) {			// Echec : fichier non trouvé
//				delete ext;
				return false;
				}
		}while(curnode->Dir);

//		delete ext;

		return true;
	}

	inline void extract_dir(char *dirname)
	{
		char *file;
		int n=0;
		while(file=find(dirname,n++)) {
			printf("Decompactage de %s\n",file);
			extract(file);
			}
	}

	void show_struct(int flag=-1,char *dir=NULL)			// Affiche la structure dans la console
	{
		root.show(0,flag,"/",dir);
	}

	inline void build_struct(char *src)			// Construit la structure dans la console
	{
		root.build_struct("\\",src,true);
	}

	void save(char *filename)
	{
		if(size==0) return;
		FILE *f=fopen(filename,"wb");
		if(f==NULL) return;
		fwrite(data,size,1,f);
		fclose(f);
	}
};

class ARCH
{
public:
	int			nb_arch;			// Nombre d'archives repertoriées
	HPI_FILE	arch[1000];			// Archives trouvées (1000 maxi)
	char		*arch_name[1000];	// Noms des archives

	inline void init()
	{
		nb_arch=0;
		for(int i=0;i<1000;i++)
			arch_name[i]=NULL;
	}

	inline void destroy()
	{
		for(int i=0;i<nb_arch;i++) {
			free(arch_name[i]);
			arch[i].destroy();
			}
		init();
	}

	inline int add(char *src)
	{
		arch_name[nb_arch]=strdup(src);
		arch[nb_arch].load(src);
		return nb_arch++;
	}

	inline ARCH()
	{
		init();
	}

	inline ~ARCH()
	{
		destroy();
	}
};

extern ARCH arch_manager;

char *find_hpi(char *file);			// Cherche l'archive qui contient le fichier file

byte *load_file(char *filename,int *size=NULL);			// Trouve l'archive contenant le fichier demandé et en extrait le fichier cherché

int get_nb_file(HPI_FILE *hpi,char *dir,char *ext);			// Renvoie le nombre de fichiers dont l'extention est ext

char **file_list(char *dir,char *ext,int *nb);			// Renvoie la liste des fichiers du répertoire dir dont l'extention est ext

#endif
