1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #define FORBIDDEN_SYMBOL_EXCEPTION_printf
24 #define FORBIDDEN_SYMBOL_EXCEPTION_abort
25 
26 #include <malloc.h>
27 
28 #include <gxflux/gfx_con.h>
29 
30 #include "common/config-manager.h"
31 #include "graphics/conversion.h"
32 #include "backends/fs/wii/wii-fs-factory.h"
33 
34 #include "osystem.h"
35 
36 #define ROUNDUP(x,n) (-(-(x) & -(n)))
37 #define MAX_FPS 30
38 #define TLUT_GAME GX_TLUT0
39 #define TLUT_MOUSE GX_TLUT1
40 
41 static const OSystem::GraphicsMode _supportedGraphicsModes[] = {
42 	{
43 		"default",
44 		"Default",
45 		OSystem_Wii::gmStandard
46 	},
47 	{
48 		"defaultbilinear",
49 		"Default, bilinear filtering",
50 		OSystem_Wii::gmStandardFiltered
51 	},
52 	{
53 		"ds",
54 		"Double-strike",
55 		OSystem_Wii::gmDoubleStrike
56 	},
57 	{
58 		"dsbilinear",
59 		"Double-strike, bilinear filtering",
60 		OSystem_Wii::gmDoubleStrikeFiltered
61 	},
62 	{ 0, 0, 0 }
63 };
64 
initGfx()65 void OSystem_Wii::initGfx() {
66 	gfx_set_underscan(ConfMan.getInt("wii_video_default_underscan_x"),
67 						ConfMan.getInt("wii_video_default_underscan_y"));
68 
69 	_overlayWidth = gfx_video_get_width();
70 	_overlayHeight = gfx_video_get_height();
71 
72 #ifndef GAMECUBE
73 	if (CONF_GetAspectRatio() && _fullscreen)
74 		_overlayHeight = 400;
75 #endif
76 
77 	_overlaySize = _overlayWidth * _overlayHeight * 2;
78 	_overlayPixels = (uint16 *) memalign(32, _overlaySize);
79 
80 	memset(&_texMouse, 0, sizeof(gfx_tex_t));
81 	memset(&_texOverlay, 0, sizeof(gfx_tex_t));
82 	memset(&_texGame, 0, sizeof(gfx_tex_t));
83 
84 	_cursorPalette = (u16 *) malloc(256 * 2);
85 	if (!_cursorPalette) {
86 		printf("could not alloc palette buffer\n");
87 		::abort();
88 	}
89 
90 	memset(_cursorPalette, 0, 256 * 2);
91 
92 	if (!gfx_tex_init(&_texOverlay, GFX_TF_RGB5A3, 0,
93 						_overlayWidth, _overlayHeight)) {
94 		printf("could not init the overlay texture\n");
95 		::abort();
96 	}
97 
98 	gfx_coords(&_coordsOverlay, &_texOverlay, GFX_COORD_FULLSCREEN);
99 }
100 
deinitGfx()101 void OSystem_Wii::deinitGfx() {
102 	gfx_tex_deinit(&_texMouse);
103 	gfx_tex_deinit(&_texGame);
104 	gfx_tex_deinit(&_texOverlay);
105 
106 	free(_cursorPalette);
107 	_cursorPalette = NULL;
108 
109 	free(_gamePixels);
110 	_gamePixels = NULL;
111 
112 	free(_overlayPixels);
113 	_overlayPixels = NULL;
114 }
115 
updateScreenResolution()116 void OSystem_Wii::updateScreenResolution() {
117 	if (_overlayVisible) {
118 		_currentWidth = _overlayWidth;
119 		_currentHeight = _overlayHeight;
120 	} else {
121 		_currentWidth = _gameWidth;
122 		_currentHeight = _gameHeight;
123 	}
124 
125 	if (_currentWidth > 0)
126 		_currentXScale = f32(gfx_video_get_width()) / f32(_currentWidth);
127 	else
128 		_currentXScale = 1.0;
129 
130 	if (_currentHeight > 0)
131 		_currentYScale = f32(gfx_video_get_height()) / f32(_currentHeight);
132 	else
133 		_currentYScale = 1.0;
134 
135 	updateEventScreenResolution();
136 }
137 
switchVideoMode(int mode)138 void OSystem_Wii::switchVideoMode(int mode) {
139 	static const struct {
140 		gfx_video_mode_t mode;
141 		bool filter;
142 	} map[] = {
143 		{ GFX_MODE_DEFAULT, false },
144 		{ GFX_MODE_DEFAULT, true },
145 		{ GFX_MODE_DS, false },
146 		{ GFX_MODE_DS, true }
147 	};
148 
149 	if (_gameHeight > 240) {
150 		if (mode == gmDoubleStrike)
151 			mode = gmStandard;
152 		else if (mode == gmDoubleStrikeFiltered)
153 			mode = gmStandardFiltered;
154 	}
155 
156 	printf("switchVideoMode %d\n", mode);
157 
158 	if (map[_actualGraphicsMode].mode != map[mode].mode) {
159 		GXRModeObj obj;
160 
161 		gfx_video_deinit();
162 		gfx_video_get_modeobj(&obj, GFX_STANDARD_AUTO, map[mode].mode);
163 		gfx_video_init(&obj);
164 		gfx_init();
165 		gfx_con_init(NULL);
166 	}
167 
168 	_actualGraphicsMode = mode;
169 
170 	_bilinearFilter = map[mode].filter;
171 	gfx_tex_set_bilinear_filter(&_texGame, _bilinearFilter);
172 	gfx_tex_set_bilinear_filter(&_texMouse, _bilinearFilter);
173 
174 	u16 usx, usy;
175 	if (map[mode].mode == GFX_MODE_DS) {
176 		usx = ConfMan.getInt("wii_video_ds_underscan_x",
177 								Common::ConfigManager::kApplicationDomain);
178 		usy = ConfMan.getInt("wii_video_ds_underscan_y",
179 								Common::ConfigManager::kApplicationDomain);
180 	} else {
181 		usx = ConfMan.getInt("wii_video_default_underscan_x",
182 								Common::ConfigManager::kApplicationDomain);
183 		usy = ConfMan.getInt("wii_video_default_underscan_y",
184 								Common::ConfigManager::kApplicationDomain);
185 	}
186 
187 	gfx_set_underscan(usx, usy);
188 	gfx_coords(&_coordsOverlay, &_texOverlay, GFX_COORD_FULLSCREEN);
189 	gfx_coords(&_coordsGame, &_texGame, GFX_COORD_FULLSCREEN);
190 	updateScreenResolution();
191 }
192 
getSupportedGraphicsModes() const193 const OSystem::GraphicsMode* OSystem_Wii::getSupportedGraphicsModes() const {
194 	return _supportedGraphicsModes;
195 }
196 
getDefaultGraphicsMode() const197 int OSystem_Wii::getDefaultGraphicsMode() const {
198 	return gmStandard;
199 }
200 
setGraphicsMode(int mode)201 bool OSystem_Wii::setGraphicsMode(int mode) {
202 	_configGraphicsMode = mode;
203 	return true;
204 }
205 
getGraphicsMode() const206 int OSystem_Wii::getGraphicsMode() const {
207 	return _configGraphicsMode;
208 }
209 
210 #ifdef USE_RGB_COLOR
getScreenFormat() const211 Graphics::PixelFormat OSystem_Wii::getScreenFormat() const {
212 	return _pfGame;
213 }
214 
getSupportedFormats() const215 Common::List<Graphics::PixelFormat> OSystem_Wii::getSupportedFormats() const {
216 	Common::List<Graphics::PixelFormat> res;
217 	res.push_back(_pfRGB565);
218 	res.push_back(Graphics::PixelFormat::createFormatCLUT8());
219 
220 	return res;
221 }
222 #endif
223 
initSize(uint width,uint height,const Graphics::PixelFormat * format)224 void OSystem_Wii::initSize(uint width, uint height,
225 							const Graphics::PixelFormat *format) {
226 	bool update = false;
227 	gfx_tex_format_t tex_format;
228 
229 #ifdef USE_RGB_COLOR
230 	Graphics::PixelFormat newFormat;
231 
232 	if (format)
233 		newFormat = *format;
234 	else
235 		newFormat = Graphics::PixelFormat::createFormatCLUT8();
236 
237 	if (newFormat.bytesPerPixel > 2)
238 		newFormat = Graphics::PixelFormat::createFormatCLUT8();
239 
240 	if (_pfGame != newFormat) {
241 		_pfGame = newFormat;
242 		update = true;
243 	}
244 #endif
245 
246 	uint newWidth, newHeight;
247 	if (_pfGame.bytesPerPixel > 1) {
248 		newWidth = ROUNDUP(width, 4);
249 		newHeight = ROUNDUP(height, 4);
250 	} else {
251 		newWidth = ROUNDUP(width, 8);
252 		newHeight = ROUNDUP(height, 4);
253 	}
254 
255 	if (_gameWidth != newWidth || _gameHeight != newHeight) {
256 		assert((newWidth <= 640) && (newHeight <= 480));
257 
258 		if (width != newWidth || height != newHeight)
259 			printf("extending texture for compability: %ux%u -> %ux%u\n",
260 					width, height, newWidth, newHeight);
261 
262 		_gameWidth = newWidth;
263 		_gameHeight = newHeight;
264 		update = true;
265 	}
266 
267 	if (_gameRunning) {
268 		switchVideoMode(_configGraphicsMode);
269 
270 		if (_arCorrection && (_gameWidth == 320) && (_gameHeight == 200))
271 			gfx_set_ar(320.0 / 240.0);
272 		else
273 			gfx_set_ar(f32(_gameWidth) / f32(_gameHeight));
274 	}
275 
276 	if (update) {
277 		free(_gamePixels);
278 
279 		tex_format = GFX_TF_PALETTE_RGB565;
280 
281 #ifdef USE_RGB_COLOR
282 		if (_pfGame.bytesPerPixel > 1) {
283 			tex_format = GFX_TF_RGB565;
284 			_pfGameTexture = _pfRGB565;
285 		}
286 
287 		printf("initSize %u*%u*%u (%u%u%u -> %u%u%u match: %d)\n",
288 				_gameWidth, _gameHeight, _pfGame.bytesPerPixel * 8,
289 				8 - _pfGame.rLoss, 8 - _pfGame.gLoss, 8 - _pfGame.bLoss,
290 				8 - _pfGameTexture.rLoss, 8 - _pfGameTexture.gLoss,
291 				8 - _pfGameTexture.bLoss, _pfGame == _pfGameTexture);
292 
293 		_gamePixels = (u8 *) memalign(32, _gameWidth * _gameHeight *
294 										_pfGame.bytesPerPixel);
295 		memset(_gamePixels, 0, _gameWidth * _gameHeight *
296 				_pfGame.bytesPerPixel);
297 #else
298 		printf("initSize %u*%u\n", _gameWidth, _gameHeight);
299 
300 		_gamePixels = (u8 *) memalign(32, _gameWidth * _gameHeight);
301 		memset(_gamePixels, 0, _gameWidth * _gameHeight);
302 #endif
303 
304 		if (!gfx_tex_init(&_texGame, tex_format, TLUT_GAME,
305 					_gameWidth, _gameHeight)) {
306 			printf("could not init the game texture\n");
307 			::abort();
308 		}
309 
310 		gfx_tex_set_bilinear_filter(&_texGame, _bilinearFilter);
311 		gfx_coords(&_coordsGame, &_texGame, GFX_COORD_FULLSCREEN);
312 
313 		updateScreenResolution();
314 	}
315 }
316 
getWidth()317 int16 OSystem_Wii::getWidth() {
318 	return _gameWidth;
319 }
320 
getHeight()321 int16 OSystem_Wii::getHeight() {
322 	return _gameHeight;
323 }
324 
setPalette(const byte * colors,uint start,uint num)325 void OSystem_Wii::setPalette(const byte *colors, uint start, uint num) {
326 #ifdef USE_RGB_COLOR
327 	assert(_pfGame.bytesPerPixel == 1);
328 #endif
329 
330 	const byte *s = colors;
331 	u16 *d = _texGame.palette;
332 
333 	for (uint i = 0; i < num; ++i, s +=3)
334 		d[start + i] = Graphics::RGBToColor<Graphics::ColorMasks<565> >(s[0], s[1], s[2]);
335 
336 	gfx_tex_flush_palette(&_texGame);
337 
338 	s = colors;
339 	d = _cursorPalette;
340 
341 	for (uint i = 0; i < num; ++i, s += 3) {
342 		d[start + i] = Graphics::ARGBToColor<Graphics::ColorMasks<3444> >(0xff, s[0], s[1], s[2]);
343 	}
344 
345 	if (_cursorPaletteDisabled) {
346 		assert(_texMouse.palette);
347 
348 		memcpy((u8 *)_texMouse.palette + start * 2,
349 			(u8 *)_cursorPalette + start * 2, num * 2);
350 
351 		_cursorPaletteDirty = true;
352 	}
353 }
354 
grabPalette(byte * colors,uint start,uint num) const355 void OSystem_Wii::grabPalette(byte *colors, uint start, uint num) const {
356 #ifdef USE_RGB_COLOR
357 	assert(_pfGame.bytesPerPixel == 1);
358 #endif
359 
360 	u16 *s = _texGame.palette;
361 	byte *d = colors;
362 
363 	u8 r, g, b;
364 	for (uint i = 0; i < num; ++i, d += 3) {
365 		Graphics::colorToRGB<Graphics::ColorMasks<565> >(s[start + i], r, g, b);
366 		d[0] = r;
367 		d[1] = g;
368 		d[2] = b;
369 	}
370 }
371 
setCursorPalette(const byte * colors,uint start,uint num)372 void OSystem_Wii::setCursorPalette(const byte *colors, uint start, uint num) {
373 	if (!_texMouse.palette) {
374 		printf("switching to palette based cursor\n");
375 
376 		if (!gfx_tex_init(&_texMouse, GFX_TF_PALETTE_RGB5A3, TLUT_MOUSE,
377 							16, 16)) {
378 			printf("could not init the mouse texture\n");
379 			::abort();
380 		}
381 
382 		gfx_tex_set_bilinear_filter(&_texMouse, _bilinearFilter);
383 	}
384 
385 	if (_cursorPaletteDisabled) {
386 		memcpy(_cursorPalette, _texMouse.palette, 256 * 2);
387 		_cursorPaletteDisabled = false;
388 	}
389 
390 	const byte *s = colors;
391 	u16 *d = _texMouse.palette;
392 
393 	for (uint i = 0; i < num; ++i, s += 3)
394 		d[start + i] = Graphics::ARGBToColor<Graphics::ColorMasks<3444> >(0xff, s[0], s[1], s[2]);
395 
396 	_cursorPaletteDirty = true;
397 }
398 
copyRectToScreen(const void * buf,int pitch,int x,int y,int w,int h)399 void OSystem_Wii::copyRectToScreen(const void *buf, int pitch, int x, int y,
400 									int w, int h) {
401 	assert(x >= 0 && x < _gameWidth);
402 	assert(y >= 0 && y < _gameHeight);
403 	assert(w > 0 && x + w <= _gameWidth);
404 	assert(h > 0 && y + h <= _gameHeight);
405 
406 #ifdef USE_RGB_COLOR
407 	if (_pfGame.bytesPerPixel > 1) {
408 		if (!Graphics::crossBlit(_gamePixels +
409 									y * _gameWidth * _pfGame.bytesPerPixel +
410 									x * _pfGame.bytesPerPixel,
411 									(const byte *)buf, _gameWidth * _pfGame.bytesPerPixel,
412 									pitch, w, h, _pfGameTexture, _pfGame)) {
413 			printf("crossBlit failed\n");
414 			::abort();
415 		}
416 	} else {
417 #endif
418 		byte *dst = _gamePixels + y * _gameWidth + x;
419 		if (_gameWidth == pitch && pitch == w) {
420 			memcpy(dst, buf, h * w);
421 		} else {
422 			const byte *src = (const byte *)buf;
423 			do {
424 				memcpy(dst, src, w);
425 				src += pitch;
426 				dst += _gameWidth;
427 			} while (--h);
428 		}
429 #ifdef USE_RGB_COLOR
430 	}
431 #endif
432 
433 	_gameDirty = true;
434 }
435 
needsScreenUpdate()436 bool OSystem_Wii::needsScreenUpdate() {
437 	if (getMillis() - _lastScreenUpdate < 1000 / MAX_FPS)
438 		return false;
439 
440 	if (_gameRunning && _gameDirty)
441 		return true;
442 
443 	if (_overlayVisible && _overlayDirty)
444 		return true;
445 
446 	if (_mouseVisible && _texMouse.palette && _cursorPaletteDirty)
447 		return true;
448 
449 	return false;
450 }
451 
updateScreen()452 void OSystem_Wii::updateScreen() {
453 	static f32 ar;
454 	static gfx_screen_coords_t cc;
455 	static f32 csx, csy;
456 
457 	u32 now = getMillis();
458 	if (now - _lastScreenUpdate < 1000 / MAX_FPS)
459 		return;
460 
461 	if (!gfx_frame_start()) {
462 		printf("last frame not done!\n");
463 		return;
464 	}
465 
466 #ifdef DEBUG_WII_MEMSTATS
467 	wii_memstats();
468 #endif
469 
470 	_lastScreenUpdate = now;
471 
472 	if (_overlayVisible || _consoleVisible)
473 		gfx_set_colorop(COLOROP_SIMPLEFADE, gfx_color_none, gfx_color_none);
474 
475 	if (_gameRunning) {
476 		if (_gameDirty) {
477 			gfx_tex_convert(&_texGame, _gamePixels);
478 			_gameDirty = false;
479 		}
480 
481 		gfx_draw_tex(&_texGame, &_coordsGame);
482 	}
483 
484 	if (_overlayVisible) {
485 		if (!_consoleVisible)
486 			gfx_set_colorop(COLOROP_NONE, gfx_color_none, gfx_color_none);
487 
488 		if (_gameRunning)
489 			ar = gfx_set_ar(4.0 / 3.0);
490 
491 		if (_overlayDirty) {
492 			gfx_tex_convert(&_texOverlay, _overlayPixels);
493 			_overlayDirty = false;
494 		}
495 
496 		gfx_draw_tex(&_texOverlay, &_coordsOverlay);
497 	}
498 
499 	if (_mouseVisible) {
500 		if (_cursorDontScale) {
501 			csx = 1.0f / _currentXScale;
502 			csy = 1.0f / _currentYScale;
503 		} else {
504 			csx = 1.0f;
505 			csy = 1.0f;
506 		}
507 
508 		cc.x = f32(_mouseX - csx * _mouseHotspotX) * _currentXScale;
509 		cc.y = f32(_mouseY - csy * _mouseHotspotY) * _currentYScale;
510 		cc.w = f32(_texMouse.width) * _currentXScale * csx;
511 		cc.h = f32(_texMouse.height) * _currentYScale * csy;
512 
513 		if (_texMouse.palette && _cursorPaletteDirty) {
514 			_texMouse.palette[_mouseKeyColor] = 0;
515 			gfx_tex_flush_palette(&_texMouse);
516 			_cursorPaletteDirty = false;
517 		}
518 
519 		gfx_draw_tex(&_texMouse, &cc);
520 	}
521 
522 	if (_consoleVisible)
523 		gfx_con_draw();
524 
525 	if (_overlayVisible && _gameRunning)
526 		gfx_set_ar(ar);
527 
528 	gfx_frame_end();
529 }
530 
lockScreen()531 Graphics::Surface *OSystem_Wii::lockScreen() {
532 	_surface.init(_gameWidth, _gameHeight,
533 #ifdef USE_RGB_COLOR
534 	              _gameWidth * _pfGame.bytesPerPixel, _gamePixels, _pfGame
535 #else
536 	              _gameWidth, _gamePixels, Graphics::PixelFormat::createFormatCLUT8()
537 #endif
538 	             );
539 
540 	return &_surface;
541 }
542 
unlockScreen()543 void OSystem_Wii::unlockScreen() {
544 	_gameDirty = true;
545 }
546 
setShakePos(int shakeXOffset,int shakeYOffset)547 void OSystem_Wii::setShakePos(int shakeXOffset, int shakeYOffset) {
548 	gfx_coords(&_coordsGame, &_texGame, GFX_COORD_FULLSCREEN);
549 	_coordsGame.x -= f32(shakeXOffset) * _currentXScale;
550 	_coordsGame.y -= f32(shakeYOffset) * _currentYScale;
551 }
552 
showOverlay()553 void OSystem_Wii::showOverlay() {
554 	_mouseX = _overlayWidth / 2;
555 	_mouseY = _overlayHeight / 2;
556 	_overlayVisible = true;
557 	updateScreenResolution();
558 	gfx_tex_set_bilinear_filter(&_texMouse, true);
559 }
560 
hideOverlay()561 void OSystem_Wii::hideOverlay() {
562 	_mouseX = _gameWidth / 2;
563 	_mouseY = _gameHeight / 2;
564 	_overlayVisible = false;
565 	updateScreenResolution();
566 	gfx_tex_set_bilinear_filter(&_texMouse, _bilinearFilter);
567 }
568 
clearOverlay()569 void OSystem_Wii::clearOverlay() {
570 	memset(_overlayPixels, 0, _overlaySize);
571 	_overlayDirty = true;
572 }
573 
grabOverlay(void * buf,int pitch)574 void OSystem_Wii::grabOverlay(void *buf, int pitch) {
575 	int h = _overlayHeight;
576 	uint16 *src = _overlayPixels;
577 	byte *dst = (byte *)buf;
578 
579 	do {
580 		memcpy(dst, src, _overlayWidth * sizeof(uint16));
581 		src += _overlayWidth;
582 		dst += pitch;
583 	} while (--h);
584 }
585 
copyRectToOverlay(const void * buf,int pitch,int x,int y,int w,int h)586 void OSystem_Wii::copyRectToOverlay(const void *buf, int pitch, int x,
587 									int y, int w, int h) {
588 	const byte *src = (const byte *)buf;
589 	if (x < 0) {
590 		w += x;
591 		src -= x * sizeof(uint16);
592 		x = 0;
593 	}
594 
595 	if (y < 0) {
596 		h += y;
597 		src -= y * pitch;
598 		y = 0;
599 	}
600 
601 	if (w > _overlayWidth - x)
602 		w = _overlayWidth - x;
603 
604 	if (h > _overlayHeight - y)
605 		h = _overlayHeight - y;
606 
607 	if (w <= 0 || h <= 0)
608 		return;
609 
610 	uint16 *dst = _overlayPixels + (y * _overlayWidth + x);
611 	if (_overlayWidth == (uint16)w && (uint16)pitch == _overlayWidth * sizeof(uint16)) {
612 		memcpy(dst, src, h * pitch);
613 	} else {
614 		do {
615 			memcpy(dst, src, w * sizeof(uint16));
616 			src += pitch;
617 			dst += _overlayWidth;
618 		} while (--h);
619 	}
620 
621 	_overlayDirty = true;
622 }
623 
getOverlayWidth()624 int16 OSystem_Wii::getOverlayWidth() {
625 	return _overlayWidth;
626 }
627 
getOverlayHeight()628 int16 OSystem_Wii::getOverlayHeight() {
629 	return _overlayHeight;
630 }
631 
getOverlayFormat() const632 Graphics::PixelFormat OSystem_Wii::getOverlayFormat() const {
633 	return Graphics::createPixelFormat<3444>();
634 }
635 
showMouse(bool visible)636 bool OSystem_Wii::showMouse(bool visible) {
637 	bool last = _mouseVisible;
638 	_mouseVisible = visible;
639 
640 	return last;
641 }
642 
warpMouse(int x,int y)643 void OSystem_Wii::warpMouse(int x, int y) {
644 	_mouseX = x;
645 	_mouseY = y;
646 }
647 
setMouseCursor(const void * buf,uint w,uint h,int hotspotX,int hotspotY,uint32 keycolor,bool dontScale,const Graphics::PixelFormat * format)648 void OSystem_Wii::setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
649 									int hotspotY, uint32 keycolor,
650 									bool dontScale,
651 									const Graphics::PixelFormat *format) {
652 	gfx_tex_format_t tex_format = GFX_TF_PALETTE_RGB5A3;
653 	uint tw, th;
654 	bool tmpBuf = false;
655 	uint32 oldKeycolor = _mouseKeyColor;
656 
657 #ifdef USE_RGB_COLOR
658 	if (!format)
659 		_pfCursor = Graphics::PixelFormat::createFormatCLUT8();
660 	else
661 		_pfCursor = *format;
662 
663 	if (_pfCursor.bytesPerPixel > 1) {
664 		tex_format = GFX_TF_RGB5A3;
665 		_mouseKeyColor = keycolor & 0xffff;
666 		tw = ROUNDUP(w, 4);
667 		th = ROUNDUP(h, 4);
668 
669 		if (_pfCursor != _pfRGB3444)
670 			tmpBuf = true;
671 	} else {
672 #endif
673 		_mouseKeyColor = keycolor & 0xff;
674 		tw = ROUNDUP(w, 8);
675 		th = ROUNDUP(h, 4);
676 #ifdef USE_RGB_COLOR
677 	}
678 #endif
679 
680 	if (!gfx_tex_init(&_texMouse, tex_format, TLUT_MOUSE, tw, th)) {
681 		printf("could not init the mouse texture\n");
682 		::abort();
683 	}
684 
685 	gfx_tex_set_bilinear_filter(&_texMouse, _bilinearFilter);
686 
687 	if ((tw != w) || (th != h))
688 		tmpBuf = true;
689 
690 	if (!tmpBuf) {
691 		gfx_tex_convert(&_texMouse, (const byte *)buf);
692 	} else {
693 		u8 bpp = _texMouse.bpp >> 3;
694 		byte *tmp = (byte *) malloc(tw * th * bpp);
695 
696 		if (!tmp) {
697 			printf("could not alloc temp cursor buffer\n");
698 			::abort();
699 		}
700 
701 		if (bpp > 1)
702 			memset(tmp, 0, tw * th * bpp);
703 		else
704 			memset(tmp, _mouseKeyColor, tw * th);
705 
706 #ifdef USE_RGB_COLOR
707 		if (bpp > 1) {
708 			if (!Graphics::crossBlit(tmp, (const byte *)buf,
709 										tw * _pfRGB3444.bytesPerPixel,
710 										w * _pfCursor.bytesPerPixel,
711 										tw, th, _pfRGB3444, _pfCursor)) {
712 				printf("crossBlit failed (cursor)\n");
713 				::abort();
714 			}
715 
716 			// nasty, shouldn't the frontend set the alpha channel?
717 			const u16 *s = (const u16 *) buf;
718 			u16 *d = (u16 *) tmp;
719 			for (u16 y = 0; y < h; ++y) {
720 				for (u16 x = 0; x < w; ++x) {
721 					if (*s++ == _mouseKeyColor)
722 						*d++ &= ~(7 << 12);
723 					else
724 						d++;
725 				}
726 
727 				d += tw - w;
728 			}
729 		} else {
730 #endif
731 			byte *dst = tmp;
732 			const byte *src = (const byte *)buf;
733 			do {
734 				memcpy(dst, src, w * bpp);
735 				src += w * bpp;
736 				dst += tw * bpp;
737 			} while (--h);
738 #ifdef USE_RGB_COLOR
739 		}
740 #endif
741 
742 		gfx_tex_convert(&_texMouse, tmp);
743 		free(tmp);
744 	}
745 
746 	_mouseHotspotX = hotspotX;
747 	_mouseHotspotY = hotspotY;
748 	_cursorDontScale = dontScale;
749 
750 	if ((_texMouse.palette) && (oldKeycolor != _mouseKeyColor))
751 		_cursorPaletteDirty = true;
752 }
753