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