aboutsummaryrefslogtreecommitdiff
path: root/backends/platform/3ds
diff options
context:
space:
mode:
authorThomas Edvalson2016-04-06 02:12:02 -0400
committerThomas Edvalson2016-04-06 02:12:02 -0400
commite2b9572a83badb7084f5b54e1a7e108af8e327f6 (patch)
treeda3df5dcf26a03635b5a74ac18ab18ecb7c7fe98 /backends/platform/3ds
parentf6c3363cf54af1c01efc12a98ab27a8af52aad3e (diff)
downloadscummvm-rg350-e2b9572a83badb7084f5b54e1a7e108af8e327f6.tar.gz
scummvm-rg350-e2b9572a83badb7084f5b54e1a7e108af8e327f6.tar.bz2
scummvm-rg350-e2b9572a83badb7084f5b54e1a7e108af8e327f6.zip
3DS: Initial commit
Diffstat (limited to 'backends/platform/3ds')
-rw-r--r--backends/platform/3ds/3ds.mk53
-rw-r--r--backends/platform/3ds/gui.cpp46
-rw-r--r--backends/platform/3ds/gui.h41
-rw-r--r--backends/platform/3ds/icon.pngbin0 -> 3800 bytes
-rw-r--r--backends/platform/3ds/main.cpp50
-rw-r--r--backends/platform/3ds/module.mk16
-rw-r--r--backends/platform/3ds/osystem-audio.cpp107
-rw-r--r--backends/platform/3ds/osystem-events.cpp194
-rw-r--r--backends/platform/3ds/osystem-graphics.cpp465
-rw-r--r--backends/platform/3ds/osystem.cpp173
-rw-r--r--backends/platform/3ds/osystem.h210
-rw-r--r--backends/platform/3ds/portdefs.h28
-rw-r--r--backends/platform/3ds/shader.v.pica40
-rw-r--r--backends/platform/3ds/sprite.cpp144
-rw-r--r--backends/platform/3ds/sprite.h71
15 files changed, 1638 insertions, 0 deletions
diff --git a/backends/platform/3ds/3ds.mk b/backends/platform/3ds/3ds.mk
new file mode 100644
index 0000000000..bd8e743e14
--- /dev/null
+++ b/backends/platform/3ds/3ds.mk
@@ -0,0 +1,53 @@
+TARGET := scummvm
+
+APP_TITLE := ScummVM
+APP_DESCRIPTION := Point-and-click adventure game engines
+APP_AUTHOR := ScummVM Team
+APP_ICON := backends/platform/3ds/icon.png
+
+ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
+CXXFLAGS += -std=gnu++11
+ASFLAGS += -mfloat-abi=hard
+LDFLAGS += -specs=3dsx.specs $(ARCH) -L$(DEVKITPRO)/libctru/lib -L$(DEVKITPRO)/portlibs/3ds/lib
+
+.PHONY: clean_3ds
+
+all: $(TARGET).3dsx
+
+clean: clean_3ds
+
+clean_3ds:
+ $(RM) $(TARGET).3dsx
+
+$(TARGET).smdh: $(APP_ICON) $(MAKEFILE_LIST)
+ @smdhtool --create "$(APP_TITLE)" "$(APP_DESCRIPTION)" "$(APP_AUTHOR)" $(APP_ICON) $@
+ @echo built ... $(notdir $@)
+
+$(TARGET).3dsx: $(EXECUTABLE) $(TARGET).smdh
+ @3dsxtool $< $@ --smdh=$(TARGET).smdh
+ @echo built ... $(notdir $@)
+
+#---------------------------------------------------------------------------------
+# rules for assembling GPU shaders
+#---------------------------------------------------------------------------------
+define shader-as
+ $(eval FILEPATH := $(patsubst %.shbin.o,%.shbin,$@))
+ $(eval FILE := $(patsubst %.shbin.o,%.shbin,$(notdir $@)))
+ picasso -o $(FILEPATH) $1
+ bin2s $(FILEPATH) | $(AS) -o $@
+ echo "extern const u8" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(FILEPATH) | tr . _)`.h
+ echo "extern const u8" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(FILEPATH) | tr . _)`.h
+ echo "extern const u32" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(FILEPATH) | tr . _)`.h
+endef
+
+%.shbin.o : %.v.pica %.g.pica
+ @echo $(notdir $^)
+ @$(call shader-as,$^)
+
+%.shbin.o : %.v.pica
+ @echo $(notdir $<)
+ @$(call shader-as,$<)
+
+%.shbin.o : %.shlist
+ @echo $(notdir $<)
+ @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file)))
diff --git a/backends/platform/3ds/gui.cpp b/backends/platform/3ds/gui.cpp
new file mode 100644
index 0000000000..0883d5a102
--- /dev/null
+++ b/backends/platform/3ds/gui.cpp
@@ -0,0 +1,46 @@
+/* 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.
+ *
+ */
+
+#include "backends/platform/3ds/gui.h"
+#include "common/system.h"
+
+StatusMessageDialog* StatusMessageDialog::_opened = 0;
+
+StatusMessageDialog::StatusMessageDialog(const Common::String &message, uint32 duration)
+ : MessageDialog(message, 0, 0) {
+ _timer = g_system->getMillis() + duration;
+ if (_opened)
+ _opened->close();
+ _opened = this;
+}
+
+void StatusMessageDialog::handleTickle() {
+ MessageDialog::handleTickle();
+ if (g_system->getMillis() > _timer)
+ close();
+}
+
+void StatusMessageDialog::close() {
+ GUI::Dialog::close();
+ if (_opened)
+ _opened = 0;
+}
diff --git a/backends/platform/3ds/gui.h b/backends/platform/3ds/gui.h
new file mode 100644
index 0000000000..66c6547139
--- /dev/null
+++ b/backends/platform/3ds/gui.h
@@ -0,0 +1,41 @@
+/* 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.
+ *
+ */
+
+#ifndef GUI_3DS_H
+#define GUI_3DS_H
+
+#include "gui/message.h"
+
+class StatusMessageDialog : public GUI::MessageDialog {
+public:
+ StatusMessageDialog(const Common::String &message, uint32 duration);
+
+ void handleTickle();
+
+protected:
+ virtual void close();
+
+ uint32 _timer;
+ static StatusMessageDialog* _opened;
+};
+
+#endif // GUI_3DS_H
diff --git a/backends/platform/3ds/icon.png b/backends/platform/3ds/icon.png
new file mode 100644
index 0000000000..07022fbac1
--- /dev/null
+++ b/backends/platform/3ds/icon.png
Binary files differ
diff --git a/backends/platform/3ds/main.cpp b/backends/platform/3ds/main.cpp
new file mode 100644
index 0000000000..4b5dbbbbd2
--- /dev/null
+++ b/backends/platform/3ds/main.cpp
@@ -0,0 +1,50 @@
+/* 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 "osystem.h"
+#include <3ds.h>
+
+int main(int argc, char *argv[]) {
+ // Initialize basic libctru stuff
+ gfxInitDefault();
+ cfguInit();
+ osSetSpeedupEnable(true);
+// consoleInit(GFX_TOP, NULL);
+
+ g_system = new OSystem_3DS();
+ assert(g_system);
+
+ // Invoke the actual ScummVM main entry point
+// if (argc > 2)
+// res = scummvm_main(argc-2, &argv[2]);
+// else
+// res = scummvm_main(argc, argv);
+ scummvm_main(0, nullptr);
+
+ delete dynamic_cast<OSystem_3DS*>(g_system);
+
+ cfguExit();
+ gfxExit();
+ return 0;
+}
diff --git a/backends/platform/3ds/module.mk b/backends/platform/3ds/module.mk
new file mode 100644
index 0000000000..9c1e26b181
--- /dev/null
+++ b/backends/platform/3ds/module.mk
@@ -0,0 +1,16 @@
+MODULE := backends/platform/3ds
+
+MODULE_OBJS := \
+ main.o \
+ shader.shbin.o \
+ sprite.o \
+ gui.o \
+ osystem.o \
+ osystem-graphics.o \
+ osystem-audio.o \
+ osystem-events.o
+
+# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
+MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
+OBJS := $(MODULE_OBJS) $(OBJS)
+MODULE_DIRS += $(sort $(dir $(MODULE_OBJS)))
diff --git a/backends/platform/3ds/osystem-audio.cpp b/backends/platform/3ds/osystem-audio.cpp
new file mode 100644
index 0000000000..7ff788b430
--- /dev/null
+++ b/backends/platform/3ds/osystem-audio.cpp
@@ -0,0 +1,107 @@
+/* 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.
+ *
+ */
+
+#include "osystem.h"
+
+static bool exitAudioThread = false;
+static bool hasAudio = false;
+
+static void audioThreadFunc(void* arg) {
+ Audio::MixerImpl *mixer = (Audio::MixerImpl *) arg;
+ OSystem_3DS *osys = (OSystem_3DS*)g_system;
+
+ int i;
+ const int channel = 0;
+ int bufferIndex = 0;
+ const int bufferCount = 3;
+ const int bufferSize = 80000; // Can't be too small, based on delayMillis duration
+ const int sampleRate = 22050;
+ int sampleLen = 0;
+ uint32 lastTime = osys->getMillis(true);
+ uint32 time = lastTime;
+ ndspWaveBuf buffers[bufferCount];
+
+ for(i = 0; i < bufferCount; ++i) {
+ memset(&buffers[i], 0, sizeof(ndspWaveBuf));
+ buffers[i].data_vaddr = linearAlloc(bufferSize);
+ buffers[i].looping = false;
+ buffers[i].status = NDSP_WBUF_FREE;
+ }
+
+ ndspChnReset(channel);
+ ndspChnSetInterp(channel, NDSP_INTERP_LINEAR);
+ ndspChnSetRate(channel, sampleRate);
+ ndspChnSetFormat(channel, NDSP_FORMAT_STEREO_PCM16);
+
+ while(!exitAudioThread) {
+ bufferIndex++;
+ bufferIndex %= bufferCount;
+ ndspWaveBuf* buf = &buffers[bufferIndex];
+
+ osys->delayMillis(100); // Note: Increasing the delay requires a bigger buffer
+
+ time = osys->getMillis(true);
+ sampleLen = (time - lastTime) * 22 * 4; // sampleRate / 1000 * channelCount * sizeof(int16);
+ lastTime = time;
+
+ if (sampleLen > 0) {
+ buf->nsamples = mixer->mixCallback(buf->data_adpcm, sampleLen);
+ if (buf->nsamples > 0) {
+ DSP_FlushDataCache(buf->data_vaddr, bufferSize);
+ ndspChnWaveBufAdd(channel, buf);
+ }
+ }
+ }
+
+ for(i = 0; i < bufferCount; ++i)
+ linearFree(buffers[i].data_pcm8);
+}
+
+void OSystem_3DS::initAudio() {
+ _mixer = new Audio::MixerImpl(this, 22050);
+
+ hasAudio = R_SUCCEEDED(ndspInit());
+ _mixer->setReady(false);
+
+ if (hasAudio) {
+ s32 prio = 0;
+ svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
+ audioThread = threadCreate(&audioThreadFunc, _mixer, 32*1048, prio-1, -2, false);
+ }
+}
+
+void OSystem_3DS::destroyAudio() {
+ if (hasAudio) {
+ exitAudioThread = true;
+ threadJoin(audioThread, U64_MAX);
+ threadFree(audioThread);
+ ndspExit();
+ }
+
+ delete _mixer;
+ _mixer = 0;
+}
+
+Audio::Mixer *OSystem_3DS::getMixer() {
+ assert(_mixer);
+ return _mixer;
+}
diff --git a/backends/platform/3ds/osystem-events.cpp b/backends/platform/3ds/osystem-events.cpp
new file mode 100644
index 0000000000..a4ce92c394
--- /dev/null
+++ b/backends/platform/3ds/osystem-events.cpp
@@ -0,0 +1,194 @@
+/* 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 "backends/platform/3ds/gui.h"
+#include "osystem.h"
+
+static Common::Mutex *eventMutex;
+static bool exitEventThread = false;
+static InputMode inputMode = MODE_DRAG;
+
+static void pushEventQueue(Common::Queue<Common::Event>* queue, Common::Event& event) {
+ Common::StackLock lock(*eventMutex);
+ queue->push(event);
+}
+
+static void eventThreadFunc(void* arg) {
+ OSystem_3DS* osys = (OSystem_3DS*) g_system;
+ auto eventQueue = (Common::Queue<Common::Event>*) arg;
+
+ uint32 touchStartTime = osys->getMillis();
+ touchPosition lastTouch = {0,0};
+ bool isRightClick = false;
+ Common::Event event;
+
+ while(!exitEventThread) {
+ osys->delayMillis(10);
+
+ hidScanInput();
+ touchPosition touch;
+ u32 held = hidKeysHeld();
+ u32 keysPressed = hidKeysDown();
+ u32 keysReleased = hidKeysUp();
+
+ if (!aptMainLoop()) {
+ event.type = Common::EVENT_QUIT;
+ pushEventQueue(eventQueue, event);
+ }
+ if (held & KEY_TOUCH) {
+ hidTouchRead(&touch);
+ osys->transformPoint(touch);
+
+ osys->warpMouse(touch.px, touch.py);
+ event.mouse.x = touch.px;
+ event.mouse.y = touch.py;
+
+ if (keysPressed & KEY_TOUCH) {
+ touchStartTime = osys->getMillis();
+ isRightClick = (held & KEY_X || held & KEY_DUP);
+ if (inputMode == MODE_DRAG) {
+ event.type = isRightClick ? Common::EVENT_RBUTTONDOWN : Common::EVENT_LBUTTONDOWN;
+ pushEventQueue(eventQueue, event);
+ }
+ }
+ else if (touch.px != lastTouch.px || touch.py != lastTouch.py) {
+ event.type = Common::EVENT_MOUSEMOVE;
+ pushEventQueue(eventQueue, event);
+ }
+
+ lastTouch = touch;
+ }
+ else if (keysReleased & KEY_TOUCH) {
+ event.mouse.x = lastTouch.px;
+ event.mouse.y = lastTouch.py;
+ printf("clicked %u, %u\n", lastTouch.px, lastTouch.py);
+ if (inputMode == MODE_DRAG) {
+ event.type = isRightClick ? Common::EVENT_RBUTTONUP : Common::EVENT_LBUTTONUP;
+ pushEventQueue(eventQueue, event);
+ }
+ else if (osys->getMillis() - touchStartTime < 200) {
+ // Process click in MODE_HOVER
+ event.type = Common::EVENT_MOUSEMOVE;
+ pushEventQueue(eventQueue, event);
+ event.type = isRightClick ? Common::EVENT_RBUTTONDOWN : Common::EVENT_LBUTTONDOWN;
+ pushEventQueue(eventQueue, event);
+ event.type = isRightClick ? Common::EVENT_RBUTTONUP : Common::EVENT_LBUTTONUP;
+ pushEventQueue(eventQueue, event);
+ }
+ }
+ if (keysPressed & KEY_R) {
+ if (inputMode == MODE_DRAG) {
+ inputMode = MODE_HOVER;
+ osys->displayMessageOnOSD("Hover Mode");
+ } else {
+ inputMode = MODE_DRAG;
+ osys->displayMessageOnOSD("Drag Mode");
+ }
+ }
+ if (keysPressed & KEY_A || keysPressed & KEY_DLEFT) {
+ // SIMULATE LEFT CLICK
+ event.mouse.x = lastTouch.px;
+ event.mouse.y = lastTouch.py;
+ event.type = Common::EVENT_LBUTTONDOWN;
+ pushEventQueue(eventQueue, event);
+ event.type = Common::EVENT_LBUTTONUP;
+ pushEventQueue(eventQueue, event);
+ }
+ if (keysPressed & KEY_X || keysPressed & KEY_DUP) {
+ // SIMULATE RIGHT CLICK
+ event.mouse.x = lastTouch.px;
+ event.mouse.y = lastTouch.py;
+ event.type = Common::EVENT_RBUTTONDOWN;
+ pushEventQueue(eventQueue, event);
+ event.type = Common::EVENT_RBUTTONUP;
+ pushEventQueue(eventQueue, event);
+ }
+ if (keysPressed & KEY_L) {
+ event.type = Common::EVENT_VIRTUAL_KEYBOARD;
+ pushEventQueue(eventQueue, event);
+ }
+ if (keysPressed & KEY_START) {
+ event.type = Common::EVENT_MAINMENU;
+ pushEventQueue(eventQueue, event);
+ }
+ if (keysPressed & KEY_SELECT) {
+ event.type = Common::EVENT_RTL;
+ pushEventQueue(eventQueue, event);
+ }
+ if (keysPressed & KEY_B || keysReleased & KEY_B || keysPressed & KEY_DDOWN || keysReleased & KEY_DDOWN) {
+ if (keysPressed & KEY_B || keysPressed & KEY_DDOWN)
+ event.type = Common::EVENT_KEYDOWN;
+ else
+ event.type = Common::EVENT_KEYUP;
+ event.kbd.keycode = Common::KEYCODE_ESCAPE;
+ event.kbd.ascii = Common::ASCII_ESCAPE;
+ event.kbd.flags = 0;
+ pushEventQueue(eventQueue, event);
+ }
+
+ // TODO: EVENT_PREDICTIVE_DIALOG
+ // EVENT_SCREEN_CHANGED
+ }
+}
+
+void OSystem_3DS::initEvents() {
+ eventMutex = new Common::Mutex();
+ s32 prio = 0;
+ svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
+ _eventThread = threadCreate(&eventThreadFunc, &_eventQueue, 2048, prio-1, -2, false);
+}
+
+void OSystem_3DS::destroyEvents() {
+ exitEventThread = true;
+ threadJoin(_eventThread, U64_MAX);
+ threadFree(_eventThread);
+ delete eventMutex;
+}
+
+void OSystem_3DS::transformPoint(touchPosition &point) {
+ if (!_overlayVisible) {
+ point.px = static_cast<float>(point.px) / _gameTexture.getScaleX() - _gameX;
+ point.py = static_cast<float>(point.py) / _gameTexture.getScaleY() - _gameY;
+ }
+}
+
+void OSystem_3DS::displayMessageOnOSD(const char *msg) {
+ _messageOSD = msg;
+ _showMessageOSD = true;
+}
+
+bool OSystem_3DS::pollEvent(Common::Event &event) {
+ if (_showMessageOSD) {
+ _showMessageOSD = false;
+ StatusMessageDialog dialog(_messageOSD, 800);
+ dialog.runModal();
+ }
+
+ Common::StackLock lock(*eventMutex);
+
+ if (_eventQueue.empty())
+ return false;
+
+ event = _eventQueue.pop();
+ return true;
+}
diff --git a/backends/platform/3ds/osystem-graphics.cpp b/backends/platform/3ds/osystem-graphics.cpp
new file mode 100644
index 0000000000..5e70dced73
--- /dev/null
+++ b/backends/platform/3ds/osystem-graphics.cpp
@@ -0,0 +1,465 @@
+/* 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 "backends/platform/3ds/osystem.h"
+#include "backends/platform/3ds/shader_shbin.h"
+#include <common/rect.h>
+#include <algorithm>
+
+// Used to transfer the final rendered display to the framebuffer
+#define DISPLAY_TRANSFER_FLAGS \
+ (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | \
+ GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | \
+ GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \
+ GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
+
+void OSystem_3DS::initGraphics() {
+ _pfGame = Graphics::PixelFormat::createFormatCLUT8();
+ _pfGameTexture = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
+
+ C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
+
+ // Initialize the render targets
+ _renderTargetTop =
+ C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
+ C3D_RenderTargetSetClear(_renderTargetTop, C3D_CLEAR_ALL, 0x0000000, 0);
+ C3D_RenderTargetSetOutput(_renderTargetTop, GFX_TOP, GFX_LEFT,
+ DISPLAY_TRANSFER_FLAGS);
+
+ _renderTargetBottom =
+ C3D_RenderTargetCreate(240, 320, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
+ C3D_RenderTargetSetClear(_renderTargetBottom, C3D_CLEAR_ALL, 0x00000000, 0);
+ C3D_RenderTargetSetOutput(_renderTargetBottom, GFX_BOTTOM, GFX_LEFT,
+ DISPLAY_TRANSFER_FLAGS);
+
+ // Load and bind simple default shader (shader.v.pica)
+ _dvlb = DVLB_ParseFile((u32*)shader_shbin, shader_shbin_size);
+ shaderProgramInit(&_program);
+ shaderProgramSetVsh(&_program, &_dvlb->DVLE[0]);
+ C3D_BindProgram(&_program);
+
+ _projectionLocation = shaderInstanceGetUniformLocation(_program.vertexShader, "projection");
+ _modelviewLocation = shaderInstanceGetUniformLocation(_program.vertexShader, "modelView");
+
+ C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
+ AttrInfo_Init(attrInfo);
+ AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position
+ AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord
+
+ Mtx_OrthoTilt(&_projectionTop, 0.0, 400.0, 240.0, 0.0, 0.0, 1.0);
+ Mtx_OrthoTilt(&_projectionBottom, 0.0, 320.0, 240.0, 0.0, 0.0, 1.0);
+
+ C3D_TexEnv* env = C3D_GetTexEnv(0);
+ C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, 0, 0);
+ C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
+ C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);
+
+ C3D_DepthTest(false, GPU_GEQUAL, GPU_WRITE_ALL);
+ C3D_CullFace(GPU_CULL_NONE);
+}
+
+void OSystem_3DS::destroyGraphics() {
+ _gameScreen.free();
+ _gameTexture.free();
+ _overlay.free();
+
+ shaderProgramFree(&_program);
+ DVLB_Free(_dvlb);
+
+ C3D_RenderTargetDelete(_renderTargetTop);
+ C3D_RenderTargetDelete(_renderTargetBottom);
+
+ C3D_Fini();
+}
+
+bool OSystem_3DS::hasFeature(OSystem::Feature f) {
+ return (f == OSystem::kFeatureFullscreenMode ||
+ f == OSystem::kFeatureCursorPalette ||
+ f == OSystem::kFeatureOverlaySupportsAlpha);
+}
+
+void OSystem_3DS::setFeatureState(OSystem::Feature f, bool enable) {
+ switch (f) {
+ case OSystem::kFeatureFullscreenMode:
+ _isFullscreen = enable;
+ break;
+ case OSystem::kFeatureCursorPalette:
+ _cursorPaletteEnabled = enable;
+ flushCursor();
+ break;
+ default:
+ break;
+ }
+}
+
+bool OSystem_3DS::getFeatureState(OSystem::Feature f) {
+ switch (f) {
+ case OSystem::kFeatureFullscreenMode:
+ return _isFullscreen;
+ case OSystem::kFeatureCursorPalette:
+ return _cursorPaletteEnabled;
+ default:
+ return false;
+ }
+}
+
+const OSystem::GraphicsMode *
+OSystem_3DS::getSupportedGraphicsModes() const {
+ return s_graphicsModes;
+}
+
+int OSystem_3DS::getDefaultGraphicsMode() const {
+ return GFX_LINEAR;
+}
+
+bool OSystem_3DS::setGraphicsMode(int mode) {
+ return true;
+}
+
+void OSystem_3DS::resetGraphicsScale() {
+ printf("resetGraphicsScale\n");
+}
+
+int OSystem_3DS::getGraphicsMode() const {
+ return GFX_LINEAR;
+}
+void OSystem_3DS::initSize(uint width, uint height,
+ const Graphics::PixelFormat *format) {
+ printf("3ds initsize w:%d h:%d\n", width, height);
+ _gameWidth = width;
+ _gameHeight = height;
+ _gameTexture.create(width, height, _pfGameTexture);
+ _overlay.create(getOverlayWidth(), getOverlayHeight(), _pfGameTexture);
+
+ if (format) {
+ printf("pixelformat: %d %d %d %d %d\n", format->bytesPerPixel, format->rBits(), format->gBits(), format->bBits(), format->aBits());;
+ _pfGame = *format;
+ }
+
+ _gameScreen.create(width, height, _pfGame);
+
+ _focusDirty = true;
+ _focusRect = Common::Rect(_gameWidth, _gameHeight);
+
+ if (_isFullscreen) {
+ _gameRatio = 320.f / 240.f;
+ _gameX = _gameY = 0;
+ _gameTexture.setScale(320.f / width, 240.f / height);
+ } else {
+ _gameRatio = static_cast<float>(width) / height;
+ if (width > height) {
+ _gameX = 0;
+ _gameY = (240.f - 320.f / width * height) / 2.f;
+ } else {
+ _gameY = 0;
+ _gameX = (320.f - 240.f / height * width) / 2.f;
+ }
+ _gameTexture.setScale((width > 320) ? 320.f / width : 1.f,
+ (height > 240) ? 240.f / height : 1.f);
+ }
+ _gameTexture.setPosition(_gameX, _gameY);
+ _cursorTexture.setScale(_gameTexture.getScaleX(), _gameTexture.getScaleY());
+}
+
+Common::List<Graphics::PixelFormat> OSystem_3DS::getSupportedFormats() const {
+ Common::List<Graphics::PixelFormat> list;
+ list.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); // GPU_RGBA8
+ list.push_back(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); // GPU_RGB565
+// list.push_back(Graphics::PixelFormat(3, 0, 0, 0, 8, 0, 8, 16, 0)); // GPU_RGB8
+ list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); // RGB555 (needed for FMTOWNS?)
+ list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)); // GPU_RGBA5551
+ list.push_back(Graphics::PixelFormat::createFormatCLUT8());
+ return list;
+}
+
+void OSystem_3DS::beginGFXTransaction() {
+ //
+}
+OSystem::TransactionError OSystem_3DS::endGFXTransaction() {
+ return OSystem::kTransactionSuccess;
+}
+
+void OSystem_3DS::setPalette(const byte *colors, uint start, uint num) {
+// printf("setPalette\n");
+ assert(start + num <= 256);
+ memcpy(_palette + 3 * start, colors, 3 * num);
+
+ // Manually update all color that were changed
+ if (_gameScreen.format.bytesPerPixel == 1) {
+ flushGameScreen();
+ }
+}
+void OSystem_3DS::grabPalette(byte *colors, uint start, uint num) {
+// printf("grabPalette\n");
+ assert(start + num <= 256);
+ memcpy(colors, _palette + 3 * start, 3 * num);
+}
+
+void OSystem_3DS::copyRectToScreen(const void *buf, int pitch, int x,
+ int y, int w, int h) {
+ Common::Rect rect(x, y, x+w, y+h);
+ _gameScreen.copyRectToSurface(buf, pitch, x, y, w, h);
+ Graphics::Surface subSurface = _gameScreen.getSubArea(rect);
+
+ Graphics::Surface *convertedSubSurface = subSurface.convertTo(_pfGameTexture, _palette);
+ _gameTexture.copyRectToSurface(*convertedSubSurface, x, y, Common::Rect(w, h));
+
+ convertedSubSurface->free();
+ delete convertedSubSurface;
+ _gameTexture.markDirty();
+}
+
+void OSystem_3DS::flushGameScreen() {
+ Graphics::Surface *converted = _gameScreen.convertTo(_pfGameTexture, _palette);
+ _gameTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h));
+ _gameTexture.markDirty();
+ converted->free();
+ delete converted;
+}
+
+Graphics::Surface *OSystem_3DS::lockScreen() {
+ printf("lockScreen\n");
+ return &_gameScreen;
+}
+void OSystem_3DS::unlockScreen() {
+ printf("unlockScreen\n");
+ flushGameScreen();
+}
+
+void OSystem_3DS::updateScreen() {
+
+ updateFocus();
+
+ C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
+ // Render top screen
+ C3D_FrameDrawOn(_renderTargetTop);
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionTop);
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, &_focusMatrix);
+ _gameTexture.render();
+ _gameTexture.render();
+
+ // Render bottom screen
+ C3D_FrameDrawOn(_renderTargetBottom);
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionBottom);
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _gameTexture.getMatrix());
+ _gameTexture.render();
+ if (_overlayVisible) {
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _overlay.getMatrix());
+ _overlay.render();
+ }
+ if (_cursorVisible) {
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _cursorTexture.getMatrix());
+ _cursorTexture.render();
+ }
+ C3D_FrameEnd(0);
+}
+
+void OSystem_3DS::setShakePos(int shakeOffset) {
+ // TODO: implement this in overlay, top screen, and mouse too
+ _screenShakeOffset = shakeOffset;
+ _gameTexture.setPosition(_gameX, _gameY + shakeOffset);
+}
+
+void OSystem_3DS::setFocusRectangle(const Common::Rect &rect) {
+// printf("setfocus: %d %d %d %d\n", rect.left, rect.top, rect.width(), rect.height());
+ _focusRect = rect;
+ _focusDirty = true;
+ _focusClearTime = 0;
+}
+
+void OSystem_3DS::clearFocusRectangle() {
+ _focusClearTime = getMillis();
+}
+
+void OSystem_3DS::updateFocus() {
+
+ if (_focusClearTime && getMillis() - _focusClearTime > 5000) {
+ _focusClearTime = 0;
+ _focusDirty = true;
+ _focusRect = Common::Rect(_gameWidth, _gameHeight);
+ }
+
+ if (_focusDirty) {
+ float duration = 1.f / 20.f; // Focus animation in frame duration
+ float w = 400.f;
+ float h = 240.f;
+ float ratio = _focusRect.width() / _focusRect.height();
+ if (ratio > w/h) {
+ _focusTargetScaleX = w / _focusRect.width();
+ float newHeight = (float)_focusRect.width() / w/h;
+ _focusTargetScaleY = h / newHeight;
+ _focusTargetPosX = _focusTargetScaleX * _focusRect.left;
+ _focusTargetPosY = _focusTargetScaleY * ((float)_focusRect.top - (newHeight - _focusRect.height())/2.f);
+ } else {
+ _focusTargetScaleY = h / _focusRect.height();
+ float newWidth = (float)_focusRect.height() * w/h;
+ _focusTargetScaleX = w / newWidth;
+ _focusTargetPosY = _focusTargetScaleY * _focusRect.top;
+ _focusTargetPosX = _focusTargetScaleX * ((float)_focusRect.left - (newWidth - _focusRect.width())/2.f);
+ }
+ if (_focusTargetPosX < 0 && _focusTargetScaleY != 240.f / _gameHeight)
+ _focusTargetPosX = 0;
+ if (_focusTargetPosY < 0 && _focusTargetScaleX != 400.f / _gameWidth)
+ _focusTargetPosY = 0;
+ _focusStepPosX = duration * (_focusTargetPosX - _focusPosX);
+ _focusStepPosY = duration * (_focusTargetPosY - _focusPosY);
+ _focusStepScaleX = duration * (_focusTargetScaleX - _focusScaleX);
+ _focusStepScaleY = duration * (_focusTargetScaleY - _focusScaleY);
+ }
+
+ if (_focusDirty || _focusPosX != _focusTargetPosX || _focusPosY != _focusTargetPosY ||
+ _focusScaleX != _focusTargetScaleX || _focusScaleY != _focusTargetScaleY) {
+ _focusDirty = false;
+
+ if ((_focusStepPosX > 0 && _focusPosX > _focusTargetPosX) || (_focusStepPosX < 0 && _focusPosX < _focusTargetPosX))
+ _focusPosX = _focusTargetPosX;
+ else if (_focusPosX != _focusTargetPosX)
+ _focusPosX += _focusStepPosX;
+
+ if ((_focusStepPosY > 0 && _focusPosY > _focusTargetPosY) || (_focusStepPosY < 0 && _focusPosY < _focusTargetPosY))
+ _focusPosY = _focusTargetPosY;
+ else if (_focusPosY != _focusTargetPosY)
+ _focusPosY += _focusStepPosY;
+
+ if ((_focusStepScaleX > 0 && _focusScaleX > _focusTargetScaleX) || (_focusStepScaleX < 0 && _focusScaleX < _focusTargetScaleX))
+ _focusScaleX = _focusTargetScaleX;
+ else if (_focusScaleX != _focusTargetScaleX)
+ _focusScaleX += _focusStepScaleX;
+
+ if ((_focusStepScaleY > 0 && _focusScaleY > _focusTargetScaleY) || (_focusStepScaleY < 0 && _focusScaleY < _focusTargetScaleY))
+ _focusScaleY = _focusTargetScaleY;
+ else if (_focusScaleY != _focusTargetScaleY)
+ _focusScaleY += _focusStepScaleY;
+
+ Mtx_Identity(&_focusMatrix);
+ Mtx_Translate(&_focusMatrix, -_focusPosX, -_focusPosY, 0);
+ Mtx_Scale(&_focusMatrix, _focusScaleX, _focusScaleY, 1.f);
+ }
+}
+
+void OSystem_3DS::showOverlay() {
+ _overlayVisible = true;
+ _cursorTexture.setScale(1.f, 1.f);
+ updateScreen();
+}
+
+void OSystem_3DS::hideOverlay() {
+ _overlayVisible = false;
+ _cursorTexture.setScale(_gameTexture.getScaleX(), _gameTexture.getScaleY());
+ updateScreen();
+}
+
+Graphics::PixelFormat OSystem_3DS::getOverlayFormat() const {
+ return _pfGameTexture;
+}
+
+void OSystem_3DS::clearOverlay() {
+ _overlay.clear();
+}
+
+void OSystem_3DS::grabOverlay(void *buf, int pitch) {
+ for(int y = 0; y < getOverlayHeight(); ++y) {
+ memcpy(buf, _overlay.getBasePtr(0, y), pitch);
+ }
+}
+
+void OSystem_3DS::copyRectToOverlay(const void *buf, int pitch, int x,
+ int y, int w, int h) {
+ _overlay.copyRectToSurface(buf, pitch, x, y, w, h);
+ _overlay.markDirty();
+}
+
+int16 OSystem_3DS::getOverlayHeight() {
+ return 240;
+}
+
+int16 OSystem_3DS::getOverlayWidth() {
+ return 320;
+}
+
+bool OSystem_3DS::showMouse(bool visible) {
+ _cursorVisible = visible;
+ flushCursor();
+ return !visible;
+}
+
+void OSystem_3DS::warpMouse(int x, int y) {
+ _cursorX = x;
+ _cursorY = y;
+ // TODO: adjust for _cursorScalable ?
+ _cursorTexture.setPosition(x - _cursorHotspotX + (_overlayVisible ? 0 : _gameX),
+ y - _cursorHotspotY + (_overlayVisible ? 0 : _gameY));
+}
+
+void OSystem_3DS::setMouseCursor(const void *buf, uint w, uint h,
+ int hotspotX, int hotspotY,
+ uint32 keycolor, bool dontScale,
+ const Graphics::PixelFormat *format) {
+ _cursorScalable = !dontScale;
+ _cursorHotspotX = hotspotX;
+ _cursorHotspotY = hotspotY;
+ _cursorKeyColor = keycolor;
+ _pfCursor = !format ? Graphics::PixelFormat::createFormatCLUT8() : *format;
+
+ if (w != _cursor.w || h != _cursor.h || _cursor.format != _pfCursor) {
+ _cursor.create(w, h, _pfCursor);
+ _cursorTexture.create(w, h, _pfGameTexture);
+ }
+
+ _cursor.copyRectToSurface(buf, w, 0, 0, w, h);
+ flushCursor();
+
+ warpMouse(_cursorX, _cursorY);
+}
+
+void OSystem_3DS::setCursorPalette(const byte *colors, uint start, uint num) {
+ assert(start + num <= 256);
+ memcpy(_cursorPalette + 3 * start, colors, 3 * num);
+ _cursorPaletteEnabled = true;
+ flushCursor();
+}
+
+void OSystem_3DS::flushCursor() {
+ if (_cursor.getPixels()) {
+ Graphics::Surface *converted = _cursor.convertTo(_pfGameTexture, _cursorPaletteEnabled ? _cursorPalette : _palette);
+ _cursorTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h));
+ _cursorTexture.markDirty();
+ converted->free();
+ delete converted;
+
+ if (_pfCursor.bytesPerPixel == 1) {
+ uint* dest = (uint*) _cursorTexture.getPixels();
+ byte* src = (byte*) _cursor.getPixels();
+ for (int y = 0; y < _cursor.h; ++y) {
+ for (int x = 0; x < _cursor.w; ++x) {
+ if (*src++ == _cursorKeyColor)
+ *dest++ = 0;
+ else
+ dest++;
+ }
+ dest += _cursorTexture.w - _cursorTexture.actualWidth;
+ }
+ }
+ }
+}
diff --git a/backends/platform/3ds/osystem.cpp b/backends/platform/3ds/osystem.cpp
new file mode 100644
index 0000000000..fa2980a361
--- /dev/null
+++ b/backends/platform/3ds/osystem.cpp
@@ -0,0 +1,173 @@
+/* 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 "osystem.h"
+
+#include "backends/saves/default/default-saves.h"
+#include "backends/timer/default/default-timer.h"
+#include "backends/events/default/default-events.h"
+#include "audio/mixer_intern.h"
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/str.h"
+
+#include "backends/fs/posix/posix-fs-factory.h"
+#include "backends/fs/posix/posix-fs.h"
+#include <unistd.h>
+#include <time.h>
+
+OSystem_3DS::OSystem_3DS():
+_focusDirty(true),
+_focusRect(Common::Rect(1,1)),
+_focusPosX(0),
+_focusPosY(0),
+_focusTargetPosX(0),
+_focusTargetPosY(0),
+_focusStepPosX(0),
+_focusStepPosY(0),
+_focusScaleX(1.f),
+_focusScaleY(1.f),
+_focusTargetScaleX(1.f),
+_focusTargetScaleY(1.f),
+_focusStepScaleX(0.f),
+_focusStepScaleY(0.f),
+_focusClearTime(0),
+_showMessageOSD(false),
+_isFullscreen(false),
+_cursorVisible(false),
+_cursorScalable(false),
+_cursorPaletteEnabled(false),
+_cursorX(0),
+_cursorY(0),
+_cursorHotspotX(0),
+_cursorHotspotY(0),
+_gameX(0),
+_gameY(0),
+_gameWidth(320),
+_gameHeight(240) {
+ chdir("/");
+ _fsFactory = new POSIXFilesystemFactory();
+ Posix::assureDirectoryExists("/3ds/scummvm/saves/");
+}
+
+OSystem_3DS::~OSystem_3DS() {
+ destroyEvents();
+ destroyAudio();
+ destroyGraphics();
+
+ delete _timerManager;
+ _timerManager = 0;
+}
+
+void OSystem_3DS::quit() {
+ //
+}
+
+void OSystem_3DS::initBackend() {
+ ConfMan.registerDefault("fullscreen", true);
+ ConfMan.registerDefault("aspect_ratio", true);
+ if (!ConfMan.hasKey("vkeybd_pack_name"))
+ ConfMan.set("vkeybd_pack_name", "vkeybd_small");
+ if (!ConfMan.hasKey("vkeybdpath"))
+ ConfMan.set("vkeybdpath", "/3ds/scummvm/kb");
+ if (!ConfMan.hasKey("themepath"))
+ ConfMan.set("themepath", "/3ds/scummvm");
+ if (!ConfMan.hasKey("gui_theme"))
+ ConfMan.set("gui_theme", "builtin");
+
+ _timerManager = new DefaultTimerManager();
+ _savefileManager = new DefaultSaveFileManager("/3ds/scummvm/saves/");
+
+ initGraphics();
+ initAudio();
+ initEvents();
+ EventsBaseBackend::initBackend();
+}
+
+Common::String OSystem_3DS::getDefaultConfigFileName() {
+ return "/3ds/scummvm/scummvm.ini";
+}
+
+uint32 OSystem_3DS::getMillis(bool skipRecord) {
+ return svcGetSystemTick() / TICKS_PER_MSEC;
+}
+
+void OSystem_3DS::delayMillis(uint msecs) {
+ svcSleepThread(msecs * 1000000);
+}
+
+void OSystem_3DS::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;
+}
+
+OSystem::MutexRef OSystem_3DS::createMutex() {
+ RecursiveLock* mutex = new RecursiveLock();
+ RecursiveLock_Init(mutex);
+ return (OSystem::MutexRef) mutex;
+}
+void OSystem_3DS::lockMutex(MutexRef mutex) {
+ RecursiveLock_Lock((RecursiveLock *)mutex);
+}
+void OSystem_3DS::unlockMutex(MutexRef mutex) {
+ RecursiveLock_Unlock((RecursiveLock *)mutex);
+}
+void OSystem_3DS::deleteMutex(MutexRef mutex) {
+ delete (RecursiveLock *)mutex;
+}
+
+Common::String OSystem_3DS::getSystemLanguage() const {
+ u8 langcode;
+ CFGU_GetSystemLanguage(&langcode);
+ switch (langcode) {
+ case CFG_LANGUAGE_JP: return "ja_JP";
+ case CFG_LANGUAGE_EN: return "en_US";
+ case CFG_LANGUAGE_FR: return "fr_FR";
+ case CFG_LANGUAGE_DE: return "de_DE";
+ case CFG_LANGUAGE_IT: return "it_IT";
+ case CFG_LANGUAGE_ES: return "es_ES";
+ case CFG_LANGUAGE_ZH: return "zh_CN";
+ case CFG_LANGUAGE_KO: return "ko_KR";
+ case CFG_LANGUAGE_NL: return "nl_NL";
+ case CFG_LANGUAGE_PT: return "pt_BR";
+ case CFG_LANGUAGE_RU: return "ru_RU";
+ case CFG_LANGUAGE_TW: return "zh_HK";
+ default: return "en_US";
+ }
+}
+
+void OSystem_3DS::fatalError() {
+ printf("FatalError!\n");
+}
+
+void OSystem_3DS::logMessage(LogMessageType::Type type, const char *message) {
+ printf("3DS log: %s\n", message);
+}
diff --git a/backends/platform/3ds/osystem.h b/backends/platform/3ds/osystem.h
new file mode 100644
index 0000000000..98d9ad0cb6
--- /dev/null
+++ b/backends/platform/3ds/osystem.h
@@ -0,0 +1,210 @@
+/* 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.
+ *
+ */
+
+#ifndef PLATFORM_3DS_H
+#define PLATFORM_3DS_H
+
+#include <citro3d.h>
+#include "backends/mutex/mutex.h"
+#include "backends/base-backend.h"
+#include "graphics/palette.h"
+#include "base/main.h"
+#include "audio/mixer_intern.h"
+#include "backends/graphics/graphics.h"
+#include "backends/platform/3ds/sprite.h"
+#include "common/rect.h"
+#include "common/queue.h"
+
+#define TICKS_PER_MSEC 268123
+
+enum {
+ GFX_LINEAR = 0,
+ GFX_NEAREST = 1
+};
+
+enum InputMode {
+ MODE_HOVER,
+ MODE_DRAG,
+};
+
+static const OSystem::GraphicsMode s_graphicsModes[] = {
+ {"default", "Default Test", GFX_LINEAR},
+ { 0, 0, 0 }
+};
+
+class OSystem_3DS : public EventsBaseBackend, public PaletteManager {
+public:
+ OSystem_3DS();
+ ~OSystem_3DS();
+
+ virtual void initBackend();
+
+ virtual bool hasFeature(OSystem::Feature f);
+ virtual void setFeatureState(OSystem::Feature f, bool enable);
+ virtual bool getFeatureState(OSystem::Feature f);
+
+ virtual bool pollEvent(Common::Event &event);
+
+ virtual uint32 getMillis(bool skipRecord = false);
+ virtual void delayMillis(uint msecs);
+ virtual void getTimeAndDate(TimeDate &t) const;
+
+ virtual MutexRef createMutex();
+ virtual void lockMutex(MutexRef mutex);
+ virtual void unlockMutex(MutexRef mutex);
+ virtual void deleteMutex(MutexRef mutex);
+
+ virtual void logMessage(LogMessageType::Type type, const char *message);
+
+ virtual Audio::Mixer *getMixer();
+ virtual PaletteManager *getPaletteManager() { return this; }
+ virtual Common::String getSystemLanguage() const;
+ virtual void fatalError();
+ virtual void quit();
+
+ virtual Common::String getDefaultConfigFileName();
+
+ // Graphics
+ virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const;
+ int getDefaultGraphicsMode() const;
+ bool setGraphicsMode(int mode);
+ void resetGraphicsScale();
+ int getGraphicsMode() const;
+ inline Graphics::PixelFormat getScreenFormat() const { return _pfGame; }
+ virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const;
+ void initSize(uint width, uint height,
+ const Graphics::PixelFormat *format = NULL);
+ virtual int getScreenChangeID() const { return 0; };
+
+ void beginGFXTransaction();
+ OSystem::TransactionError endGFXTransaction();
+ int16 getHeight(){ return _gameHeight; }
+ int16 getWidth(){ return _gameWidth; }
+ void setPalette(const byte *colors, uint start, uint num);
+ void grabPalette(byte *colors, uint start, uint num);
+ void copyRectToScreen(const void *buf, int pitch, int x, int y, int w,
+ int h);
+ Graphics::Surface *lockScreen();
+ void unlockScreen();
+ void updateScreen();
+ void setShakePos(int shakeOffset);
+ void setFocusRectangle(const Common::Rect &rect);
+ void clearFocusRectangle();
+ void showOverlay();
+ void hideOverlay();
+ Graphics::PixelFormat getOverlayFormat() const;
+ void clearOverlay();
+ void grabOverlay(void *buf, int pitch);
+ void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w,
+ int h);
+ virtual int16 getOverlayHeight();
+ virtual int16 getOverlayWidth();
+ virtual void displayMessageOnOSD(const char *msg);
+
+ bool showMouse(bool visible);
+ void warpMouse(int x, int y);
+ void setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
+ int hotspotY, uint32 keycolor, bool dontScale = false,
+ const Graphics::PixelFormat *format = NULL);
+ void setCursorPalette(const byte *colors, uint start, uint num);
+
+ void transformPoint(touchPosition &point);
+
+ void updateFocus();
+
+private:
+ void initGraphics();
+ void destroyGraphics();
+ void initAudio();
+ void destroyAudio();
+ void initEvents();
+ void destroyEvents();
+
+ void flushGameScreen();
+ void flushCursor();
+
+protected:
+ Audio::MixerImpl *_mixer;
+
+private:
+ u16 _gameWidth, _gameHeight;
+ u16 _gameX, _gameY;
+ float _gameRatio;
+
+ // Audio
+ Thread audioThread;
+
+ // Graphics
+ Graphics::PixelFormat _pfGame;
+ Graphics::PixelFormat _pfGameTexture;
+ Graphics::PixelFormat _pfCursor;
+ byte _palette[3 * 256];
+ byte _cursorPalette[3 * 256];
+
+ Graphics::Surface _gameScreen;
+ Sprite _gameTexture;
+ Sprite _overlay;
+
+ int _screenShakeOffset;
+ bool _overlayVisible;
+ bool _isFullscreen;
+
+ DVLB_s *_dvlb;
+ shaderProgram_s _program;
+ int _projectionLocation;
+ int _modelviewLocation;
+ C3D_Mtx _projectionTop;
+ C3D_Mtx _projectionBottom;
+ C3D_RenderTarget* _renderTargetTop;
+ C3D_RenderTarget* _renderTargetBottom;
+
+ // Focus
+ Common::Rect _focusRect;
+ bool _focusDirty;
+ C3D_Mtx _focusMatrix;
+ int _focusPosX, _focusPosY;
+ int _focusTargetPosX, _focusTargetPosY;
+ float _focusStepPosX, _focusStepPosY;
+ float _focusScaleX, _focusScaleY;
+ float _focusTargetScaleX, _focusTargetScaleY;
+ float _focusStepScaleX, _focusStepScaleY;
+ uint32 _focusClearTime;
+
+ // Events
+ Thread _eventThread;
+ Common::Queue<Common::Event> _eventQueue;
+
+ Common::String _messageOSD;
+ bool _showMessageOSD;
+
+ // Cursor
+ Graphics::Surface _cursor;
+ Sprite _cursorTexture;
+ bool _cursorPaletteEnabled;
+ bool _cursorVisible;
+ bool _cursorScalable;
+ int _cursorX, _cursorY;
+ int _cursorHotspotX, _cursorHotspotY;
+ uint32 _cursorKeyColor;
+};
+
+#endif
diff --git a/backends/platform/3ds/portdefs.h b/backends/platform/3ds/portdefs.h
new file mode 100644
index 0000000000..f58cf5a675
--- /dev/null
+++ b/backends/platform/3ds/portdefs.h
@@ -0,0 +1,28 @@
+/* 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.
+ *
+ */
+
+#ifndef _PORTDEFS_H_
+#define _PORTDEFS_H_
+
+#define LURE_CLICKABLE_MENUS
+
+#endif
diff --git a/backends/platform/3ds/shader.v.pica b/backends/platform/3ds/shader.v.pica
new file mode 100644
index 0000000000..a359768218
--- /dev/null
+++ b/backends/platform/3ds/shader.v.pica
@@ -0,0 +1,40 @@
+; PICA200 vertex shader
+
+; Uniforms
+.fvec projection[4], modelView[4]
+
+; Constants
+.constf myconst(0.0, 1.0, -1.0, 0.1)
+.alias zeros myconst.xxxx ; Vector full of zeros
+.alias ones myconst.yyyy ; Vector full of ones
+
+; Outputs
+.out outpos position
+.out outtex texcoord0
+
+; Inputs (defined as aliases for convenience)
+.alias inpos v0
+.alias intex v1
+
+.proc main
+ ; Force the w component of inpos to be 1.0
+ mov r0.xyz, inpos
+ mov r0.w, ones
+
+ ; r1 = modelView * inpos
+ dp4 r1.x, modelView[0], r0
+ dp4 r1.y, modelView[1], r0
+ dp4 r1.z, modelView[2], r0
+ dp4 r1.w, modelView[3], r0
+
+ ; outpos = projection * r1
+ dp4 outpos.x, projection[0], r1
+ dp4 outpos.y, projection[1], r1
+ dp4 outpos.z, projection[2], r1
+ dp4 outpos.w, projection[3], r1
+
+ mov outtex, intex
+
+ end
+.end
+
diff --git a/backends/platform/3ds/sprite.cpp b/backends/platform/3ds/sprite.cpp
new file mode 100644
index 0000000000..d779fa40ff
--- /dev/null
+++ b/backends/platform/3ds/sprite.cpp
@@ -0,0 +1,144 @@
+/* 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 "backends/platform/3ds/sprite.h"
+#include <algorithm>
+#include <3ds.h>
+
+static uint nextHigher2(uint v) {
+ if (v == 0)
+ return 1;
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ return ++v;
+}
+
+Sprite::Sprite()
+: dirtyPixels(true)
+, dirtyMatrix(true)
+, actualWidth(0)
+, actualHeight(0)
+, posX(0)
+, posY(0)
+, scaleX(1.f)
+, scaleY(1.f)
+{
+ Mtx_Identity(&modelview);
+
+ vertices = (vertex*)linearAlloc(sizeof(vertex) * 4);
+}
+
+Sprite::~Sprite() {
+ //
+}
+
+void Sprite::create(uint16 width, uint16 height, const Graphics::PixelFormat &f) {
+ free();
+
+ actualWidth = width;
+ actualHeight = height;
+ format = f;
+ w = std::max(nextHigher2(width), 64u);
+ h = std::max(nextHigher2(height), 64u);;
+ pitch = w * format.bytesPerPixel;
+ dirtyPixels = true;
+
+ if (width && height) {
+ pixels = linearAlloc(h * pitch);
+ C3D_TexInit(&texture, w, h, GPU_RGBA8);
+ C3D_TexSetFilter(&texture, GPU_LINEAR, GPU_NEAREST);
+ assert(pixels && texture.data);
+ clear();
+ }
+
+ float x = 0.f, y = 0.f;
+ float u = (float)width/w;
+ float v = (float)height/h;
+ vertex tmp[4] = {
+ {{x, y, 0.5f}, {0, 0}},
+ {{x+width, y, 0.5f}, {u, 0}},
+ {{x, y+height, 0.5f}, {0, v}},
+ {{x+width, y+height, 0.5f}, {u, v}},
+ };
+ memcpy(vertices, tmp, sizeof(vertex) * 4);
+}
+
+
+void Sprite::free() {
+ linearFree(vertices);
+ linearFree(pixels);
+ C3D_TexDelete(&texture);
+ pixels = 0;
+ w = h = pitch = 0;
+ actualWidth = actualHeight = 0;
+ format = Graphics::PixelFormat();
+}
+
+void Sprite::convertToInPlace(const Graphics::PixelFormat &dstFormat, const byte *palette) {
+ //
+}
+
+void Sprite::render() {
+ if (dirtyPixels) {
+ dirtyPixels = false;
+ GSPGPU_FlushDataCache(pixels, w * h * format.bytesPerPixel);
+ C3D_SafeDisplayTransfer((u32*)pixels, GX_BUFFER_DIM(w, h), (u32*)texture.data, GX_BUFFER_DIM(w, h), TEXTURE_TRANSFER_FLAGS);
+ gspWaitForPPF();
+ }
+ C3D_TexBind(0, &texture);
+
+ C3D_BufInfo* bufInfo = C3D_GetBufInfo();
+ BufInfo_Init(bufInfo);
+ BufInfo_Add(bufInfo, vertices, sizeof(vertex), 2, 0x10);
+ C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4);
+}
+
+void Sprite::clear(uint32 color) {
+ dirtyPixels = true;
+ memset(pixels, color, w * h * format.bytesPerPixel);
+}
+
+void Sprite::setScale (float x, float y) {
+ scaleX = x;
+ scaleY = y;
+ dirtyMatrix = true;
+}
+
+void Sprite::setPosition(int x, int y) {
+ posX = x;
+ posY = y;
+ dirtyMatrix = true;
+}
+
+C3D_Mtx* Sprite::getMatrix() {
+ if (dirtyMatrix) {
+ dirtyMatrix = false;
+ Mtx_Identity(&modelview);
+ Mtx_Scale(&modelview, scaleX, scaleY, 1.f);
+ Mtx_Translate(&modelview, posX, posY, 0);
+ }
+ return &modelview;
+}
diff --git a/backends/platform/3ds/sprite.h b/backends/platform/3ds/sprite.h
new file mode 100644
index 0000000000..6d88ae4ce1
--- /dev/null
+++ b/backends/platform/3ds/sprite.h
@@ -0,0 +1,71 @@
+/* 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.
+ *
+ */
+
+#ifndef GRAPHICS_SPRITE_3DS_H
+#define GRAPHICS_SPRITE_3DS_H
+
+#include <citro3d.h>
+#include "graphics/surface.h"
+
+#define TEXTURE_TRANSFER_FLAGS \
+ (GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | \
+ GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) | \
+ GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
+
+typedef struct {
+ float position[3];
+ float texcoord[2];
+} vertex;
+
+class Sprite : public Graphics::Surface {
+public:
+ Sprite();
+ ~Sprite();
+ void create(uint16 width, uint16 height, const Graphics::PixelFormat &format);
+ void free();
+ void convertToInPlace(const Graphics::PixelFormat &dstFormat, const byte *palette = 0);
+ void render();
+ void clear(uint32 color = 0);
+ void markDirty(){ dirtyPixels = true; }
+
+ void setPosition(int x, int y);
+ void setScale(float x, float y);
+ float getScaleX(){ return scaleX; }
+ float getScaleY(){ return scaleY; }
+ C3D_Mtx* getMatrix();
+
+ uint16 actualWidth;
+ uint16 actualHeight;
+
+private:
+ bool dirtyPixels;
+ bool dirtyMatrix;
+ C3D_Mtx modelview;
+ C3D_Tex texture;
+ vertex* vertices;
+ int posX;
+ int posY;
+ float scaleX;
+ float scaleY;
+};
+
+#endif