//Fichiers standard inclus
#include <stdio.h>
//Fichiers inclus
#include "commun.h"
#include "texte.h"

/*
   Pour le son, j'utilise le le logiciel Audacity.
   Les fréquences sont les suivantes:
   Musique  : 12014 Hz (devrait être 12105)
   Bruitages: 24209 Hz
   Les volumes sont les suivants:
   Musique  : -3 dB
   Bruitages: -3 dB
   NORMALISER LA HAUTEUR!
*/

/*
   TACHES IMPORTANTES

   Enlever les trucs là uniquement pour augmenter la conso CPU ou le débogage
   - Membre Debug_ID des sprites!
   - INT_HBLANK!
*/

/*
   Fonctions et routines lointaines...

   Pour exécuter les routines qui se trouvent en IWRAM sans utiliser __FarFunction
   et __FarProcedure (je l'utilise trop intensément sans savoir comment elles
   fonctionnent ;)) qui rendent les prototypes des fonctions trop compliqués
   (u32 LaFonction(u32 *FuncAddr, ...)), j'ai écrit deux simples routines de bran-
   chement qui sont certainement plus rapides et qui n'imposent pas de complications
   inutiles (déjà que ça l'est bien assez :D).
   Note: Si l'optimiseur est amélioré, il faudra peut-être ajouter volatile aux poin-
   teurs sur les fonctions pour pouvoir le recompiler correctement...
*/
void (*pRoutineLointaine) ();
u32 (*pFctLointaine) ();

const LEVEL_INFO *gblLI;
int nFrame = 0, nTimer = 0, premFois = 1;
EWRAM_DATA u16 *indexMap[2][TABLE_Y_MAX + 1];
int nLignes = 0;

//unsigned char EWRAM_DATA tableauMap[TABLE_Y_MAX][TABLE_X_MAX/8];
EWRAM_DATA u16 * indexTemp[2][TABLE_Y_MAX + 1];
EWRAM_DATA u16 indexVal[2][TABLE_Y_MAX + 1];
int nCurLevel = 0;
u32 nbSpritesAvantObjets = 0;

EWRAM_DATA OBJATTR MonOAM[128];
OBJAFFINE *rotData = (OBJAFFINE*)MonOAM;
EWRAM_DATA PERSO persos[LIMITE_PERSOS + 2];
EWRAM_DATA u16 id_perso[LIMITE_PERSOS + 2];
EWRAM_DATA unsigned char sdUsed[2048];
EWRAM_DATA SONIC copie_sonic;

int TABLE_X, TABLE_Y;
u16 Pause, gblScore, gblTemps, gblTempsBloque;
u16 gblGameState;

//unsigned short *laMap=EWRAM;
//unsigned short *laMap=(unsigned short *)Niveau2_map;
//#define mat(x,y)    (laMap[(y)*TABLE_X+(x)])
extern __attribute__ ((long_call)) IWRAM_CODE void limite_iwram();
extern char limite_var[100];
EWRAM_DATA extern char limite_ewram[100];

void InitLevel(const LEVEL_INFO *li, SONIC *s)
{
	InitSonic(s, 32, 256, 24, 32, 0);        //Crée le premier sprite: Sonic
	TABLE_X = li->fgmi.largeur, TABLE_Y = li->fgmi.hauteur;
	niveau_reel_eau = li->niveau_eau;
	s->x = li->fgmi.sonicX, s->y = li->fgmi.sonicY;

	if (li->bgmi.BGtileset && li->bgmi.BGpalette)
	{
		CopieDMA((void *)li->bgmi.BGtileset, (void *)CHAR_BASE_BLOCK(2), li->bgmi.BGtilTaille >> 1, DMA_16im);
//		ChargePalette(li->bgmi.BGpalette, BG_PALETTE+192, li->bgmi.BGpalTaille>>1);
	}

	calcIndex(0, (unsigned short *) li->fgmi.mapGfx);
	calcIndex(1, (unsigned short *) li->fgmi.mapCol);

	adrTiles256 = li->fgmi.tileset;
	map_maxTiles = li->fgmi.maxTiles;

	// Calcule le décalage pour connaître le nombre d'octets qu'utilise une tile correctement:
	// normalement c'est 16x16 = 256 octets en 8 bits, donc un décalage de 8
	if (li->fgmi.bitsPerPixel == 4)
		map_bitsPerPixelShift = 7;
	else if (li->fgmi.bitsPerPixel == 8)
		map_bitsPerPixelShift = 8;
	else
		ErreurFatale("main.c :: InitLevel #01",
					 "La profondeur du tileset de la FGMAP est invalide! (%i)", li->fgmi.bitsPerPixel);

	if (li->fgmi.maxTiles == 0 || li->fgmi.maxTiles > 768)
		ErreurFatale("main.c :: InitLevel #02",
					 "Le nombre de tiles propose (%i) n'est pas valable!", li->fgmi.maxTiles);

	gblLI = li;
	viewportX0 = desiredViewportX0 = 0;
	viewportY0 = desiredViewportY0 = 0;
	viewportX1 = desiredViewportX1 = TILE * (TABLE_X + 1);
	viewportY1 = desiredViewportY1 = TILE * (TABLE_Y + 1);

	InitSysPalettes();
}

int scrlX, scrlY;

void RecreePalettes()
{
	int i;

	//Copie les palettes, 128 valeurs 16 bits
	if (gblLI->fctPalette)
		gblLI->fctPalette();
	ChargePalette(Pal_10_palette, nPalObj(15), 16);

	//Fond
	//    PaletteNormale[0]=RGB(0,0,15);
	//Texte
	PaletteNormale[0xf1] = RGB(31, 31, 31);
	if (gblLI->ei.using_bg3)
	{

		//Menu type sprites
		PaletteNormale[0x1e1] = RGB(31, 31, 31);

		//Entrée des couleurs dans la palette pour le menu
		PaletteNormale[0x1e2] = RGB(0, 0, 28);
		PaletteNormale[0x1e3] = RGB(31, 31, 0);
		PaletteNormale[0x1e8] = RGB(0, 0, 0);
		PaletteNormale[0x1e9] = RGB(31, 31, 31);
		PaletteNormale[0x1ea] = RGB(31, 31, 0);
	}
	else
	{

		//Entrée des couleurs dans la palette pour le menu
		PaletteNormale[0xf2] = RGB(0, 0, 28);
		PaletteNormale[0xf3] = RGB(31, 31, 0);
		PaletteNormale[0xf8] = RGB(0, 0, 0);
		PaletteNormale[0xf9] = RGB(31, 31, 31);
		PaletteNormale[0xfa] = RGB(31, 31, 0);
	}

	CreePaletteSousEau(PaletteNormale, PaletteNormale, PaletteEau, 240);
	CreePaletteSousEau(PaletteNormale + 256 + 16 * 12, PaletteNormale + 256 + 16 * 12, PaletteEau + 256 + 16 * 12, 48);

	for (i = 0xe0; i < 0xff; i++)
	{
		PaletteEau[i] = PaletteNormale[i];
		PaletteEau[0x100 + i] = PaletteNormale[0x100 + i];
	}

	if (!premFois)
	{
		MajSysPalettes();
	}

	if (gblLI->fctApresPalette)
		gblLI->fctApresPalette();
}

void VerifNiveau()
{
	int i, j;

	//Vérifie que le type des bornes n'a pas changé
	if (handlersCreePerso[18] != cpBorne)
	{
		ErreurFatale("main.c :: VerifNiveau #01",
					 "Le type des bornes a change. cpBorne doit etre place en 19eme position.");
	}

	for (i = 0; i < *gblLI->spi.nbEle; i++)
	{
		if (gblLI->spi.liste[i].type == 19)
		{
			if (!(gblLI->spi.liste[i].param1 >= 1 && gblLI->spi.liste[i].param1 <= 10))
			{
				ErreurFatale("main.c :: VerifNiveau #02",
							 "L'identificateur de la borne %i n'est pas valide. Son param1 doit etre entre 1 et 10.",
							 i);
			}

			for (j = 0; j < i; j++)
			{
				if (gblLI->spi.liste[j].type == 19)
				{
					if (gblLI->spi.liste[i].param1 == gblLI->spi.liste[j].param1 && i != j)
					{
						ErreurFatale("main.c :: VerifNiveau #03",
									 "L'identificateur de borne %i a ete trouve en double (%i,%i).",
									 gblLI->spi.liste[i].param1,
									 i, j);
					}
				}
			}
		}
	}
}

void CreePFInclinees()
{
	int i;
	for (i = 0; i < 0xf0; i++)
		incline[i] = std_incline[i];
	for (i = 0; i < 0xd0; i++)
		inclineH[i] = std_inclineH[i];
	for (i = 0; i < 0xf0; i++)
		anglesPF[i] = std_anglesPF[i];
	for (i = 0; i < 0xd0; i++)
		anglesPFH[i] = std_anglesPFH[i];
	for (i = 0; i < 0xf0; i++)
		anglesPFcomplexes[i] = std_anglesPFcomplexes[i];
}

void HUD_drawInt_Pal31(u32 x, u32 y, u32 chiffres, u32 valeur)
{
	do
	{
		CreeObjetStandardHUD(x, y, chiffres + (valeur % 10), 15, TAILLE_8x8);
		valeur /= 10;
		x -= 7;
	}
	while (valeur);
}

void AfficheHUD(SONIC *s, u32 force)
{
	int i, x, j, k;
	u32 pal_1, pal_2;
	pal_1 = AjPalette(Pal_1_palette);
	pal_2 = AjPalette(Pal_2_palette);

	if (gblTypeHUD == 0)
	{
		i = AjSprite(Pal_1_hud_tiles, sizeof(Pal_1_hud_tiles) << 1);
		x = AjSprite(Pal_1_chiffres_tiles, sizeof(Pal_1_chiffres_tiles) << 1);
		CreeObjetStandardHUD(6, 4, i, pal_1, TAILLE_32x8);
		if (gblTemps >= 3600 * 9 && ((nFrame / 12) & 1))
			CreeObjetStandardHUD(6, 4 + 10, i + 4, pal_2, TAILLE_32x8);
		else
			CreeObjetStandardHUD(6, 4 + 10, i + 4, pal_1, TAILLE_32x8);
		if (s->bagues == 0 && ((nFrame / 12) & 1))
			CreeObjetStandardHUD(6, 4 + 20, i + 8, pal_2, TAILLE_32x8);
		else
			CreeObjetStandardHUD(6, 4 + 20, i + 8, pal_1, TAILLE_32x8);
		CreeObjetStandardHUD(6 + 32, 4, i + 12, pal_1, TAILLE_8x8);

		//         CreeObjetStandard(scrlX+80,scrlY+4,AjSprite(Pal_1_chiffres_tiles,8*8),1,TAILLE_8x8);
		j = gblScore * 10;
		i = 0;
		do
		{
			CreeObjetStandardHUD(80 - i * 7, 4, x + (j % 10), pal_1, TAILLE_8x8);
			j /= 10;
			i++;
		}
		while (j);
		j = s->bagues;
		i = 0;
		do
		{
			CreeObjetStandardHUD(59 - i * 7, 4 + 20, x + (j % 10), pal_1, TAILLE_8x8);
			j /= 10;
			i++;
		}
		while (j);
		CreeObjetStandardHUD(41, 4 + 10, x + ((gblTemps / 3600) % 10), pal_1, TAILLE_8x8);
		CreeObjetStandardHUD(48, 4 + 10, x + 10, pal_1, TAILLE_8x8);

		/*         j=mod32(div32(gblTemps, 60), 60);
		for (i=0;i<2;i++)      {
		CreeObjetStandardHUD(59-i*7,4+10,x+mod32(j, 10),pal_1,TAILLE_8x8);
		j = div32(j, 10);
		}*/
		j = (gblTemps / 60) % 60;
		for (i = 0; i < 2; i++)
		{
			CreeObjetStandardHUD(59 - i * 7, 4 + 10, x + (j % 10), pal_1, TAILLE_8x8);
			j /= 10;
		}
	}
	else if (gblTypeHUD == 1)
	{

		//                 i=AjSprite(Pal_1_hud_tiles,sizeof(Pal_1_hud_tiles)<<1);
		if (s->bagues == 0 && ((nFrame / 12) & 1))
			CreeObjetStandardHUD(6, 1, AjSprite(powerups_tiles + 4 * 16 * 8, 16 * 16), pal_1, TAILLE_16x16);
		else
			CreeObjetStandardHUD(6, 1, AjSprite(powerups_tiles + 4 * 16 * 8, 16 * 16), pal_2, TAILLE_16x16);
		x = AjSprite(Pal_1_chiffres_tiles, sizeof(Pal_1_chiffres_tiles) << 1);
		j = s->bagues;
		i = 0;
		do
		{
			CreeObjetStandardHUD(38 - i * 7, 3, x + (j % 10), pal_1, TAILLE_8x8);
			j /= 10;
			i++;
		}
		while (j);
		CreeObjetStandardHUD(108, 3, x + ((gblTemps / 3600) % 10), pal_1, TAILLE_8x8);
		CreeObjetStandardHUD(108 + 7, 3, x + 10, pal_1, TAILLE_8x8);
		j = (gblTemps / 60) % 60;
		for (i = 0; i < 2; i++)
		{
			CreeObjetStandardHUD(108 + 18 - i * 7, 3, x + (j % 10), pal_1, TAILLE_8x8);
			j /= 10;
		}

		j = gblScore * 10;
		i = 0;
		do
		{
			CreeObjetStandardHUD(228 - i * 7, 3, x + (j % 10), pal_1, TAILLE_8x8);
			j /= 10;
			i++;
		}
		while (j);
	}
	else if (gblTypeHUD == 2)
	{
		static int HUDringsY = -8, HUDringsT = 0, HUDlastRings = 0, HUDscoreY = -8, HUDscoreT = 0, HUDlastScore = 0,
			HUDtimeY = -8, HUDtimeT;
		int HUDringsYd, HUDscoreYd, HUDtimeYd, y;
#define TPS_AFF_HUD 120
		k = AjSprite(Pal_1_hud_tiles, sizeof(Pal_1_hud_tiles) << 1);
		x = AjSprite(Pal_1_chiffres_tiles, sizeof(Pal_1_chiffres_tiles) << 1);
		if (HUDlastScore != gblScore || force)
		{
			HUDscoreT = TPS_AFF_HUD;
			HUDlastScore = gblScore;
		}
		else if (HUDscoreT > 0)
			HUDscoreT--;
		if (HUDlastRings != s->bagues || s->bagues == 0 || force)
		{
			HUDringsT = TPS_AFF_HUD;
			HUDlastRings = s->bagues;
		}
		else if (HUDringsT > 0)
			HUDringsT--;
		if (gblTemps % (30 * 60) == 0 && gblTemps > 0)
			HUDtimeT = TPS_AFF_HUD * 2;
		else if (force || gblTemps >= 3600 * 9)
			HUDtimeT = TPS_AFF_HUD;
		else if (HUDtimeT > 0)
			HUDtimeT--;
		y = 4;
		if (HUDscoreT == 0)
			HUDscoreYd = -6;
		else
		{
			HUDscoreYd = y;
			y += 10;
		}

		if (HUDtimeT == 0)
			HUDtimeYd = -6;
		else
		{
			HUDtimeYd = y;
			y += 10;
		}

		if (HUDringsT == 0)
			HUDringsYd = -6;
		else
		{
			HUDringsYd = y;
			y += 10;
		}

		if (force)
		{
			if (HUDscoreY == -6 && HUDtimeY == -6 && HUDringsY == -6)
				HUDscoreY = -26, HUDtimeY = -16;
			if (HUDscoreY == -6 && HUDtimeY == -6)
				HUDscoreY = -16;
		}

		if (HUDscoreY > HUDscoreYd)
			HUDscoreY -= 1;
		else if (HUDscoreY < HUDscoreYd)
			HUDscoreY += 1;
		if (HUDtimeY > HUDtimeYd)
			HUDtimeY -= 1;
		else if (HUDtimeY < HUDtimeYd)
			HUDtimeY += 1;
		if (HUDringsY > HUDringsYd)
			HUDringsY -= 1;
		else if (HUDringsY < HUDringsYd)
			HUDringsY += 1;

		if (HUDscoreY > -6)
		{
			CreeObjetStandardHUDPlus(6, HUDscoreY, k, pal_1, TAILLE_32x8);
			CreeObjetStandardHUDPlus(6 + 32, HUDscoreY, k + 12, pal_1, TAILLE_8x8);
			j = gblScore * 10;
			i = 0;
			do
			{
				CreeObjetStandardHUDPlus(80 - i * 7, HUDscoreY, x + (j % 10), pal_1, TAILLE_8x8);
				j /= 10;
				i++;
			}
			while (j);
		}

		if (HUDtimeY > -6)
		{
			if (gblTemps >= 3600 * 9 && ((nFrame / 12) & 1))
				CreeObjetStandardHUDPlus(6, HUDtimeY, k + 4, pal_2, TAILLE_32x8);
			else
				CreeObjetStandardHUDPlus(6, HUDtimeY, k + 4, pal_1, TAILLE_32x8);
			CreeObjetStandardHUDPlus(41, HUDtimeY, x + ((gblTemps / 3600) % 10), pal_1, TAILLE_8x8);
			CreeObjetStandardHUDPlus(48, HUDtimeY, x + 10, pal_1, TAILLE_8x8);
			j = (gblTemps / 60) % 60;
			for (i = 0; i < 2; i++)
			{
				CreeObjetStandardHUDPlus(59 - i * 7, HUDtimeY, x + (j % 10), pal_1, TAILLE_8x8);
				j /= 10;
			}
		}

		if (HUDringsY > -6)
		{
			if (s->bagues == 0 && ((nFrame / 12) & 1))
				CreeObjetStandardHUDPlus(6, HUDringsY, k + 8, pal_2, TAILLE_32x8);
			else
				CreeObjetStandardHUDPlus(6, HUDringsY, k + 8, pal_1, TAILLE_32x8);
			j = s->bagues;
			i = 0;
			do
			{
				CreeObjetStandardHUDPlus(59 - i * 7, HUDringsY, x + (j % 10), pal_1, TAILLE_8x8);
				j /= 10;
				i++;
			}
			while (j);
		}
	}
}

void NiveauSuivant()
{

	//Annule la borne
	sauve_saved = 0;
	nCurLevel++;
	if (nCurLevel >= ExtNbreNiveaux)
		nCurLevel -= ExtNbreNiveaux;
	Quitte();
}

/*unsigned short IWRAM_CODE mod16(unsigned short x, unsigned short y)       {
   //x%y
   s32 i;
   u32 j;
   for (i=14;i>=0;i-=2)      {
   j=y<<i;
   if (j==0)
   continue;
   while(x>=j)
   x-=j;
   }
   return x;
   }*/
int random_seed;
int rand()
{
	random_seed = random_seed * 1103515245 + 12345;
	return (unsigned int) (random_seed / 65536) % 32768;
}

extern void __iheap_start();
extern void __eheap_start();

/*const u16 viewportScript[]=         {
   0,      0,81,
   20,     0,40,
   0xffff
   };

   void GereViewportScript(const u16 *vs)      {
   while(*vs!=0xffff)      {
   if (scrlX>=*vs && scrlX<=*(vs+3))           {
   desiredViewportY0=(*(vs+1))*TILE, desiredViewportY1=(*(vs+2))*TILE+TILE+1;
   }
   vs+=3;
   }
   }*/
void GoldDrawText(u32 x, u32 y, u32 align, const char *texte)
{
	u32 tiles;
	char *txtsauv = texte;
	tiles = AjSprite(Pal_10_lettres_tiles, sizeof(Pal_10_lettres_tiles) << 1);
	if (align == 1 || align == 2)
	{
		while (*texte++)
			x -= align * 4;
		texte = txtsauv;
	}

	while (*texte)
	{
		if (*texte >= 'A' && *texte <= 'Z')
			CreeObjetStandardHUDHorsEcran(x, y, tiles + ((*texte - 'A') << 1), 15, TAILLE_8x16);
		else if (*texte >= 'a' && *texte <= 'z')
			CreeObjetStandardHUDHorsEcran(x, y, tiles + ((*texte - 'a') << 1), 15, TAILLE_8x16);
		x += 8;
		texte++;
	}
}

void GoldDrawChiffre(u32 x, u32 y, u32 numero)
{
	u32 tiles;
	static int sangle, sdiv;
	int angle, alpha;

	//Passer numero==-1 pour initialiser
	if (numero == -1)
		sangle = 90 + 90, sdiv = 2;
	if (numero < 1 || numero > 3)
		return;
	tiles = AjSprite(Pal_10_chiffres_tiles + 16 * 16 * (numero - 1), 16 * 32);
	if (sangle <= 360)
	{
		CreeObjetStandardHUDRotation(x - 8, y - 16, tiles, 15, 1, TAILLE_16x32);
		if (sangle > 360)
			sangle = 360;
		if (sdiv >= 256)
			sdiv = 256;
		angle = 360 - sangle;
		rotData[1].pa = (256 * cosinus[angle]) / sdiv;
		rotData[1].pb = -(256 * sinus[angle]) / sdiv;
		rotData[1].pc = (256 * sinus[angle]) / sdiv;
		rotData[1].pd = (256 * cosinus[angle]) / sdiv;
		sangle += 8;
		sdiv += 11;
	}
	else
	{
		CreeObjetStandardHUD(x, y, tiles, 15, TAILLE_16x32);
		if (sdiv <= 512)
		{
			if (sdiv < 384)
				alpha = 0;
			else
				alpha = (sdiv - 384) / 8;
			gbaSetAlphaCoeff(16 - alpha, alpha);
			CreeObjetStandardHUDAlphaRot(x - 8, y - 16, tiles, 15, 1, TAILLE_16x32);
			rotData[1].pa = (256 * cosinus[0]) / sdiv;
			rotData[1].pb = -(256 * sinus[0]) / sdiv;
			rotData[1].pc = (256 * sinus[0]) / sdiv;
			rotData[1].pd = (256 * cosinus[0]) / sdiv;
		}
		else if (sdiv >= 600)
			gbaSetAlphaCoeff(16, 8);
		sdiv += 16;
	}
}

void DessineBarreTitreZone()
{
	int barre, i, j, y, x;
	barre = AjSprite(Pal_10_barres_tiles, sizeof(Pal_10_barres_tiles) << 1);
	if (nFrame >= 0x1000 + 20)
		y = -(nFrame - 0x1000 - 20) * 8;
	else
		y = -180 + nFrame * 6;
	x = 43;
	if (y > 0)
		y = 0;
	for (i = 0; i < 5; i++)
		CreeObjetStandardHUDHorsEcran(x, y + i * 32, barre + 6, 15, TAILLE_8x32);
	CreeObjetStandardHUDHorsEcran(x, y + 5 * 32, barre + 5, 15, TAILLE_8x8);

	for (j = 0; j < 7; j++)
	{
		y -= 7;
		if (y > 0)
			y = 0;
		x += 7;
		for (i = 0; i < 5; i++)
		{
			if (j == 6)
				CreeObjetStandardHUDHorsEcran(x, y + i * 32, barre + 10, 15, TAILLE_8x32);
			else
				CreeObjetStandardHUDHorsEcran(x, y + i * 32, barre + 1, 15, TAILLE_8x32);
		}

		CreeObjetStandardHUDHorsEcran(x, y + 5 * 32, barre, 15, TAILLE_8x8);
	}
}

//Mode: 0: écran titre, 1: juste afficher
void AfficheTitreZone(int mode)
{
	static int x1, x2, x3, x4;
	int nFrameSauv;
	int bActeAffiche;
	int barre;
	if (mode == 0)
	{
		nFrame = 0;
		x1 = -10;
		x2 = -150;
		x3 = 180;
		x4 = 160;
		PaletteNormale[0x1fc] = 0;
		PaletteEau[0x1fc] = 0;
		AttendVSync();
	}
	else
	{
		nFrameSauv = nFrame;
		nFrame += 0x1000;
	}

	bActeAffiche = (gblLI->acte >= 1 && gblLI->acte <= 3);
	while (nFrame < 90 || mode == 1)
	{
		if (mode == 0)
		{
			gbaSetBlendingMode(SFX_ALPHA, 1 << 3, 0x3f);
			InitSprites();
			x1 += 8, x2 += 8;
			if (x1 >= 160)
				x1 = 160;
			if (bActeAffiche)
			{
				if (x2 >= 152)
					x2 = 152;
			}
			else
			{
				if (x2 >= 160)
					x2 = 160;
			}
		}

		if (nFrame >= 0x1000 + 20)
			x1 += 8;
		if (nFrame >= 0x1000 + 30)
			x2 += 8, x3 += 8, x4 += 8;
		GoldDrawText(x1, 40, 2, gblLI->nom);
		GoldDrawText(x2, 60, 2, "ZONE");
		if (bActeAffiche)
		{
			if (nFrame == 30)
				GoldDrawChiffre(x3, 42, -1);
			else if (nFrame > 30)
				GoldDrawChiffre(x3, 42, gblLI->acte);
			if (nFrame < 35)
				PaletteNormale[0x1fc] = 0;
			else if (nFrame <= 66)
				PaletteNormale[0x1fc] = RVB((25 * (nFrame - 35)) >> 5, (25 * (nFrame - 35)) >> 5, nFrame - 35);
			PaletteEau[0x1fc] = PaletteNormale[0x1fc];
			barre = AjSprite(Pal_10_barres_tiles, sizeof(Pal_10_barres_tiles) << 1);
			CreeObjetStandardHUDHorsEcran(x4, 69, barre + 14, 15, TAILLE_16x8);
		}

		DessineBarreTitreZone();
		if (mode == 0)
		{
			AttendVSync();
			CopieSprites();
			CopieDMA(MonOAM, OAM, 1024 >> 2, DMA_32im);
			nFrame++;
		}
		else
			break;
	}

	if (mode == 0)
		nFrame = 2;
	else
		nFrame = nFrameSauv;
}

int main(void)
{
	int /*scrlX, scrlY,*/ i, j = 0, k, l, x;
	u32 zero = 0;
	u32 finDeNiveauCpt;
	char str[100];
	SONIC sonic;
	ECRAN Ecran;
	int tmp, PauseRequise = 0;
	FICHIER *fichier;
	u32 CPU = 0, cpuMax = 0, tpsMax = 0;
	u16 lastCpuUsage = 0;
	bool reinit;

	InitFS();

	//Active le prefetch et autres trucs spaces du gamepack
	//Réglages rapides (bidouilleurs)
	//    REG_WAITRAM=0x0E000020;             //Booste la RAM (non-recommandé, dangereux)
	//    REG_WSCNT=0x431B;                   //Pas beaucoup plus rapide sur vraie GBA? A tester
	//Réglages normaux (jeux commerciaux)
	REG_WAITCNT = 0x4317;                //Booste la ROM (recommandé, pas dangereux)
//	REG_WAITCNT = 0x000f;
	coeffs = tblGamma[gblGamma];
	sauve_saved = 0;
	gblScore = 0;

	//Requis car cameraViewportHighestPriorite prend sa première valeur, et certains handlers s'en servent, donc elle doit être initialisée à -1 (=aucun)
	cameraViewportPriorite = cameraViewportHighestPriorite = -1;

reinit:
	gbaSetBlendingMode(SFX_FADE_BLACK, 0x3f, 0x3f);
	gbaSetFadeCoeff(31);

	gblTempsBloque = 0;
	gblTemps = 0;
	nFrame = 0;
	nTimer = 0;
	Pause = 0;
	gblGameState = STATE_INGAME;
	finDeNiveauCpt = 0;

	//DEBUG ONLY!!!!
	gblDebugRestart = 0;
	advMapOffsetX = 1, advMapOffsetY = 1;

	//OKAY
	ancienRegTouches = 0xffff;
	reinit = 1;
	jeuEnCours = 0;
	DesactiveInterruptions();

	for (i = 0; i < 4; i++)
		Bruitage[i] = SON_ZERO;
	for (i = 0; i < 408; i++)
		SON_ZERO[i] = 0;

	//Réglages lents (le pire qu'on puisse faire)
	//    REG_WSCNT=0x30f;
	//    REG_WAITRAM=0x01000020;             //Booste la RAM (non-recommandé, dangereux)
	// Passer en mode 0, avec le BG0 activé:
	REG_DISPCNT = MODE_0 | BG0_ENABLE | BG1_ENABLE | BG2_ENABLE | OBJ_ENABLE | OBJ_1D_MAP;

	// Configure les différents plans
	//ATTENTION LE MODE 16 COULEURS EST SUPPORTE AUSSI!!!
	REG_BG0CNT = BG_256_COLOR | BG_WRAP | SCREEN_BASE(31) | BG_PRIORITY(2) | CHAR_BASE(0);
	REG_BG1CNT = BG_16_COLOR  | BG_WRAP | SCREEN_BASE(29) | BG_PRIORITY(3) | CHAR_BASE(2);
	REG_BG2CNT = BG_256_COLOR | BG_WRAP | SCREEN_BASE(30) | BG_PRIORITY(1) | CHAR_BASE(0);

	// Initialise la mémoire vidéo
	_memset(VRAM, &zero, VRAM_SIZE);
	InitLevel(&ExtNiveaux[nCurLevel], &sonic);

	// Initialise le BG3 pour du texte
	if (!gblLI->ei.using_bg3)
	{
		REG_BG3CNT = BG_16_COLOR  | SCREEN_BASE(28) | BG_PRIORITY(0) | CHAR_BASE(3);
		CopieDMA((void*)charset_tiles, (void*)CHAR_BASE_BLOCK(3), sizeof(charset_tiles) >> 1, DMA_16im);
		REG_BG3HOFS = 0;
		REG_BG3VOFS = 0;
	}
	else
		REG_DISPCNT &= ~BG3_ENABLE;

	//Vide l'OAM
	for (i = 0; i < 128; i++)
	{
		TOOL_OAM_OBJ_KILL(i);
	}

	//Et mon système de sprites
	InitZero(persos);
	InitZero(sdUsed);

	//    InitZero(tableauMap);
	//Initialisation
	scrlX = 0;
	scrlY = 96;

	//set transparency level
	gbaSetAlphaCoeff(16, 8);

	InitTileSystem();                //Pourquoi je nomme cette fonction en anglais?
	nbrePersos = 0;
	super_vitesse = 0;
	prioDefaut = 2;                        //Priorité par défaut des objets = 2

	premFois = 1;
	ancienRegTouches = REG_KEYS;
	Pause = 0;
	if (sauve_saved > 0)
		LoadState(&sonic);
	else
		InitZero(lstBornes);

	sonic.y--;

	CreePFInclinees();

	if (gblLI->fctInit)
		gblLI->fctInit();

palettes:

	//   jeuEnCours=0;

	/*    ChargePalette(palette1_16x16_palette, nPalObj(1), 16);
	ChargePalette(pont_palette, nPalObj(2), 16);
	ChargePalette(powerups_palette, nPalObj(12), 16);

	ChargePalette(greenhill_specific_sprites_palette, nPalObj(10), 16);*/
	InitTimers();

	RecreePalettes();

	if (reinit)
	{
		CopieDMA(PaletteNormale, PaletteNormaleSauv, 512, DMA_16im);
		CopieDMA(PaletteEau, PaletteEauSauv, 512, DMA_16im);

#define WANT_INIT_FADE
#ifdef WANT_INIT_FADE
		AssombritPalette(PaletteNormale, PaletteNormaleSauv, 32);
		AssombritPalette(PaletteEau, PaletteEauSauv, 32);
#endif
		CopieDMA(PaletteNormale, BG_PALETTE, 512, DMA_16im);

		/*    while(REG_VCOUNT>=160);
		while(REG_VCOUNT<160);*/

		// Set up our interrupt handler
		//            REG_INTERRUPT=(u32)&InterruptHandler;
		init_sound();
		JoueMusique(gblLI->nom_musique, 0, 0);

		/*            audio_dat = SymFind(gblLI->nom_musique,&fichier);
		end = fichier->taille + audio_dat;

		start_ad(audio_dat);*/
		BG_PALETTE[0] = RGB(31, 31, 31);
		initInterrupt();
#ifdef WANT_INIT_FADE
		fade = -24;
#else
		fade = 0;
		REG_COLEY = 0;
#endif
	}

	jeuEnCours = 1;
	reinit = 0;

	//Vérifie si le niveau est valide et affiche des infos de débogage sinon
	VerifNiveau();

	/*    for (i=0;i<512;i++)
	CreePerso(0,0,1,16,16,hBague,INSTR_PAS_EFFACER|INSTR_SEULEMENT_ECRAN|INSTR_HORS_MAP);*/
	int ramage_power = 2;
	if (gblLI->ei.using_bg3)
		ramage_power = 1;
	gblSonic = &sonic;

	EcrSprites = &Ecran;

	while (1)
	{
		GestionnairePalette();

		if (!gblLI->ei.using_bg3)
		{
			//A ENLEVER DEBOGAGE2!!!
			if (gblDebogage2)
				REG_DISPCNT |= BG3_ENABLE;
			else
				REG_DISPCNT &= ~BG3_ENABLE;
		}

		InitSprites();
		if (gblDebogage)
		{
			Message(9, "%i", coordValide(158 * 16, 32 * 16));
			Message(10, "%i %i", advMapOffsetX, plan);
			Message(11, "%i %i %i", sonic.croche, sonic.colBasObj, desiredViewportY1);
		}

		if ((REG_KEYS & (KEY_START | KEY_SELECT)) == (KEY_START | KEY_SELECT))
		{
			goto reinit;
		}

		if (!sonic.type && sonic.fctEtat != heMort)
		{
			JoueSon(3, SFX_PERDU);
			ChangeEtat(&sonic, heMort);
		}

		TOUCHES = (REG_KEYS) &~ancienRegTouches;
		ancienRegTouches = REG_KEYS;
		if (TOUCHES)
			gblRepete = 0;        //Changement de touche -> désactive la répétition pour le moment
		else
			gblRepete++;        //Aucun changement de touche? Si ça continue, on enclenchera la répétition des touches
		if (gblRepete > 30 && gblRepete % 8 == 0)
			TOUCHES = REG_KEYS & 0x03f0;
		GestionDebug(&sonic);

		//Pause
		if (TOUCHES & KEY_START)
		{
			if (gblTypeHUD == 2)
				PauseRequise = 2;        //Ne pas redessiner le HUD, les sprites, etc.
			else
				PauseRequise = 1;
		}

		//Select
		if (TOUCHES & KEY_SELECT || gblDebugRestart)
		{
			goto reinit;
		}

		if (TOUCHES & KEY_R)
		{
			ramage_power++;
		}

		//Sauvegarde/Chargement

		/*        if (TOUCHES&KEY_L)     {
		for (i=0;i<nbrePersos;i++)
		LiberePerso(i);
		Exec(SaveState,&sonic);
		for (i=0;i<nbrePersos;i++)
		RemetPerso(i);
		}
		else if (TOUCHES&KEY_R && save_largeur>0)        {
		for (i=nbrePersos-1;i>=0;i--)
		EffacePerso(i);
		LoadState(&sonic);
		premFois=1;
		}*/

		//        for (i=0;i<ralentit;i++)
		//            j=(int)(i/0.8);

		/*        if (REG_KEYS&KEY_SELECT)      {
		if (Pause)
		break;
		}*/
		if (!Pause)
		{
			sonic.score = 0;
			if (sonic.fctEtat != NULL)
				sonic.fctEtat(&sonic, ETAT_ACTION_GERE);

			/*            if (viewportScript)
			GereViewportScript(viewportScript);*/
			if (LitControle(&sonic, CNT_SCROLLING))
				GereScrolling(&sonic, &scrlX, &scrlY);
			Ecran.scrlX = scrlX;
			Ecran.scrlY = scrlY;

			/*            if (objetsUtilises>=127 || nSpritesSup>=SPRITES_SUP_MAX)
			ErreurFatale("main.c :: main #01","Trop de sprites (%i %i)",objetsUtilises,nSpritesSup);*/

			//Titre de la zone
			if (nFrame == 1)
				AfficheTitreZone(0);
			else if (nFrame >= 2 && nFrame <= 60)
				AfficheTitreZone(1);
			if (gblGameState == STATE_FINNIVEAU)
				FinDeNiveau(&sonic, finDeNiveauCpt++);

			VerifMapBouge(scrlX >> 4, scrlY >> 4);

			/*            _ihScrlX=scrlX;
			_ihScrlY=scrlY;*/
			cameraViewportHighestPriorite = cameraViewportPriorite;
			cameraViewportPriorite = -1;

			//Pour le HUD
			if (PauseRequise != 2)
				AfficheHUD(&sonic, 0);

			if (gblLI->fctLoop)
				gblLI->fctLoop();

			DessineEau();

			//Fin du HUD
			AfficheSonic(&sonic, &Ecran);

			//Sonic est sous l'eau?
			i = sonic.sous_leau;
			sonic.sous_leau = (sonic.y + 10 >= niveau_reel_eau);
			if (!premFois)
			{

				//Sauvegarde des coordonnées pour détecter un mouvement
				sonic.lastX = (sonic.x << 8) + sonic.virgule_x;
				sonic.lastY = (sonic.y << 8) + sonic.virgule_y;
				if (LitControle(&sonic, CNT_COLLISIONS))
				{
					if (ABS(sonic.vy) >= vMUL(1) && i != sonic.sous_leau)
					{
						if (!sonic.croche)
						{
							JoueSon(2, SFX_PLAF);
							CreePerso(sonic.x, niveau_reel_eau - 32, 0, 32, 32, hPlafEau, 0);
						}
					}

					if (sonic.sous_leau && (nTimer & 1))
						;
					else
						GereTouches(&sonic);
					GereEau(&sonic);
					GereSonicEnBoule(&sonic);
					Moteur2D(&sonic);
				}

				nbSpritesAvantObjets = objetsUtilises + 1;

				//ATTENTION: jamais                             objetsUtilises>=127!
				if (LitControle(&sonic, CNT_COLLISIONS))
					GerePersos(&sonic, &Ecran);
				else
					GerePersos(&copie_sonic, &Ecran);
			}

			gblScore += sonic.score;
		}

		if (!Pause)
		{
			depassement = 0;
			CopieMap_16(scrlX >> 4, scrlY >> 4, TABLE_X, TABLE_Y, LARGEUR_MAP_HARDWARE);

			AffObjets(scrlX, scrlY, TAILLEX_ECRAN, TAILLEY_ECRAN, TABLE_X, TABLE_Y, LARGEUR_MAP_HARDWARE);
		}

		if (super_vitesse)
			super_vitesse--;

		if (gblDebogage)
		{
			printf_xy(17, 0, "CPU  : %02i.%i  ",  lastCpuUsage / 10, lastCpuUsage % 10);
		}

		if (!PauseRequise)
		{
			if (gblDebogage)
			{
				i = (int) hnest;
				printf_xy(17, 2, "Code : %i  ", limite_iwram - 0x3000000);
				if (i != 0x3008000)
				{
					printf_xy(
						0,
						1,
						"Iwram: %i (%i%%)  ",
						limite_var - 0x3000000,
						(int) ((0x3008000 - i) + (limite_var - 0x3000000)) / 240);
					printf_xy(0, 3, "Stack: %i (#%i) ", 0x3008000 - i, idfct);
					printf_xy(0, 4, "Tiles: %03i", map_nTiles);
				}
				else
				{
					printf_xy(17, 1, "Peak : %i.%i  ", cpuMax / 10, cpuMax % 10);
					printf_xy(17, 3, "Iwram: %i  ", __iheap_start - 0x3000000);
					printf_xy(17, 4, "Ewram: %i", __eheap_start - 0x2000000);
					printf_xy(17, 5, "Tiles: %03i", map_nTiles);
				}

				for (i = 0; i < ramage_power; i++)
				{
					printf_xy(
						0,
						19,
						"%02i %03i %03i %03i %03i %02i %02i %01i %01i %02i %02i       ",
						sonic.angle,
						objetsUtilises,
						nbrePersos,
						num_sprite_actif,
						plan,
						sonic.vx >> 8,
						sonic.vy >> 8,
						sonic.crochePF,
						sonic.croche,
						99,
						99);
				}
			}
		}

		if (Pause)
			DessTexte(25, 19, "Pause");

		nFrame++;
		if (!gblTempsBloque)
			gblTemps++;

		i = UtilCPU();
		if (i >= CPU - 5 && i <= CPU)
			i = CPU;
		else
			CPU = i;

		if (i >= cpuMax)
		{
			cpuMax = i;
			tpsMax = 60;
		}
		else
		{
			tpsMax--;
			if (tpsMax <= 0)
				cpuMax = i, tpsMax = 60;
		}

		//        REG_WSCNT=0x030f;                   //Ralentit la ROM, histoire d'économiser des piles et d'accélérer l'émulateur
		//L'indicateur de CPU sera supprimé, donc faut déjà prévoir...
		i = UtilCPU();
		lastCpuUsage = i;

		//PAL -> saute une frame sur six
		//Touche L == TURBO
		if (gblRegionPal && !(REG_KEYS & KEY_L))
		{
			if (nFrame % 6 == 0)
			{
				AttendVSync();
				vblSet = 0;
				while (REG_VCOUNT > 0);
				i -= 1000;
			}
		}

		//Détermination du niveau de l'eau relatif à l'écran
		//Touche L == TURBO
		if (!(REG_KEYS & KEY_L))
		{
			if (vblSet > 3 || gblAntiRame == 0)
				vblSet = 0;
			if (vblSet > 0)
			{        //Ca rame
				niveau_reel = niveau_reel_eau - scrlY;
				NouvelleTrame();

				//                  Debug("Ca rame (pas ok)");
			}
			else
			{

				//                  while(REG_VCOUNT>=159);
				while (REG_VCOUNT >= 162);

				//                REG_STOPCNT=0<<7;
				niveau_reel = niveau_reel_eau - scrlY;
				while (REG_VCOUNT < 159);

				//                REG_STOPCNT=0<<7;
				NouvelleTrame();
				while (REG_VCOUNT < 160);
			}
		}

		vblSet--;

		//Meilleure méthode (désactivée pour le moment)
		//               if ((REG_VCOUNT>=159 && i>=350 && gblAntiRame) || (i>=1200 && gblAntiRame==2))   {      //Ca rame
		//Ancienne méthode

		/*               if ((vblSet || (REG_VCOUNT>=158 && REG_VCOUNT<=159)) && gblAntiRame==2)
		goto carame;

		//Normal: i>=1200 au lieu de 1100
		if ((REG_VCOUNT>=159 && i>=350 && gblAntiRame==1) || (i>=1100 && gblAntiRame==1))   {      //Ca rame
		carame:
		niveau_reel=niveau_reel_eau-scrlY;
		NouvelleTrame();
		//                  Debug("Ca rame (pas ok)");
		}
		else  {
		while(REG_VCOUNT>=159);
		//                REG_STOPCNT=0<<7;
		niveau_reel=niveau_reel_eau-scrlY;
		while(REG_VCOUNT<159);
		//                REG_STOPCNT=0<<7;
		NouvelleTrame();
		while(REG_VCOUNT<160);
		}
		}
		vblSet=0;*/

		/*               if ((vblSet) && gblAntiRame==2)
		goto carame;
		if ((REG_VCOUNT>=160 && i>=350 && gblAntiRame==1) || (i>=1200 && gblAntiRame==1))   {      //Ca rame
		carame:
		niveau_reel=niveau_reel_eau-scrlY;
		NouvelleTrame();
		//                  Debug("Ca rame (pas ok)");
		}
		else  {
		niveau_reel=niveau_reel_eau-scrlY;
		while(REG_VCOUNT>=160);
		//                REG_STOPCNT=0<<7;
		while(REG_VCOUNT<159);
		//                REG_STOPCNT=0<<7;
		NouvelleTrame();
		while(REG_VCOUNT<160);
		}
		}
		vblSet=0;*/

		//                REG_STOPCNT=0<<7;
		//        REG_WSCNT=0x4317;                   //Remet la ROM à l'état standard
		//Méthode plus rapide (basse-ressources)
		if (!Pause)
		{

			// Définir le décalage (quelle partie de la carte afficher ?)
			CopieDMA((void*)tableau1,
					 (void*)SCREEN_BASE_BLOCK(31),
					 LARGEUR_MAP_HARDWARE * HAUTEUR_MAP_HARDWARE / 2,
					 DMA_32im);
			REG_BG0VOFS = modulo16(scrlY);
			REG_BG0HOFS = modulo16(scrlX);

			/*            REG_BG1VOFS = modulo8((scrlY*gblLI->bgmi.scrollY)>>8);
			//            REG_BG1HOFS = modulo8(scrlX>>2);*/
			CopieDMA((void*)tableau3,
					 (void*)SCREEN_BASE_BLOCK(30),
					 LARGEUR_MAP_HARDWARE * HAUTEUR_MAP_HARDWARE / 2,
					 DMA_32im);
			REG_BG2VOFS = modulo16(scrlY);
			REG_BG2HOFS = modulo16(scrlX);

			if (PauseRequise != 2)
			{
				CopieSprites();
				if (gblSonicComplexe != -1)
					AfficheSonicComplexe();
				CopieDMA(MonOAM, OAM, 1024 >> 2, DMA_32im);
			}

			GereAnims();
			nTimer++;

			if (gblLI->fctVBL)
				gblLI->fctVBL();
		}

		if (fade > 0)
		{
			GereFade();
			goto reinit;
		}
		else if (fade < 0 && !premFois)
		{
			gbaSetBlendingMode(SFX_ALPHA, 1 << 3, 0x3f);
			GereFade();
		}

		premFois = 0;
		if (PauseRequise)
		{
			PauseRequise = 0;
			MenuStart(&Ecran);
			if (!gblLI->ei.using_bg3)
			{
				CLS();
				CopieDMA((void *) charset_tiles, (void *) CHAR_BASE_BLOCK(3), sizeof(charset_tiles) >> 1, DMA_16im);
			}
		}
	}

	return 0;
}
