/* 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. * */ // Allow use of stuff in #define FORBIDDEN_SYMBOL_EXCEPTION_time_h // Allow use of stuff in #define FORBIDDEN_SYMBOL_EXCEPTION_printf #define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h #include #include #include "common/scummsys.h" #include "common/system.h" #include "common/util.h" #include "common/rect.h" #include "common/savefile.h" #include "common/translation.h" #include "osystem_ds.h" #include "dsmain.h" #include "blitters.h" #include "common/config-manager.h" #include "common/str.h" #include "graphics/surface.h" #include "backends/fs/devoptab/devoptab-fs-factory.h" #include "backends/keymapper/hardware-input.h" #include "backends/audiocd/default/default-audiocd.h" #include "backends/events/default/default-events.h" #include "backends/mixer/maxmod/maxmod-mixer.h" #include "backends/mutex/null/null-mutex.h" #include "backends/saves/default/default-saves.h" #include "backends/timer/default/default-timer.h" #include OSystem_DS *OSystem_DS::_instance = NULL; OSystem_DS::OSystem_DS() : _eventSource(NULL), _isOverlayShown(true), _graphicsMode(GFX_HWSCALE), _stretchMode(100), _disableCursorPalette(true), _graphicsEnable(true), _callbackTimer(10), _currentTimeMillis(0) { _instance = this; nitroFSInit(NULL); _fsFactory = new DevoptabFilesystemFactory(); _mutexManager = new NullMutexManager(); } OSystem_DS::~OSystem_DS() { } void timerTickHandler() { OSystem_DS *system = OSystem_DS::instance(); if (system->_callbackTimer > 0) { system->_callbackTimer--; } system->_currentTimeMillis++; } void OSystem_DS::initBackend() { DS::initHardware(); defaultExceptionHandler(); ConfMan.setInt("autosave_period", 0); ConfMan.setBool("FM_medium_quality", true); _eventSource = new DSEventSource(); _eventManager = new DefaultEventManager(_eventSource); _savefileManager = new DefaultSaveFileManager(); _timerManager = new DefaultTimerManager(); timerStart(0, ClockDivider_1, (u16)TIMER_FREQ(1000), timerTickHandler); _mixerManager = new MaxModMixerManager(11025, 4096); _mixerManager->init(); oamInit(&oamMain, SpriteMapping_Bmp_1D_128, false); _cursorSprite = oamAllocateGfx(&oamMain, SpriteSize_64x64, SpriteColorFormat_Bmp); _overlay.create(256, 192, Graphics::PixelFormat(2, 5, 5, 5, 1, 0, 5, 10, 15)); BaseBackend::initBackend(); } bool OSystem_DS::hasFeature(Feature f) { return (f == kFeatureCursorPalette) || (f == kFeatureStretchMode); } void OSystem_DS::setFeatureState(Feature f, bool enable) { if (f == kFeatureCursorPalette) { _disableCursorPalette = !enable; refreshCursor(_cursorSprite, _cursor, !_disableCursorPalette ? _cursorPalette : _palette); } } bool OSystem_DS::getFeatureState(Feature f) { if (f == kFeatureCursorPalette) return !_disableCursorPalette; return false; } static const OSystem::GraphicsMode graphicsModes[] = { { "NONE", _s("Unscaled"), GFX_NOSCALE }, { "HW", _s("Hardware scale (fast, but low quality)"), GFX_HWSCALE }, { "SW", _s("Software scale (good quality, but slower)"), GFX_SWSCALE }, { nullptr, nullptr, 0 } }; const OSystem::GraphicsMode *OSystem_DS::getSupportedGraphicsModes() const { return graphicsModes; } int OSystem_DS::getDefaultGraphicsMode() const { return GFX_HWSCALE; } bool OSystem_DS::setGraphicsMode(int mode) { switch (mode) { case GFX_NOSCALE: case GFX_HWSCALE: case GFX_SWSCALE: _graphicsMode = mode; return true; default: return false; } } int OSystem_DS::getGraphicsMode() const { return _graphicsMode; } static const OSystem::GraphicsMode stretchModes[] = { { "100", "100%", 100 }, { "150", "150%", 150 }, { "200", "200%", 200 }, { nullptr, nullptr, 0 } }; const OSystem::GraphicsMode *OSystem_DS::getSupportedStretchModes() const { return stretchModes; } int OSystem_DS::getDefaultStretchMode() const { return 100; } bool OSystem_DS::setStretchMode(int mode) { _stretchMode = mode; DS::setTopScreenZoom(mode); return true; } int OSystem_DS::getStretchMode() const { return _stretchMode; } void OSystem_DS::initSize(uint width, uint height, const Graphics::PixelFormat *format) { _framebuffer.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); // For Lost in Time, the title screen is displayed in 640x400. // In order to support this game, the screen mode is set, but // all draw calls are ignored until the game switches to 320x200. if ((width == 640) && (height == 400)) { _graphicsEnable = false; } else { _graphicsEnable = true; DS::setGameSize(width, height); } } int16 OSystem_DS::getHeight() { return _framebuffer.h; } int16 OSystem_DS::getWidth() { return _framebuffer.w; } void OSystem_DS::setPalette(const byte *colors, uint start, uint num) { for (unsigned int r = start; r < start + num; r++) { int red = *colors; int green = *(colors + 1); int blue = *(colors + 2); red >>= 3; green >>= 3; blue >>= 3; { u16 paletteValue = red | (green << 5) | (blue << 10); if (!_isOverlayShown) { BG_PALETTE[r] = paletteValue; #ifdef DISABLE_TEXT_CONSOLE BG_PALETTE_SUB[r] = paletteValue; #endif } _palette[r] = paletteValue; } colors += 3; } } void OSystem_DS::setCursorPalette(const byte *colors, uint start, uint num) { for (unsigned int r = start; r < start + num; r++) { int red = *colors; int green = *(colors + 1); int blue = *(colors + 2); red >>= 3; green >>= 3; blue >>= 3; u16 paletteValue = red | (green << 5) | (blue << 10); _cursorPalette[r] = paletteValue; colors += 3; } _disableCursorPalette = false; refreshCursor(_cursorSprite, _cursor, !_disableCursorPalette ? _cursorPalette : _palette); } void OSystem_DS::grabPalette(unsigned char *colors, uint start, uint num) const { for (unsigned int r = start; r < start + num; r++) { *colors++ = (BG_PALETTE[r] & 0x001F) << 3; *colors++ = (BG_PALETTE[r] & 0x03E0) >> 5 << 3; *colors++ = (BG_PALETTE[r] & 0x7C00) >> 10 << 3; } } void OSystem_DS::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) { _framebuffer.copyRectToSurface(buf, pitch, x, y, w, h); } void OSystem_DS::updateScreen() { oamSet(&oamMain, 0, _cursorPos.x - _cursorHotX, _cursorPos.y - _cursorHotY, 0, 15, SpriteSize_64x64, SpriteColorFormat_Bmp, _cursorSprite, 0, false, !_cursorVisible, false, false, false); oamUpdate(&oamMain); if (_isOverlayShown) { u16 *back = (u16 *)_overlay.getPixels(); dmaCopyHalfWords(3, back, BG_GFX, 256 * 192 * 2); } else if (!_graphicsEnable) { return; } else if (_graphicsMode == GFX_SWSCALE) { u16 *base = BG_GFX + 0x10000; Rescale_320x256xPAL8_To_256x256x1555( base, (const u8 *)_framebuffer.getPixels(), 256, _framebuffer.pitch, BG_PALETTE, _framebuffer.h ); } else { // The DS video RAM doesn't support 8-bit writes because Nintendo wanted // to save a few pennies/euro cents on the hardware. u16 *bg = BG_GFX + 0x10000; s32 stride = 512; u16 *src = (u16 *)_framebuffer.getPixels(); for (int dy = 0; dy < _framebuffer.h; dy++) { DC_FlushRange(src, _framebuffer.w << 1); u16 *dest1 = bg + (dy * (stride >> 1)); DC_FlushRange(dest1, _framebuffer.w << 1); #ifdef DISABLE_TEXT_CONSOLE u16 *dest2 = (u16 *)BG_GFX_SUB + (dy << 8); DC_FlushRange(dest2, _framebuffer.w << 1); dmaCopyHalfWordsAsynch(2, src, dest2, _framebuffer.w); #endif dmaCopyHalfWordsAsynch(3, src, dest1, _framebuffer.w); while (dmaBusy(2) || dmaBusy(3)); src += _framebuffer.pitch >> 1; } } } void OSystem_DS::setShakePos(int shakeXOffset, int shakeYOffset) { DS::setShakePos(shakeXOffset, shakeYOffset); } void OSystem_DS::showOverlay() { dmaFillHalfWords(0, BG_GFX, 256 * 192 * 2); videoBgEnable(2); lcdMainOnBottom(); _isOverlayShown = true; } void OSystem_DS::hideOverlay() { videoBgDisable(2); DS::displayMode8Bit(); _isOverlayShown = false; } bool OSystem_DS::isOverlayVisible() const { return _isOverlayShown; } void OSystem_DS::clearOverlay() { memset(_overlay.getPixels(), 0, _overlay.pitch * _overlay.h); } void OSystem_DS::grabOverlay(void *buf, int pitch) { byte *dst = (byte *)buf; for (int y = 0; y < _overlay.h; ++y) { memcpy(dst, _overlay.getBasePtr(0, y), _overlay.w * _overlay.format.bytesPerPixel); dst += pitch; } } void OSystem_DS::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) { _overlay.copyRectToSurface(buf, pitch, x, y, w, h); } int16 OSystem_DS::getOverlayHeight() { return _overlay.h; } int16 OSystem_DS::getOverlayWidth() { return _overlay.w; } Graphics::PixelFormat OSystem_DS::getOverlayFormat() const { return _overlay.format; } Common::Point OSystem_DS::transformPoint(uint16 x, uint16 y) { return DS::transformPoint(x, y, _isOverlayShown); } bool OSystem_DS::showMouse(bool visible) { const bool last = _cursorVisible; _cursorVisible = visible; return last; } void OSystem_DS::warpMouse(int x, int y) { _cursorPos = DS::warpMouse(x, y, _isOverlayShown); } void OSystem_DS::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, u32 keycolor, bool dontScale, const Graphics::PixelFormat *format) { if (!buf || w == 0 || h == 0 || (format && *format != Graphics::PixelFormat::createFormatCLUT8())) return; if (_cursor.w != w || _cursor.h != h) _cursor.create(w, h, Graphics::PixelFormat::createFormatCLUT8()); _cursor.copyRectToSurface(buf, w, 0, 0, w, h); _cursorHotX = hotspotX; _cursorHotY = hotspotY; _cursorKey = keycolor; refreshCursor(_cursorSprite, _cursor, !_disableCursorPalette ? _cursorPalette : _palette); } void OSystem_DS::refreshCursor(u16 *dst, const Graphics::Surface &src, const uint16 *palette) { uint w = MIN(src.w, 64); uint h = MIN(src.h, 64); dmaFillHalfWords(0, dst, 64 * 64 * 2); for (uint y = 0; y < h; y++) { const uint8 *row = (const uint8 *)src.getBasePtr(0, y); for (uint x = 0; x < w; x++) { uint8 color = *row++; if (color != _cursorKey) dst[y * 64 + x] = palette[color] | 0x8000; } } } void OSystem_DS::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) { s.add("nitro:/", new Common::FSDirectory("nitro:/"), priority); } uint32 OSystem_DS::getMillis(bool skipRecord) { return _currentTimeMillis; } void OSystem_DS::delayMillis(uint msecs) { int st = getMillis(); while (st + msecs >= getMillis()); doTimerCallback(); } void OSystem_DS::doTimerCallback(int interval) { DefaultTimerManager *tm = (DefaultTimerManager *)getTimerManager(); if (_callbackTimer <= 0) { _callbackTimer += interval; tm->handler(); } } void OSystem_DS::getTimeAndDate(TimeDate &td) 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; } void OSystem_DS::quit() { } Graphics::Surface *OSystem_DS::lockScreen() { return &_framebuffer; } void OSystem_DS::unlockScreen() { // No need to do anything here. The screen will be updated in updateScreen(). } void OSystem_DS::setFocusRectangle(const Common::Rect& rect) { DS::setTalkPos(rect.left + rect.width() / 2, rect.top + rect.height() / 2); } void OSystem_DS::clearFocusRectangle() { } void OSystem_DS::engineInit() { #ifdef DISABLE_TEXT_CONSOLE videoBgEnableSub(3); #endif } void OSystem_DS::engineDone() { #ifdef DISABLE_TEXT_CONSOLE videoBgDisableSub(3); #endif } void OSystem_DS::logMessage(LogMessageType::Type type, const char *message) { #ifndef DISABLE_TEXT_CONSOLE printf("%s", message); #endif } static const Common::HardwareInputTableEntry ndsJoystickButtons[] = { { "JOY_A", Common::JOYSTICK_BUTTON_A, _s("A") }, { "JOY_B", Common::JOYSTICK_BUTTON_B, _s("B") }, { "JOY_X", Common::JOYSTICK_BUTTON_X, _s("X") }, { "JOY_Y", Common::JOYSTICK_BUTTON_Y, _s("Y") }, { "JOY_BACK", Common::JOYSTICK_BUTTON_BACK, _s("Select") }, { "JOY_START", Common::JOYSTICK_BUTTON_START, _s("Start") }, { "JOY_LEFT_SHOULDER", Common::JOYSTICK_BUTTON_LEFT_SHOULDER, _s("L") }, { "JOY_RIGHT_SHOULDER", Common::JOYSTICK_BUTTON_RIGHT_SHOULDER, _s("R") }, { "JOY_UP", Common::JOYSTICK_BUTTON_DPAD_UP, _s("D-pad Up") }, { "JOY_DOWN", Common::JOYSTICK_BUTTON_DPAD_DOWN, _s("D-pad Down") }, { "JOY_LEFT", Common::JOYSTICK_BUTTON_DPAD_LEFT, _s("D-pad Left") }, { "JOY_RIGHT", Common::JOYSTICK_BUTTON_DPAD_RIGHT, _s("D-pad Right") }, { nullptr, 0, nullptr } }; static const Common::AxisTableEntry ndsJoystickAxes[] = { { nullptr, 0, Common::kAxisTypeFull, nullptr } }; const Common::HardwareInputTableEntry ndsMouseButtons[] = { { "MOUSE_LEFT", Common::MOUSE_BUTTON_LEFT, _s("Touch") }, { nullptr, 0, nullptr } }; Common::HardwareInputSet *OSystem_DS::getHardwareInputSet() { using namespace Common; CompositeHardwareInputSet *inputSet = new CompositeHardwareInputSet(); // Touch input sends mouse events for now, so we need to declare we have a mouse... inputSet->addHardwareInputSet(new MouseHardwareInputSet(ndsMouseButtons)); inputSet->addHardwareInputSet(new JoystickHardwareInputSet(ndsJoystickButtons, ndsJoystickAxes)); return inputSet; } Common::String OSystem_DS::getSystemLanguage() const { switch (PersonalData->language) { case 0: return "ja_JP"; case 1: return "en_US"; case 2: return "fr_FR"; case 3: return "de_DE"; case 4: return "it_IT"; case 5: return "es_ES"; case 6: return "zh_CN"; default: return "en_US"; } }