/*  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*/

/*-----------------------------------------------------------------------------------\
|                                         gaf.cpp                                    |
|  ce fichier contient les structures, classes et fonctions nécessaires à la lecture |
| des fichiers gaf de total annihilation qui sont les fichiers contenant les images  |
| et les animations du jeu.                                                          |
|                                                                                    |
\-----------------------------------------------------------------------------------*/

#include <allegro.h>
#include <alleggl.h>
#include <GL/glu.h>
#include "ta3dbase.h"
#include "gaf.h"

int get_gaf_nb_entry(byte *buf)
{
	GAFHEADER header;		// En-tête du fichier

	header.IDVersion=((int*)buf)[0];		// Lit l'en-tête
	header.Entries=((int*)buf)[1];
	header.Unknown1=((int*)buf)[2];

	return header.Entries;
}

char *get_gaf_entry_name(byte *buf,int entry_idx)
{
	GAFHEADER header;		// En-tête du fichier

	header.IDVersion=((int*)buf)[0];		// Lit l'en-tête
	header.Entries=((int*)buf)[1];
	header.Unknown1=((int*)buf)[2];

	if(entry_idx>=header.Entries)		// Si le fichier contient moins d'images que img_idx, il y a erreur
		return NULL;

#ifdef GAF_DEBUG_MODE
	printf("nombre d'entrées dans le GAF: %d\n",header.Entries);
#endif

	int i;
	int f_pos=12;
	int pointers[header.Entries];

	for(i=0;i<header.Entries;i++)			// Lit la liste de pointeurs vers les objets du fichier
		pointers[i]=((int*)buf)[3+i];
	f_pos+=header.Entries*4;

	GAFENTRY entry;
	entry.Frames=((short*)(buf+pointers[entry_idx]))[0];		// Lit l'en-tête de l'entrée
	entry.Unknown1=((short*)(buf+pointers[entry_idx]))[1];
	entry.Unknown2=((int*)(buf+pointers[entry_idx]))[1];
	memcpy(entry.Name,buf+pointers[entry_idx]+8,32);
	f_pos=pointers[entry_idx]+40;

#ifdef GAF_DEBUG_MODE
	printf("nombre d'images dans l'entrée: %d\n",entry.Frames);
	printf("nom de l'entrée: %s\n",entry.Name);
#endif

	return strdup(entry.Name);
}

int get_gaf_entry_index(byte *buf,char *name)
{
	int nb_entry=get_gaf_nb_entry(buf);

	for(int i=0;i<nb_entry;i++) {
		char *entry_name=get_gaf_entry_name(buf,i);
		if(strcasecmp(entry_name,name)==0) {
			free(entry_name);
			return i;
			}
		free(entry_name);
		}
	return -1;
}

int get_gaf_nb_img(byte *buf,int entry_idx)
{
	GAFHEADER header;		// En-tête du fichier

	header.IDVersion=((int*)buf)[0];		// Lit l'en-tête
	header.Entries=((int*)buf)[1];
	header.Unknown1=((int*)buf)[2];

	if(entry_idx>=header.Entries)		// Si le fichier contient moins d'images que img_idx, il y a erreur
		return 0;

#ifdef GAF_DEBUG_MODE
	printf("nombre d'entrées dans le GAF: %d\n",header.Entries);
#endif

	int i;
	int f_pos=12;
	int pointers[header.Entries];

	for(i=0;i<header.Entries;i++)			// Lit la liste de pointeurs vers les objets du fichier
		pointers[i]=((int*)buf)[3+i];
	f_pos+=header.Entries*4;

	GAFENTRY entry;
	entry.Frames=((short*)(buf+pointers[entry_idx]))[0];		// Lit l'en-tête de l'entrée
	entry.Unknown1=((short*)(buf+pointers[entry_idx]))[1];
	entry.Unknown2=((int*)(buf+pointers[entry_idx]))[1];
	memcpy(entry.Name,buf+pointers[entry_idx]+8,32);
	f_pos=pointers[entry_idx]+40;

#ifdef GAF_DEBUG_MODE
	printf("nombre d'images dans l'entrée: %d\n",entry.Frames);
	printf("nom de l'entrée: %s\n",entry.Name);
#endif

	return entry.Frames;
}

BITMAP *read_gaf_img(byte *buf,int entry_idx,int img_idx,short *ofs_x,short *ofs_y,bool truecol)			// Lit une image d'un fichier gaf en mémoire
{
	GAFHEADER header;		// En-tête du fichier

	header.IDVersion=((int*)buf)[0];		// Lit l'en-tête
	header.Entries=((int*)buf)[1];
	header.Unknown1=((int*)buf)[2];

	if(entry_idx>=header.Entries)		// Si le fichier contient moins d'images que img_idx, il y a erreur
		return NULL;

#ifdef GAF_DEBUG_MODE
	printf("nombre d'entrées dans le GAF: %d\n",header.Entries);
#endif

	int i;
	int f_pos=12;
	int pointers[header.Entries];

	for(i=0;i<header.Entries;i++)			// Lit la liste de pointeurs vers les objets du fichier
		pointers[i]=((int*)buf)[3+i];
	f_pos+=header.Entries*4;

	GAFENTRY entry;
	entry.Frames=((short*)(buf+pointers[entry_idx]))[0];		// Lit l'en-tête de l'entrée
	entry.Unknown1=((short*)(buf+pointers[entry_idx]))[1];
	entry.Unknown2=((int*)(buf+pointers[entry_idx]))[1];
	memcpy(entry.Name,buf+pointers[entry_idx]+8,32);
	f_pos=pointers[entry_idx]+40;

#ifdef GAF_DEBUG_MODE
	printf("nombre d'images dans l'entrée: %d\n",entry.Frames);
	printf("nom de l'entrée: %s\n",entry.Name);
#endif

	GAFFRAMEENTRY frame[entry.Frames];

	for(i=0;i<entry.Frames;i++) {
		frame[i].PtrFrameTable=*((int*)(buf+f_pos));
		f_pos+=4;
		frame[i].Unknown1=*((int*)(buf+f_pos));
		f_pos+=4;
		}

	f_pos=frame[img_idx].PtrFrameTable;

	GAFFRAMEDATA framedata;								// Lit les informations sur l'image
	framedata.Width=*((short*)(buf+f_pos));			f_pos+=2;
	framedata.Height=*((short*)(buf+f_pos));		f_pos+=2;
	framedata.XPos=*((short*)(buf+f_pos));			f_pos+=2;
	framedata.YPos=*((short*)(buf+f_pos));			f_pos+=2;
	framedata.Unknown1=*((char*)(buf+f_pos));		f_pos+=1;
	framedata.Compressed=*((char*)(buf+f_pos));		f_pos+=1;
	framedata.FramePointers=*((short*)(buf+f_pos));	f_pos+=2;
	framedata.Unknown2=*((int*)(buf+f_pos));		f_pos+=4;
	framedata.PtrFrameData=*((int*)(buf+f_pos));	f_pos+=4;
	framedata.Unknown3=*((int*)(buf+f_pos));		f_pos+=4;

#ifdef GAF_DEBUG_MODE
	printf("taille de l'image: %dx%d\n",framedata.Width,framedata.Height);
	printf("position de l'image: (%d,%d)\n",framedata.XPos,framedata.YPos);
	printf("nombre de sous-images: %d\n",framedata.FramePointers);
	printf("framedata.Compressed=%d\n",framedata.Compressed);
#endif

	if(framedata.FramePointers)
		return NULL;

	if(ofs_x)
		*ofs_x=framedata.XPos;
	if(ofs_y)
		*ofs_y=framedata.YPos;

	BITMAP *img;
	if(!truecol)
		img=create_bitmap_ex(8,framedata.Width,framedata.Height);
	else
		img=create_bitmap_ex(32,framedata.Width,framedata.Height);
	clear(img);

	if(framedata.Compressed) {			// Si l'image est comprimée
		short length;
		f_pos=framedata.PtrFrameData;
		for(i=0;i<img->h;i++) {			// Décode les lignes les unes après les autres
			length=*((short*)(buf+f_pos));
			f_pos+=2;
			int x=0,e=0;
			do
			{
				byte mask=buf[f_pos++];
				e++;
				if(mask&0x01) {
					if(!truecol)
						x+=mask>>1;
					else {
						int l=mask>>1;
						while(l>0) {
							putpixel(img,x++,i,0x00000000);
							l--;
							}
						}
					}
				else if(mask&0x02) {
					int l=(mask>>2)+1;
					while(l>0) {
						if(!truecol)
							img->line[i][x++]=buf[f_pos];
						else
							putpixel(img,x++,i,makeacol32(pal[buf[f_pos]].r<<2,pal[buf[f_pos]].g<<2,pal[buf[f_pos]].b<<2,0xFF));
						l--;
						}
					f_pos++;
					e++;
					}
				else {
					int l=(mask>>2)+1;
					while(l>0) {
						if(truecol) {
							putpixel(img,x++,i,makeacol32(pal[buf[f_pos]].r<<2,pal[buf[f_pos]].g<<2,pal[buf[f_pos]].b<<2,0xFF));
							f_pos++;
							}
						else
							img->line[i][x++]=buf[f_pos++];
						e++;
						l--;
						}
					}
			}while(e<length && x<img->w);
			f_pos+=length-e;
			}
		}
	else {								// Si l'image n'est pas comprimée
		f_pos=framedata.PtrFrameData;
		for(i=0;i<img->h;i++) {			// Copie les octets de l'image
			memcpy(img->line[i],buf+f_pos,img->w);
			f_pos+=img->w;
			}
		}

	return img;
}
