/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "common/system.h"
#include "graphics/palette.h"
#include "common/file.h"
#include "common/rect.h"
#include "engines/util.h"
#include "hopkins/files.h"
#include "hopkins/globals.h"
#include "hopkins/graphics.h"
#include "hopkins/hopkins.h"

namespace Hopkins {

GraphicsManager::GraphicsManager() {
	_lockCtr = 0;
	SDL_MODEYES = false;
	SDL_ECHELLE = 0;
	XSCREEN = YSCREEN = 0;
	WinScan = 0;
	Winbpp = 0;
	PAL_PIXELS = NULL;
	nbrligne = 0;
	Linear = false;
	VideoPtr = NULL;
	ofscroll = 0;
	SCROLL = 0;
	PCX_L = PCX_H = 0;
	DOUBLE_ECRAN = false;
	OLD_SCROLL = 0;

	nbrligne2 = 0;
	Agr_x = Agr_y = 0;
	Agr_Flag_x = Agr_Flag_y = 0;
	FADESPD = 15;
	FADE_LINUX = 0;
	NOLOCK = false;
	no_scroll = 0;
	REDRAW = false;
	min_x = 0;
	min_y = 20;
	max_x = SCREEN_WIDTH * 2;
	max_y = SCREEN_HEIGHT - 20;
	clip_x = clip_y = 0;
	clip_x1 = clip_y1 = 0;
	clip_flag = false;
	SDL_NBLOCS = 0;
	Red_x = Red_y = 0;
	Red = 0;
	Largeur = 0;
	Compteur_y = 0;
	spec_largeur = 0;

	Common::fill(&SD_PIXELS[0], &SD_PIXELS[PALETTE_SIZE * 2], 0);
	Common::fill(&TABLE_COUL[0], &TABLE_COUL[PALETTE_EXT_BLOCK_SIZE], 0);
	Common::fill(&cmap[0], &cmap[PALETTE_BLOCK_SIZE], 0);
	Common::fill(&Palette[0], &Palette[PALETTE_EXT_BLOCK_SIZE], 0);
	Common::fill(&OLD_PAL[0], &OLD_PAL[PALETTE_EXT_BLOCK_SIZE], 0);
}

GraphicsManager::~GraphicsManager() {
	_vm->_globals.dos_free2(VESA_SCREEN);
	_vm->_globals.dos_free2(VESA_BUFFER);
}

void GraphicsManager::setParent(HopkinsEngine *vm) {
	_vm = vm;

	if (_vm->getIsDemo()) {
		// CHECKME: Should be 0?
		MANU_SCROLL = 1;
		SPEED_SCROLL = 16;
	} else {
		MANU_SCROLL = 0;
		SPEED_SCROLL = 32;
	}
}

void GraphicsManager::SET_MODE(int width, int height) {
	if (!SDL_MODEYES) {
		SDL_ECHELLE = 0;

		if (_vm->_globals.XSETMODE == 1)
			SDL_ECHELLE = 0;
		if (_vm->_globals.XSETMODE == 2)
			SDL_ECHELLE = 25;
		if (_vm->_globals.XSETMODE == 3)
			SDL_ECHELLE = 50;
		if (_vm->_globals.XSETMODE == 4)
			SDL_ECHELLE = 75;
		if (_vm->_globals.XSETMODE == 5)
			SDL_ECHELLE = _vm->_globals.XZOOM;
		
		int bpp = 8;
		if (_vm->_globals.XFORCE8 == true)
			bpp = 8;
		if (_vm->_globals.XFORCE16 == true)
			bpp = 16;

		if (SDL_ECHELLE) {
			width = Reel_Zoom(width, SDL_ECHELLE);
			height = Reel_Zoom(height, SDL_ECHELLE);
		}

		Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);

		if (bpp == 8) {
			initGraphics(width, height, true);
		} else {
			initGraphics(width, height, true, &pixelFormat16);
		}

		// Init surfaces
		VESA_SCREEN = _vm->_globals.dos_malloc2(SCREEN_WIDTH * 2 * SCREEN_HEIGHT);
		VESA_BUFFER = _vm->_globals.dos_malloc2(SCREEN_WIDTH * 2 * SCREEN_HEIGHT);

		VideoPtr = NULL;
		XSCREEN = width;
		YSCREEN = height;

		Linear = true;
		Winbpp = bpp / 8;
		WinScan = width * Winbpp;

		PAL_PIXELS = SD_PIXELS;
		nbrligne = width;

		Common::fill(&cmap[0], &cmap[256 * 3], 0);
		SDL_MODEYES = true;
	} else {
		error("Called SET_MODE multiple times");
	}
}

void GraphicsManager::DD_Lock() {
	if (!NOLOCK) {
		if (_lockCtr++ == 0)
			VideoPtr = g_system->lockScreen();
	}
}

void GraphicsManager::DD_Unlock() {
	assert(VideoPtr);
	if (--_lockCtr == 0) {
		g_system->unlockScreen();
		VideoPtr = NULL;
	}
}

// Clear Screen
void GraphicsManager::Cls_Video() {
	assert(VideoPtr);

	VideoPtr->fillRect(Common::Rect(0, 0, XSCREEN, YSCREEN), 0);
}

void GraphicsManager::LOAD_IMAGE(const Common::String &file) {
	Common::String filename	= Common::String::format("%s.PCX", file.c_str());
	CHARGE_ECRAN(filename);
	INIT_TABLE(165, 170, Palette);
}

// Load Screen
void GraphicsManager::CHARGE_ECRAN(const Common::String &file) {
	bool flag;
	Common::File f;

	_vm->_fileManager.DMESS1();

	flag = true;
	if (_vm->_fileManager.RECHERCHE_CAT(file, 6)) {
		_vm->_fileManager.CONSTRUIT_FICHIER(_vm->_globals.HOPIMAGE, file);
		if (!f.open(_vm->_globals.NFICHIER))
			error("CHARGE_ECRAN - %s", file.c_str());

		f.seek(0, SEEK_END);
		f.close();
		flag = false;
	}

	SCROLL_ECRAN(0);
	A_PCX640_480((byte *)VESA_SCREEN, file, Palette, flag);

	SCROLL = 0;
	OLD_SCROLL = 0;
	Cls_Pal();

	if (!DOUBLE_ECRAN) {
		_vm->_eventsManager.souris_max();
		SCANLINE(SCREEN_WIDTH);
		max_x = SCREEN_WIDTH;
		DD_Lock();
		Cls_Video();
		if (Winbpp == 2) {
			if (SDL_ECHELLE)
				m_scroll16A(VESA_SCREEN, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
			else
				m_scroll16(VESA_SCREEN, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
		} else if (Winbpp == 1) {
			if (!SDL_ECHELLE)
				m_scroll2(VESA_SCREEN, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
			else 
				m_scroll2A(VESA_SCREEN, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
		}

		DD_Unlock();
	} else {
		SCANLINE(SCREEN_WIDTH * 2);
		max_x = SCREEN_WIDTH * 2;
		DD_Lock();
		Cls_Video();
		DD_Unlock();

		if (MANU_SCROLL == 1) {
			DD_Lock();
			if (Winbpp == 2) {
				if (SDL_ECHELLE)
					m_scroll16A(VESA_SCREEN, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
				else
					m_scroll16(VESA_SCREEN, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
			}
			if (Winbpp == 1) {
				if (!SDL_ECHELLE)
					m_scroll2(VESA_SCREEN, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
				else
					m_scroll2A(VESA_SCREEN, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
			}

			DD_Unlock();
		}
	}

	memcpy(VESA_BUFFER, VESA_SCREEN, SCREEN_WIDTH * 2 * SCREEN_HEIGHT);
}

void GraphicsManager::INIT_TABLE(int minIndex, int maxIndex, byte *palette) {
	for (int idx = 0; idx < 256; ++idx)
		TABLE_COUL[idx] = idx;
  
	Trans_bloc(TABLE_COUL, palette, 256, minIndex, maxIndex);

	for (int idx = 0; idx < 256; ++idx) {
		byte v = TABLE_COUL[idx];
		if (v > 27)
			TABLE_COUL[idx] = 0;
		if (!v)
			TABLE_COUL[idx] = 0;
	}

	TABLE_COUL[0] = 1;
}

// Scroll Screen
int GraphicsManager::SCROLL_ECRAN(int amount) {
	int result = CLIP(amount, 0, SCREEN_WIDTH);
	_vm->_eventsManager.start_x = result;
	ofscroll = result;
	SCROLL = result;
	return result;
}

void GraphicsManager::Trans_bloc(byte *destP, const byte *srcP, int count, int minThreshold, int maxThreshold) {
	byte *destPosP;
	int palIndex;
	int srcOffset;
	int col1, col2;

	destPosP = destP;
	for (int idx = 0; idx < count; ++idx) {
		palIndex = *(byte *)destPosP++;
		srcOffset = 3 * palIndex;
		col1 = *(srcP + srcOffset) + *(srcP + srcOffset + 1) + *(srcP + srcOffset + 2);

		for (int idx2 = 0; idx2 < 38; ++idx2) {
			srcOffset = 3 * idx2;
			col2 = *(srcP + srcOffset) + *(srcP + srcOffset + 1) + *(srcP + srcOffset + 2);

			col2 += minThreshold;
			if (col2 < col1)
				continue;
			
			col2 -= maxThreshold;
			if (col2 > col1)
				continue;

			*(destPosP - 1) = (idx2 == 0) ? 1 : idx2;
			break;
		}
	}
}

void GraphicsManager::Trans_bloc2(byte *surface, byte *col, int size) {
	byte *dataP; 
	int count; 
	byte dataVal; 

	dataP = surface;
	count = size - 1;
	do {
		dataVal = *dataP++;
		*(dataP - 1) = *(dataVal + col);
		--count;
	} while (count);
}

// TODO: See if it's feasible and/or desirable to change this to use the Common PCX decoder
void GraphicsManager::A_PCX640_480(byte *surface, const Common::String &file, byte *palette, bool typeFlag) {
	int filesize; 
	int v6;
	int v7;
	int v8;
	int v9; 
	int v10;
	int v11;
	byte v12; 
	int v13;
	int v14;
	char v15; 
	int v18;
	int v19; 
	int v20;
	unsigned int v21;
	int v22;
	int32 v23;
	byte *ptr;
	Common::File f;

	// Clear the passed surface
	memset(surface, 0, SCREEN_WIDTH * 2 * SCREEN_HEIGHT);

	if (typeFlag) {
		_vm->_fileManager.CONSTRUIT_FICHIER(_vm->_globals.HOPIMAGE, "PIC.RES");
		if (!f.open(_vm->_globals.NFICHIER))
			error("(nom)Erreur en cours de lecture.");
		f.seek(_vm->_globals.CAT_POSI);

		v7 = _vm->_globals.CAT_TAILLE - 896;
		v8 = f.read(HEADER_PCX, 128);

		v6 = (int16)READ_LE_UINT16(&HEADER_PCX[8]) + 1;
		v20 = (int16)READ_LE_UINT16(&HEADER_PCX[10]) + 1;
		if (((int16)READ_LE_UINT16(&HEADER_PCX[8]) + 1) <= SCREEN_WIDTH) {
			DOUBLE_ECRAN = false;
		} else {
			v6 = SCREEN_WIDTH * 2;
			DOUBLE_ECRAN = true;
		}
		if (v20 > SCREEN_HEIGHT)
			v20 = SCREEN_HEIGHT;
		PCX_L = v6;
		PCX_H = v20;
		if (v8 == -1)
		  error("Erreur en cours de lecture.");
	} else {
		_vm->_fileManager.CONSTRUIT_FICHIER(_vm->_globals.HOPIMAGE, file);
		if (!f.open(_vm->_globals.NFICHIER))
		  error("(nom)Erreur en cours de lecture.");

		filesize = f.size();
		int bytesRead = f.read(HEADER_PCX, 128);
		if (bytesRead < 128)
			error("Erreur en cours de lecture.");

		v6 = (int16)READ_LE_UINT16(&HEADER_PCX[8]) + 1;
		v20 = (int16)READ_LE_UINT16(&HEADER_PCX[10]) + 1;
		if (v6 <= SCREEN_WIDTH) {
			DOUBLE_ECRAN = false;
		} else {
			v6 = SCREEN_WIDTH * 2;
			DOUBLE_ECRAN = true;
		}
		if (v20 > SCREEN_HEIGHT)
			v20 = SCREEN_HEIGHT;
		PCX_L = v6;
		PCX_H = v20;
		v7 = filesize - 896;
	}

	ptr = _vm->_globals.dos_malloc2(0xEE60u);
	if (v7 >= 60000) {
		v21 = v7 / 60000 + 1;
		v23 = 60000 * (v7 / 60000) - v7;
    
		if (((uint32)v23 & 0x80000000u) != 0)
			v23 = -v23;
		f.read(ptr, 60000);
		v7 = 60000;
	} else {
		v21 = 1;
		v23 = v7;
		f.read(ptr, v7);
	}
	v22 = v21 - 1;
	v18 = 0;
	v9 = 0;
	v10 = 0;
	v19 = v6;
  
	do {
		if (v9 == v7) {
			v9 = 0;
			--v22;
			v7 = 60000;
			if ( !v22 )
				v7 = v23;
			v11 = v10;
			f.read(ptr, v7);
			v10 = v11;
		}

		v12 = *((byte *)ptr + v9++);
		if (v12 > 0xC0u) {
			v13 = v12 - 192;
			if (v9 == v7) {
				v9 = 0;
				--v22;
				v7 = 60000;
				if ( v22 == 1 )
					v7 = v23;
				v14 = v10;
				f.read(ptr, v7);
				v10 = v14;
			}
			v15 = *((byte *)ptr + v9++);

			do {
				*((byte *)surface + v10++) = v15;
				++v18;
				--v13;
			} while (v13);
		} else {
			*((byte *)surface + v10++) = v12;
			++v18;
		}
	} while (v18 < v19 * v20);

	if (typeFlag) {
		f.seek(_vm->_globals.CAT_TAILLE + _vm->_globals.CAT_POSI - 768);
	} else {
		filesize = f.size();
		f.seek(filesize - 768);
	}

	if (f.read(palette, PALETTE_BLOCK_SIZE) != (PALETTE_BLOCK_SIZE))
		error("A_PCX640_480");
  
	f.close();
	_vm->_globals.dos_free2(ptr);
}

// Clear Palette
void GraphicsManager::Cls_Pal() {
	Common::fill(&cmap[0], &cmap[PALETTE_BLOCK_SIZE], 0);
	SD_PIXELS[0] = 0;

	if (Winbpp == 1) {
		g_system->getPaletteManager()->setPalette(cmap, 0, 256);
	}
}

void GraphicsManager::SCANLINE(int pitch) {
	nbrligne = nbrligne2 = pitch;
}

void GraphicsManager::m_scroll(const byte *surface, int xs, int ys, int width, int height, int destX, int destY) {
	const byte *srcP;
	byte *destP;
	int yNext;
	int yCtr;
	byte *dest2P;
	const byte *src2P;
	unsigned int widthRemaining;

	assert(VideoPtr);
	srcP = xs + nbrligne2 * ys + surface;
	destP = destX + WinScan * destY + (byte *)VideoPtr->pixels;
	yNext = height;
	do {
		yCtr = yNext;
		memcpy((byte *)destP, (const byte *)srcP, 4 * (width >> 2));
		src2P = (const byte *)(srcP + 4 * (width >> 2));
		dest2P = (byte *)(destP + 4 * (width >> 2));
		widthRemaining = width - 4 * (width >> 2);
		memcpy(dest2P, src2P, widthRemaining);
		destP = dest2P + widthRemaining + WinScan - width;
		srcP = src2P + widthRemaining + nbrligne2 - width;
		yNext = yCtr - 1;
	} while (yCtr != 1);
}

void GraphicsManager::m_scroll2(const byte *surface, int xs, int ys, int width, int height, int destX, int destY) {
	const byte *srcP;
	byte *destP;
	int destPitch;
	int srcPitch;
	int yCtr;

	assert(VideoPtr);
	srcP = xs + nbrligne2 * ys + surface;
	destP = destX + WinScan * destY + (byte *)VideoPtr->pixels;
	destPitch = WinScan - SCREEN_WIDTH;
	srcPitch = nbrligne2 - SCREEN_WIDTH;
	yCtr = height;

	do {
		memcpy(destP, srcP, SCREEN_WIDTH);
		destP = destP + destPitch + SCREEN_WIDTH;
		srcP = srcP + srcPitch + SCREEN_WIDTH;
		--yCtr;
	} while (yCtr);
}

void GraphicsManager::m_scroll2A(const byte *surface, int xs, int ys, int width, int height, int destX, int destY) {
	const byte *srcP;
	byte *destP;
	int yCtr;
	int xCtr;
	byte srcByte;
	const byte *srcCopyP;
	byte *destCopyP;

	assert(VideoPtr);
	srcP = xs + nbrligne2 * ys + surface;
	destP = destX + WinScan * destY + (byte *)VideoPtr->pixels;
	yCtr = height;
	Agr_x = 0;
	Agr_y = 0;
	Agr_Flag_y = 0;
	do {
		for (;;) {
			destCopyP = destP;
			srcCopyP = srcP;
			xCtr = width;
			Agr_x = 0;
			do {
				srcByte = *srcP;
				*destP++ = *srcP++;
				Agr_x += SDL_ECHELLE;
				if ((unsigned int)Agr_x >= 100) {
					Agr_x -= 100;
					*destP++ = srcByte;
				}
				--xCtr;
			} while ( xCtr );
	      
			srcP = srcCopyP;
			destP = WinScan + destCopyP;
			if (Agr_Flag_y)
				break;

			Agr_y += SDL_ECHELLE;
			if ((unsigned int)Agr_y < 100)
				break;

			Agr_y -= 100;
			Agr_Flag_y = 1;
		}

		Agr_Flag_y = 0;
		srcP = nbrligne2 + srcCopyP;
		--yCtr;
	} while (yCtr);
}

/**
 * Copies data from a 8-bit palette surface into the 16-bit screen
 */
void GraphicsManager::m_scroll16(const byte *surface, int xs, int ys, int width, int height, int destX, int destY) {
	DD_Lock();

	assert(VideoPtr);
	const byte *srcP = xs + nbrligne2 * ys + surface;
	uint16 *destP = (uint16 *)((byte *)VideoPtr->pixels + destX * 2 + WinScan * destY);

	for (int yp = 0; yp < height; ++yp) {
		// Copy over the line, using the source pixels as lookups into the pixels palette
		const byte *lineSrcP = srcP;
		uint16 *lineDestP = destP;

		for (int xp = 0; xp < width; ++xp)
			*lineDestP++ = *(uint16 *)&PAL_PIXELS[*lineSrcP++ * 2];

		// Move to the start of the next line
		srcP += nbrligne2;
		destP += WinScan / 2;
	}

	DD_Unlock();
}

// TODO: See if PAL_PIXELS can be converted to a uint16 array
void GraphicsManager::m_scroll16A(const byte *surface, int xs, int ys, int width, int height, int destX, int destY) {
	const byte *srcP;
	const byte *destP;
	int yNext;
	int xCtr;
	const byte *palette;
	int pixelWord;
	int yCtr;
	const byte *srcCopyP;
	const byte *destCopyP;

	assert(VideoPtr);
	srcP = xs + nbrligne2 * ys + surface;
	destP = destX + destX + WinScan * destY + (byte *)VideoPtr->pixels;
	yNext = height;
	Agr_x = 0;
	Agr_y = 0;
	Agr_Flag_y = 0;

	do {
		for (;;) {
			destCopyP = destP;
			srcCopyP = srcP;
			xCtr = width;
			yCtr = yNext;
			palette = PAL_PIXELS;
			Agr_x = 0;

			do {
				pixelWord = *(uint16 *)(palette + 2 * *srcP);
				*(uint16 *)destP = pixelWord;
				++srcP;
				destP += 2;
				Agr_x += SDL_ECHELLE;
				if ((unsigned int)Agr_x >= 100) {
					Agr_x -= 100;
					*(uint16 *)destP = pixelWord;
					destP += 2;
				}
        
				--xCtr;
			} while (xCtr);

			yNext = yCtr;
			srcP = srcCopyP;
			destP = WinScan + destCopyP;
			if (Agr_Flag_y == 1)
				break;
			
			Agr_y += SDL_ECHELLE;
			
			if ((unsigned int)Agr_y < 100)
				break;
      
			Agr_y -= 100;
			Agr_Flag_y = 1;
		}

		Agr_Flag_y = 0;
		srcP = nbrligne2 + srcCopyP;
		yNext = yCtr - 1;
	} while (yCtr != 1);
}

void GraphicsManager::Copy_Vga(const byte *surface, int xp, int yp, int width, int height, int destX, int destY) {
	const byte *srcP;
	byte *destP;
	int yCount;
	int xCount;
	byte srcByte;
	byte *loopDestP; 
	byte *loopSrcP; 
	byte *loopSrc2P; 
	byte *tempDestP; 
	const byte *tempSrcP; 
	int yCtr; 

	assert(VideoPtr);
	srcP = xp + 320 * yp + surface;
	destP = 30 * WinScan + destX + destX + WinScan * 2 * destY + (byte *)VideoPtr->pixels;
	yCount = height;
  
	do {
		yCtr = yCount;
		xCount = width;
		tempSrcP = srcP;
		tempDestP = destP;
		do {
			srcByte = *srcP;
			*destP = *srcP;
			loopDestP = WinScan + destP;
			*loopDestP = srcByte;
			loopSrcP = loopDestP - WinScan + 1;
			*loopSrcP = srcByte;
			loopSrc2P = WinScan + loopSrcP;
			*loopSrc2P = srcByte;
			++srcP;
			destP = loopSrc2P - WinScan + 1;
			--xCount;
		} while (xCount);
    
		destP = WinScan + WinScan + tempDestP;
		srcP = tempSrcP + 320;
		yCount = yCtr - 1;
	} while (yCtr != 1);
}

void GraphicsManager::Copy_Vga16(const byte *surface, int xp, int yp, int width, int height, int destX, int destY) {
	const byte *srcP; 
	uint16 *destP; 
	int yCount; 
	int xCount;
	int xCtr;
	const byte *palette;
	uint16 *tempSrcP;
	uint16 srcByte;
	uint16 *tempDestP;
	int savedXCount;
	uint16 *loopDestP;
	const byte *loopSrcP;
	int yCtr;

	assert(VideoPtr);
	srcP = xp + 320 * yp + surface;
	destP = (uint16 *)(30 * WinScan + destX + destX + destX + destX + WinScan * 2 * destY + (byte *)VideoPtr->pixels);
	yCount = height;
	xCount = width;
  
	do {
		yCtr = yCount;
		xCtr = xCount;
		loopSrcP = srcP;
		loopDestP = destP;
		savedXCount = xCount;
		palette = PAL_PIXELS;
		
		do {
			tempSrcP = (uint16 *)(palette + 2 * *srcP);
			srcByte = *tempSrcP;
			*destP = *tempSrcP;
			*(destP + 1) = srcByte;
			
			tempDestP = (uint16 *)((byte *)destP + WinScan);
			*tempDestP = srcByte;
			*(tempDestP + 1) = srcByte;
			++srcP;
			destP = (uint16 *)((byte *)tempDestP - WinScan + 4);
			--xCtr;
		} while (xCtr);
    
		xCount = savedXCount;
		destP = (uint16 *)((byte *)loopDestP + WinScan * 2);
		srcP = loopSrcP + 320;
		yCount = yCtr - 1;
	} while (yCtr != 1);
}

void GraphicsManager::fade_in(const byte *palette, int step, const byte *surface) {
	uint16 palData1[PALETTE_BLOCK_SIZE * 2];
	byte palData2[PALETTE_BLOCK_SIZE];

	// Initialise temporary palettes
	Common::fill(&palData1[0], &palData1[PALETTE_BLOCK_SIZE], 0);
	Common::fill(&palData2[0], &palData2[PALETTE_BLOCK_SIZE], 0);

	// Set current palette to black
	setpal_vga256(palData2);
  
	// Loop through fading in the palette
	uint16 *pTemp1 = &palData1[1];
	for (int fadeIndex = 0; fadeIndex < FADESPD; ++fadeIndex) {
		uint16 *pTemp2 = &palData1[2];

		for (int palOffset = 0; palOffset < PALETTE_BLOCK_SIZE; palOffset += 3) {
			if (palData2[palOffset] < palette[palOffset]) {
				uint16 v = (palette[palOffset] & 0xff) * 256 / FADESPD;
				palData1[palOffset] = v;
				palData2[palOffset] = (v >> 8) & 0xff;
			}

			if (palData2[palOffset + 1] < palette[palOffset + 1]) {
				uint16 *pDest = &pTemp1[palOffset];
				uint16 v = (palette[palOffset] & 0xff) * 256 / FADESPD + *pDest; 
				*pDest = v;
				palData2[palOffset + 1] = (v >> 8) & 0xff;
			}

			if (palData2[palOffset + 2] < palette[palOffset + 2]) {
				uint16 *pDest = &pTemp2[palOffset];
				uint16 v = (palette[palOffset] & 0xff) * 256 / FADESPD + *pDest; 
				*pDest = v;
				palData2[palOffset + 2] = (v >> 8) & 0xff;
			}
		}

		setpal_vga256(palData2);
		if (Winbpp == 2) {
			if (SDL_ECHELLE)
				m_scroll16A(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
			else
				m_scroll16(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
			DD_VBL();
		}
	}

	// Set the final palette
	setpal_vga256(palette);

	// Refresh the screen
	if (Winbpp == 2) {
		if (SDL_ECHELLE)
			m_scroll16A(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
		else
			m_scroll16(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
		DD_VBL();
	}
}

void GraphicsManager::fade_out(const byte *palette, int step, const byte *surface) {
	int palByte;
	int palIndex;
	int palDataIndex; 
	int palCtr3;
	int palCtr4;
	int palCtr5;
	int palValue;
	int palCtr2;
	int palCtr1;
	uint16 palMax;
	byte palData[PALETTE_BLOCK_SIZE];
	int tempPalette[PALETTE_BLOCK_SIZE];

	palMax = palByte = FADESPD;
	if (palette) {
		palIndex = 0;
		do {
			palDataIndex = palIndex;
			palByte = *(palIndex + palette);
			palByte <<= 8;
			tempPalette[palDataIndex] = palByte;
			palData[palDataIndex] = *(palIndex++ + palette);
		} while (palIndex < PALETTE_BLOCK_SIZE);
    
		setpal_vga256(palData);
		if (Winbpp == 2) {
			if (SDL_ECHELLE)
				m_scroll16A(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
			else
				m_scroll16(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
			DD_VBL();
		}
	
		palCtr3 = 0;
		if (palMax > 0) {
			do {
				palCtr4 = 0;
				do {
					palCtr5 = palCtr4;
					palValue = tempPalette[palCtr4] - (*(palCtr4 + palette) << 8) / palMax;
					tempPalette[palCtr5] = palValue;
					palData[palCtr5] = (palValue >> 8) & 0xff;
					++palCtr4;
				} while (palCtr4 < (PALETTE_BLOCK_SIZE));
				
				setpal_vga256(palData);
				if (Winbpp == 2) {
					if (SDL_ECHELLE)
						m_scroll16A(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
					else
						m_scroll16(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
			
					DD_VBL();
				}
				++palCtr3;
			} while (palMax > palCtr3);
		}

		palCtr2 = 0;
		do {
			palData[palCtr2++] = 0;
		} while (palCtr2 < (PALETTE_BLOCK_SIZE));

		setpal_vga256(palData);
    
		if (Winbpp == 2) {
			if (!SDL_ECHELLE) {
				m_scroll16(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
				return DD_VBL();
			}
			goto LABEL_28;
		}
	} else {
		palCtr1 = 0;
		do {
			palData[palCtr1++] = 0;
		} while (palCtr1 < (PALETTE_BLOCK_SIZE));

		setpal_vga256(palData);
		if (Winbpp == 2) {
			if (!SDL_ECHELLE) {
				m_scroll16(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
				return DD_VBL();
			}

LABEL_28:
			m_scroll16A(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
			return DD_VBL();
		}
	}
}

void GraphicsManager::FADE_INS() {
	FADESPD = 1;
	fade_in(Palette, 1, (const byte *)VESA_BUFFER);
}

void GraphicsManager::FADE_OUTS() {
  FADESPD = 1;
  fade_out(Palette, 1, (const byte *)VESA_BUFFER);
}

void GraphicsManager::FADE_INW() {
	FADESPD = 15;
	fade_in(Palette, 20, (const byte *)VESA_BUFFER);
}

void GraphicsManager::FADE_OUTW() {
	FADESPD = 15;
	fade_out(Palette, 20, (const byte *)VESA_BUFFER);
}

void GraphicsManager::setpal_vga256(const byte *palette) {
	CHANGE_PALETTE(palette);
}

void GraphicsManager::setpal_vga256_linux(const byte *palette, const byte *surface) {
	CHANGE_PALETTE(palette);
  
	if (Winbpp == 2) {
		if (SDL_ECHELLE)
			m_scroll16A(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
		else
			m_scroll16(surface, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
		
		DD_VBL();
	}
}

void GraphicsManager::SETCOLOR(int palIndex, int r, int g, int b) {
	_vm->_graphicsManager.Palette[palIndex * 3] = 255 * r / 100;
	_vm->_graphicsManager.Palette[palIndex * 3 + 1] = 255 * g / 100;
	_vm->_graphicsManager.Palette[palIndex * 3 + 2] = 255 * b / 100;
	
	setpal_vga256(Palette);
}

void GraphicsManager::SETCOLOR2(int palIndex, int r, int g, int b) {
	return SETCOLOR(palIndex, r, g, b);
}

void GraphicsManager::SETCOLOR3(int palIndex, int r, int g, int b) {
	Palette[palIndex * 3] = 255 * r / 100;
	Palette[palIndex * 3 + 1] = 255 * g / 100;
	Palette[palIndex * 3 + 2] = 255 * b / 100;
}

void GraphicsManager::SETCOLOR4(int palIndex, int r, int g, int b) {
	int rv, gv, bv;
	int palOffset; 
	int v8; 

	rv = 255 * r / 100;
	gv = 255 * g / 100;
	bv = 255 * b / 100;
	palOffset = 3 * palIndex;
	Palette[palOffset] = 255 * r / 100;
	Palette[palOffset + 1] = gv;
	Palette[palOffset + 2] = bv;

	v8 = 4 * palIndex;
	cmap[v8] = rv;
	cmap[v8 + 1] = gv;
	cmap[v8 + 2] = bv;
	
	WRITE_LE_UINT16(&SD_PIXELS[2 * palIndex], MapRGB(rv, gv, bv));
	
	if (Winbpp == 1)
		g_system->getPaletteManager()->setPalette(cmap, palIndex, 1);
}

void GraphicsManager::CHANGE_PALETTE(const byte *palette) {
	// Copy the palette into the PALPCX block
// TODO: Likely either one or both of the destination arrays can be removed,
// since PALPCX is only used in SAVE_IMAGE, and cmap in the original was a RGBA
// array specifically intended just for passing to the SDL palette setter
	Common::copy(&palette[0], &palette[PALETTE_BLOCK_SIZE], &PALPCX[0]);
	Common::copy(&palette[0], &palette[PALETTE_BLOCK_SIZE], &cmap[0]);

	const byte *srcP = &palette[0];
	for (int idx = 0; idx < PALETTE_SIZE; ++idx, srcP += 3) {
		*(uint16 *)&SD_PIXELS[2 * idx] = MapRGB(*srcP, *(srcP + 1), *(srcP + 2));
	}

	if (Winbpp == 1)
		g_system->getPaletteManager()->setPalette(cmap, 0, PALETTE_SIZE);
}

uint16 GraphicsManager::MapRGB(byte r, byte g, byte b) {
	if (Winbpp == 1) {
		error("TODO: Support in 8-bit graphics mode");
	} else {
		Graphics::PixelFormat format = g_system->getScreenFormat();

		return (r >> format.rLoss) << format.rShift
				| (g >> format.gLoss) << format.gShift
				| (b >> format.bLoss) << format.bShift;
	}
}

void GraphicsManager::DD_VBL() {
	// TODO: Is this okay here?
	g_system->updateScreen();
}

void GraphicsManager::FADE_OUTW_LINUX(const byte *surface) {
	assert(surface);
	fade_out(Palette, FADESPD, surface);
}

void GraphicsManager::FADE_INW_LINUX(const byte *surface) {
	assert(surface);
	fade_in(Palette, FADESPD, surface);
}

void GraphicsManager::Copy_WinScan_Vbe3(const byte *srcData, byte *destSurface) {
	int rleValue;
	int destOffset; 
	const byte *srcP; 
	byte srcByte;
	byte destLen1;
	byte *destSlice1P; 
	byte destLen2; 
	byte *destSlice2P; 

	rleValue = 0;
	destOffset = 0;
	srcP = srcData;
	for (;;) {
		srcByte = *srcP;
		if (*srcP < 0xDEu)
			goto Video_Cont3_wVbe;
		if (srcByte == (byte)-4)
			return;

		if (srcByte < 0xFBu) {
			destOffset += (byte)(*srcP + 35);
			srcByte = *(srcP++ + 1);
		} else if (srcByte == (byte)-3) {
			destOffset += *(srcP + 1);
			srcByte = *(srcP + 2);
			srcP += 2;
		} else if (srcByte == (byte)-2) {
			destOffset += READ_LE_UINT16(srcP + 1);
			srcByte = *(srcP + 3);
			srcP += 3;
		} else {
			destOffset += READ_LE_UINT32(srcP + 1);
			srcByte = *(srcP + 5);
			srcP += 5;
		}
Video_Cont3_wVbe:
		if (srcByte > 0xD2u) {
			if (srcByte == (byte)-45) {
				destLen1 = *(srcP + 1);
				rleValue = *(srcP + 2);
				destSlice1P = destOffset + destSurface;
				destOffset += destLen1;
				memset(destSlice1P, rleValue, destLen1);
				srcP += 3;
			} else {
				destLen2 = (byte)(*srcP + 45);
				rleValue = *(srcP + 1);
				destSlice2P = destOffset + destSurface;
				destOffset += destLen2;
				memset(destSlice2P, rleValue, destLen2);
				srcP += 2;
			}
		} else {
			*(destOffset + destSurface) = srcByte;
			++srcP;
			++destOffset;
		}
	}}

void GraphicsManager::Copy_Video_Vbe3(const byte *srcData) {
	int rleValue;
	int destOffset;
	const byte *srcP;
	uint8 srcByte;
	int destLen1;
	byte *destSlice1P;
	int destLen2;
	byte *destSlice2P;

	assert(VideoPtr);
	rleValue = 0;
	destOffset = 0;
	srcP = srcData;
	for (;;) {
		srcByte = *srcP;
		if (*srcP < 222)
			goto Video_Cont3_Vbe;
    
		if (srcByte == 252)
			return;
		if (srcByte < 251) {
			destOffset += *srcP + 35;
			srcByte = *(srcP++ + 1);
		} else if (srcByte == (byte)-3) {
			destOffset += *(srcP + 1);
			srcByte = *(srcP + 2);
			srcP += 2;
		} else if (srcByte == (byte)-2) {
			destOffset += READ_LE_UINT16(srcP + 1);
			srcByte = *(srcP + 3);
			srcP += 3;
		} else {
			destOffset += READ_LE_UINT32(srcP + 1);
			srcByte = *(srcP + 5);
			srcP += 5;
		}
Video_Cont3_Vbe:
		if (srcByte > 210) {
			if (srcByte == 211) {
				destLen1 = *(srcP + 1);
				rleValue = *(srcP + 2);
				destSlice1P = destOffset + (byte *)VideoPtr->pixels;
				destOffset += destLen1;
				memset(destSlice1P, rleValue, destLen1);
				srcP += 3;
			} else {
				destLen2 = (byte)(*srcP + 45);
				rleValue = *(srcP + 1);
				destSlice2P = (byte *)(destOffset + (byte *)VideoPtr->pixels);
				destOffset += destLen2;
				memset(destSlice2P, rleValue, destLen2);
				srcP += 2;
			}
		} else {
			*(destOffset + (byte *)VideoPtr->pixels) = srcByte;
			++srcP;
			++destOffset;
		}
	}
}

void GraphicsManager::Copy_Video_Vbe16(const byte *srcData) {
	const byte *srcP = srcData;
	int destOffset = 0;
	assert(VideoPtr);

	for (;;) {
		byte srcByte = *srcP;
		if (srcByte >= 222) {
			if (srcByte == 252)
				return;
			if (srcByte < 251) {
				destOffset += srcByte - 221;
				srcByte = *++srcP;
			} else if (srcByte == 253) {
				destOffset += *(const byte *)(srcP + 1);
				srcByte = *(const byte *)(srcP + 2);
				srcP += 2;
			} else if (srcByte == 254) {
				destOffset += READ_LE_UINT16(srcP + 1);
				srcByte = *(const byte *)(srcP + 3);
				srcP += 3;
			} else {
				destOffset += READ_LE_UINT32(srcP + 1);
				srcByte = *(const byte *)(srcP + 5);
				srcP += 5;
			}
		}

		if (srcByte > 210) {
			if (srcByte == 211) {
				int pixelCount = *(srcP + 1);
				int pixelIndex = *(srcP + 2);
				uint16 *destP = (uint16 *)((byte *)VideoPtr->pixels + destOffset * 2);
				uint16 pixelValue = *(uint16 *)(PAL_PIXELS + 2 * pixelIndex);
				destOffset += pixelCount;

				while (pixelCount--)
					*destP++ = pixelValue;

				srcP += 3;
			} else {
				int pixelCount = srcByte - 211;
				int pixelIndex = *(srcP + 1);
				uint16 *destP = (uint16 *)((byte *)VideoPtr->pixels + destOffset * 2);
				uint16 pixelValue = *(uint16 *)(PAL_PIXELS + 2 * pixelIndex);
				destOffset += pixelCount;

				while (pixelCount--)
					*destP++ = pixelValue;

				srcP += 2;
			}
		} else {
			*((uint16 *)VideoPtr->pixels + destOffset) = *(uint16 *)(PAL_PIXELS + 2 * srcByte);
			++srcP;
			++destOffset;
		}
	}
}

void GraphicsManager::Copy_Video_Vbe16a(const byte *srcData) {
	int destOffset; 
	const byte *srcP; 
	byte pixelIndex; 

	destOffset = 0;
	srcP = srcData;
	for (;;) {
		pixelIndex = *srcP;
		if (*srcP < 0xFCu)
			goto Video_Cont_Vbe16a;
		if (pixelIndex == (byte)-4)
			return;
		if (pixelIndex == (byte)-3) {
			destOffset += *(srcP + 1);
			pixelIndex = *(srcP + 2);
			srcP += 2;
		} else if (pixelIndex == (byte)-2) {
			destOffset += READ_LE_UINT16(srcP + 1);
			pixelIndex = *(srcP + 3);
			srcP += 3;
		} else {
			destOffset += READ_LE_UINT32(srcP + 1);
			pixelIndex = *(srcP + 5);
			srcP += 5;
		}
Video_Cont_Vbe16a:
		WRITE_LE_UINT16((byte *)VideoPtr->pixels + destOffset * 2, READ_LE_UINT16(PAL_PIXELS + 2 * pixelIndex));
		++srcP;
		++destOffset;
	}
}

void GraphicsManager::Capture_Mem(const byte *srcSurface, byte *destSurface, int xs, int ys, int width, int height) {
	const byte *srcP;
	byte *destP;
	int rowCount; 
	int i;
	int rowCount2; 

	// TODO: This code in the original is potentially dangerous, as it doesn't clip the area to within
	// the screen, and so thus can read areas outside of the allocated surface buffer
	srcP = xs + nbrligne2 * ys + srcSurface;
	destP = destSurface;
	rowCount = height;
	do {
		rowCount2 = rowCount;
		if (width & 1) {
			memcpy(destP, srcP, width);
			srcP += width;
			destP += width;
		} else if (width & 2) {
			for (i = width >> 1; i; --i) {
				*(uint16 *)destP = *(uint16 *)srcP;
				srcP += 2;
				destP += 2;
			}
		} else {
			memcpy(destP, srcP, 4 * (width >> 2));
			srcP += 4 * (width >> 2);
			destP += 4 * (width >> 2);
		}
		srcP = nbrligne2 + srcP - width;
		rowCount = rowCount2 - 1;
	} while (rowCount2 != 1);
}

void GraphicsManager::Sprite_Vesa(byte *surface, const byte *spriteData, int xp, int yp, int spriteIndex) {
	// Get a pointer to the start of the desired sprite
	const byte *spriteP = spriteData + 3;
	for (int i = spriteIndex; i; --i)
		spriteP += READ_LE_UINT32(spriteP) + 16;

	clip_x = 0;
	clip_y = 0;
	clip_flag = false;

	spriteP += 4;
	int width = READ_LE_UINT16(spriteP);
	spriteP += 2;
	int height = READ_LE_UINT16(spriteP);
	
	// Clip X
	clip_x1 = width;
	if ((xp + width) <= (min_x + 300))
		return;
	if (xp < (min_x + 300)) {
		clip_x = min_x + 300 - xp;
		clip_flag = true;
	}

	// Clip Y
	// TODO: This is weird, but it's that way in the original. Original game bug?
	if ((yp + height) <= height)
		return;
	if (yp < (min_y + 300)) {
		clip_y = min_y + 300 - yp;
		clip_flag = true;
	}

	// Clip X1
	if (xp >= (max_x + 300))
		return;
	if ((xp + width) > (max_x + 300)) {
		int xAmount = width + 10 - (xp + width - (max_x + 300));
		if (xAmount <= 10)
			return;

		clip_x1 = xAmount - 10;
		clip_flag = true;
	}

	// Clip Y1
	if (yp >= (max_y + 300))
		return;
	if ((yp + height) > (max_y + 300)) {
		int yAmount = height + 10 - (yp + height - (max_y + 300));
		if (yAmount <= 10)
			return;

		clip_y1 = yAmount - 10;
		clip_flag = true;
	}

	// Sprite display

	// Set up source
	spriteP += 6;
	int srcOffset = READ_LE_UINT16(spriteP);
	spriteP += 4;
	const byte *srcP = spriteP;
	spriteP += srcOffset;

	// Set up surface destination
	byte *destP = surface + (yp - 300) * nbrligne2 + (xp - 300);
	
	// Handling for clipped versus non-clipped
	if (clip_flag) {
		// Clipped version
		for (int yc = 0; yc < height; ++yc, destP += nbrligne2) {
			byte *tempDestP = destP;
			byte byteVal;
			int xc = 0;

			while ((byteVal = *srcP) != 253) {
				++srcP;
				width = READ_LE_UINT16(srcP);
				srcP += 2;

				if (byteVal == 254) {
					// Copy pixel range
					for (int xv = 0; xv < width; ++xv, ++xc, ++spriteP, ++tempDestP) {
						if (clip_y == 0 && xc >= clip_x && xc < clip_x1)
							*tempDestP = *spriteP;
					}
				} else {
					// Skip over bytes
					tempDestP += width;
					xc += width;
				}
			}

			if (clip_y > 0)
				--clip_y;
			srcP += 3;
		}
	} else {
		// Non-clipped
		for (int yc = 0; yc < height; ++yc, destP += nbrligne2) {
			byte *tempDestP = destP;
			byte byteVal;

			while ((byteVal = *srcP) != 253) {
				++srcP;
				width = READ_LE_UINT16(srcP);
				srcP += 2;

				if (byteVal == 254) {
					// Copy pixel range
					Common::copy(spriteP, spriteP + width, tempDestP);
					spriteP += width;
				}
				
				tempDestP += width;
			}

			// Skip over control byte and width
			srcP += 3;
		}
	}
}

void GraphicsManager::FIN_VISU() {
	for (int idx = 1; idx <= 20; ++idx) {
		if (_vm->_globals.Bqe_Anim[idx].field4 == 1)
			_vm->_objectsManager.BOB_OFF(idx);
	}

	_vm->_eventsManager.VBL();
	_vm->_eventsManager.VBL();

	for (int idx = 1; idx <= 20; ++idx) {
		if (_vm->_globals.Bqe_Anim[idx].field4 == 1)
			_vm->_objectsManager.BOB_ZERO(idx);
	}

	for (int idx = 1; idx <= 29; ++idx) {
		_vm->_globals.BL_ANIM[idx].v1 = 0;
	}

	for (int idx = 1; idx <= 20; ++idx) {
		_vm->_globals.Bqe_Anim[idx].field4 = 0;
	}
}

void GraphicsManager::VISU_ALL() {
	for (int idx = 1; idx <= 20; ++idx) {
		if (_vm->_globals.Bqe_Anim[idx].field4 == 1)
			_vm->_objectsManager.BOB_VISU(idx);
	}
}

void GraphicsManager::RESET_SEGMENT_VESA() {
	int idx;
	int blocCount;

	if (_vm->_globals.NBBLOC > 0) {
		idx = 0;
		blocCount = _vm->_globals.NBBLOC;
		do {
			_vm->_globals.BLOC[idx++].field0 = 0;
		} while (idx != blocCount);
    
		_vm->_globals.NBBLOC = 0;
	}
}

// Add VESA Segment
void GraphicsManager::Ajoute_Segment_Vesa(int x1, int y1, int x2, int y2) {
	int tempX;
	int blocCount;
	int16 blocIndex;
	bool addFlag;

	tempX = x1;
	addFlag = true;
	if (x2 > max_x)
		x2 = max_x;
	if (y2 > max_y)
		y2 = max_y;
	if (x1 < min_x)
		tempX = min_x;
	if (y1 < min_y)
		y1 = min_y;

	blocCount = _vm->_globals.NBBLOC;
	if (_vm->_globals.NBBLOC > 1) {

		blocIndex = 0;
		do {
			BlocItem &bloc = _vm->_globals.BLOC[blocIndex];

			if (bloc.field0 == 1 
					&& tempX >= bloc.x1 && x2 <= bloc.x2
					&& y1 >= bloc.y1 && y2 <= bloc.y2)
				addFlag = false;
			++blocIndex;
			blocCount = blocIndex;
		} while (_vm->_globals.NBBLOC + 1 != blocIndex);
	}
	
	if (addFlag) {
		assert(_vm->_globals.NBBLOC < 50);
		BlocItem &bloc = _vm->_globals.BLOC[++_vm->_globals.NBBLOC];

		bloc.field0 = 1;
		bloc.x1 = tempX;
		bloc.x2 = x2;
		bloc.y1 = y1;
		bloc.y2 = y2;
	}
}

int GraphicsManager::Magic_Number(signed int v) {
	int result = v;

	if (!v)
		result = 4;
	if (result & 1)
		++result;
	if (result & 2)
		result += 2;
  
	return result;
}

// Display VESA Segment
void GraphicsManager::Affiche_Segment_Vesa() {
	if (_vm->_globals.NBBLOC == 0)
		return;

	SDL_NBLOCS = _vm->_globals.NBBLOC;
	DD_Lock();

	for (int idx = 1; idx <= _vm->_globals.NBBLOC; ++idx) {
		BlocItem &bloc = _vm->_globals.BLOC[idx];
		Common::Rect &dstRect = dstrect[idx - 1];
		if (bloc.field0 != 1)
			continue;
	
		if (_vm->_eventsManager.CASSE != false) {
			if (Winbpp == 1) {
				Copy_Vga(VESA_BUFFER, bloc.x1, bloc.y1, bloc.x2 - bloc.x1, bloc.y2 - bloc.y1, bloc.x1, bloc.y1);
			} else if (Winbpp == 2) {
				Copy_Vga16(VESA_BUFFER, bloc.x1, bloc.y1, bloc.x2 - bloc.x1, bloc.y2 - bloc.y1, bloc.x1, bloc.y1);
			}
			
			dstRect.left = bloc.x1 * 2;
			dstRect.top = bloc.y1 * 2 + 30;
			dstRect.setWidth((bloc.x2 - bloc.x1) * 2);
			dstRect.setHeight((bloc.y2 - bloc.y1) * 2);
		} else if (bloc.x2 > _vm->_eventsManager.start_x && bloc.x1 < (_vm->_eventsManager.start_x + SCREEN_WIDTH)) {
			if (bloc.x1 < _vm->_eventsManager.start_x)
				bloc.x1 = _vm->_eventsManager.start_x;
			if (bloc.x2 > (_vm->_eventsManager.start_x + SCREEN_WIDTH))
				bloc.x2 = _vm->_eventsManager.start_x + SCREEN_WIDTH;
			
			if (SDL_ECHELLE) {
				// Calculate the bounds
				int xp = Magic_Number(bloc.x1) - 4;
				if (xp < _vm->_eventsManager.start_x)
					xp = _vm->_eventsManager.start_x;
				int yp = Magic_Number(bloc.y1) - 4;
				if (yp < 0)
					yp = 0;
				int width = Magic_Number(bloc.x2) + 4 - xp;
				if (width < 4)
					width = 4;
				int height = Magic_Number(bloc.y2) + 4 - yp;
				if (height < 4)
					height = 4;

				if ((xp - _vm->_eventsManager.start_x + width) > SCREEN_WIDTH)
					xp -= 4;
				if ((height - yp) > (SCREEN_HEIGHT - 40))
					yp -= 4;

				// WORKAROUND: Original didn't lock the screen for access
				DD_Lock();

				if (Winbpp == 2) {
					m_scroll16A(VESA_BUFFER, xp, yp, width, height, 
						Reel_Zoom(xp - _vm->_eventsManager.start_x, SDL_ECHELLE), Reel_Zoom(yp, SDL_ECHELLE));
				} else {
					m_scroll2A(VESA_BUFFER, xp, yp, width, height, 
						Reel_Zoom(xp - _vm->_eventsManager.start_x, SDL_ECHELLE), Reel_Zoom(yp, SDL_ECHELLE));
				}

				DD_Unlock();

				dstRect.left = Reel_Zoom(xp - _vm->_eventsManager.start_x, SDL_ECHELLE);
				dstRect.top = Reel_Zoom(yp, SDL_ECHELLE);
				dstRect.setWidth(Reel_Zoom(width, SDL_ECHELLE));
				dstRect.setHeight(Reel_Zoom(height, SDL_ECHELLE));
			} else {
				// WORKAROUND: Original didn't lock the screen for access
				DD_Lock();

				if (Winbpp == 2) {
					m_scroll16(VESA_BUFFER, bloc.x1, bloc.y1, bloc.x2 - bloc.x1, bloc.y2 - bloc.y1,
						bloc.x1 - _vm->_eventsManager.start_x, bloc.y1);
				} else {
					m_scroll(VESA_BUFFER, bloc.x1, bloc.y1, bloc.x2 - bloc.x1, bloc.y2 - bloc.y1,
						bloc.x1 - _vm->_eventsManager.start_x, bloc.y1);
				}

				dstRect.left = bloc.x1 - _vm->_eventsManager.start_x;
				dstRect.top = bloc.y1;
				dstRect.setWidth(bloc.x2 - bloc.x1);
				dstRect.setHeight(bloc.y2 - bloc.y1);

				DD_Unlock();
			}
		}
		
		_vm->_globals.BLOC[idx].field0 = 0;
	}

	_vm->_globals.NBBLOC = 0;
	DD_Unlock();
	if (!_vm->_globals.BPP_NOAFF) {
//		SDL_UpdateRects(LinuxScr, SDL_NBLOCS, dstrect);
	}
	SDL_NBLOCS = 0;
}

void GraphicsManager::CopyAsm(const byte *surface) {
	const byte *srcP;
	byte srcByte;
	byte *destP; 
	int yCtr;
	int xCtr;
	byte *dest1P; 
	byte *dest2P; 
	byte *dest3P; 
	byte *destPitch; 
	const byte *srcPitch;

	assert(VideoPtr);
	srcP = surface;
	srcByte = 30 * WinScan;
	destP = (byte *)VideoPtr->pixels + 30 * WinScan;
	yCtr = 200;
	do {
		srcPitch = srcP;
		destPitch = destP;
		xCtr = 320;
    
		do {
			srcByte = *srcP;
			*destP = *srcP;
			dest1P = WinScan + destP;
			*dest1P = srcByte;
			dest2P = dest1P - WinScan + 1;
			*dest2P = srcByte;
			dest3P = WinScan + dest2P;
			*dest3P = srcByte;
			destP = dest3P - WinScan + 1;
			++srcP;
			--xCtr;
		} while (xCtr);
		
		srcP = srcPitch + 320;
		destP = WinScan + WinScan + destPitch;
		--yCtr;
	} while (yCtr);
}

void GraphicsManager::Restore_Mem(byte *destSurface, const byte *src, int xp, int yp, int width, int height) {
	byte *destP;
	int yNext;
	const byte *srcP;
	int i;
	int yCtr;

	destP = xp + nbrligne2 * yp + destSurface;
	yNext = height;
	srcP = src;
	do {
		yCtr = yNext;
		if (width & 1) {
			memcpy(destP, srcP, width);
			srcP += width;
			destP += width;
		} else if (width & 2) {
			for (i = width >> 1; i; --i) {
				*(uint16 *)destP = *(uint16 *)srcP;
				srcP += 2;
				destP += 2;
			}
		} else {
			memcpy(destP, srcP, 4 * (width >> 2));
			srcP += 4 * (width >> 2);
			destP += 4 * (width >> 2);
		}
		destP = nbrligne2 + destP - width;
		yNext = yCtr - 1;
	} while (yCtr != 1);
}

int GraphicsManager::Reel_Zoom(int v, int percentage) {
	return Asm_Zoom(v, percentage);
}

int GraphicsManager::Asm_Zoom(int v, int percentage) {
	if (v)
		v += percentage * (long int)v / 100;
  
	return v;
}

int GraphicsManager::Reel_Reduc(int v, int percentage) {
	return Asm_Reduc(v, percentage);
}

int GraphicsManager::Asm_Reduc(int v, int percentage) {
	if (v)
		v -= percentage * (long int)v / 100;
  
	return v;
}

// Display 'Perfect?' 
void GraphicsManager::Affiche_Perfect(byte *surface, const byte *srcData, int xp300, int yp300, int frameIndex, int zoom1, int zoom2, int modeFlag) {
	const byte *spriteStartP; 
	int i; 
	const byte *spriteSizeP;
	int spriteWidth;
	int spriteHeight1; 
	const byte *spritePixelsP; 
	byte *dest1P; 
	int xCtr1; 
	byte *dest2P; 
	int xLeft; 
	int clippedWidth; 
	int xCtr2; 
	int v20; 
	int v21 = 0; 
	int v22; 
	int v23; 
	int v24; 
	int v25; 
	int v26; 
	int v27; 
	int v28; 
	byte *v29; 
	int v30; 
	int v31; 
	int v32; 
	int v33; 
	int v34; 
	int v35; 
	int spriteHeight2; 
	int v37;
	int v38; 
	uint16 v39; 
	byte *v40; 
	int v41; 
	uint16 v42; 
	const byte *spritePixelsCopyP; 
	const byte *spritePixelsCopy2P; 
	const byte *v45; 
	const byte *v46; 
	byte *dest1CopyP; 
	byte *destCopy2P; 
	int v49; 
	int v50; 
	byte *v51; 
	int v52; 
	byte *v53; 
	byte *v54; 
	byte *v55; 
	int yCtr1; 
	int yCtr2; 
	int v58; 
	int v59; 
	int v60; 
	int v61; 
	int v62; 
	int v63; 
	int v64; 
	int v65; 

	spriteStartP = srcData + 3;
	for (i = frameIndex; i; --i)
		spriteStartP += READ_LE_UINT32(spriteStartP) + 16;

	spriteWidth = 0;
	spriteHeight1 = 0;
	spriteSizeP = spriteStartP + 4;
	spriteWidth = (int16)READ_LE_UINT16(spriteSizeP);
	spriteSizeP += 2;
	spriteHeight1 = spriteHeight2 = (int16)READ_LE_UINT16(spriteSizeP);
	spritePixelsP = spriteSizeP + 10;
	clip_x = 0;
	clip_y = 0;
	clip_x1 = 0;
	clip_y1 = 0;
	if ((uint16)xp300 > min_x) {
		if ((uint16)xp300 < (uint16)(min_x + 300))
			clip_x = min_x + 300 - xp300;
		if ((uint16)yp300 > min_y) {
			if ((uint16)yp300 < (uint16)(min_y + 300))
				clip_y = min_y + 300 - yp300;
			if ((uint16)xp300 < (uint16)(max_x + 300)) {
				clip_x1 = max_x + 300 - xp300;
				if ((uint16)yp300 < (uint16)(max_y + 300)) {
					clip_y1 = max_y + 300 - yp300;
					dest1P = xp300 + nbrligne2 * (yp300 - 300) - 300 + surface;
					if (zoom2) {
						Compteur_y = 0;
						Agr_x = 0;
						Agr_y = 0;
						Agr_Flag_y = 0;
						Agr_Flag_x = 0;
						Largeur = spriteWidth;
						v20 = Asm_Zoom(spriteWidth, zoom2);
						v22 = Asm_Zoom(v21, zoom2);
						if (modeFlag) {
							v29 = v20 + dest1P;
							if (clip_y) {
								if ((uint16)clip_y >= v22)
									return;
								v61 = v22;
								v52 = v20;
								v30 = 0;
								v31 = (uint16)clip_y;
								while (Asm_Zoom(v30 + 1, zoom2) < v31)
									;
								v20 = v52;
								spritePixelsP += Largeur * v30;
								v29 += nbrligne2 * (uint16)clip_y;
								v22 = v61 - (uint16)clip_y;
							}
							if (v22 > (uint16)clip_y1)
								v22 = (uint16)clip_y1;
							if (clip_x) {
								if ((uint16)clip_x >= v20)
									return;
								v20 -= (uint16)clip_x;
							}
							if (v20 > (uint16)clip_x1) {
								v32 = v20 - (uint16)clip_x1;
								v29 -= v32;
								v62 = v22;
								v33 = 0;
								while (Asm_Zoom(v33 + 1, zoom2) < v32)
									;
								v34 = v33;
								v22 = v62;
								spritePixelsP += v34;
								v20 = (uint16)clip_x1;
							}
							do {
								while (1) {
									v63 = v22;
									v53 = v29;
									v46 = spritePixelsP;
									Agr_Flag_x = 0;
									Agr_x = 0;
									v35 = v20;
									do {
										while (1) {
											if (*spritePixelsP)
												*v29 = *spritePixelsP;
											--v29;
											++spritePixelsP;
											if (!Agr_Flag_x)
												Agr_x = zoom2 + Agr_x;
											if ((uint16)Agr_x < 0x64u)
												break;
											Agr_x = Agr_x - 100;
											--spritePixelsP;
											Agr_Flag_x = 1;
											--v35;
											if (!v35)
												goto R_Aff_Zoom_Larg_Cont1;
										}
										Agr_Flag_x = 0;
										--v35;
									} while (v35);
R_Aff_Zoom_Larg_Cont1:
									spritePixelsP = Largeur + v46;
									v29 = nbrligne2 + v53;
									++Compteur_y;
									if (!Agr_Flag_y)
										Agr_y = zoom2 + Agr_y;
									if ((uint16)Agr_y < 0x64u)
										break;
									Agr_y = Agr_y - 100;
									spritePixelsP = v46;
									Agr_Flag_y = 1;
									v22 = v63 - 1;
									if (v63 == 1)
										return;
								}
								Agr_Flag_y = 0;
								v22 = v63 - 1;
							} while (v63 != 1);
						} else {
							if (clip_y) {
								if ((uint16)clip_y >= v22)
									return;
								v58 = v22;
								v49 = v20;
								v23 = 0;
								v24 = (uint16)clip_y;
								while (Asm_Zoom(v23 + 1, zoom2) < v24)
									;
								v20 = v49;
								spritePixelsP += Largeur * v23;
								dest1P += nbrligne2 * (uint16)clip_y;
								v22 = v58 - (uint16)clip_y;
							}
							if (v22 > (uint16)clip_y1)
								v22 = (uint16)clip_y1;
							if (clip_x) {
								if ((uint16)clip_x >= v20)
									return;
								v59 = v22;
								v50 = v20;
								v25 = (uint16)clip_x;
								v26 = 0;
								while (Asm_Zoom(v26 + 1, zoom2) < v25)
									;
								v27 = v26;
								v22 = v59;
								spritePixelsP += v27;
								dest1P += (uint16)clip_x;
								v20 = v50 - (uint16)clip_x;
							}
							if (v20 > (uint16)clip_x1)
								v20 = (uint16)clip_x1;
							do {
								while (1) {
									v60 = v22;
									v51 = dest1P;
									v45 = spritePixelsP;
									v28 = v20;
									Agr_Flag_x = 0;
									Agr_x = 0;
									do {
										while (1) {
											if (*spritePixelsP)
												*dest1P = *spritePixelsP;
											++dest1P;
											++spritePixelsP;
											if (!Agr_Flag_x)
												Agr_x = zoom2 + Agr_x;
											if ((uint16)Agr_x < 0x64u)
												break;
											Agr_x = Agr_x - 100;
											--spritePixelsP;
											Agr_Flag_x = 1;
											--v28;
											if (!v28)
												goto Aff_Zoom_Larg_Cont1;
										}
										Agr_Flag_x = 0;
										--v28;
									} while (v28);
Aff_Zoom_Larg_Cont1:
									spritePixelsP = Largeur + v45;
									dest1P = nbrligne2 + v51;
									if (!Agr_Flag_y)
										Agr_y = zoom2 + Agr_y;
									if ((uint16)Agr_y < 0x64u)
										break;
									Agr_y = Agr_y - 100;
									spritePixelsP = v45;
									Agr_Flag_y = 1;
									v22 = v60 - 1;
									if (v60 == 1)
										return;
								}
								Agr_Flag_y = 0;
								v22 = v60 - 1;
							} while (v60 != 1);
						}
					} else if (zoom1) {
						Compteur_y = 0;
						Red_x = 0;
						Red_y = 0;
						Largeur = spriteWidth;
						Red = zoom1;
						if (zoom1 < 100) {
							v37 = Asm_Reduc(spriteWidth, Red);
							if (modeFlag) {
								v40 = v37 + dest1P;
								do {
									v65 = spriteHeight2;
									v55 = v40;
									Red_y = Red + Red_y;
									if ((uint16)Red_y < 0x64u) {
										Red_x = 0;
										v41 = Largeur;
										v42 = v37;
										do {
											Red_x = Red + Red_x;
											if ((uint16)Red_x < 0x64u) {
												if (v42 >= clip_x && v42 < clip_x1 && *spritePixelsP)
													*v40 = *spritePixelsP;
												--v40;
												++spritePixelsP;
												--v42;
											} else {
												Red_x = Red_x - 100;
												++spritePixelsP;
											}
											--v41;
										} while (v41);
										spriteHeight2 = v65;
										v40 = nbrligne2 + v55;
									} else {
										Red_y = Red_y - 100;
										spritePixelsP += Largeur;
									}
									--spriteHeight2;
								} while (spriteHeight2);
							} else {
								do {
									v64 = spriteHeight2;
									v54 = dest1P;
									Red_y = Red + Red_y;
									if ((uint16)Red_y < 0x64u) {
										Red_x = 0;
										v38 = Largeur;
										v39 = 0;
										do {
											Red_x = Red + Red_x;
											if ((uint16)Red_x < 0x64u) {
												if (v39 >= clip_x && v39 < clip_x1 && *spritePixelsP)
													*dest1P = *spritePixelsP;
												++dest1P;
												++spritePixelsP;
												++v39;
											} else {
												Red_x = Red_x - 100;
												++spritePixelsP;
											}
											--v38;
										} while (v38);
										spriteHeight2 = v64;
										dest1P = nbrligne2 + v54;
									} else {
										Red_y = Red_y - 100;
										spritePixelsP += Largeur;
									}
									--spriteHeight2;
								} while (spriteHeight2);
							}
						}
					} else {
						Largeur = spriteWidth;
						Compteur_y = 0;
						if (modeFlag) {
							dest2P = spriteWidth + dest1P;
							spec_largeur = spriteWidth;
							if (clip_y) {
								if ((uint16)clip_y >= (unsigned int)spriteHeight1)
									return;
								spritePixelsP += spriteWidth * (uint16)clip_y;
								dest2P += nbrligne2 * (uint16)clip_y;
								spriteHeight1 -= (uint16)clip_y;
							}
							xLeft = (uint16)clip_y1;
							if (spriteHeight1 > clip_y1)
								spriteHeight1 = clip_y1;
							xLeft = clip_x;
							if (clip_x) {
								if (xLeft >= spriteWidth)
									return;
								spriteWidth -= xLeft;
							}
							if (spriteWidth > (uint16)clip_x1) {
								clippedWidth = spriteWidth - (uint16)clip_x1;
								spritePixelsP += clippedWidth;
								dest2P -= clippedWidth;
								spriteWidth = (uint16)clip_x1;
							}
							do {
								yCtr2 = spriteHeight1;
								destCopy2P = dest2P;
								spritePixelsCopy2P = spritePixelsP;
								xCtr2 = spriteWidth;
								do {
									if (*spritePixelsP)
										*dest2P = *spritePixelsP;
									++spritePixelsP;
									--dest2P;
									--xCtr2;
								} while (xCtr2);
								spritePixelsP = spec_largeur + spritePixelsCopy2P;
								dest2P = nbrligne2 + destCopy2P;
								spriteHeight1 = yCtr2 - 1;
							} while (yCtr2 != 1);
						} else {
							spec_largeur = spriteWidth;
							if (clip_y) {
								if ((uint16)clip_y >= (unsigned int)spriteHeight1)
									return;
								spritePixelsP += spriteWidth * (uint16)clip_y;
								dest1P += nbrligne2 * (uint16)clip_y;
								spriteHeight1 -= (uint16)clip_y;
							}
							if (spriteHeight1 > clip_y1)
								spriteHeight1 = clip_y1;
							if (clip_x) {
								if ((uint16)clip_x >= spriteWidth)
									return;
								spritePixelsP += (uint16)clip_x;
								dest1P += (uint16)clip_x;
								spriteWidth -= (uint16)clip_x;
							}
							if (spriteWidth > (uint16)clip_x1)
								spriteWidth = (uint16)clip_x1;
							do {
								yCtr1 = spriteHeight1;
								dest1CopyP = dest1P;
								spritePixelsCopyP = spritePixelsP;
								xCtr1 = spriteWidth;
								do {
									if (*spritePixelsP)
										*dest1P = *spritePixelsP;
									++dest1P;
									++spritePixelsP;
									--xCtr1;
								} while (xCtr1);
								spritePixelsP = spec_largeur + spritePixelsCopyP;
								dest1P = nbrligne2 + dest1CopyP;
								spriteHeight1 = yCtr1 - 1;
							} while (yCtr1 != 1);
						}
					}
				}
			}
		}
	}
}

// Display Speed
void GraphicsManager::AFFICHE_SPEED(const byte *spriteData, int xp, int yp, int spriteIndex) {
	int width, height;

	width = _vm->_objectsManager.Get_Largeur(spriteData, spriteIndex);
	height = _vm->_objectsManager.Get_Hauteur(spriteData, spriteIndex);
	if (*spriteData == 78) {
		Affiche_Perfect(VESA_SCREEN, spriteData, xp + 300, yp + 300, spriteIndex, 0, 0, 0);
		Affiche_Perfect(VESA_BUFFER, spriteData, xp + 300, yp + 300, spriteIndex, 0, 0, 0);
	} else {
		Sprite_Vesa(VESA_BUFFER, spriteData, xp + 300, yp + 300, spriteIndex);
		Sprite_Vesa(VESA_SCREEN, spriteData, xp + 300, yp + 300, spriteIndex);
	}
	if (!_vm->_globals.NO_VISU)
		Ajoute_Segment_Vesa(xp, yp, xp + width, yp + height);
}

void GraphicsManager::SCOPY(const byte *surface, int x1, int y1, int width, int height, byte *destSurface, int destX, int destY) {
	int top; 
	int croppedWidth; 
	int croppedHeight;
	int xRight; 
	int height2; 
	int top2; 
	int left;

	left = x1;
	top = y1;
	croppedWidth = width;
	croppedHeight = height;
  
	if (x1 < min_x) {
		croppedWidth = width - (min_x - x1);
		left = min_x;
	}
	if (y1 < min_y) {
		croppedHeight = height - (min_y - y1);
		top = min_y;
	}
	top2 = top;
	if (top + croppedHeight > max_y)
		croppedHeight = max_y - top;
	xRight = left + croppedWidth;
	if (xRight > max_x)
		croppedWidth = max_x - left;

	if (croppedWidth > 0 && croppedHeight > 0) {
		height2 = croppedHeight;
		Copy_Mem(surface, left, top2, croppedWidth, croppedHeight, destSurface, destX, destY);
		Ajoute_Segment_Vesa(left, top2, left + croppedWidth, top2 + height2);
	}
}

void GraphicsManager::Copy_Mem(const byte *srcSurface, int x1, int y1, unsigned int width, int height, byte *destSurface, int destX, int destY) {
	const byte *srcP; 
	byte *destP; 
	int yp;
	int yCurrent;
	byte *dest2P;
	const byte *src2P;
	unsigned int pitch; 

	srcP = x1 + nbrligne2 * y1 + srcSurface;
	destP = destX + nbrligne2 * destY + destSurface;
	yp = height;
	do {
		yCurrent = yp;
		memcpy(destP, srcP, 4 * (width >> 2));
		src2P = (srcP + 4 * (width >> 2));
		dest2P = (destP + 4 * (width >> 2));
		pitch = width - 4 * (width >> 2);
		memcpy(dest2P, src2P, pitch);
		destP = (dest2P + pitch + nbrligne2 - width);
		srcP = (src2P + pitch + nbrligne2 - width);
		yp = yCurrent - 1;
	} while (yCurrent != 1);
}

// Display Font
void GraphicsManager::Affiche_Fonte(byte *surface, const byte *spriteData, int xp, int yp, 
									int characterIndex, int colour) {
	const byte *spriteDataP;
	int i; 
	const byte *spriteSizeP; 
	int spriteWidth; 
	int spriteHeight;
	const byte *spritePixelsP;
	byte *destP;
	int xCtr; 
	byte destByte;
	byte *destLineP;
	int yCtr;

	spriteDataP = spriteData + 3;
	for (i = characterIndex; i; --i)
		spriteDataP += READ_LE_UINT32(spriteDataP) + 16;

	spriteWidth = 0;
	spriteHeight = 0;
	spriteSizeP = spriteDataP + 4;
	spriteWidth = (int16)READ_LE_UINT16(spriteSizeP);
	spriteSizeP += 2;
	spriteHeight = (int16)READ_LE_UINT16(spriteSizeP);
	spritePixelsP = spriteSizeP + 10;
	destP = surface + xp + nbrligne2 * yp;
	Largeur = spriteWidth;

	do {
		yCtr = spriteHeight;
		destLineP = destP;
		xCtr = spriteWidth;
		do {
			destByte = *spritePixelsP;
			if (*spritePixelsP) {
				if (destByte == (byte)-4)
					destByte = colour;
				*destP = destByte;
			}
	      
			++destP;
			++spritePixelsP;
			--xCtr;
		} while (xCtr);
		destP = nbrligne2 + destLineP;
		spriteHeight = yCtr - 1;
	} while (yCtr != 1);
}

// Init Screen
void GraphicsManager::INI_ECRAN(const Common::String &file) {
	OPTI_INI(file, 0);
}

// Init Screen 2
void GraphicsManager::INI_ECRAN2(const Common::String &file) {
	OPTI_INI(file, 2);
}

void GraphicsManager::OPTI_INI(const Common::String &file, int mode) {
	int dataOffset; 
	int dataVal1; 
	bool doneFlag; 
	byte *ptr; 
	Common::String filename; 

	dataOffset = 1;
	
	filename = file + ".ini";
	ptr = _vm->_fileManager.RECHERCHE_CAT(filename, 1);
	if (ptr == g_PTRNUL) {
		_vm->_fileManager.CONSTRUIT_FICHIER(_vm->_globals.HOPLINK, filename);
		ptr = _vm->_fileManager.CHARGE_FICHIER(_vm->_globals.NFICHIER);
	}
	if (!mode) {
		filename = file + ".spr";
		if (g_PTRNUL != _vm->_globals.SPRITE_ECRAN)
			_vm->_globals.SPRITE_ECRAN = _vm->_fileManager.LIBERE_FICHIER(_vm->_globals.SPRITE_ECRAN);
		if (!_vm->_globals.NOSPRECRAN) {
			_vm->_globals.SPRITE_ECRAN = _vm->_fileManager.RECHERCHE_CAT(filename, 8);
			if (_vm->_globals.SPRITE_ECRAN) {
				_vm->_globals.CAT_FLAG = false;
				_vm->_fileManager.CONSTRUIT_FICHIER(_vm->_globals.HOPLINK, filename);
			} else {
				_vm->_globals.CAT_FLAG = true;
				_vm->_fileManager.CONSTRUIT_FICHIER(_vm->_globals.HOPLINK, "RES_SLI.RES");
			}
			_vm->_globals.SPRITE_ECRAN = _vm->_fileManager.CHARGE_FICHIER(_vm->_globals.NFICHIER);
			_vm->_globals.CAT_FLAG = false;
		}
	}
	if (*ptr != 'I' || *(ptr + 1) != 'N' || *(ptr + 2) != 'I') {
		error("Error, file not ini");
	} else {
		doneFlag = false;
		do {
			dataVal1 = _vm->_scriptManager.Traduction(ptr + 20 * dataOffset);
			if (_vm->shouldQuit())
				return;

			if (dataVal1 == 2)
				dataOffset =  _vm->_scriptManager.Control_Goto((ptr + 20 * dataOffset));
			if (dataVal1 == 3)
				dataOffset =  _vm->_scriptManager.Control_If(ptr, dataOffset);
			if (dataOffset == -1)
				error("Error, defective IFF");
			if (dataVal1 == 1 || dataVal1 == 4)
				++dataOffset;
			if (!dataVal1 || dataVal1 == 5)
				doneFlag = true;
		} while (!doneFlag);
	}
	_vm->_globals.dos_free2(ptr);
	if (mode != 1) {
		if (g_PTRNUL != _vm->_globals.COUCOU)
			_vm->_globals.COUCOU = _vm->_globals.dos_free2(_vm->_globals.COUCOU);
		
		filename = file + ".rep";
		byte *dataP = _vm->_fileManager.RECHERCHE_CAT(filename, 2);
		_vm->_globals.COUCOU = dataP;
		if (g_PTRNUL == dataP) {
			_vm->_fileManager.CONSTRUIT_FICHIER(_vm->_globals.HOPLINK, filename);
			dataP = _vm->_fileManager.CHARGE_FICHIER(_vm->_globals.NFICHIER);
			_vm->_globals.COUCOU = dataP;
		}
	}
	_vm->_objectsManager.FORCEZONE = 1;
	_vm->_objectsManager.CHANGEVERBE = 0;
}

void GraphicsManager::NB_SCREEN() {
	byte *destP; 
	const byte *srcP;

	if (!_vm->_globals.NECESSAIRE)
		INIT_TABLE(50, 65, Palette);
	if (nbrligne == SCREEN_WIDTH)
		Trans_bloc2(VESA_BUFFER, TABLE_COUL, 307200);
	if (nbrligne == 1280)
		Trans_bloc2(VESA_BUFFER, TABLE_COUL, 614400);
	_vm->_graphicsManager.DD_Lock();
	if (Winbpp == 2) {
		if (SDL_ECHELLE)
			m_scroll16A(VESA_BUFFER, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
		else
			m_scroll16(VESA_BUFFER, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
	}
	if (Winbpp == 1) {
		if (SDL_ECHELLE)
			m_scroll2A(VESA_BUFFER, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
		else
			m_scroll2(VESA_BUFFER, _vm->_eventsManager.start_x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
	}
	_vm->_graphicsManager.DD_Unlock();
	
	destP = VESA_SCREEN;
	srcP = VESA_BUFFER;
	memcpy(VESA_SCREEN, VESA_BUFFER, 0x95FFCu);
	srcP = srcP + 614396;
	destP = destP + 614396;
	*destP = *srcP;
	*(destP + 2) = *(srcP + 2);
	DD_VBL();
}

void GraphicsManager::SHOW_PALETTE() {
	setpal_vga256(Palette);
}

void GraphicsManager::videkey() {
	// Empty in original
}

void GraphicsManager::Copy_WinScan_Vbe(const byte *src, byte *dest) {
	int result; 
	int destOffset; 
	const byte *srcPtr; 
	byte byteVal; 

	result = 0;
	destOffset = 0;
	srcPtr = src;
	while (1) {
		byteVal = *srcPtr;
		if (*srcPtr < 0xFCu)
			goto Video_Cont_wVbe;
		if (byteVal == (byte)-4)
			return;
		if (byteVal == (byte)-3) {
			destOffset += *(srcPtr + 1);
			byteVal = *(srcPtr + 2);
			srcPtr += 2;
		} else if (byteVal == (byte)-2) {
			destOffset += READ_LE_UINT16(srcPtr + 1);
			byteVal = *(srcPtr + 3);
			srcPtr += 3;
		} else {
			destOffset += READ_LE_UINT32(srcPtr + 1);
			byteVal = *(srcPtr + 5);
			srcPtr += 5;
		}
Video_Cont_wVbe:
		*(dest + destOffset) = byteVal;
		++srcPtr;
		++destOffset;
	}
}

void GraphicsManager::Copy_Video_Vbe(const byte *src) {
	int destOffset; 
	const byte *srcP; 
	byte byteVal; 
	
	assert(VideoPtr);
	destOffset = 0;
	srcP = src;
	while (1) {
		byteVal = *srcP;
		if (*srcP < 0xFCu)
			goto Video_Cont_Vbe;
		if (byteVal == (byte)-4)
			return;
		if (byteVal == (byte)-3) {
			destOffset += *(srcP + 1);
			byteVal = *(srcP + 2);
			srcP += 2;
		} else if (byteVal == (byte)-2) {
			destOffset += READ_LE_UINT16(srcP + 1);
			byteVal = *(srcP + 3);
			srcP += 3;
		} else {
			destOffset += READ_LE_UINT32(srcP + 1);
			byteVal = *(srcP + 5);
			srcP += 5;
		}
Video_Cont_Vbe:
		*((byte *)VideoPtr->pixels + destOffset) = byteVal;
		++srcP;
		++destOffset;
	}
}

// Reduce Screen
void GraphicsManager::Reduc_Ecran(const byte *srcSurface, byte *destSurface, int xp, int yp, int width, int height, int zoom) {
	const byte *srcP; 
	byte *destP; 
	const byte *lineSrcP;

	srcP = xp + nbrligne2 * yp + srcSurface;
	destP = destSurface;
	Red = zoom;
	Largeur = width;
	Red_x = 0;
	Red_y = 0;
	if (zoom < 100) {
		Reduc_Ecran_L = Asm_Reduc(width, Red);
		Reduc_Ecran_H = Asm_Reduc(height, Red);

		for (int yCtr = 0; yCtr < height; ++yCtr, srcP += nbrligne2) {
			Red_y += Red;
			if (Red_y < 100) {
				Red_x = 0;
				lineSrcP = srcP;

				for (int xCtr = 0; xCtr < Largeur; ++xCtr) {
					Red_x += Red;
					if (Red_x < 100) {
						*destP++ = *lineSrcP++;
					} else {
						Red_x -= 100;
						++lineSrcP;
					}
				}
			} else {
				Red_y -= 100;
			}
		}
	}
}

void GraphicsManager::Plot_Hline(byte *surface, int xp, int yp, unsigned int width, byte col) {
	memset(surface + xp + nbrligne2 * yp, col, width);
}

void GraphicsManager::Plot_Vline(byte *surface, int xp, int yp, int height, byte col) {
	byte *destP;
	int yCtr;

	destP = surface + xp + nbrligne2 * yp;
	yCtr = height;
	do {
		*destP = col;
		destP += nbrligne2;
		--yCtr;
	} while (yCtr);
}


} // End of namespace Hopkins