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

// Disable printf override in common/forbidden.h to avoid
// clashes with pspdebug.h from the PSP SDK.
// That header file uses
//   __attribute__((format(printf,1,2)));
// which gets messed up by our override mechanism; this could
// be avoided by either changing the PSP SDK to use the equally
// legal and valid
//   __attribute__((format(__printf__,1,2)));
// or by refining our printf override to use a varadic macro
// (which then wouldn't be portable, though).
// Anyway, for now we just disable the printf override globally
// for the PSP port
#define FORBIDDEN_SYMBOL_EXCEPTION_printf

#include <pspgu.h>
#include <pspkerneltypes.h>
#include <pspdisplay.h>
#include <psputils.h>

#include "common/scummsys.h"
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/display_client.h"
#include "backends/platform/psp/display_manager.h"
#define PSP_INCLUDE_SWAP
#include "backends/platform/psp/memory.h"

//#define __PSP_DEBUG_FUNCS__	/* For debugging the stack */
//#define __PSP_DEBUG_PRINT__
#include "backends/platform/psp/trace.h"

#define PSP_BUFFER_WIDTH (512)
#define	PSP_SCREEN_WIDTH	480
#define	PSP_SCREEN_HEIGHT	272
#define PSP_FRAME_SIZE (PSP_BUFFER_WIDTH * PSP_SCREEN_HEIGHT)

DisplayManager *GuRenderer::_displayManager = 0;


// class Palette ------------------------------------------------------------
//
void Palette::clear() {
	DEBUG_ENTER_FUNC();

	if (_values && _numOfEntries)
		memset(_values, 0, getSizeInBytes());

	PSP_DEBUG_PRINT("_values[%p]\n", _values);
}

// Used to clear the specific keycolor
//
void Palette::setColorPositionAlpha(uint32 position, bool alpha) {
	DEBUG_ENTER_FUNC();

	assert(_values);
	assert(position < _numOfEntries);

	PSP_DEBUG_PRINT("position[%d], numofEntries[%u], bpp[%u], values[%p]\n", position, _numOfEntries,
	                _pixelFormat.bitsPerPixel, _values);

	if (_numOfEntries <= 16)
		position &= 0xF;
	else if (_numOfEntries <= 256)
		position &= 0xFF;

	switch (_pixelFormat.bitsPerPixel) {
	case 16: {
		uint16 *shortVal = (uint16 *) & _values[_pixelFormat.pixelsToBytes(position)];
		*shortVal = _pixelFormat.setColorAlpha((uint32) * shortVal, alpha ? 255 : 0);
	}
	break;
	case 32: {
		uint32 *wordVal = (uint32 *) & _values[_pixelFormat.pixelsToBytes(position)];
		*wordVal = _pixelFormat.setColorAlpha((uint32) * wordVal, alpha ? 255 : 0);
	}
	break;
	default:
		PSP_ERROR("Incorrect bits per pixel value[%u]\n", _pixelFormat.bitsPerPixel);
	}
}

//	Set some of the palette to color values in array
//	By default, ScummVm doesn't support alpha values in palettes
void Palette::setPartial(const byte *colors, uint32 start, uint32 num, bool supportsAlpha /* = false */) {
	DEBUG_ENTER_FUNC();

	assert(_values);
	assert(_numOfEntries);

	const byte *src = colors;

	if (start + num > _numOfEntries)	// Check boundary
		num = _numOfEntries - start;

	if (_pixelFormat.bitsPerPixel == 16) {
		uint16 *palette = (uint16 *)_values;
		palette += start;

		for (uint32 i = 0; i < num; ++i) {
			byte alphaVal = supportsAlpha ? src[3] : 0xFF;
			*palette = (uint16)_pixelFormat.rgbaToColor(src[0], src[1], src[2], alphaVal);
			src += 3;
			palette++;
		}
	} else if (_pixelFormat.bitsPerPixel == 32) {
		uint32 *palette = (uint32 *)_values;
		palette += start;

		for (uint32 i = 0; i < num; ++i) {
			byte alphaVal = supportsAlpha ? src[3] : 0xFF;
			*palette = _pixelFormat.rgbaToColor(src[0], src[1], src[2], alphaVal);
			src += 3;
			palette++;
		}
	}
}

// Sets pixel format and number of entries by the buffer's pixel format */
void Palette::setPixelFormats(PSPPixelFormat::Type paletteType, PSPPixelFormat::Type bufferType, bool swapRedBlue /* = false */) {
	DEBUG_ENTER_FUNC();

	if (paletteType == PSPPixelFormat::Type_Unknown)
		PSP_ERROR("Unknown paletteType[%u]\n", paletteType);

	switch (bufferType) {
	case PSPPixelFormat::Type_Palette_8bit:
		_numOfEntries = 256;
		break;
	case PSPPixelFormat::Type_Palette_4bit:
		_numOfEntries = 16;
		break;
	case PSPPixelFormat::Type_Unknown:
	case PSPPixelFormat::Type_None:
		PSP_ERROR("Unhandled bufferType[%u]\n", bufferType);
		break;
	default:		// No palette
		_numOfEntries = 0;
		break;
	}

	_pixelFormat.set(paletteType, swapRedBlue);
}

bool Palette::allocate() {
	DEBUG_ENTER_FUNC();
	PSP_DEBUG_PRINT("_numOfEntries[%u]\n", _numOfEntries);
	PSP_DEBUG_PRINT("_pixelFormat: format[%u], bpp[%u]\n", _pixelFormat.format, _pixelFormat.bitsPerPixel);

	if (_values) {
		free(CACHED(_values));
		_values = 0;
	}

	// We allocate on 64bytes to get a cache line, and round up to 64bytes to get the full line
	uint32 amountInBytes = getSizeInBytes();
	if (amountInBytes < 64)
		amountInBytes = 64;
	_values = (byte *)memalign(64, amountInBytes);

	// Use uncached memory
	GuRenderer::cacheInvalidate(_values, amountInBytes);
	_values = UNCACHED(_values);

	if (!_values) {
		PSP_ERROR("Couldn't allocate palette.\n");
		return false;
	}

	PSP_DEBUG_PRINT("_values[%p]\n", _values);
	clear();

	return true;
}

void Palette::deallocate() {
	DEBUG_ENTER_FUNC();

	free(CACHED(_values));
	_values = 0;
	_numOfEntries = 0;
}

// Copy some of the palette to an array of colors
//
void Palette::getPartial(byte *colors, uint start, uint num) {
	DEBUG_ENTER_FUNC();

	assert(_values);
	assert(_numOfEntries);

	uint32 r, g, b, a;

	if (start + num > _numOfEntries)	// Check boundary
		num = _numOfEntries - start;

	if (_pixelFormat.bitsPerPixel == 16) {
		uint16 *palette = (uint16 *)_values;
		palette += start;

		for (uint32 i = start; i < start + num; i++) {
			_pixelFormat.colorToRgba(*palette, r, g, b, a);

			*colors++ = (byte)r;
			*colors++ = (byte)g;
			*colors++ = (byte)b;
			palette++;
		}
	} else if (_pixelFormat.bitsPerPixel == 32) {
		uint32 *palette = (uint32 *)_values;
		palette += start;

		for (uint32 i = start; i < start + num; i++) {
			_pixelFormat.colorToRgba(*palette, r, g, b, a);

			*colors++ = (byte)r;
			*colors++ = (byte)g;
			*colors++ = (byte)b;
			palette++;
		}
	}
}

void Palette::setSingleColorRGBA(uint32 num, byte r, byte g, byte b, byte a) {
	// DEBUG_ENTER_FUNC();
	uint16 *shortValues;
	uint32 *wordValues;

	assert(_values);
	assert(num < _numOfEntries);

	switch (_pixelFormat.bitsPerPixel) {
	case 16:
		shortValues = (uint16 *)_values;
		shortValues[num] = _pixelFormat.rgbaToColor(r, g, b, a);
		break;
	case 32:
		wordValues = (uint32 *)_values;
		wordValues[num] = _pixelFormat.rgbaToColor(r, g, b, a);
		break;
	default:
		PSP_ERROR("Incorrect bitsPerPixel[%d]\n", _pixelFormat.bitsPerPixel);
		break;
	}
}

// Print to screen
void Palette::print(uint32 numToPrint /* = 0 */) {
	if (_numOfEntries > 0) {
		assert(_values);

		if (numToPrint > _numOfEntries || numToPrint == 0)
			numToPrint = _numOfEntries;

		PSP_INFO_PRINT("cursor palette:\n");

		for (unsigned int i = 0; i < numToPrint; i++) {
			byte *pcolor = &_values[_pixelFormat.pixelsToBytes(i)];
			uint32 color = _pixelFormat.getColorValueAt(pcolor);

			PSP_INFO_PRINT("[%u=%x] ", i, color);
		}

		PSP_INFO_PRINT("\n");
	}
}

uint32 Palette::getRawColorAt(uint32 position) {
	byte *pcolor = &_values[_pixelFormat.pixelsToBytes(position)];
	uint32 color = _pixelFormat.getColorValueAt(pcolor);
	return color;
}

uint32 Palette::getRGBAColorAt(uint32 position) {
	uint32 color = getRawColorAt(position);
	uint32 r, g, b, a;
	_pixelFormat.colorToRgba(color, r, g, b, a);
	return (a << 24 | b << 16 | g << 8 |  r);
}

// class Buffer ---------------------------------------------------

void Buffer::setPixelFormat(PSPPixelFormat::Type type, bool swapRedBlue) {
	if (type == PSPPixelFormat::Type_None ||
	        type == PSPPixelFormat::Type_Unknown)
		PSP_ERROR("Unhandled buffer format[%u]\n", type);

	_pixelFormat.set(type, swapRedBlue);
}

bool Buffer::hasPalette() {
	if (_pixelFormat.format == PSPPixelFormat::Type_Palette_8bit ||
	        _pixelFormat.format == PSPPixelFormat::Type_Palette_4bit)
		return true;

	return false;
}

/* pitch is in bytes */
void Buffer::copyFromArray(const byte *buffer, int pitch) {
	DEBUG_ENTER_FUNC();

	// We use sourceSize because outside, they won't know what the true size is
	copyFromRect(buffer, pitch, 0, 0, _sourceSize.width, _sourceSize.height);
}

/* pitch is in bytes */
void Buffer::copyFromRect(const byte *buf, uint32 pitch, int destX, int destY, uint32 recWidth, uint32 recHeight) {
	// Removed silly clipping code
	DEBUG_ENTER_FUNC();
	assert(_pixels);

	if (recWidth > _sourceSize.width - destX) {
		recWidth = _sourceSize.width - destX;
	}

	if (recHeight > _sourceSize.height - destY) {
		recHeight = _sourceSize.height - destY;
	}

	if (recWidth <= 0 || recHeight <= 0) {
		return;
	}

	byte *dst = _pixels + _pixelFormat.pixelsToBytes((destY * _width) + destX);

	uint32 recWidthInBytes = _pixelFormat.pixelsToBytes(recWidth);
	uint32 realWidthInBytes = _pixelFormat.pixelsToBytes(_width);

	if (pitch == realWidthInBytes && pitch == recWidthInBytes) {
		//memcpy(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth));
		if (_pixelFormat.swapRB)
			PspMemorySwap::fastSwap(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth), _pixelFormat);
		else
			PspMemory::fastCopy(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth));
	} else {
		do {
			if (_pixelFormat.swapRB)
				PspMemorySwap::fastSwap(dst, buf, recWidthInBytes, _pixelFormat);
			else
				PspMemory::fastCopy(dst, buf, recWidthInBytes);
			buf += pitch;
			dst += realWidthInBytes;
		} while (--recHeight);
	}
}

/* pitch is in bytes */
void Buffer::copyToArray(byte *dst, int pitch) {
	DEBUG_ENTER_FUNC();
	assert(_pixels);

	uint32 h = _height;
	byte *src = _pixels;
	uint32 sourceWidthInBytes = _pixelFormat.pixelsToBytes(_sourceSize.width);
	uint32 realWidthInBytes = _pixelFormat.pixelsToBytes(_width);

	do {
		//memcpy(dst, src, sourceWidthInBytes);
		if (_pixelFormat.swapRB)
			PspMemorySwap::fastSwap(dst, src, sourceWidthInBytes, _pixelFormat);
		else
			PspMemory::fastCopy(dst, src, sourceWidthInBytes);
		src += realWidthInBytes;
		dst += pitch;
	} while (--h);
}

void Buffer::setSize(uint32 width, uint32 height, HowToSize textureOrSource/*=kSizeByTextureSize*/) {
	DEBUG_ENTER_FUNC();

	// We can size the buffer either by texture size (multiple of 2^n) or source size.
	// At higher sizes, increasing the texture size to 2^n is a waste of space. At these sizes kSizeBySourceSize should be used.

	_sourceSize.width = width;
	_sourceSize.height = height;

	_textureSize.width = scaleUpToPowerOfTwo(width);		// can only scale up to 512
	_textureSize.height = scaleUpToPowerOfTwo(height);

	if (textureOrSource == kSizeByTextureSize) {
		_width = _textureSize.width;
		_height = _textureSize.height;
	} else { // sizeBySourceSize
		_width =  _sourceSize.width;
		_height = _sourceSize.height;

		// adjust allocated width to be divisible by 32.
		// The GU can only handle multiples of 16 bytes. A 4 bit image x 32 will give us 16 bytes
		// We don't necessarily know the depth of the pixels here. So just make it divisible by 32.
		uint32 checkDiv = _width & 31;
		if (checkDiv)
			_width += 32 - checkDiv;
	}

	PSP_DEBUG_PRINT("width[%u], height[%u], texW[%u], texH[%u], sourceW[%d], sourceH[%d] %s\n", _width, _height, _textureSize.width, _textureSize.height, _sourceSize.width, _sourceSize.height, textureOrSource ? "size by source" : "size by texture");
}

// Scale a dimension (width/height) up to power of 2 for the texture
// Will only go up to 512 since that's the maximum PSP texture size
uint32 Buffer::scaleUpToPowerOfTwo(uint32 size) {

	uint32 textureDimension = 16;
	while (size > textureDimension && textureDimension < 512)
		textureDimension <<= 1;

	PSP_DEBUG_PRINT("size[%u]. power of 2[%u]\n", size, textureDimension);

	return textureDimension;
}

bool Buffer::allocate(bool inVram/*=false*/) {
	DEBUG_ENTER_FUNC();

	PSP_DEBUG_PRINT("_width[%u], _height[%u]\n", _width, _height);
	PSP_DEBUG_PRINT("_pixelFormat: format[%u], bpp[%u]\n", _pixelFormat.format, _pixelFormat.bitsPerPixel);

	if (_pixels) {
		if (VramAllocator::isAddressInVram(_pixels)) 	// Check if in VRAM
			VramAllocator::instance().deallocate(_pixels);
		else	// not in VRAM
			free(CACHED(_pixels));

		_pixels = 0;
	}

	uint32 size = getSizeInBytes();

	if (inVram) {
		_pixels = (byte *)VramAllocator::instance().allocate(size);
	}

	if (!_pixels) {	// Either we are not in vram or we didn't manage to allocate in vram
		// Align to 64 bytes. All normal buffer sizes are multiples of 64 anyway
		_pixels = (byte *)memalign(64, size);
	}

	if (!_pixels) {
		PSP_ERROR("couldn't allocate buffer.\n");
		return false;
	}

	// Use uncached memory
	GuRenderer::cacheInvalidate(_pixels, size);
	_pixels = UNCACHED(_pixels);

	clear();
	return true;
}

void Buffer::deallocate() {
	DEBUG_ENTER_FUNC();

	if (!_pixels)
		return;

	if (VramAllocator::isAddressInVram(_pixels)) 	// Check if in VRAM
		VramAllocator::instance().deallocate(_pixels);
	else
		free(CACHED(_pixels));

	_pixels = 0;
}

void Buffer::clear() {
	DEBUG_ENTER_FUNC();

	if (_pixels)
		memset(_pixels, 0, getSizeInBytes());
}

/* Convert 4 bit images to match weird PSP format */
void Buffer::flipNibbles() {
	DEBUG_ENTER_FUNC();

	if (_pixelFormat.bitsPerPixel != 4)
		return;

	assert(_pixels);

	uint32 *dest = (uint32 *)_pixels;

	for (uint32 i = 0; i < getSourceHeight(); i++) {
		for (uint32 j = 0; j < (getWidth() >> 3); j++) {	// /8 because we do it in 32bit chunks
			uint32 val = *dest;
			*dest++ = ((val >> 4) & 0x0F0F0F0F) | ((val << 4) & 0xF0F0F0F0);
		}
	}
}

// Print buffer contents to screen (only source size is printed out)
void Buffer::print(uint32 mask, uint32 numToPrint /*=0*/) {
	assert(_pixels);

	if (numToPrint > _sourceSize.width * _sourceSize.height || numToPrint == 0)
		numToPrint = _sourceSize.width * _sourceSize.height;

	PSP_INFO_PRINT("buffer: \n");
	PSP_INFO_PRINT("width[%u], height[%u]\n\n", _sourceSize.width, _sourceSize.height);

	for (unsigned int i = 0; i < _sourceSize.height; i++) {
		for (unsigned int j = 0; j < _sourceSize.width; j++) {
			if (numToPrint <= 0)	// check if done
				break;

			byte *pcolor = &_pixels[_pixelFormat.pixelsToBytes((i * _width) + j)];
			uint32 color = _pixelFormat.getColorValueAt(pcolor);

			//if (color != 0) PSP_INFO_PRINT("[%x] ", color);
			PSP_INFO_PRINT("[%x] ", mask & color);

			numToPrint--;
		}
		PSP_INFO_PRINT("\n");
	}
	PSP_INFO_PRINT("\n");
}

// class GuRenderer -------------------------------------------------
//#define __PSP_DEBUG_FUNCS__	/* For debugging the stack */
//#define __PSP_DEBUG_PRINT__

#include "backends/platform/psp/trace.h"


void GuRenderer::render() {
	DEBUG_ENTER_FUNC();
	PSP_DEBUG_PRINT("Buffer[%p] Palette[%p]\n", _buffer->getPixels(), _palette->getRawValues());

	guProgramDrawBehavior();

	if (_buffer->hasPalette())
		guLoadPalette();

	guProgramTextureFormat();

	// Loop over patches of 512x512 pixel textures and draw them
	for (uint32 j = 0; j < _buffer->getSourceHeight(); j += 512) {
		_textureLoadOffset.y = j;

		for (uint32 i = 0; i < _buffer->getSourceWidth(); i += 512) {
			_textureLoadOffset.x = i;

			guLoadTexture();
			Vertex *vertices = guGetVertices();
			fillVertices(vertices);

			guDrawVertices(vertices);
		}
	}
}

inline void GuRenderer::guProgramDrawBehavior() {
	DEBUG_ENTER_FUNC();
	PSP_DEBUG_PRINT("blending[%s] colorTest[%s] reverseAlpha[%s] keyColor[%u]\n",
		_blending ? "on" : "off", _colorTest ? "on" : "off",
		_alphaReverse ? "on" : "off", _keyColor);

	if (_blending) {
		sceGuEnable(GU_BLEND);

		if (_alphaReverse)	// Reverse the alpha value (ie. 0 is 1) easier to do in some cases
			sceGuBlendFunc(GU_ADD, GU_ONE_MINUS_SRC_ALPHA, GU_SRC_ALPHA, 0, 0);
		else				// Normal alpha values
			sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);

	} else
		sceGuDisable(GU_BLEND);

	if (_colorTest) {
		sceGuEnable(GU_COLOR_TEST);
		sceGuColorFunc(GU_NOTEQUAL, 	// show only colors not equal to this color
					   _keyColor,
					   0x00ffffff);		// match everything but alpha
	} else
		sceGuDisable(GU_COLOR_TEST);
}

inline void GuRenderer::guLoadPalette() {
	DEBUG_ENTER_FUNC();

	uint32 mask;

	if (_buffer->getBitsPerPixel() == 4)
		mask = 0x0F;
	else if (_buffer->getBitsPerPixel() == 8)
		mask = 0xFF;
	else
		assert(0);	/* error */

	PSP_DEBUG_PRINT("numOfEntries[%d]\n", _palette->getNumOfEntries());
	PSP_DEBUG_PRINT("bpp[%d], pixelformat[%d], mask[%x]\n", _buffer->getBitsPerPixel(), _palette->getPixelFormat(), mask);

	sceGuClutMode(convertToGuPixelFormat(_palette->getPixelFormat()), 0, mask, 0);
	sceGuClutLoad(_palette->getNumOfEntries() >> 3, 	// it's in batches of 8 for some reason
				  _palette->getRawValues());
}

inline void GuRenderer::guProgramTextureFormat() {
	DEBUG_ENTER_FUNC();
	PSP_DEBUG_PRINT("pixelFormat[%d]\n", _buffer->getPixelFormat());

	sceGuTexMode(convertToGuPixelFormat(_buffer->getPixelFormat()), 0, 0, 0);
}

inline uint32 GuRenderer::convertToGuPixelFormat(PSPPixelFormat::Type format) {
	DEBUG_ENTER_FUNC();

	uint32 guFormat = 0;

	switch (format) {
	case PSPPixelFormat::Type_4444:
		guFormat = GU_PSM_4444;
		break;
	case PSPPixelFormat::Type_5551:
		guFormat = GU_PSM_5551;
		break;
	case PSPPixelFormat::Type_5650:
		guFormat = GU_PSM_5650;
		break;
	case PSPPixelFormat::Type_8888:
		guFormat = GU_PSM_8888;
		break;
	case PSPPixelFormat::Type_Palette_8bit:
		guFormat = GU_PSM_T8;
		break;
	case PSPPixelFormat::Type_Palette_4bit:
		guFormat = GU_PSM_T4;
		break;
	default:
		break;
	}

	PSP_DEBUG_PRINT("Pixelformat[%d], guFormat[%d]\n", format, guFormat);

	return guFormat;
}

inline void GuRenderer::guLoadTexture() {
	DEBUG_ENTER_FUNC();

	byte *startPoint = _buffer->getPixels();
	if (_textureLoadOffset.x)
		startPoint += _buffer->_pixelFormat.pixelsToBytes(_textureLoadOffset.x);
	if (_textureLoadOffset.y)
		startPoint += _buffer->getWidthInBytes() * _textureLoadOffset.y;

	sceGuTexImage(0,
				_buffer->getTextureWidth(), 	// texture width (must be power of 2)
				_buffer->getTextureHeight(), 	// texture height (must be power of 2)
				_buffer->getWidth(),			// width of a line of the image (to get to the next line)
				startPoint);					// where to start reading
}

inline Vertex *GuRenderer::guGetVertices() {
	DEBUG_ENTER_FUNC();

	Vertex *ret = (Vertex *)sceGuGetMemory(2 * sizeof(Vertex));

	return ret;
}

// Fills the vertices. Most of the logic is here.
void GuRenderer::fillVertices(Vertex *vertices) {
	DEBUG_ENTER_FUNC();

	uint32 outputWidth = _displayManager->getOutputWidth();
	uint32 outputHeight = _displayManager->getOutputHeight();

	// Texture adjustments for eliminating half-pixel artifacts from scaling
	// Not necessary if we don't scale
	float textureFix = 0.0f;
	if (_useGlobalScaler &&
	        (_displayManager->getScaleX() != 1.0f || _displayManager->getScaleY() != 1.0f))
			textureFix = 0.5f;

	// These coordinates describe an area within the texture. ie. we already loaded a square of texture,
	// now the coordinates within it are 0 to the edge of the area of the texture we want to draw
	float textureStartX = textureFix + _offsetInBuffer.x;
	float textureStartY = textureFix + _offsetInBuffer.y;

	int textureLeftX = _drawSize.width - _textureLoadOffset.x;
	if (textureLeftX > 512)
		textureLeftX = 512;
	int textureLeftY = _drawSize.height - _textureLoadOffset.y;
	if (textureLeftY > 512)
		textureLeftY = 512;
	float textureEndX = -textureFix + _offsetInBuffer.x + textureLeftX;
	float textureEndY = -textureFix + _offsetInBuffer.y + textureLeftY;
	// For scaling to the final image size, calculate the gaps on both sides
	uint32 gapX = _useGlobalScaler ? (PSP_SCREEN_WIDTH - outputWidth) >> 1 : 0;
	uint32 gapY = _useGlobalScaler ? (PSP_SCREEN_HEIGHT - outputHeight) >> 1 : 0;

	// Save scaled offset on screen
	float scaledOffsetOnScreenX = scaleSourceToOutput(true, _offsetOnScreen.x);
	float scaledOffsetOnScreenY = scaleSourceToOutput(false, _offsetOnScreen.y);

	float imageStartX = gapX + scaledOffsetOnScreenX + (scaleSourceToOutput(true, stretch(true, _textureLoadOffset.x)));
	float imageStartY = gapY + scaledOffsetOnScreenY + (scaleSourceToOutput(false, stretch(false, _textureLoadOffset.y)));

	float imageEndX, imageEndY;

	imageEndX = imageStartX + scaleSourceToOutput(true, stretch(true, textureLeftX));
	imageEndY = imageStartY + scaleSourceToOutput(false, stretch(false, textureLeftY));

	vertices[0].u = textureStartX;
	vertices[0].v = textureStartY;
	vertices[1].u = textureEndX;
	vertices[1].v = textureEndY;

	vertices[0].x = imageStartX;
	vertices[0].y = imageStartY;
	vertices[0].z = 0;
	vertices[1].x = imageEndX;
	vertices[1].y = imageEndY;
	vertices[1].z = 0;

	PSP_DEBUG_PRINT("TextureStart: X[%f] Y[%f] TextureEnd: X[%.1f] Y[%.1f]\n", textureStartX, textureStartY, textureEndX, textureEndY);
	PSP_DEBUG_PRINT("ImageStart:   X[%f] Y[%f] ImageEnd:   X[%.1f] Y[%.1f]\n", imageStartX, imageStartY, imageEndX, imageEndY);
}

/* Scale the input X/Y offset to appear in proper position on the screen */
inline float GuRenderer::scaleSourceToOutput(bool x, float offset) {
	float result;

	if (!_useGlobalScaler)
		result = offset;
	else if (!offset)
		result = 0.0f;
	else
		result = x ? offset * _displayManager->getScaleX() : offset * _displayManager->getScaleY();

	return result;
}

/* Scale the input X/Y offset to appear in proper position on the screen */
inline float GuRenderer::stretch(bool x, float size) {
	if (!_stretch)
		return size;
	return (x ? size * _stretchX : size * _stretchY);
}

inline void GuRenderer::guDrawVertices(Vertex *vertices) {
	DEBUG_ENTER_FUNC();

	// This function shouldn't need changing. The '32' here refers to floating point vertices.
	sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, 2, 0, vertices);
}

void GuRenderer::cacheInvalidate(void *pointer, uint32 size) {
	sceKernelDcacheWritebackInvalidateRange(pointer, size);
}
