#include "commun.h"

unsigned short EWRAM_DATA tableau1[HAUTEUR_MAP_HARDWARE][LARGEUR_MAP_HARDWARE],
						  tableau3[HAUTEUR_MAP_HARDWARE][LARGEUR_MAP_HARDWARE];

u16 EWRAM_DATA MapGfx[HAUTEUR_MAP_BUFFER][LARGEUR_MAP_BUFFER];
u16 (*tableau)[LARGEUR_MAP_HARDWARE] = NULL;
const u8 *adrTiles256 = NULL;
u16 EWRAM_DATA decompMap[128][128];
s16 advMapOffsetX = 1, advMapOffsetY = 1;
u16 *cMap;
u16 cMapLargeur;
// Il semble que ça ne soit absolument pas vital: seulement pour le débogage
static EWRAM_DATA u16 contents[4][4] =
{
        { 0x0000, 0x0020, 0x0040, 0x0060 },
        { 0x2000, 0x2020, 0x2040, 0x2060 },
        { 0x4000, 0x4020, 0x4040, 0x4060 },
        { 0x6000, 0x6020, 0x6040, 0x6060 },
};
int gblNoObjet = 0;

#if 1
	u8  EWRAM_DATA decomp_traitementEnCours;
	s16 EWRAM_DATA decomp_ligneATraiter;
	s16 EWRAM_DATA decomp_colonneATraiter;
	s16 EWRAM_DATA decomp_premierElementATraiter;

	// Cette fonction se charge de décompresser les bonnes parties de la map en fonction de Sonic qui bouge
	void VerifMapBouge(int scrlX, int scrlY)
	{
		int tmp;
		int i;

		if (premFois)
		{
			// Première fois: décompresse tout (lent)
			DecompresseDonnees(1, 0, 0, 128, 128, 0, 0, decompMap, 128);
			decomp_traitementEnCours = 0;
		}
		else if (!decomp_traitementEnCours)
		{
			// Est-ce qu'il faut décompresser à droite?
			// >> 5 = / 32 (le nombre de tiles par "block")
			if ((scrlX + TAILLEX_ECRAN) >> 5 > advMapOffsetX + 1)
			{
				// Avance vers la droite
				advMapOffsetX++;
				// Détermine le n° de bloc en Y
				tmp = (advMapOffsetY - 1) << 5;
				if (tmp < 0)
					tmp = 0;

				// Demande l'exécution d'une décompression planifiée (dès que possible)
				decomp_traitementEnCours = 1;
				// Permet d'identifier une décompression en X d'une décompression en Y
				decomp_ligneATraiter = -1;
				// Bloc tout à droite (milieu + 2)
				decomp_colonneATraiter = advMapOffsetX + 2;
				// Premier bloc en Y à traiter
				decomp_premierElementATraiter = tmp;
			}

			// Décompression à gauche
			while ((scrlX) >> 5 < advMapOffsetX && advMapOffsetX > 1)
			{
				advMapOffsetX--;
				tmp = (advMapOffsetY - 1) << 5;
				if (tmp < 0)
					tmp = 0;

				// Demande l'exécution d'une décompression planifiée (dès que possible)
				decomp_traitementEnCours = 1;
				// Permet d'identifier une décompression en X d'une décompression en Y
				decomp_ligneATraiter = -1;
				// Bloc tout à gauche (milieu - 1)
				decomp_colonneATraiter = advMapOffsetX - 1;
				// Premier bloc en Y à traiter
				decomp_premierElementATraiter = tmp;
			}

			// Décompresser en bas?
			while ((scrlY + TAILLEY_ECRAN) >> 5 > advMapOffsetY + 1)
			{
				// Descend d'un cran
				advMapOffsetY++;
				// Calcule l'offset X
				tmp = (advMapOffsetX - 1) << 5;
				if (tmp < 0)
					tmp = 0;

				// Demande l'exécution d'une décompression planifiée (dès que possible)
				decomp_traitementEnCours = 1;
				// Bloc tout en bas (milieu + 2)
				decomp_ligneATraiter = advMapOffsetY + 2;
				// Permet d'identifier une décompression en X d'une décompression en Y
				decomp_colonneATraiter = -1;
				// Premier bloc en X à traiter
				decomp_premierElementATraiter = tmp;
			}

			// Décompression en haut
			while ((scrlY) >> 5 < advMapOffsetY && advMapOffsetY > 1)
			{
				// Remonte d'un cran
				advMapOffsetY--;
				// Calcule l'offset en X
				tmp = (advMapOffsetX - 1) << 5;
				if (tmp < 0)
					tmp = 0;

				// Demande l'exécution d'une décompression planifiée (dès que possible)
				decomp_traitementEnCours = 1;
				// Bloc tout en haut (milieu - 1)
				decomp_ligneATraiter = advMapOffsetY - 1;
				// Permet d'identifier une décompression en X d'une décompression en Y
				decomp_colonneATraiter = -1;
				// Premier bloc en X à traiter
				decomp_premierElementATraiter = tmp;
			}
		}

		// Une décompression planifiée?
		if (decomp_traitementEnCours)
		{
			int etape = decomp_traitementEnCours - 1;
			u32 blockX, blockY;

			if (decomp_ligneATraiter >= 0)
				blockX = decomp_premierElementATraiter + etape * 32,
				blockY = decomp_ligneATraiter << 5;
			else
				blockX = decomp_colonneATraiter << 5,
				blockY = decomp_premierElementATraiter + etape * 32;

			// Je le laisse pour le débogage, mais à virer par la suite...
			contents[(blockX % 128) >> 5][(blockY % 128) >> 5] = blockX + (blockY << 8);

			DecompresseDonnees(1,				// Plan collisions
				blockX, blockY,					// Source de la décompression
				32,	32,							// Bloc de 32x32
				blockX % 128,					// Destination de la décompression
				blockY % 128,					// Comme la decompMap est un buffer circulaire, il suffit de s'assurer qu'on est bien à l'intérieur (mod 128)
				decompMap,
				128);

			// 4 étapes (4 blocs de 32x32) à décompresser
			if (++decomp_traitementEnCours > 4)
				decomp_traitementEnCours = 0, decomp_ligneATraiter = -1, decomp_colonneATraiter = -1;
		}

	}
#else
	// Cette fonction se charge de décompresser les bonnes parties de la map en fonction de Sonic qui bouge
	void VerifMapBouge(int scrlX, int scrlY)
	{
		int tmp;
		int i;

		if (premFois)
		{
			// Première fois: décompresse tout (lent)
			DecompresseDonnees(1, 0, 0, 128, 128, 0, 0, decompMap, 128);
		}
		else
		{
			// Est-ce qu'il faut décompresser à droite?
			// >> 5 = / 32 (le nombre de tiles par "block")
			while ((scrlX + TAILLEX_ECRAN) >> 5 > advMapOffsetX + 1)
			{
				// Avance vers la droite
				advMapOffsetX++;
				// Détermine le n° de bloc en Y
				tmp = (advMapOffsetY - 1) << 5;
				if (tmp < 0)
					tmp = 0;

				// Pour toute la colonne
				for (i = 0; i < 4; i++)
				{
					contents[((tmp + i * 32) % 128) >> 5][(((advMapOffsetX + 2) << 5) % 128) >> 5] =
						( (advMapOffsetX + 2) << 5 ) + ( (tmp + i * 32) << 8 );
					DecompresseDonnees(1,				// Plan collisions
						(advMapOffsetX + 2) << 5,		// Bloc tout à droite (milieu + 2)
						tmp + i * 32,					// En Y: toute la colonne
						32,	32,							// Bloc de 32x32
						((advMapOffsetX + 2) << 5) % 128,	// Comme la decompMap est un buffer circulaire, il suffit de s'assurer qu'on est bien à l'intérieur (mod 128)
						(tmp + i * 32) % 128,
						decompMap,
						128);
				}
			}

			// Décompression à gauche
			while ((scrlX) >> 5 < advMapOffsetX && advMapOffsetX > 1)
			{
				advMapOffsetX--;
				tmp = (advMapOffsetY - 1) << 5;
				if (tmp < 0)
					tmp = 0;

				// Pour toute la colonne...
				for (i = 0; i < 4; i++)
				{
					contents[((tmp + i * 32) % 128) >> 5][(((advMapOffsetX - 1) << 5) % 128) >> 5] =
						( (advMapOffsetX - 1) << 5 ) + ( (tmp + i * 32) << 8 );

					DecompresseDonnees(1,				// Plan collisions
						(advMapOffsetX - 1) << 5,		// Bloc tout à gauche (milieu - 1)
						tmp + i * 32,					// En Y: toute la colonne
						32,	32,							// Bloc de 32x32
						((advMapOffsetX - 1) << 5) % 128,	// Comme la decompMap est un buffer circulaire, il suffit de s'assurer qu'on est bien à l'intérieur (mod 128)
						(tmp + i * 32) % 128,
						decompMap,
						128);
				}
			}

			// Décompresser en bas?
			while ((scrlY + TAILLEY_ECRAN) >> 5 > advMapOffsetY + 1)
			{
				// Descend d'un cran
				advMapOffsetY++;
				// Calcule l'offset X
				tmp = (advMapOffsetX - 1) << 5;
				if (tmp < 0)
					tmp = 0;

				// Pour toute une ligne (4 blocs)...
				for (i = 0; i < 4; i++)
				{
					// Infos de débogage utiles pour savoir où on est
					contents[(((advMapOffsetY + 2) << 5) % 128) >> 5][((tmp + i * 32) % 128) >> 5] =
						(tmp + i * 32) + (((advMapOffsetY + 2) << 5) << 8);
					DecompresseDonnees(1,				// Plan collisions
						tmp + i * 32,					// En X: toute la ligne
						(advMapOffsetY + 2) << 5,		// Bloc tout en bas (milieu + 2)
						32,	32,							// Bloc de 32x32
						(tmp + i * 32) % 128,			// Comme la decompMap est un buffer circulaire, il suffit de s'assurer qu'on est bien à l'intérieur (mod 128)
						((advMapOffsetY + 2) << 5) % 128,
						decompMap,
						128);
				}
			}

			// Décompression en haut
			while ((scrlY) >> 5 < advMapOffsetY && advMapOffsetY > 1)
			{
				// Remonte d'un cran
				advMapOffsetY--;
				// Calcule l'offset en X
				tmp = (advMapOffsetX - 1) << 5;
				if (tmp < 0)
					tmp = 0;

				// Pour toute la ligne...
				for (i = 0; i < 4; i++)
				{
					contents[(((advMapOffsetY - 1) << 5) % 128) >> 5][((tmp + i * 32) % 128) >> 5] =
						(tmp + i * 32) + (((advMapOffsetY - 1) << 5) << 8);
					DecompresseDonnees(1,				// Plan collisions
						tmp + i * 32,					// En X: toute la ligne
						(advMapOffsetY - 1) << 5,		// Bloc tout en haut (milieu - 1)
						32,	32,							// Bloc de 32x32
						(tmp + i * 32) % 128,			// Comme la decompMap est un buffer circulaire, il suffit de s'assurer qu'on est bien à l'intérieur (mod 128)
						((advMapOffsetY - 1) << 5) % 128,
						decompMap,
						128);
				}
			}
		}
	}
#endif

//Retourne si oui ou non la coordonnée spécifiée est dans la zone décompressée
int blocValide(int coordX, int coordY)
{
	// En dehors des bornes décompressées?
	if (coordX > ((advMapOffsetX + 2) << 5) ||
		coordY > ((advMapOffsetY + 2) << 5) ||
		coordX < ((advMapOffsetX - 1) << 5) ||
		coordY < ((advMapOffsetY - 1) << 5) ||
		// Traitement en cours?
		coordX >> 5 == decomp_colonneATraiter ||
		coordY >> 5 == decomp_ligneATraiter)
		return 0;
	else
		return 1;
}

//Avec une tolérance (enrobage du point pX,pY)
int zoneValide(int pX, int pY, int tolX, int tolY)
{
	if (pX + tolX > (advMapOffsetX + 2) << 9 ||
		pY + tolY > (advMapOffsetY + 2) << 9 ||
		pX - tolX < (advMapOffsetX - 1) << 9 ||
		pY - tolY < (advMapOffsetY - 1) << 9 ||
		// Traitement en cours?
		((pX - tolX) >> 9 < decomp_colonneATraiter &&
		(pX + tolX) >> 9 > decomp_colonneATraiter) ||
		((pY - tolY) >> 9 < decomp_ligneATraiter &&
		(pY + tolY) >> 9 > decomp_ligneATraiter))
		return 0;
	else
		return 1;
}

//Retourne si oui ou non la coordonnée spécifiée est dans la zone décompressée
int coordValide(int coordX, int coordY)
{
	if (coordX > (advMapOffsetX + 2) << 9 ||
		coordY > (advMapOffsetY + 2) << 9 ||
		coordX < (advMapOffsetX - 1) << 9 ||
		coordY < (advMapOffsetY - 1) << 9 ||
		// Traitement en cours?
		coordX >> 9 == decomp_colonneATraiter ||
		coordY >> 9 == decomp_ligneATraiter)
		return 0;
	else
		return 1;
}

// Gestionnaire de tiles
u16 map_maxTiles;								// Nombre max. de tiles en même temps
u8 map_bitsPerPixelShift;						// (1 << ce_nombre) => nombre d'octets par tile (128 en mode 4 bits, donc cette variable vaut 7)
u16 EWRAM_DATA utilTiles[2000];
u16 EWRAM_DATA numTiles[800];					//C'est trop. map_maxTiles+16*11 suffirait largement, je suppose.
u16 EWRAM_DATA copieTiles[2000];
u16 map_nTiles;
u32 map_nEtape;

void InitTileSystem()
{
        InitZero(utilTiles);
        InitZero(numTiles);
        InitZero(copieTiles);
        map_nTiles = 1;
        map_nEtape = 1;
}

void CopieTile(unsigned short tileVirtuelle, unsigned short tile)
{
	//Tests facultatifs, mais là pour éviter qu'en cas de débordement de tile le désastre soit trop grand...
	if (tile > 0 && tile < map_maxTiles)
		CopieDMA(adrTiles256 + (tileVirtuelle << map_bitsPerPixelShift),
				 CHAR_BASE_BLOCK(0) + (tile << map_bitsPerPixelShift),
				 1 << (map_bitsPerPixelShift - 1), DMA_16im);
}

int depassement = 0;        //Uniquement pour la détection d'erreurs. Il faut que l'on sache que si le jeu plante, il faudra prévoir plus de tiles à disposition ou (en l'occurence) réduire le nombre de tiles utilisées par un monde. En imaginant que chaque tile est différente, cela nécessiterait 176 tiles, mais avec 128 tiles, je remplis déjà la VRAM à moitié, reste le fond, le texte, ... faut pas exagérer!
void OptTiles()
{
        unsigned int i, j;
        unsigned short val;

        /****************************************************************************
            Vidage et optimisation de la VRAM:
            Cette partie va déterminer quelles tiles doivent êtres retirées de la
            mémoire vidéo. L'algo est le suivant:
            -Recherche des tiles communes entre les tiles déjà existantes et celles
             que l'on veut pour afficher l'image courante.
            -Les tiles qui n'existent pas déjà se voient allouer un bloc en mémoire
             vidéo.
            -Les tiles pour lequel on a alloué un bloc mais qui ne l'utilisent plus
             (notamment parce qu'elles ne sont plus visible à l'écran) sont libérées
             et pourront ensuite être remplacées par les nouvelles tiles.
        ****************************************************************************/
        if (depassement)        //Déjà eu lieu
                return;

        map_nTiles = 1;
        depassement = 1;

        //"Dévalide" les tiles utilisées la dernière fois. On va les "revalider" ensuite. Si certaines d'entre elles n'est pas validée, alors elles seront jartées de la mémoire vidéo.

        /*        for (i=1;i<map_maxTiles;i++)     {
            copieTiles[numTiles[i]]=0;
        }*/
        map_nEtape++;

        //Etape de validation
        for (j = 0; j < TAILLEY_ECRAN; j++)
        {
                for (i = 0; i < TAILLEX_ECRAN; i++)
                {
                        val = cMap[(j * cMapLargeur) + i] & 2047;
                        if (utilTiles[val])
                                copieTiles[val] = map_nEtape;        //La tile existe?
                }
        }

        /*
            Nécessaire pour la meilleure qualité à cause de:
            int IWRAM_CODE ChercheTile(int val)      {
                while(copieTiles[numTiles[map_nTiles]]==map_nEtape)
                    map_nTiles++;
            En fait, je pense que le problème vient de la ligne:
                if (utilTiles[val])
            Juste plus haut, car si les utilTiles ne sont pas remis à zéro, il ne va pas pouvoir libérer assez de tiles.
        */

        /*        if (bRecuperationMaxTiles)          {
            //Ici, on va vérifier si chacune des tiles énumérées plus haut existe bien sur l'écran courant. Si ce n'est pas le cas, elle sera sortie de la VRAM.
            for (i=1;i<map_maxTiles;i++)     {
                if (copieTiles[numTiles[i]]!=map_nEtape)     {          //La tile est à jour?
                    //Rajout
//                    copieTiles[numTiles[i]]--;
                    //End Rajout
                    utilTiles[numTiles[i]]=0;
                    numTiles[i]=0;
                }
            }
        }*/
}

#if 1
/*
	Cette version décompresse systématiquement toute la partie visible de la map en cours.
	Ce n'est pas vraiment plus lent que la décompresser par parties, car dans l'autre cas
	il faut faire un déplacement de mémoire en cas de scrolling, et c'est lent.
*/
// Copie un morceau de map en fonction des paramètres. Reste à déplacer ça dans la mémoire
// vidéo à la fin.
void CopieMap_16(int departX,
				 int departY,        /* int tailleX, int tailleY,*/
				 int tailleMapX,
				 int tailleMapY,
				 int largeurEcran)
{
        unsigned int i;
		int tailleX = TAILLEX_ECRAN, tailleY = TAILLEY_ECRAN;
        static int lastX = 0, lastY = 0;
        int rLeft, rTop, rRight, rBottom;

        cMapLargeur = LARGEUR_MAP_BUFFER;
        cMap = MapGfx;

        if (departX + tailleX >= TABLE_X)
                tailleX = TABLE_X - departX;
        if (departY + tailleY >= TABLE_Y)
                tailleY = TABLE_Y - departY;

		DecompresseDonnees(0, departX, departY, tailleX, tailleY, 0, 0, MapGfx, cMapLargeur);

#pragma ameliorer!
        rLeft = rTop = 0;
        rRight = tailleX;
        rBottom = tailleY;

        if (departY > lastY)
        {
			// 2 car tiles de 16x16 et on compte en 8x8
                MemMove16bits(tableau1, tableau1 + 2, sizeof(tableau1) - sizeof(tableau1[0]) * 2);
                MemMove16bits(tableau3, tableau3 + 2, sizeof(tableau3) - sizeof(tableau3[0]) * 2);
        }
        else if (departY < lastY)
        {
                MemMove16bits(tableau1 + 2, tableau1, sizeof(tableau1) - sizeof(tableau1[0]) * 2);
                MemMove16bits(tableau3 + 2, tableau3, sizeof(tableau3) - sizeof(tableau3[0]) * 2);
        }

        if (premFois)
        {
                /*        //Efface les maps actuelles
        Exec(MemSet,&zero,tableau1,sizeof(tableau1)>>2,DMA_32im);
        Exec(MemSet,&zero,tableau3,sizeof(tableau3)>>2,DMA_32im);*/
        }
        else if (departX > lastX)
        {
                MemMove16bits(tableau1[0], tableau1[0] + 2, sizeof(tableau1) - 4);
                MemMove16bits(tableau3[0], tableau3[0] + 2, sizeof(tableau3) - 4);
                rLeft = tailleX - 1;
        }
        else if (departX < lastX)
        {
                MemMove16bits(tableau1[0] + 2, tableau1[0], sizeof(tableau1) - 4);
                MemMove16bits(tableau3[0] + 2, tableau3[0], sizeof(tableau3) - 4);
                rRight = 1;
        }
        else
                goto apres;

        DessineRect(rLeft, rTop, rRight, rBottom);

apres:
        if (premFois)
                goto apres2;

        rLeft = rTop = 0;
        rRight = tailleX;
        rBottom = tailleY;

        if (departY > lastY)
        {
                rTop = tailleY - 1;
        }
        else if (departY < lastY)
        {
                rBottom = 1;
        }
        else
                goto apres2;

        DessineRect(rLeft, rTop, rRight, rBottom);

apres2:
        lastX = departX;
        lastY = departY;
}
#else
/*
	Cette version est censée être plus rapide mais ne l'est pas de beaucoup.
	On pourrait l'améliorer en utilisant des buffers circulaires...
*/
// Copie un morceau de map en fonction des paramètres. Reste à déplacer ça dans la mémoire
// vidéo à la fin.
void CopieMap_16(int departX,
				 int departY,        /* int tailleX, int tailleY,*/
				 int tailleMapX,
				 int tailleMapY,
				 int largeurEcran)
{
        unsigned int i;
		int tailleX = TAILLEX_ECRAN, tailleY = TAILLEY_ECRAN;
        static int lastX = 0, lastY = 0;
        int rLeft, rTop, rRight, rBottom;

        cMapLargeur = LARGEUR_MAP_BUFFER;
        cMap = MapGfx;

        if (departX + tailleX >= TABLE_X)
                tailleX = TABLE_X - departX;
        if (departY + tailleY >= TABLE_Y)
                tailleY = TABLE_Y - departY;

#pragma ameliorer!
        rLeft = rTop = 0;
        rRight = tailleX;
        rBottom = tailleY;

        if (departY > lastY)
        {
                MemMove16bits(tableau1, tableau1 + 2, sizeof(tableau1) - sizeof(tableau1[0]) * 2);
                MemMove16bits(tableau3, tableau3 + 2, sizeof(tableau3) - sizeof(tableau3[0]) * 2);
                MemMove16bits(MapGfx, MapGfx + 1, sizeof(MapGfx) - sizeof(MapGfx[0]));
        }
        else if (departY < lastY)
        {
                MemMove16bits(tableau1 + 2, tableau1, sizeof(tableau1) - sizeof(tableau1[0]) * 2);
                MemMove16bits(tableau3 + 2, tableau3, sizeof(tableau3) - sizeof(tableau3[0]) * 2);
                MemMove16bits(MapGfx + 1, MapGfx, sizeof(MapGfx) - sizeof(MapGfx[0]));
        }

        if (premFois)
        {
        }
        else if (departX > lastX)
        {
                MemMove16bits(tableau1[0], tableau1[0] + 2, sizeof(tableau1) - 4);
                MemMove16bits(tableau3[0], tableau3[0] + 2, sizeof(tableau3) - 4);
                MemMove16bits(MapGfx[0], MapGfx[0] + 1, sizeof(MapGfx) - 2);
                rLeft = tailleX - 1;
        }
        else if (departX < lastX)
        {
                MemMove16bits(tableau1[0] + 2, tableau1[0], sizeof(tableau1) - 4);
                MemMove16bits(tableau3[0] + 2, tableau3[0], sizeof(tableau3) - 4);
                MemMove16bits(MapGfx[0] + 1, MapGfx[0], sizeof(MapGfx) - 2);
                rRight = 1;
        }
        else
                goto apres;

		DecompresseDonnees(0, departX + rLeft, departY + rTop, rRight - rLeft, rBottom - rTop, rLeft, rTop, MapGfx, cMapLargeur);
        DessineRect(rLeft, rTop, rRight, rBottom);

apres:
        if (premFois)
                goto apres2;

        rLeft = rTop = 0;
        rRight = tailleX;
        rBottom = tailleY;

        if (departY > lastY)
        {
                rTop = tailleY - 1;
        }
        else if (departY < lastY)
        {
                rBottom = 1;
        }
        else
                goto apres2;

		DecompresseDonnees(0, departX + rLeft, departY + rTop, rRight - rLeft, rBottom - rTop, rLeft, rTop, MapGfx, cMapLargeur);
        DessineRect(rLeft, rTop, rRight, rBottom);

apres2:
        lastX = departX;
        lastY = departY;
}
#endif

void calcIndex(int plan, unsigned short *ptr)
{

        //        unsigned short *ptr=(unsigned short*)mapCour;
        int i = 0;

        indexMap[plan][0] = ptr;
        while (1)
        {
                if (*ptr == 0xfffc)
                        break;
                else if (*ptr == 0xfffd)
                {
                        indexMap[plan][++i] = ++ptr;
                }
                else
                        ptr++;
        }

        nLignes = i;
        for (i = 0; i < nLignes; i++)
        {
                indexTemp[plan][i] = indexMap[plan][i];
                indexVal[plan][i] = 0;
        }

        if (nLignes <= 0)        //Erreur
                return;
        for (i = nLignes; i < TABLE_Y_MAX; i++)
        {
                indexMap[plan][i] = indexMap[plan][nLignes - 1];
                indexTemp[plan][i] = indexTemp[plan][nLignes - 1];
                indexVal[plan][i] = indexVal[plan][nLignes - 1];
        }
}

// Analyse la liste d'objets et crée les objet dans le rectangle spécifié
void AffObjets(int scrlX, int scrlY, int tailleX, int tailleY, int tailleMapX, int tailleMapY, int largeurEcran)
{
	int departX, departY;
	static int lastX = 0, lastY = 0;
	int rLeft, rRight, rTop, rBottom;
	int tropbas = 0;

	departX = scrlX >> 4, departY = scrlY >> 4;
	// Laisse un peu d'espace pour détecter les objets hors écran aussi
	rLeft = -16, rTop = -16, rRight = tailleX + 14, rBottom = tailleY + 16;

	if (premFois)
		goto analyseEnY;
	else if (departX < lastX)
		rRight = rLeft + 1;
	else if (departX > lastX)
		rLeft = rRight - 1;
	else
		goto calculEnY;

	// Analyse le rectangle (crée les objets dans ce rectangle)
	AnalyseRect(rLeft, rTop, rRight, rBottom, departX, departY);

calculEnY:
	rLeft = -16, rTop = -16, rRight = tailleX + 14, rBottom = tailleY + 16;

	if (departY < lastY)
		rBottom = rTop + 1;
	else if (departY > lastY)
		rTop = rBottom - 1;
	else
		goto fin;

analyseEnY:
	AnalyseRect(rLeft, rTop, rRight, rBottom, departX, departY);

fin:
	lastX = departX;
	lastY = departY;
}

/*//Car -1/16 ne semble pas valoir -1, ce qui serait censé être le cas :|
int divisionEntiere(nombre,diviseur)     {
    return nombre/diviseur-(nombre>=0?0:1);
}*/

/*//Reste de la division entière, avec support correct pour les nombres négatifs.
int modulo(nombre,diviseur)     {
    return (nombre-divisionEntiere(nombre,diviseur)*diviseur);
}*/

//unsigned short *map=sol_nv1_map;
//#include "sprites.h"
//#define Collision(x,y)      (Map((x)/16,(y)/16))
s32 viewportX0, viewportY0, viewportX1, viewportY1;
s32 desiredViewportX0, desiredViewportY0, desiredViewportX1, desiredViewportY1;

int PallierGereCamera(int vitesse, int *pallier, int vitessePallier1, int vitessePallier2)
{
        int change = 0;
        if (*pallier == 0)
        {
                if (vitesse >= vitessePallier1)
                        *pallier = 1;
                if (vitesse <= -vitessePallier1)
                        *pallier = -1;
        }
        else if (*pallier == 1)
        {
                if (vitesse < vitessePallier2)
                        *pallier = 0;
        }
        else if (*pallier == -1)
        {
                if (vitesse > -vitessePallier2)
                        *pallier = 0;
        }

        if (*pallier == 1)
                change = 1;
        else if (*pallier == 0)
                change = 0;
        else if (*pallier == -1)
                change = -1;
        return change;
}

u8 gblSFXTrembleCamera = 0;

void GereScrolling(SONIC *sonic, int *scrlX, int *scrlY)
{
        int i, j;
        static int lchangeX = 0, lchangeY = 0, lPallierX = 0, lPallierY = 0;
        int changeX, changeY, vx, vy;

        i = sonic->x - (*scrlX);
        j = sonic->y - (*scrlY) + sonic->hauteur;

        if (gblAdvCamera && !premFois)
        {

                //WORKAROUND
                vx = ((sonic->x << 8) + sonic->virgule_x) - sonic->lastX;
                vy = ((sonic->y << 8) + sonic->virgule_y) - sonic->lastY;

                /*       if (vx>=vMUL(3))
         changeX=vDIV(8*(vx-vMUL(3)));
       else if (vx<=-vMUL(3))
         changeX=vDIV(8*(vx+vMUL(3)));
       else
         changeX=0;*/

                /*        if (vx>=lPallierX+128)
            lPallierX+=64;
        else if (vx<=lPallierX-128)
            lPallierX-=64;
        Message(15,"%i",lPallierX);
        if (lPallierX>=vMUL(3))
            changeX=vDIV(8*(lPallierX-vMUL(3)));
        else if (lPallierX<=-vMUL(3))
            changeX=vDIV(8*(lPallierX+vMUL(3)));
        else
            changeX=0;*/
                changeX = PallierGereCamera(vx, &lPallierX, vMUL(5), vMUL(3)) * 32;
                changeY = PallierGereCamera(vy, &lPallierY, vMUL(5), vMUL(3)) * 32;

                /*       if (vy>=vMUL(6))
         changeY=vDIV(8*(vy-vMUL(6)));
       else if (vy<=-vMUL(6))
         changeY=vDIV(8*(vy+vMUL(6)));
       else
         changeY=0;*/
                if (changeX > lchangeX)
                        lchangeX++;
                else if (changeX < lchangeX)
                        lchangeX--;

                if (changeY > lchangeY)
                        lchangeY++;
                else if (changeY < lchangeY)
                        lchangeY--;

                i += lchangeX;
                j += lchangeY;

                //Fin workaround
        }

        if (i < 104)
        {
                if (104 - i >= 16 && !premFois)
                        (*scrlX) -= 16;
                else
                        (*scrlX) -= 104 - i;
        }

        if (i > 104)
        {
                if (i - 104 >= 16 && !premFois)
                        (*scrlX) += 16;
                else
                        (*scrlX) += i - 104;
        }

        if (j < 92)
        {
                if (92 - j >= 16 && !premFois)
                        (*scrlY) -= 16;
                else
                        (*scrlY) -= 92 - j;
        }

        if (j > 100)
        {
                if (j - 100 >= 16 && !premFois)
                        (*scrlY) += 16;
                else
                        (*scrlY) += j - 100;
        }

        if (*scrlX < viewportX0)
                *scrlX = viewportX0;
        if (*scrlY < viewportY0)
                *scrlY = viewportY0;
        if (*scrlX > viewportX1 - 240 - 16)
                *scrlX = viewportX1 - 240 - 16;
        if (*scrlY > viewportY1 - 160 - 16)
                *scrlY = viewportY1 - 160 - 16;

        /*
        Changement du viewport
    */

        //Y1
        if (viewportY1 > desiredViewportY1)
        {
                if (*scrlY + 160 + 16 < desiredViewportY1)
                        viewportY1 = desiredViewportY1;
                else if (*scrlY + 160 + 16 < viewportY1)
                        viewportY1 = *scrlY + 160 + 16 - 2;
                else
                        viewportY1 -= 2;

                /*        if (*scrlY+160+16<=desiredViewportY1)
            viewportY1=desiredViewportY1;
        else if (*scrlY+160+16<viewportY1)
            viewportY1=*scrlY+160+16-2;
        else
            viewportY1-=2;*/
        }

        if (desiredViewportY1 > viewportY1)
        {
                if (*scrlY + 160 + 16 < viewportY1) //Si le fond de l'écran est plus haut que le viewport actuel (avant qu'il descende)
                        viewportY1 = desiredViewportY1; //On peut le faire descendre d'un coup
                else
                        viewportY1 += 2;
        }

        //Y0
        if (desiredViewportY0 > viewportY0)
        {
                if (*scrlY >= desiredViewportY0)
                        viewportY0 = desiredViewportY0; //On peut le faire descendre d'un coup
                else if (*scrlY > viewportY0)
                        viewportY0 = *scrlY + 2;
                else
                        viewportY0 += 2;
        }

        if (desiredViewportY0 < viewportY0)
        {
                if (*scrlY > viewportY0)
                        viewportY0 = desiredViewportY0;
                else
                        viewportY0 -= 2;
        }

        //X0
        if (desiredViewportX0 > viewportX0)
        {
                if (*scrlX >= desiredViewportX0)
                        viewportX0 = desiredViewportX0; //On peut le faire descendre d'un coup
                else if (*scrlX > viewportX0)
                        viewportX0 = *scrlX + 2;
                else
                        viewportX0 += 2;
        }

        if (desiredViewportX0 < viewportX0)
        {
                if (*scrlX > viewportX0)
                        viewportX0 = desiredViewportX0;
                else
                        viewportX0 -= 2;
        }

        //X1
        if (viewportX1 > desiredViewportX1)
        {
                if (*scrlX + 240 + 16 < desiredViewportX1)
                        viewportX1 = desiredViewportX1;
                else if (*scrlX + 240 + 16 < viewportX1)
                        viewportX1 = *scrlX + 240 + 16 - 2;
                else
                        viewportX1 -= 2;
        }

        if (desiredViewportX1 > viewportX1)
        {
                if (*scrlY + 240 + 16 < viewportX1) //Si le fond de l'écran est plus haut que le viewport actuel (avant qu'il descende)
                        viewportX1 = desiredViewportX1; //On peut le faire descendre d'un coup
                else
                        viewportX1 += 2;
        }

        if (gblSFXTrembleCamera)
        {
                const s8 modif[] = { 2, 0, -2, 1, -2, 1, 2, -2 };
                (*scrlY) += modif[nFrame & 7];
        }
}

/*//Dessine du texte en utilisant un DMA
void DessTexte(int x, int y, char *texte)       {
    unsigned short buf[100];
    int i=0;
    while(*texte && i<100)
        buf[i++]=(*texte++)-32;
    CopieDMA((unsigned short*)buf,(unsigned short*)SCREEN_BASE_BLOCK(28)+y*TAILLE_MAP+x,i,DMA_16im);
}*/
static unsigned int memSprites;
u32 map_maxSpriteMem = 24576;

void CopieSpriteMem(const unsigned char *tiles, unsigned int taille)
{
        if (memSprites + taille > map_maxSpriteMem)
                return;
		// Attend que le canal DMA soit libre
		while (REG_DMA3CNT & DMA_ENABLE);
        REG_DMA3SAD = (u32) tiles;
		REG_DMA3DAD = (u32) (OBJ_BASE_ADR + memSprites);
        REG_DMA3CNT = (taille >> 1) | (DMA_16im);
        memSprites += taille;
}

void CopieSprites()
{
        int i;

        memSprites = 0;

        for (i = 0; i < nSprGba; i++)
        {
                if (sprGba[i].tile_sprite)                        //Le sprite n'est pas nul...
                        CopieSpriteMem(sprGba[i].tile_sprite, sprGba[i].taille_tiles);
                else
                        memSprites += sprGba[i].taille_tiles;
        }
}

void ChangeTile(int tileSrc, int tileDst)
{
        if (utilTiles[tileDst])
                CopieTile(tileSrc, utilTiles[tileDst]);
}

void ChangePlusieursTiles(int tileSrc, int tileDst, int nombre)
{
        while (nombre--)
        {
                if (utilTiles[tileDst])
                        CopieTile(tileSrc, utilTiles[tileDst]);
                tileSrc++, tileDst++;
        }
}

void GereAnims()
{
        u32 i, j;
        ANIMATION *anims = gblLI->fgmi.tblAnims;

        for (i = 0; anims[i].tile; i++)
        {
                if (utilTiles[anims[i].tile])
                {
                        if (anims[i].etapes == 1)
                        {
                                if ((nFrame >> anims[i].timing) & 1)
                                        CopieTile(anims[i].animbase, utilTiles[anims[i].tile]);
                                else
                                        CopieTile(anims[i].tile, utilTiles[anims[i].tile]);
                        }
                        else
                        {
                                j = (nFrame >> anims[i].timing) & anims[i].etapes;
                                CopieTile(anims[i].animbase + j, utilTiles[anims[i].tile]);
                        }
                }
        }
}

void ExecHandler(const SPRITE_LISTE *sl)
{
        int i = sl->type;
        i--;

        //Vérification (à virer si optimisation) que la fonction existe bien
        if (i >= 0 && i < taille_handlers_cp)
        {
#pragma DEBUG_ONLY
                if (handlersCreePerso[i] != NULL)
                        Exec(handlersCreePerso[i], sl);
        }

        if (i >= 0x1000 && i < 0x1000 + ExtCreePersoTaille)
        {
                i -= 0x1000;
#pragma DEBUG_ONLY
                if (ExtCreePerso[i] != NULL)
                        Exec(ExtCreePerso[i], sl);
        }

        //    else if ((i-0x3000)<ExtCreePersoTaille)
        //        Exec(ExtCreePerso[i-0x3000],xxx,yyy);
        //    if (i!=0x2000)           //cpAvance
        //        tableauMap[yyy][xxx>>3]|=(1<<(xxx&7));
}

