#include <stdio.h>
#include <string.h>

#include "programme.h"
#include "bin.h"
#include "pread.h"

/*
	Auteur: Benoit Papillault
	Creation: 12/01/1998

	Manipulation d'une structure representant l'integralite des
	informations extraites d'un fichier executable et qui representent
	un programme.

*/

LISTE2_c(liste_fonction,fonction);
LISTE_c(liste_entier,int);

void programme_init(struct programme *pg)
{
	pg->fp  = NULL;
	donnee_binaire_init(&pg->d_binaire);
	liste_fonction_init(&pg->list_fonction);
	liste_entier_init(&pg->a_desassembler);
	pg->code = NULL;
}

void programme_done(struct programme *pg)
{
	if (pg->fp != NULL)
		fclose(pg->fp);
	if (pg->code != NULL)
		free(pg->code);
	donnee_binaire_done(&pg->d_binaire);
	liste_fonction_done(&pg->list_fonction);
	liste_entier_done(&pg->a_desassembler);
}

int programme_charger(struct programme *pg,const char *name,
	const struct binaire *b)
{
	/* Attention: dans le cas des fichiers win16, on ouvre en mode lecture
		ecriture pour pouvoir appliquer directement la relocation.
		Le fichier original est donc modifie */

	pg->fp = fopen(name,"rb");
	if (pg->fp == NULL)
		return 0;

	if (!b->analyser_fichier(pg->fp,&pg->d_binaire))
		return 0;

	return 1;
}

/* desassemble la fonction dont l'adresse de la premiere instruction est
	addr. Retourne 1 si tout est ok, 0 sinon */

int programme_desassembler_debut(struct programme *pg,const struct desas *dasm,
	int addr)
{
	struct fonction f, *pf;
	struct symbole sym;
	int i_f;

	/* printf("programme_desassembler_debut: %x\n",addr); */

	/* on commence par ajouter la fonction que l'on va desassembler dans
		la liste car sinon comme fonction_desassembler() peut aussi
		modifier cette liste, on ne pourra plus etre assure que cette
		fonction sera la premiere de la liste.

	  Cette stategie ne convient pas car comme on passe un pointeur dans
		une liste, des que cette liste est augmentee et a cause
		du realloc() que cela implique, l'adresse dans la liste n'est
		plus valide et donc les fonctions qui utilisaient cette
		adresse calculent n'importe quoi.

	On ajoute donc les fonctions a desassembler dans une autre liste en
		notant simplement l'adresse de la premiere instruction
	*/

	fonction_init(&f,addr);
	i_f = liste_fonction_ajouter(&pg->list_fonction,&f);
	fonction_done(&f);

	pf = &pg->list_fonction.liste[i_f];

	chercher_fonction_par_adresse(&pg->d_binaire,addr,&sym);
	pf->name = strdup(sym.nom);
	if (!fonction_desassembler(pf,dasm,pg))
		return 0;

	return 1;
}

/* desassemble toute les fonctions restantes. Retourne 1 si tout est ok,
	0 sinon */

int programme_desassembler_reste(struct programme *pg,const struct desas *dasm)
{
	int i,i_f;
	struct fonction f, *pf;
	struct symbole sym;

	for (i=0;i<pg->a_desassembler.nb;i++)
	{
		fonction_init(&f,pg->a_desassembler.liste[i]);
		i_f = liste_fonction_ajouter(&pg->list_fonction,&f);
		fonction_done(&f);

		pf = &pg->list_fonction.liste[i_f];

		chercher_fonction_par_adresse(&pg->d_binaire,
			pg->a_desassembler.liste[i],&sym);

		pf->name = strdup(sym.nom);
		
		fonction_desassembler(pf,dasm,pg);
	}

	/* on pourrait egalement continuer a desassembler les autres fonctions
		au lieu de s'arreter a la premiere erreur */

	return 1;
}

/* Attention bonnes gens, risque de gros bugs due a cette fonction. En
	effet, le pointeur retourne dans code pourrat tres bien
	etre realloue par un second appel a cette fonction. Neanmoins,
	cette restriction ne devrait pas nous gener */

int programme_get_byte(struct programme *pg,int addr,byte **code,int *len)
{
	struct section sect;

	if (pg->code == NULL || addr<pg->code_addr ||
		pg->code_addr+pg->code_len<=addr)
	{
		/* on doit lire une nouvelle section */

		if (!donnee_binaire_chercher_section(&pg->d_binaire,addr,&sect))
			return 0;

		printf("section %s: debut_memoire=%x longueur=%d\n",sect.nom,
			sect.debut_memoire,sect.longueur);

		pg->code_addr = sect.debut_memoire;
		pg->code_len = sect.longueur;
		pg->code = (byte *)realloc(pg->code,pg->code_len);
		if (pg->code == NULL)
			return 0;

		if (freadp(pg->fp,pg->code,pg->code_len,sect.debut_fichier)
			!= pg->code_len)
			return 0;
	}

	*code = pg->code + (addr-pg->code_addr);
	*len = pg->code_len + (addr-pg->code_addr);

	return 1;
}

int comparer_fonction(const void *a,const void *b)
{
	const struct fonction *fa = (const struct fonction *)a;
	const struct fonction *fb = (const struct fonction *) b;

	return (fa->addr_first - fb->addr_first);
}

void programme_print(const struct programme *pg)
{
	int i;

	/* on trie d'abord les fonctions par adresse croissante */

	qsort(pg->list_fonction.liste,pg->list_fonction.nb,
		sizeof(struct fonction),comparer_fonction);

	for (i=0;i<pg->list_fonction.nb;i++)
		fonction_print(&pg->list_fonction.liste[i],pg);
}

void programme_ecrire_graphe(const struct programme *pg)
{
	int i;

	for (i=0;i<pg->list_fonction.nb;i++)
		fonction_ecrire_graphe(&pg->list_fonction.liste[i]);
}

/* le format du fichier est le suivant:

	extern	strcpy	0xff56
	intern	memmove	0x5678
	#commentaire
	retourne 1 en cas de succes, 0 en cas d'echec 
*/

int programme_charger_symbole(struct programme *pg,const char *file)
{
	FILE *fp;
	char buffer[256];
	int	line = 0;
	char	nom[256];
	char	type_string[256];
	int	dst_addr,type;

	fp = fopen(file,"r");
	if (fp == NULL)
	{
		perror(file);
		return 0;
	}

	while (fgets(buffer,sizeof(buffer),fp) != NULL)
	{
		line ++;

		if (buffer[0] == '#')
			continue;

		if (sscanf(buffer,"%s %s %x",type_string,nom,&dst_addr) != 3)
		{
			printf("erreur ligne %d de %s\n",line,file);
			continue;
		}

		if (strcmp(type_string,"extern")==0)
			type = EXTERNAL_FUNCTION;
		else if (strcmp(type_string,"intern")==0)
			type = INTERNAL_FUNCTION;
		else
		{
			printf("erreur ligne %d de %s\n",line,file);
			continue;
		}

		ajouter_symbole(&pg->d_binaire,nom,dst_addr,dst_addr,type);

	}

	return 1;
}
