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_ALLOW_ALL
24 
25 #include <pspuser.h>
26 #include <pspgu.h>
27 #include <pspdisplay.h>
28 
29 #include <time.h>
30 #include <zlib.h>
31 
32 #include "common/config-manager.h"
33 #include "common/events.h"
34 #include "common/scummsys.h"
35 
36 #include "backends/platform/psp/psppixelformat.h"
37 #include "backends/platform/psp/osys_psp.h"
38 #include "backends/platform/psp/powerman.h"
39 #include "backends/platform/psp/rtc.h"
40 
41 #include "backends/saves/default/default-saves.h"
42 #include "backends/timer/default/default-timer.h"
43 #include "graphics/surface.h"
44 #include "audio/mixer_intern.h"
45 
46 //#define __PSP_DEBUG_FUNCS__	/* For debugging function calls */
47 //#define __PSP_DEBUG_PRINT__	/* For debug printouts */
48 
49 #include "backends/platform/psp/trace.h"
50 
51 #define	SAMPLES_PER_SEC	44100
52 
timer_handler(int t)53 static int timer_handler(int t) {
54 	DefaultTimerManager *tm = (DefaultTimerManager *)g_system->getTimerManager();
55 	tm->handler();
56 	return t;
57 }
58 
~OSystem_PSP()59 OSystem_PSP::~OSystem_PSP() {}
60 
61 #define PSP_SCREEN_WIDTH 480
62 #define PSP_SCREEN_HEIGHT 272
63 
64 #define PSP_DEFAULT_SAVE_PATH "ms0:/scummvm_savegames"
65 
initBackend()66 void OSystem_PSP::initBackend() {
67 	DEBUG_ENTER_FUNC();
68 
69 	ConfMan.registerDefault("aspect_ratio", false);
70 	ConfMan.registerDefault("gfx_mode", "Fit to Screen");
71 	ConfMan.registerDefault("kbdmouse_speed", 3);
72 	ConfMan.registerDefault("joystick_deadzone", 3);
73 
74 	// Instantiate real time clock
75 	PspRtc::instance();
76 
77 	_cursor.enableCursorPalette(false);
78 	_cursor.setXY(PSP_SCREEN_WIDTH >> 1, PSP_SCREEN_HEIGHT >> 1);	// Mouse in the middle of the screen
79 
80 	// Set pointers for display manager
81 	_displayManager.setCursor(&_cursor);
82 	_displayManager.setScreen(&_screen);
83 	_displayManager.setOverlay(&_overlay);
84 	_displayManager.setKeyboard(&_keyboard);
85 	_displayManager.setImageViewer(&_imageViewer);
86 	_displayManager.init();
87 
88 	// Set pointers for input handler
89 	_inputHandler.setCursor(&_cursor);
90 	_inputHandler.setKeyboard(&_keyboard);
91 	_inputHandler.setImageViewer(&_imageViewer);
92 	_inputHandler.init();
93 
94 	// Set pointers for image viewer
95 	_imageViewer.setInputHandler(&_inputHandler);
96 	_imageViewer.setDisplayManager(&_displayManager);
97 
98 	_savefileManager = new DefaultSaveFileManager(PSP_DEFAULT_SAVE_PATH);
99 
100 	_timerManager = new DefaultTimerManager();
101 
102 	PSP_DEBUG_PRINT("calling keyboard.load()\n");
103 	_keyboard.load();	// Load virtual keyboard files into memory
104 
105 	setTimerCallback(&timer_handler, 10);
106 
107 	setupMixer();
108 
109 	EventsBaseBackend::initBackend();
110 }
111 
112 // Let's us know an engine
engineDone()113 void OSystem_PSP::engineDone() {
114 	// for now, all we need is to reset the image number on the viewer
115 	_imageViewer.resetOnEngineDone();
116 }
117 
hasFeature(Feature f)118 bool OSystem_PSP::hasFeature(Feature f) {
119 	return (f == kFeatureOverlaySupportsAlpha || f == kFeatureCursorPalette ||
120 			f == kFeatureKbdMouseSpeed || f == kFeatureJoystickDeadzone);
121 }
122 
setFeatureState(Feature f,bool enable)123 void OSystem_PSP::setFeatureState(Feature f, bool enable) {
124 	if (f == kFeatureCursorPalette) {
125 		_pendingUpdate = false;
126 		_cursor.enableCursorPalette(enable);
127 	}
128 }
129 
getFeatureState(Feature f)130 bool OSystem_PSP::getFeatureState(Feature f) {
131 	if (f == kFeatureCursorPalette) {
132 		return _cursor.isCursorPaletteEnabled();
133 	}
134 	return false;
135 }
136 
getSupportedGraphicsModes() const137 const OSystem::GraphicsMode* OSystem_PSP::getSupportedGraphicsModes() const {
138 	return _displayManager.getSupportedGraphicsModes();
139 }
140 
getDefaultGraphicsMode() const141 int OSystem_PSP::getDefaultGraphicsMode() const {
142 	DEBUG_ENTER_FUNC();
143 	return _displayManager.getDefaultGraphicsMode();
144 }
145 
setGraphicsMode(int mode)146 bool OSystem_PSP::setGraphicsMode(int mode) {
147 	DEBUG_ENTER_FUNC();
148 	_displayManager.waitUntilRenderFinished();
149 	_pendingUpdate = false;
150 	return _displayManager.setGraphicsMode(mode);
151 }
152 
setGraphicsMode(const char * name)153 bool OSystem_PSP::setGraphicsMode(const char *name) {
154 	DEBUG_ENTER_FUNC();
155 	_displayManager.waitUntilRenderFinished();
156 	_pendingUpdate = false;
157 	return _displayManager.setGraphicsMode(name);
158 }
159 
getGraphicsMode() const160 int OSystem_PSP::getGraphicsMode() const {
161 	DEBUG_ENTER_FUNC();
162 	return _displayManager.getGraphicsMode();
163 }
164 
165 #ifdef USE_RGB_COLOR
166 
getScreenFormat() const167 Graphics::PixelFormat OSystem_PSP::getScreenFormat() const {
168 	return _screen.getScummvmPixelFormat();
169 }
170 
getSupportedFormats() const171 Common::List<Graphics::PixelFormat> OSystem_PSP::getSupportedFormats() const {
172 	return _displayManager.getSupportedPixelFormats();
173 }
174 
175 #endif
176 
initSize(uint width,uint height,const Graphics::PixelFormat * format)177 void OSystem_PSP::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
178 	DEBUG_ENTER_FUNC();
179 	_displayManager.waitUntilRenderFinished();
180 	_pendingUpdate = false;
181 	_displayManager.setSizeAndPixelFormat(width, height, format);
182 
183 	_cursor.setVisible(false);
184 	_cursor.setLimits(_screen.getWidth(), _screen.getHeight());
185 }
186 
getWidth()187 int16 OSystem_PSP::getWidth() {
188 	DEBUG_ENTER_FUNC();
189 	return (int16)_screen.getWidth();
190 }
191 
getHeight()192 int16 OSystem_PSP::getHeight() {
193 	DEBUG_ENTER_FUNC();
194 	return (int16)_screen.getHeight();
195 }
196 
setPalette(const byte * colors,uint start,uint num)197 void OSystem_PSP::setPalette(const byte *colors, uint start, uint num) {
198 	DEBUG_ENTER_FUNC();
199 	_displayManager.waitUntilRenderFinished();
200 	_pendingUpdate = false;
201 	_screen.setPartialPalette(colors, start, num);
202 	_cursor.setScreenPalette(colors, start, num);
203 	_cursor.clearKeyColor();
204 }
205 
setCursorPalette(const byte * colors,uint start,uint num)206 void OSystem_PSP::setCursorPalette(const byte *colors, uint start, uint num) {
207 	DEBUG_ENTER_FUNC();
208 	_displayManager.waitUntilRenderFinished();
209 	_pendingUpdate = false;
210 	_cursor.setCursorPalette(colors, start, num);
211 	_cursor.enableCursorPalette(true);
212 	_cursor.clearKeyColor();	// Do we need this?
213 }
214 
copyRectToScreen(const void * buf,int pitch,int x,int y,int w,int h)215 void OSystem_PSP::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
216 	DEBUG_ENTER_FUNC();
217 	_displayManager.waitUntilRenderFinished();
218 	_pendingUpdate = false;
219 	_screen.copyFromRect((const byte *)buf, pitch, x, y, w, h);
220 }
221 
lockScreen()222 Graphics::Surface *OSystem_PSP::lockScreen() {
223 	DEBUG_ENTER_FUNC();
224 	_displayManager.waitUntilRenderFinished();
225 	_pendingUpdate = false;
226 	return _screen.lockAndGetForEditing();
227 }
228 
unlockScreen()229 void OSystem_PSP::unlockScreen() {
230 	DEBUG_ENTER_FUNC();
231 	_pendingUpdate = false;
232 	// The screen is always completely updated anyway, so we don't have to force a full update here.
233 	_screen.unlock();
234 }
235 
updateScreen()236 void OSystem_PSP::updateScreen() {
237 	DEBUG_ENTER_FUNC();
238 	_pendingUpdate = !_displayManager.renderAll();	// if we didn't update, we have a pending update
239 }
240 
setShakePos(int shakeXOffset,int shakeYOffset)241 void OSystem_PSP::setShakePos(int shakeXOffset, int shakeYOffset) {
242 	DEBUG_ENTER_FUNC();
243 	_displayManager.waitUntilRenderFinished();
244 	_pendingUpdate = false;
245 	_screen.setShakePos(shakeXOffset, shakeYOffset);
246 }
247 
showOverlay()248 void OSystem_PSP::showOverlay() {
249 	DEBUG_ENTER_FUNC();
250 	_pendingUpdate = false;
251 	_overlay.setVisible(true);
252 	_cursor.setLimits(_overlay.getWidth(), _overlay.getHeight());
253 	_cursor.useGlobalScaler(false);	// mouse with overlay is 1:1
254 }
255 
hideOverlay()256 void OSystem_PSP::hideOverlay() {
257 	DEBUG_ENTER_FUNC();
258 	_pendingUpdate = false;
259 	_overlay.setVisible(false);
260 	_cursor.setLimits(_screen.getWidth(), _screen.getHeight());
261 	_cursor.useGlobalScaler(true);	// mouse needs to be scaled with screen
262 }
263 
clearOverlay()264 void OSystem_PSP::clearOverlay() {
265 	DEBUG_ENTER_FUNC();
266 	_displayManager.waitUntilRenderFinished();
267 	_pendingUpdate = false;
268 	_overlay.clearBuffer();
269 }
270 
grabOverlay(void * buf,int pitch)271 void OSystem_PSP::grabOverlay(void *buf, int pitch) {
272 	DEBUG_ENTER_FUNC();
273 	_overlay.copyToArray(buf, pitch);
274 }
275 
copyRectToOverlay(const void * buf,int pitch,int x,int y,int w,int h)276 void OSystem_PSP::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
277 	DEBUG_ENTER_FUNC();
278 	_displayManager.waitUntilRenderFinished();
279 	_pendingUpdate = false;
280 	_overlay.copyFromRect(buf, pitch, x, y, w, h);
281 }
282 
getOverlayWidth()283 int16 OSystem_PSP::getOverlayWidth() {
284 	return (int16)_overlay.getWidth();
285 }
286 
getOverlayHeight()287 int16 OSystem_PSP::getOverlayHeight() {
288 	return (int16)_overlay.getHeight();
289 }
290 
grabPalette(byte * colors,uint start,uint num) const291 void OSystem_PSP::grabPalette(byte *colors, uint start, uint num) const {
292 	DEBUG_ENTER_FUNC();
293 	_screen.getPartialPalette(colors, start, num);
294 }
295 
showMouse(bool v)296 bool OSystem_PSP::showMouse(bool v) {
297 	DEBUG_ENTER_FUNC();
298 	_pendingUpdate = false;
299 
300 	PSP_DEBUG_PRINT("%s\n", v ? "true" : "false");
301 	bool last = _cursor.isVisible();
302 	_cursor.setVisible(v);
303 
304 	return last;
305 }
306 
warpMouse(int x,int y)307 void OSystem_PSP::warpMouse(int x, int y) {
308 	DEBUG_ENTER_FUNC();
309 	_displayManager.waitUntilRenderFinished();
310 	_pendingUpdate = false;
311 	_cursor.setXY(x, y);
312 }
313 
setMouseCursor(const void * buf,uint w,uint h,int hotspotX,int hotspotY,uint32 keycolor,bool dontScale,const Graphics::PixelFormat * format)314 void OSystem_PSP::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
315 	DEBUG_ENTER_FUNC();
316 	_displayManager.waitUntilRenderFinished();
317 	_pendingUpdate = false;
318 
319 	PSP_DEBUG_PRINT("pbuf[%p], w[%u], h[%u], hotspot:X[%d], Y[%d], keycolor[%d], scale[%d], pformat[%p]\n", buf, w, h, hotspotX, hotspotY, keycolor, !dontScale, format);
320 	if (format) {
321 		PSP_DEBUG_PRINT("format: bpp[%d], rLoss[%d], gLoss[%d], bLoss[%d], aLoss[%d], rShift[%d], gShift[%d], bShift[%d], aShift[%d]\n", format->bytesPerPixel, format->rLoss, format->gLoss, format->bLoss, format->aLoss, format->rShift, format->gShift, format->bShift, format->aShift);
322 	}
323 
324 	_cursor.setKeyColor(keycolor);
325 	// TODO: The old target scale was saved but never used. Should the new
326 	// "do not scale" logic be implemented?
327 	//_cursor.setCursorTargetScale(cursorTargetScale);
328 	_cursor.setSizeAndScummvmPixelFormat(w, h, format);
329 	_cursor.setHotspot(hotspotX, hotspotY);
330 	_cursor.clearKeyColor();
331 	_cursor.copyFromArray((const byte *)buf);
332 }
333 
pollEvent(Common::Event & event)334 bool OSystem_PSP::pollEvent(Common::Event &event) {
335 
336 	// If we're polling for events, we should check for pausing the engine
337 	// Pausing the engine is a necessary fix for games that use the timer for music synchronization
338 	// 	recovering many hours later causes the game to crash. We're polling without mutexes since it's not critical to
339 	//  get it right now.
340 	PowerMan.pollPauseEngine();
341 
342 	// A hack:
343 	// Check if we have a pending update that we missed for some reason (FPS throttling for example)
344 	// Time between event polls is usually 5-10ms, so waiting for 4 calls before checking to update the screen should be fine
345 	if (_pendingUpdate) {
346 		_pendingUpdateCounter++;
347 
348 		if (_pendingUpdateCounter >= 4) {
349 			PSP_DEBUG_PRINT("servicing pending update\n");
350 			updateScreen();
351 			if (!_pendingUpdate) 	// we handled the update
352 				_pendingUpdateCounter = 0;
353 		}
354 	} else
355 		_pendingUpdateCounter = 0;	// reset the counter, no pending
356 
357 	return _inputHandler.getAllInputs(event);
358 }
359 
getMillis(bool skipRecord)360 uint32 OSystem_PSP::getMillis(bool skipRecord) {
361 	return PspRtc::instance().getMillis();
362 }
363 
delayMillis(uint msecs)364 void OSystem_PSP::delayMillis(uint msecs) {
365 	PspThread::delayMillis(msecs);
366 }
367 
setTimerCallback(TimerProc callback,int interval)368 void OSystem_PSP::setTimerCallback(TimerProc callback, int interval) {
369 	_pspTimer.setCallback((PspTimer::CallbackFunc)callback);
370 	_pspTimer.setIntervalMs(interval);
371 	_pspTimer.start();
372 }
373 
createMutex(void)374 OSystem::MutexRef OSystem_PSP::createMutex(void) {
375 	return (MutexRef) new PspMutex(true);	// start with a full mutex
376 }
377 
lockMutex(MutexRef mutex)378 void OSystem_PSP::lockMutex(MutexRef mutex) {
379 	((PspMutex *)mutex)->lock();
380 }
381 
unlockMutex(MutexRef mutex)382 void OSystem_PSP::unlockMutex(MutexRef mutex) {
383 	((PspMutex *)mutex)->unlock();
384 }
385 
deleteMutex(MutexRef mutex)386 void OSystem_PSP::deleteMutex(MutexRef mutex) {
387 	delete (PspMutex *)mutex;
388 }
389 
mixCallback(void * sys,byte * samples,int len)390 void OSystem_PSP::mixCallback(void *sys, byte *samples, int len) {
391 	OSystem_PSP *this_ = (OSystem_PSP *)sys;
392 	assert(this_);
393 
394 	if (this_->_mixer)
395 		this_->_mixer->mixCallback(samples, len);
396 }
397 
setupMixer(void)398 void OSystem_PSP::setupMixer(void) {
399 
400 	// Determine the desired output sampling frequency.
401 	uint32 samplesPerSec = 0;
402 	if (ConfMan.hasKey("output_rate"))
403 		samplesPerSec = ConfMan.getInt("output_rate");
404 	if (samplesPerSec <= 0)
405 		samplesPerSec = SAMPLES_PER_SEC;
406 
407 	// Determine the sample buffer size. We want it to store enough data for
408 	// at least 1/16th of a second (though at most 8192 samples). Note
409 	// that it must be a power of two. So e.g. at 22050 Hz, we request a
410 	// sample buffer size of 2048.
411 	uint32 samples = 8192;
412 	while (samples * 16 > samplesPerSec * 2)
413 		samples >>= 1;
414 
415 	assert(!_mixer);
416 
417 	if (!_audio.open(samplesPerSec, 2, samples, mixCallback, this)) {
418 		PSP_ERROR("failed to open audio\n");
419 		return;
420 	}
421 	samplesPerSec = _audio.getFrequency();	// may have been changed by audio system
422 	_mixer = new Audio::MixerImpl(samplesPerSec);
423 	assert(_mixer);
424 	_mixer->setReady(true);
425 	_audio.unpause();
426 }
427 
quit()428 void OSystem_PSP::quit() {
429 	_audio.close();
430 	sceKernelExitGame();
431 }
432 
logMessage(LogMessageType::Type type,const char * message)433 void OSystem_PSP::logMessage(LogMessageType::Type type, const char *message) {
434 	FILE *output = 0;
435 
436 	if (type == LogMessageType::kInfo || type == LogMessageType::kDebug)
437 		output = stdout;
438 	else
439 		output = stderr;
440 
441 	fputs(message, output);
442 	fflush(output);
443 
444 	if (type == LogMessageType::kError)
445 		PspDebugTrace(false, "%s", message);	// write to file
446 }
447 
getTimeAndDate(TimeDate & td) const448 void OSystem_PSP::getTimeAndDate(TimeDate &td) const {
449 	time_t curTime = time(0);
450 	struct tm t = *localtime(&curTime);
451 	td.tm_sec = t.tm_sec;
452 	td.tm_min = t.tm_min;
453 	td.tm_hour = t.tm_hour;
454 	td.tm_mday = t.tm_mday;
455 	td.tm_mon = t.tm_mon;
456 	td.tm_year = t.tm_year;
457 	td.tm_wday = t.tm_wday;
458 }
459 
getDefaultConfigFileName()460 Common::String OSystem_PSP::getDefaultConfigFileName() {
461 	return "ms0:/scummvm.ini";
462 }
463