scummvm/backends/platform/ds/osystem_ds.cpp

526 lines
14 KiB
C++
Raw Normal View History

/* 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 <time.h>
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
// Allow use of stuff in <nds.h>
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
2020-08-06 17:24:14 +01:00
#include <nds.h>
#include <filesystem.h>
#include "common/scummsys.h"
#include "common/system.h"
#include "common/util.h"
#include "common/rect.h"
#include "common/savefile.h"
2020-08-06 15:04:24 +01:00
#include "common/translation.h"
#include "osystem_ds.h"
#include "dsmain.h"
2020-08-06 22:20:35 +01:00
#include "blitters.h"
#include "common/config-manager.h"
#include "common/str.h"
#include "graphics/surface.h"
#include "backends/fs/devoptab/devoptab-fs-factory.h"
2020-08-06 15:04:24 +01:00
#include "backends/keymapper/hardware-input.h"
#include "backends/audiocd/default/default-audiocd.h"
2020-08-06 15:04:24 +01:00
#include "backends/events/default/default-events.h"
2020-06-01 22:47:20 +01:00
#include "backends/saves/default/default-saves.h"
#include "backends/timer/default/default-timer.h"
#include <time.h>
OSystem_DS *OSystem_DS::_instance = NULL;
OSystem_DS::OSystem_DS()
: _eventSource(NULL), _mixer(NULL), _isOverlayShown(true),
2020-08-11 22:28:25 +01:00
_graphicsMode(GFX_HWSCALE), _stretchMode(100),
2020-08-13 17:17:16 +01:00
_disableCursorPalette(true), _graphicsEnable(true),
_callbackTimer(10), _currentTimeMillis(0)
{
_instance = this;
2020-08-06 17:24:14 +01:00
nitroFSInit(NULL);
_fsFactory = new DevoptabFilesystemFactory();
}
OSystem_DS::~OSystem_DS() {
delete _mixer;
_mixer = 0;
}
2020-08-13 17:17:16 +01:00
void timerTickHandler() {
OSystem_DS *system = OSystem_DS::instance();
if (system->_callbackTimer > 0) {
system->_callbackTimer--;
}
system->_currentTimeMillis++;
}
void OSystem_DS::initBackend() {
2020-08-11 22:28:25 +01:00
DS::initHardware();
defaultExceptionHandler();
ConfMan.setInt("autosave_period", 0);
ConfMan.setBool("FM_medium_quality", true);
2020-08-06 15:04:24 +01:00
_eventSource = new DSEventSource();
_eventManager = new DefaultEventManager(_eventSource);
2020-06-01 22:47:20 +01:00
_savefileManager = new DefaultSaveFileManager();
_timerManager = new DefaultTimerManager();
2020-08-13 17:17:16 +01:00
timerStart(0, ClockDivider_1, (u16)TIMER_FREQ(1000), timerTickHandler);
REG_IME = 1;
2020-07-23 14:22:16 +01:00
_mixer = new Audio::MixerImpl(11025);
_mixer->setReady(true);
2020-08-11 17:37:26 +01:00
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));
2020-08-06 15:04:24 +01:00
BaseBackend::initBackend();
}
bool OSystem_DS::hasFeature(Feature f) {
2020-08-11 22:28:25 +01:00
return (f == kFeatureCursorPalette) || (f == kFeatureStretchMode);
}
void OSystem_DS::setFeatureState(Feature f, bool enable) {
2020-08-06 15:04:24 +01:00
if (f == kFeatureCursorPalette) {
_disableCursorPalette = !enable;
2020-08-11 17:37:26 +01:00
refreshCursor(_cursorSprite, _cursor, !_disableCursorPalette ? _cursorPalette : _palette);
}
}
bool OSystem_DS::getFeatureState(Feature f) {
if (f == kFeatureCursorPalette)
return !_disableCursorPalette;
return false;
}
2020-08-11 22:28:25 +01:00
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) {
2020-08-06 22:20:35 +01:00
_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() {
2020-08-06 22:20:35 +01:00
return _framebuffer.h;
}
int16 OSystem_DS::getWidth() {
2020-08-06 22:20:35 +01:00
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) {
2020-08-11 22:28:25 +01:00
BG_PALETTE[r] = paletteValue;
2020-08-06 17:24:14 +01:00
#ifdef DISABLE_TEXT_CONSOLE
2020-08-11 22:28:25 +01:00
BG_PALETTE_SUB[r] = paletteValue;
2020-08-06 17:24:14 +01:00
#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;
2020-08-11 17:37:26 +01:00
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++) {
2011-04-14 14:12:27 +02:00
*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) {
2020-08-06 22:20:35 +01:00
_framebuffer.copyRectToSurface(buf, pitch, x, y, w, h);
}
2020-08-06 22:20:35 +01:00
void OSystem_DS::updateScreen() {
2020-08-11 17:37:26 +01:00
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);
2020-08-06 22:20:35 +01:00
} else if (!_graphicsEnable) {
return;
2020-08-11 22:28:25 +01:00
} else if (_graphicsMode == GFX_SWSCALE) {
2020-08-06 22:20:35 +01:00
u16 *base = BG_GFX + 0x10000;
Rescale_320x256xPAL8_To_256x256x1555(
base,
(const u8 *)_framebuffer.getPixels(),
256,
_framebuffer.pitch,
BG_PALETTE,
_framebuffer.h );
} else {
2020-08-06 22:20:35 +01:00
// The DS video RAM doesn't support 8-bit writes because Nintendo wanted
// to save a few pennies/euro cents on the hardware.
2020-08-06 22:20:35 +01:00
u16 *bg = BG_GFX + 0x10000;
s32 stride = 512;
2020-08-06 22:20:35 +01:00
u16 *src = (u16 *)_framebuffer.getPixels();
2020-08-06 22:20:35 +01:00
for (int dy = 0; dy < _framebuffer.h; dy++) {
DC_FlushRange(src, _framebuffer.w << 1);
2020-08-06 22:20:35 +01:00
u16 *dest1 = bg + (dy * (stride >> 1));
DC_FlushRange(dest1, _framebuffer.w << 1);
2020-08-06 17:24:14 +01:00
#ifdef DISABLE_TEXT_CONSOLE
2020-08-06 22:20:35 +01:00
u16 *dest2 = (u16 *)BG_GFX_SUB + (dy << 8);
DC_FlushRange(dest2, _framebuffer.w << 1);
dmaCopyHalfWordsAsynch(2, src, dest2, _framebuffer.w);
2020-08-06 17:24:14 +01:00
#endif
dmaCopyHalfWordsAsynch(3, src, dest1, _framebuffer.w);
2020-08-06 15:04:24 +01:00
while (dmaBusy(2) || dmaBusy(3));
2020-08-06 22:20:35 +01:00
src += _framebuffer.pitch >> 1;
}
}
}
void OSystem_DS::setShakePos(int shakeXOffset, int shakeYOffset) {
DS::setShakePos(shakeXOffset, shakeYOffset);
}
2009-01-21 04:03:22 +00:00
void OSystem_DS::showOverlay() {
dmaFillHalfWords(0, BG_GFX, 256 * 192 * 2);
videoBgEnable(2);
lcdMainOnBottom();
_isOverlayShown = true;
}
2009-01-21 04:03:22 +00:00
void OSystem_DS::hideOverlay() {
videoBgDisable(2);
DS::displayMode8Bit();
_isOverlayShown = false;
}
bool OSystem_DS::isOverlayVisible() const {
return _isOverlayShown;
}
2009-01-21 04:03:22 +00:00
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) {
2020-08-11 17:37:26 +01:00
const bool last = _cursorVisible;
_cursorVisible = visible;
return last;
}
void OSystem_DS::warpMouse(int x, int y) {
2020-08-11 17:37:26 +01:00
_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) {
2020-08-11 17:37:26 +01:00
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);
}
2020-08-11 17:37:26 +01:00
void OSystem_DS::refreshCursor(u16 *dst, const Graphics::Surface &src, const uint16 *palette) {
uint w = MIN<uint>(src.w, 64);
uint h = MIN<uint>(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;
}
}
}
2013-05-17 00:18:09 +03:00
uint32 OSystem_DS::getMillis(bool skipRecord) {
2020-08-13 17:17:16 +01:00
return _currentTimeMillis;
}
void OSystem_DS::delayMillis(uint msecs) {
int st = getMillis();
2020-07-23 14:22:16 +01:00
while (st + msecs >= getMillis());
2020-08-13 17:17:16 +01:00
doTimerCallback();
}
2020-08-13 17:17:16 +01:00
void OSystem_DS::doTimerCallback(int interval) {
DefaultTimerManager *tm = (DefaultTimerManager *)getTimerManager();
if (_callbackTimer <= 0) {
_callbackTimer += interval;
tm->handler();
}
}
void OSystem_DS::getTimeAndDate(TimeDate &td) const {
2020-08-13 17:17:16 +01:00
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;
}
OSystem::MutexRef OSystem_DS::createMutex(void) {
return NULL;
}
void OSystem_DS::lockMutex(MutexRef mutex) {
}
void OSystem_DS::unlockMutex(MutexRef mutex) {
}
void OSystem_DS::deleteMutex(MutexRef mutex) {
}
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
2020-07-23 13:35:11 +01:00
printf("%s", message);
2020-08-06 17:24:14 +01:00
#endif
}
2020-08-06 15:04:24 +01:00
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;
}