/* 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. * */ #define FORBIDDEN_SYMBOL_ALLOW_ALL #include #include #include #include #include #include "common/config-manager.h" #include "common/events.h" #include "common/scummsys.h" #include "backends/platform/psp/psppixelformat.h" #include "backends/platform/psp/osys_psp.h" #include "backends/platform/psp/powerman.h" #include "backends/platform/psp/rtc.h" #include "backends/saves/default/default-saves.h" #include "backends/timer/psp/timer.h" #include "graphics/surface.h" #include "audio/mixer_intern.h" //#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */ //#define __PSP_DEBUG_PRINT__ /* For debug printouts */ #include "backends/platform/psp/trace.h" #define SAMPLES_PER_SEC 44100 OSystem_PSP::~OSystem_PSP() {} #define PSP_SCREEN_WIDTH 480 #define PSP_SCREEN_HEIGHT 272 #define PSP_DEFAULT_SAVE_PATH "ms0:/scummvm_savegames" void OSystem_PSP::initBackend() { DEBUG_ENTER_FUNC(); ConfMan.registerDefault("aspect_ratio", false); ConfMan.registerDefault("gfx_mode", "Fit to Screen"); ConfMan.registerDefault("kbdmouse_speed", 3); ConfMan.registerDefault("joystick_deadzone", 3); // Instantiate real time clock PspRtc::instance(); _cursor.enableCursorPalette(false); _cursor.setXY(PSP_SCREEN_WIDTH >> 1, PSP_SCREEN_HEIGHT >> 1); // Mouse in the middle of the screen // Set pointers for display manager _displayManager.setCursor(&_cursor); _displayManager.setScreen(&_screen); _displayManager.setOverlay(&_overlay); _displayManager.setKeyboard(&_keyboard); _displayManager.setImageViewer(&_imageViewer); _displayManager.init(); // Set pointers for input handler _inputHandler.setCursor(&_cursor); _inputHandler.setKeyboard(&_keyboard); _inputHandler.setImageViewer(&_imageViewer); _inputHandler.init(); // Set pointers for image viewer _imageViewer.setInputHandler(&_inputHandler); _imageViewer.setDisplayManager(&_displayManager); _savefileManager = new DefaultSaveFileManager(PSP_DEFAULT_SAVE_PATH); _timerManager = new PspTimerManager(); PSP_DEBUG_PRINT("calling keyboard.load()\n"); _keyboard.load(); // Load virtual keyboard files into memory setupMixer(); EventsBaseBackend::initBackend(); } // Let's us know an engine void OSystem_PSP::engineDone() { // for now, all we need is to reset the image number on the viewer _imageViewer.resetOnEngineDone(); } bool OSystem_PSP::hasFeature(Feature f) { return (f == kFeatureOverlaySupportsAlpha || f == kFeatureCursorPalette || f == kFeatureKbdMouseSpeed || f == kFeatureJoystickDeadzone); } void OSystem_PSP::setFeatureState(Feature f, bool enable) { if (f == kFeatureCursorPalette) { _pendingUpdate = false; _cursor.enableCursorPalette(enable); } } bool OSystem_PSP::getFeatureState(Feature f) { if (f == kFeatureCursorPalette) { return _cursor.isCursorPaletteEnabled(); } return false; } const OSystem::GraphicsMode* OSystem_PSP::getSupportedGraphicsModes() const { return _displayManager.getSupportedGraphicsModes(); } int OSystem_PSP::getDefaultGraphicsMode() const { DEBUG_ENTER_FUNC(); return _displayManager.getDefaultGraphicsMode(); } bool OSystem_PSP::setGraphicsMode(int mode, uint flags) { DEBUG_ENTER_FUNC(); _displayManager.waitUntilRenderFinished(); _pendingUpdate = false; return _displayManager.setGraphicsMode(mode); } int OSystem_PSP::getGraphicsMode() const { DEBUG_ENTER_FUNC(); return _displayManager.getGraphicsMode(); } #ifdef USE_RGB_COLOR Graphics::PixelFormat OSystem_PSP::getScreenFormat() const { return _screen.getScummvmPixelFormat(); } Common::List OSystem_PSP::getSupportedFormats() const { return _displayManager.getSupportedPixelFormats(); } #endif void OSystem_PSP::initSize(uint width, uint height, const Graphics::PixelFormat *format) { DEBUG_ENTER_FUNC(); _displayManager.waitUntilRenderFinished(); _pendingUpdate = false; _displayManager.setSizeAndPixelFormat(width, height, format); _cursor.setVisible(false); _cursor.setLimits(_screen.getWidth(), _screen.getHeight()); } int16 OSystem_PSP::getWidth() { DEBUG_ENTER_FUNC(); return (int16)_screen.getWidth(); } int16 OSystem_PSP::getHeight() { DEBUG_ENTER_FUNC(); return (int16)_screen.getHeight(); } void OSystem_PSP::setPalette(const byte *colors, uint start, uint num) { DEBUG_ENTER_FUNC(); _displayManager.waitUntilRenderFinished(); _pendingUpdate = false; _screen.setPartialPalette(colors, start, num); _cursor.setScreenPalette(colors, start, num); _cursor.clearKeyColor(); } void OSystem_PSP::setCursorPalette(const byte *colors, uint start, uint num) { DEBUG_ENTER_FUNC(); _displayManager.waitUntilRenderFinished(); _pendingUpdate = false; _cursor.setCursorPalette(colors, start, num); _cursor.enableCursorPalette(true); _cursor.clearKeyColor(); // Do we need this? } void OSystem_PSP::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) { DEBUG_ENTER_FUNC(); _displayManager.waitUntilRenderFinished(); _pendingUpdate = false; _screen.copyFromRect((const byte *)buf, pitch, x, y, w, h); } Graphics::Surface *OSystem_PSP::lockScreen() { DEBUG_ENTER_FUNC(); _displayManager.waitUntilRenderFinished(); _pendingUpdate = false; return _screen.lockAndGetForEditing(); } void OSystem_PSP::unlockScreen() { DEBUG_ENTER_FUNC(); _pendingUpdate = false; // The screen is always completely updated anyway, so we don't have to force a full update here. _screen.unlock(); } void OSystem_PSP::updateScreen() { DEBUG_ENTER_FUNC(); _pendingUpdate = !_displayManager.renderAll(); // if we didn't update, we have a pending update } void OSystem_PSP::setShakePos(int shakeXOffset, int shakeYOffset) { DEBUG_ENTER_FUNC(); _displayManager.waitUntilRenderFinished(); _pendingUpdate = false; _screen.setShakePos(shakeXOffset, shakeYOffset); } void OSystem_PSP::showOverlay() { DEBUG_ENTER_FUNC(); _pendingUpdate = false; _overlay.setVisible(true); _cursor.setLimits(_overlay.getWidth(), _overlay.getHeight()); _cursor.useGlobalScaler(false); // mouse with overlay is 1:1 } void OSystem_PSP::hideOverlay() { DEBUG_ENTER_FUNC(); _pendingUpdate = false; _overlay.setVisible(false); _cursor.setLimits(_screen.getWidth(), _screen.getHeight()); _cursor.useGlobalScaler(true); // mouse needs to be scaled with screen } bool OSystem_PSP::isOverlayVisible() const { DEBUG_ENTER_FUNC(); return _overlay.isVisible(); } void OSystem_PSP::clearOverlay() { DEBUG_ENTER_FUNC(); _displayManager.waitUntilRenderFinished(); _pendingUpdate = false; _overlay.clearBuffer(); } void OSystem_PSP::grabOverlay(Graphics::Surface &surface) { DEBUG_ENTER_FUNC(); assert(surface.w >= _overlay.getWidth()); assert(surface.h >= _overlay.getHeight()); assert(surface.format.bytesPerPixel == 2); _overlay.copyToArray(surface.getPixels(), surface.pitch); } void OSystem_PSP::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) { DEBUG_ENTER_FUNC(); _displayManager.waitUntilRenderFinished(); _pendingUpdate = false; _overlay.copyFromRect(buf, pitch, x, y, w, h); } int16 OSystem_PSP::getOverlayWidth() { return (int16)_overlay.getWidth(); } int16 OSystem_PSP::getOverlayHeight() { return (int16)_overlay.getHeight(); } void OSystem_PSP::grabPalette(byte *colors, uint start, uint num) const { DEBUG_ENTER_FUNC(); _screen.getPartialPalette(colors, start, num); } bool OSystem_PSP::showMouse(bool v) { DEBUG_ENTER_FUNC(); _pendingUpdate = false; PSP_DEBUG_PRINT("%s\n", v ? "true" : "false"); bool last = _cursor.isVisible(); _cursor.setVisible(v); return last; } void OSystem_PSP::warpMouse(int x, int y) { DEBUG_ENTER_FUNC(); _displayManager.waitUntilRenderFinished(); _pendingUpdate = false; _cursor.setXY(x, y); } void OSystem_PSP::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) { DEBUG_ENTER_FUNC(); _displayManager.waitUntilRenderFinished(); _pendingUpdate = false; 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); if (format) { 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); } _cursor.setKeyColor(keycolor); // TODO: The old target scale was saved but never used. Should the new // "do not scale" logic be implemented? //_cursor.setCursorTargetScale(cursorTargetScale); _cursor.setSizeAndScummvmPixelFormat(w, h, format); _cursor.setHotspot(hotspotX, hotspotY); _cursor.clearKeyColor(); _cursor.copyFromArray((const byte *)buf); } bool OSystem_PSP::pollEvent(Common::Event &event) { // If we're polling for events, we should check for pausing the engine // Pausing the engine is a necessary fix for games that use the timer for music synchronization // recovering many hours later causes the game to crash. We're polling without mutexes since it's not critical to // get it right now. PowerMan.pollPauseEngine(); // A hack: // Check if we have a pending update that we missed for some reason (FPS throttling for example) // Time between event polls is usually 5-10ms, so waiting for 4 calls before checking to update the screen should be fine if (_pendingUpdate) { _pendingUpdateCounter++; if (_pendingUpdateCounter >= 4) { PSP_DEBUG_PRINT("servicing pending update\n"); updateScreen(); if (!_pendingUpdate) // we handled the update _pendingUpdateCounter = 0; } } else _pendingUpdateCounter = 0; // reset the counter, no pending return _inputHandler.getAllInputs(event); } uint32 OSystem_PSP::getMillis(bool skipRecord) { return PspRtc::instance().getMillis(); } void OSystem_PSP::delayMillis(uint msecs) { PspThread::delayMillis(msecs); } OSystem::MutexRef OSystem_PSP::createMutex(void) { return (MutexRef) new PspMutex(true); // start with a full mutex } void OSystem_PSP::lockMutex(MutexRef mutex) { ((PspMutex *)mutex)->lock(); } void OSystem_PSP::unlockMutex(MutexRef mutex) { ((PspMutex *)mutex)->unlock(); } void OSystem_PSP::deleteMutex(MutexRef mutex) { delete (PspMutex *)mutex; } void OSystem_PSP::mixCallback(void *sys, byte *samples, int len) { OSystem_PSP *this_ = (OSystem_PSP *)sys; assert(this_); if (this_->_mixer) this_->_mixer->mixCallback(samples, len); } void OSystem_PSP::setupMixer(void) { // Determine the desired output sampling frequency. uint32 samplesPerSec = 0; if (ConfMan.hasKey("output_rate")) samplesPerSec = ConfMan.getInt("output_rate"); if (samplesPerSec <= 0) samplesPerSec = SAMPLES_PER_SEC; // Determine the sample buffer size. We want it to store enough data for // at least 1/16th of a second (though at most 8192 samples). Note // that it must be a power of two. So e.g. at 22050 Hz, we request a // sample buffer size of 2048. uint32 samples = 8192; while (samples * 16 > samplesPerSec * 2) samples >>= 1; assert(!_mixer); if (!_audio.open(samplesPerSec, 2, samples, mixCallback, this)) { PSP_ERROR("failed to open audio\n"); return; } samplesPerSec = _audio.getFrequency(); // may have been changed by audio system _mixer = new Audio::MixerImpl(samplesPerSec); assert(_mixer); _mixer->setReady(true); _audio.unpause(); } void OSystem_PSP::quit() { _audio.close(); sceKernelExitGame(); } void OSystem_PSP::logMessage(LogMessageType::Type type, const char *message) { FILE *output = 0; if (type == LogMessageType::kInfo || type == LogMessageType::kDebug) output = stdout; else output = stderr; fputs(message, output); fflush(output); if (type == LogMessageType::kError) PspDebugTrace(false, "%s", message); // write to file } void OSystem_PSP::getTimeAndDate(TimeDate &td, bool skipRecord) const { time_t curTime = time(0); struct tm t = *localtime(&curTime); td.tm_sec = t.tm_sec; td.tm_min = t.tm_min; td.tm_hour = t.tm_hour; td.tm_mday = t.tm_mday; td.tm_mon = t.tm_mon; td.tm_year = t.tm_year; td.tm_wday = t.tm_wday; } Common::String OSystem_PSP::getDefaultConfigFileName() { return "ms0:/scummvm.ini"; }