diff options
Diffstat (limited to 'backends/platform/3ds')
22 files changed, 2544 insertions, 0 deletions
diff --git a/backends/platform/3ds/3ds.mk b/backends/platform/3ds/3ds.mk new file mode 100644 index 0000000000..7ab58995f6 --- /dev/null +++ b/backends/platform/3ds/3ds.mk @@ -0,0 +1,64 @@ +TARGET := scummvm + +APP_TITLE := ScummVM +APP_DESCRIPTION := Point-and-click adventure game engines +APP_AUTHOR := ScummVM Team +APP_ICON := backends/platform/3ds/app/icon.png + +APP_RSF := backends/platform/3ds/app/scummvm.rsf +APP_BANNER_IMAGE:= backends/platform/3ds/app/banner.png +APP_BANNER_AUDIO:= backends/platform/3ds/app/banner.wav + +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 + +clean: clean_3ds + +clean_3ds: + $(RM) $(TARGET).3dsx + $(RM) $(TARGET).cia + +$(TARGET).smdh: $(APP_ICON) + @bannertool makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i $(APP_ICON) -o $@ + @echo built ... $(notdir $@) + +$(TARGET).3dsx: $(EXECUTABLE) $(TARGET).smdh + @3dsxtool $< $@ --smdh=$(TARGET).smdh + @echo built ... $(notdir $@) + +$(TARGET).bnr: $(APP_BANNER_IMAGE) $(APP_BANNER_AUDIO) + @bannertool makebanner -o $@ -i $(APP_BANNER_IMAGE) -a $(APP_BANNER_AUDIO) + @echo built ... $(notdir $@) + +$(TARGET).cia: $(EXECUTABLE) $(APP_RSF) $(TARGET).smdh $(TARGET).bnr + @makerom -f cia -target t -exefslogo -o $@ -elf $(EXECUTABLE) -rsf $(APP_RSF) -banner $(TARGET).bnr -icon $(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/README b/backends/platform/3ds/README new file mode 100644 index 0000000000..516e694f64 --- /dev/null +++ b/backends/platform/3ds/README @@ -0,0 +1,185 @@ +ScummVM 3DS README +------------------------------------------------------------------------ + +Table of Contents: +------------------ +1.0) Installation + * 1.1 3DSX installation + * 1.2 CIA installation +2.0) Controls + * 2.1 Default key mappings + * 2.2 Hover mode + * 2.3 Drag mode +3.0) Supported Games +4.0) Compiling + * 4.1 Prerequisites + * * 4.1.1 Compiling third-party libraries + * 4.2 Compiling ScummVM + * 4.3 Warning for 3DSX build + + + +1.0) Installation +----------------- +There are two possible formats to be used: 3DSX and CIA (recommended). +The 3DSX format is exclusively used by the Homebrew Launcher and its derivatives. +The CIA format can be installed directly to the 3DS home menu and can be launched +using any CFW (Custom Firmware) of your choice. + +Installing the Homebrew Launcher or any CFW is beyond the scope of this README. +Look elsewhere to see how to install those if you do not already have them set up. + + +1.1) 3DSX installation +---------------- +The CIA format is recommended for stability and maximum game support. If that is +not an option, you will need one of a collection of 3DS titles installed on your +system in order to properly launch ScummVM as a 3DSX. This is because the +Homebrew Launcher hijacks other processes to run 3DSX homebrew, and ScummVM is a +particularly large homebrew that can't be launched with the resources provided +by standard system applications. + +You will need one of the following (installed or physically in cart slot): + +- Youtube +- Monster Hunter 4 Ultimate Special Demo +- Monster Hunter 4 Ultimate +- Monster Hunter 4G +- Super Smash Bros. for Nintendo 3DS Demo +- Super Smash Bros. for Nintendo 3DS Special Demo +- Super Smash Bros. for Nintendo 3DS + +Once you have one of the above, you need to merely extract all ScummVM 3DS files +to the root of your SD card so that all files reside in the /3ds/scummvm/ directory. + + +1.2) CIA installation +--------------------- +The CIA format requires a DSP binary dump saved on your SD card as /3ds/dspfirm.cdc +for proper audio support. You can search online to find software to dump this. +Not having this file will cause many problems with games that need audio, sometimes +even crashing, so this is NOT considered optional. + +Using any CIA installation software (search elsewhere for that), you need to install +the scummvm.cia file. Then, just like what is done with the 3DSX installation, you +need to extract all ScummVM 3DS files (scummvm.cia excluded) to the root of your SD +card so that all files reside in the /3ds/scummvm/ directory. + + + +2.0) Controls +------------- + +2.1) Default key mappings +------------------------- +The D-Pad and A/B/X/Y buttons have mirrored usage. So they do the same things +depending on if you're right or left-handed. + +| Buttons | Function | +|------------|--------------------------------| +| A / D-left | Left-click | +| X / D-up | Right-click | +| B / D-down | ESC (skips cutscenes and such) | +| L | Use virtual keyboard | +| R | Toggle hover/drag modes | +| Start | Open game menu | +| Select | Open 3DS config menu | +| Circle Pad | Move the cursor | + + +2.2) Hover mode +--------------- +When you use the touchscreen, you are simulating the mere moving of the mouse. You +can click only with taps, meaning it is impossible to drag stuff or hold down a +mouse button without using buttons mapped to right/left-click. + + +2.3) Drag mode +-------------- +Every time you touch and release the touchscreen, you are simulating the click and +release of the mouse buttons. At the moment, this is only a left-click. + + + +3.0) Supported Games +-------------------- +The full game engine compatibility list can be found here: +http://scummvm.org/compatibility/ + +While all the above games should run on the 3DS (report if they do not), there are +many games which are unplayable due to the lack of CPU speed on the 3DS. So if +you play any games that run really slow, this is not considered a bug, but rather +a hardware limitation. Though possible GPU optimizations are always in the works. +The New 3DS console has much better performance, but there are still many newer and +high-resolution games that cannot be played. A list of these unplayable games and +game engines will eventually be listed here. + + + +4.0) Compiling +-------------- + +4.1) Prerequisites +------------------ + - devkitARM (presumably with libctru, picasso and such) + - citro3d + - Optional: You should compile third-party libraries for the 3ds (commonly referred + to as portlibs in the devkitPRO community). Some games requires these to operate + properly. + + +4.1.1) Compiling third-party libraries +-------------------------------------- +Most libraries used can be compiled with same commands and configuration flags. + +It is assumed that you have these environment variables defined: + - DEVKITPRO Your root devkitPro directory + - DEVKITARM Your root devkitARM directory (probably same as $DEVKITPRO/devkitARM) + - CTRULIB Your root libctru directory (probably same as $DEVKITPRO/libctru) + +In the source directory of the library: + - $ export PORTLIBS=$DEVKITPRO/portlibs/armv6k + - $ export PATH=$DEVKITARM/bin:$PATH + - $ export PKG_CONFIG_PATH=$PORTLIBS/lib/pkgconfig + - $ export CFLAGS="-g -march=armv6k -mtune=mpcore -mfloat-abi=hard -O2 + -mword-relocations -ffunction-sections -fdata-sections" + - $ export CPPFLAGS="-I$PORTLIBS/include -I$CTRULIB/include" + - $ export LDFLAGS="-L$PORTLIBS/lib" + - $ mkdir -p $PORTLIBS + - $ ./configure --prefix=$PORTLIBS --host=arm-none-eabi --disable-shared + --enable-static + - $ make + - $ make install + +Useful libraries (and special config flags needed): + - zlib + - libpng + - libjpeg + - freetype2 --without-bzip2 --without-harfbuzz + - libmad + - tremor + - flac --disable-cpplibs --without-flac + - faad + + +4.2) Compiling ScummVM +---------------------- + - $ ./configure --host=3ds + - $ make + +Additionally compile to specific formats to be used on the 3ds: + - $ make scummvm.3dsx + - $ make scummvm.cia + + +4.3) Warning for 3DSX build +--------------------------- +The above configuration command will include all game engines by default and will +likely be too massive to run using the 3DSX format. Until dynamic modules are figured +out, you should configure engines like this for 3DSX builds: + + - $ ./configure --host=3ds --disable-all-engines--enable-engine=scumm-7-8,myst,riven, + sword1,sword2,sword25,sci,lure,sky,agi,agos + +Choose whatever engines you want, but if the ELF's .text section exceeds ~10MB, it +won't be playable unless it's a CIA. diff --git a/backends/platform/3ds/app/banner.png b/backends/platform/3ds/app/banner.png Binary files differnew file mode 100644 index 0000000000..a3b02150ec --- /dev/null +++ b/backends/platform/3ds/app/banner.png diff --git a/backends/platform/3ds/app/banner.wav b/backends/platform/3ds/app/banner.wav Binary files differnew file mode 100644 index 0000000000..e0b684b62f --- /dev/null +++ b/backends/platform/3ds/app/banner.wav diff --git a/backends/platform/3ds/app/icon.png b/backends/platform/3ds/app/icon.png Binary files differnew file mode 100644 index 0000000000..07022fbac1 --- /dev/null +++ b/backends/platform/3ds/app/icon.png diff --git a/backends/platform/3ds/app/scummvm.rsf b/backends/platform/3ds/app/scummvm.rsf new file mode 100644 index 0000000000..a4518949bb --- /dev/null +++ b/backends/platform/3ds/app/scummvm.rsf @@ -0,0 +1,219 @@ +BasicInfo: + Title : ScummVM + ProductCode : ScummVM + Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem + +TitleInfo: + Category : Application + UniqueId : 0xFF321 + +Option: + UseOnSD : true # true if App is to be installed to SD + FreeProductCode : true # Removes limitations on ProductCode + MediaFootPadding : false # If true CCI files are created with padding + EnableCrypt : false # Enables encryption for NCCH and CIA + EnableCompress : false # Compresses where applicable (currently only exefs:/.code) + +AccessControlInfo: + CoreVersion : 2 + + # Exheader Format Version + DescVersion : 2 + + # Minimum Required Kernel Version (below is for 4.5.0) + ReleaseKernelMajor : "02" + ReleaseKernelMinor : "33" + + # ExtData + UseExtSaveData : false # enables ExtData + #ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId + + # FS:USER Archive Access Permissions + # Uncomment as required + FileSystemAccess: + #- CategorySystemApplication + #- CategoryHardwareCheck + #- CategoryFileSystemTool + #- Debug + #- TwlCardBackup + #- TwlNandData + #- Boss + - DirectSdmc + #- Core + #- CtrNandRo + #- CtrNandRw + #- CtrNandRoWrite + #- CategorySystemSettings + #- CardBoard + #- ExportImportIvs + #- DirectSdmcWrite + #- SwitchCleanup + #- SaveDataMove + #- Shop + #- Shell + #- CategoryHomeMenu + + # Process Settings + MemoryType : Application # Application/System/Base + SystemMode : 64MB # 64MB(Default)/96MB/80MB/72MB/32MB + IdealProcessor : 0 + AffinityMask : 1 + Priority : 16 + MaxCpu : 0 # Let system decide + HandleTableSize : 0x200 + DisableDebug : false + EnableForceDebug : false + CanWriteSharedPage : true + CanUsePrivilegedPriority : false + CanUseNonAlphabetAndNumber : true + PermitMainFunctionArgument : true + CanShareDeviceMemory : true + RunnableOnSleep : false + SpecialMemoryArrange : true + + # New3DS Exclusive Process Settings + SystemModeExt : 124MB # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode + CpuSpeed : 804MHz # 268MHz(Default)/804MHz + EnableL2Cache : true # false(default)/true + CanAccessCore2 : true + + # Virtual Address Mappings + IORegisterMapping: + - 1ff00000-1ff7ffff # DSP memory + MemoryMapping: + - 1f000000-1f5fffff:r # VRAM + + # Accessible SVCs, <Name>:<ID> + SystemCallAccess: + ArbitrateAddress: 34 + Break: 60 + CancelTimer: 28 + ClearEvent: 25 + ClearTimer: 29 + CloseHandle: 35 + ConnectToPort: 45 + ControlMemory: 1 + CreateAddressArbiter: 33 + CreateEvent: 23 + CreateMemoryBlock: 30 + CreateMutex: 19 + CreateSemaphore: 21 + CreateThread: 8 + CreateTimer: 26 + DuplicateHandle: 39 + ExitProcess: 3 + ExitThread: 9 + GetCurrentProcessorNumber: 17 + GetHandleInfo: 41 + GetProcessId: 53 + GetProcessIdOfThread: 54 + GetProcessIdealProcessor: 6 + GetProcessInfo: 43 + GetResourceLimit: 56 + GetResourceLimitCurrentValues: 58 + GetResourceLimitLimitValues: 57 + GetSystemInfo: 42 + GetSystemTick: 40 + GetThreadContext: 59 + GetThreadId: 55 + GetThreadIdealProcessor: 15 + GetThreadInfo: 44 + GetThreadPriority: 11 + MapMemoryBlock: 31 + OutputDebugString: 61 + QueryMemory: 2 + ReleaseMutex: 20 + ReleaseSemaphore: 22 + SendSyncRequest1: 46 + SendSyncRequest2: 47 + SendSyncRequest3: 48 + SendSyncRequest4: 49 + SendSyncRequest: 50 + SetThreadPriority: 12 + SetTimer: 27 + SignalEvent: 24 + SleepThread: 10 + UnmapMemoryBlock: 32 + WaitSynchronization1: 36 + WaitSynchronizationN: 37 + Backdoor: 123 + + # Service List + # Maximum 34 services (32 if firmware is prior to 9.3.0) + ServiceAccessControl: + - cfg:u + - fs:USER + - gsp::Gpu + - hid:USER + - ndm:u + - pxi:dev + - APT:U + - ac:u + - act:u + - am:net + - boss:U + - cam:u + - cecd:u + - dsp::DSP + - frd:u + - http:C + - ir:USER + - ir:u + - ir:rst + - ldr:ro + - mic:u + - news:u + - nim:aoc + - nwm::UDS + - ptm:u + - qtm:u + - soc:U + - ssl:C + - y2r:u + - gsp::Lcd + + +SystemControlInfo: + SaveDataSize: 0K + RemasterVersion: 0 + StackSize: 0x40000 + + # Modules that run services listed above should be included below + # Maximum 48 dependencies + # If a module is listed that isn't present on the 3DS, the title will get stuck at the logo (3ds waves) + # So act, nfc and qtm are commented for 4.x support. Uncomment if you need these. + # <module name>:<module titleid> + Dependency: + ac: 0x0004013000002402 + #act: 0x0004013000003802 + am: 0x0004013000001502 + boss: 0x0004013000003402 + camera: 0x0004013000001602 + cecd: 0x0004013000002602 + cfg: 0x0004013000001702 + codec: 0x0004013000001802 + csnd: 0x0004013000002702 + dlp: 0x0004013000002802 + dsp: 0x0004013000001a02 + friends: 0x0004013000003202 + gpio: 0x0004013000001b02 + gsp: 0x0004013000001c02 + hid: 0x0004013000001d02 + http: 0x0004013000002902 + i2c: 0x0004013000001e02 + ir: 0x0004013000003302 + mcu: 0x0004013000001f02 + mic: 0x0004013000002002 + ndm: 0x0004013000002b02 + news: 0x0004013000003502 + #nfc: 0x0004013000004002 + nim: 0x0004013000002c02 + nwm: 0x0004013000002d02 + pdn: 0x0004013000002102 + ps: 0x0004013000003102 + ptm: 0x0004013000002202 + #qtm: 0x0004013020004202 + ro: 0x0004013000003702 + socket: 0x0004013000002e02 + spi: 0x0004013000002302 + ssl: 0x0004013000002f02 diff --git a/backends/platform/3ds/config.cpp b/backends/platform/3ds/config.cpp new file mode 100644 index 0000000000..117b979d9f --- /dev/null +++ b/backends/platform/3ds/config.cpp @@ -0,0 +1,87 @@ +/* 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 "config.h" +#include "osystem.h" +#include "options-dialog.h" +#include "common/config-manager.h" +#include <3ds.h> + +namespace _3DS { + +Config config; +static Common::String prefix = "3ds_"; + +static bool confGetBool(Common::String key, bool defaultVal) { + if (ConfMan.hasKey(prefix + key)) + return ConfMan.getBool(prefix + key); + return defaultVal; +} + +static void confSetBool(Common::String key, bool val) { + ConfMan.setBool(prefix + key, val); +} + +static int confGetInt(Common::String key, int defaultVal) { + if (ConfMan.hasKey(prefix + key)) + return ConfMan.getInt(prefix + key); + return defaultVal; +} + +static void confSetInt(Common::String key, int val) { + ConfMan.setInt(prefix + key, val); +} + +void loadConfig() { + config.showCursor = confGetBool("showcursor", true); + config.snapToBorder = confGetBool("snaptoborder", true); + config.stretchToFit = confGetBool("stretchtofit", false); + config.sensitivity = confGetInt("sensitivity", -5); + config.screen = confGetInt("screen", kScreenBoth); + + // Turn off the backlight of any screen not used + if (R_SUCCEEDED(gspLcdInit())) { + if (config.screen == kScreenTop) { + GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_TOP); + GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_BOTTOM); + } else if (config.screen == kScreenBottom) { + GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTTOM); + GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_TOP); + } else + GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH); + gspLcdExit(); + } + + OSystem_3DS *osys = (OSystem_3DS *)g_system; + osys->updateConfig(); +} + +void saveConfig() { + confSetBool("showcursor", config.showCursor); + confSetBool("snaptoborder", config.snapToBorder); + confSetBool("stretchtofit", config.stretchToFit); + confSetInt("sensitivity", config.sensitivity); + confSetInt("screen", config.screen); + ConfMan.flushToDisk(); +} + +} // namespace _3DS diff --git a/backends/platform/3ds/config.h b/backends/platform/3ds/config.h new file mode 100644 index 0000000000..c8b75736ad --- /dev/null +++ b/backends/platform/3ds/config.h @@ -0,0 +1,45 @@ +/* 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 CONFIG_3DS_H +#define CONFIG_3DS_H + +#include "common/str.h" + +namespace _3DS { + +struct Config { + bool showCursor; + bool snapToBorder; + bool stretchToFit; + int sensitivity; + int screen; +}; + +extern Config config; + +void loadConfig(); +void saveConfig(); + +} // namespace _3DS + +#endif // CONFIG_3DS_H 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/main.cpp b/backends/platform/3ds/main.cpp new file mode 100644 index 0000000000..6cc2c5cf5d --- /dev/null +++ b/backends/platform/3ds/main.cpp @@ -0,0 +1,54 @@ +/* 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" +#include <3ds.h> + +int main(int argc, char *argv[]) { + // Initialize basic libctru stuff + gfxInitDefault(); + cfguInit(); + osSetSpeedupEnable(true); +// consoleInit(GFX_TOP, NULL); + + g_system = new _3DS::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<_3DS::OSystem_3DS*>(g_system); + + // Turn on both screen backlights before exiting. + if (R_SUCCEEDED(gspLcdInit())) { + GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH); + gspLcdExit(); + } + + cfguExit(); + gfxExit(); + return 0; +} diff --git a/backends/platform/3ds/module.mk b/backends/platform/3ds/module.mk new file mode 100644 index 0000000000..3eb15aef81 --- /dev/null +++ b/backends/platform/3ds/module.mk @@ -0,0 +1,18 @@ +MODULE := backends/platform/3ds + +MODULE_OBJS := \ + main.o \ + shader.shbin.o \ + sprite.o \ + gui.o \ + config.o \ + options-dialog.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/options-dialog.cpp b/backends/platform/3ds/options-dialog.cpp new file mode 100644 index 0000000000..0f8bfd0c66 --- /dev/null +++ b/backends/platform/3ds/options-dialog.cpp @@ -0,0 +1,98 @@ +/* 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 "options-dialog.h" +#include "config.h" +#include "gui/dialog.h" +#include "gui/gui-manager.h" +#include "gui/widgets/list.h" +#include "gui/widgets/tab.h" +#include "osystem.h" +#include "engines/scumm/scumm.h" +#include "gui/widgets/popup.h" + +#include "common/translation.h" + +namespace _3DS { + +bool optionMenuOpened = false; + +OptionsDialog::OptionsDialog() : GUI::Dialog(20, 20, 280, 200) { + + optionMenuOpened = true; + + new GUI::ButtonWidget(this, 120, 180, 72, 16, _("~C~lose"), 0, GUI::kCloseCmd); + new GUI::ButtonWidget(this, 200, 180, 72, 16, _("~S~ave"), 0, GUI::kOKCmd); + + _showCursorCheckbox = new GUI::CheckboxWidget(this, 5, 5, 130, 20, _("Show mouse cursor"), 0, 0, 'T'); + _showCursorCheckbox->setState(config.showCursor); + + _snapToBorderCheckbox = new GUI::CheckboxWidget(this, 5, 22, 130, 20, _("Snap to edges"), 0, 0, 'T'); + _snapToBorderCheckbox->setState(config.snapToBorder); + + _stretchToFitCheckbox = new GUI::CheckboxWidget(this, 140, 5, 130, 20, _("Stretch to fit"), 0, 0, 'T'); + _stretchToFitCheckbox->setState(config.stretchToFit); + + new GUI::StaticTextWidget(this, 0, 60, 110, 15, _("Use Screen:"), Graphics::kTextAlignRight); + _screenRadioGroup = new GUI::RadiobuttonGroup(this, kScreenRadioGroup); + _screenTopRadioWidget = new GUI::RadiobuttonWidget(this, 120, 50, 60, 20, _screenRadioGroup, kScreenTop, _("Top")); + _screenBottomRadioWidget = new GUI::RadiobuttonWidget(this, 190, 50, 80, 20, _screenRadioGroup, kScreenBottom, _("Bottom")); + _screenBothRadioWidget = new GUI::RadiobuttonWidget(this, 155, 70, 80, 20, _screenRadioGroup, kScreenBoth, _("Both")); + _screenRadioGroup->setValue(config.screen); + + new GUI::StaticTextWidget(this, 0, 100, 110, 15, _("C-Pad Sensitivity:"), Graphics::kTextAlignRight); + _sensitivity = new GUI::SliderWidget(this, 115, 100, 160, 15, "TODO: Add tooltip", 1); + _sensitivity->setMinValue(-15); + _sensitivity->setMaxValue(30); + _sensitivity->setValue(config.sensitivity); + _sensitivity->setFlags(GUI::WIDGET_CLEARBG); +} + +OptionsDialog::~OptionsDialog() { + optionMenuOpened = false; +} + +void OptionsDialog::updateConfigManager() { + config.showCursor = _showCursorCheckbox->getState(); + config.snapToBorder = _snapToBorderCheckbox->getState(); + config.stretchToFit = _stretchToFitCheckbox->getState(); + config.sensitivity = _sensitivity->getValue(); + config.screen = _screenRadioGroup->getValue(); + saveConfig(); + loadConfig(); +} + +void OptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { + switch(cmd) { + case GUI::kOKCmd: + updateConfigManager(); + // Fall through + case GUI::kCloseCmd: + close(); + break; + default: + Dialog::handleCommand(sender, cmd, data); + break; + } +} + +} // namespace _3DS diff --git a/backends/platform/3ds/options-dialog.h b/backends/platform/3ds/options-dialog.h new file mode 100644 index 0000000000..6673b88e7b --- /dev/null +++ b/backends/platform/3ds/options-dialog.h @@ -0,0 +1,70 @@ +/* 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 OPTIONS_DIALOG_3DS_H +#define OPTIONS_DIALOG_3DS_H + + +#include "common/scummsys.h" +#include "common/str.h" +#include "gui/object.h" +#include "gui/widget.h" +#include "gui/dialog.h" +#include "gui/widgets/tab.h" +#include "scumm/dialogs.h" + +namespace _3DS { + +enum { + kSave = 0x10000000, + kScreenRadioGroup, + kScreenTop, + kScreenBottom, + kScreenBoth, +}; + +extern bool optionMenuOpened; + +class OptionsDialog : public GUI::Dialog { + +public: + OptionsDialog(); + ~OptionsDialog(); + +protected: + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + void updateConfigManager(); + + GUI::SliderWidget *_sensitivity; + GUI::CheckboxWidget *_showCursorCheckbox; + GUI::CheckboxWidget *_snapToBorderCheckbox; + GUI::CheckboxWidget *_stretchToFitCheckbox; + + GUI::RadiobuttonGroup *_screenRadioGroup; + GUI::RadiobuttonWidget *_screenTopRadioWidget; + GUI::RadiobuttonWidget *_screenBottomRadioWidget; + GUI::RadiobuttonWidget *_screenBothRadioWidget; +}; + +} // namespace _3DS + +#endif // OPTIONS_DIALOG_3DS_H diff --git a/backends/platform/3ds/osystem-audio.cpp b/backends/platform/3ds/osystem-audio.cpp new file mode 100644 index 0000000000..17e419c36d --- /dev/null +++ b/backends/platform/3ds/osystem-audio.cpp @@ -0,0 +1,110 @@ +/* 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" +#include "audio/mixer.h" + +namespace _3DS { + +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 = mixer->getOutputRate(); + 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 (!osys->exiting) { + 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 (!osys->sleeping && sampleLen > 0) { + bufferIndex++; + bufferIndex %= bufferCount; + ndspWaveBuf *buf = &buffers[bufferIndex]; + + 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) { + threadJoin(audioThread, U64_MAX); + threadFree(audioThread); + ndspExit(); + } + + delete _mixer; + _mixer = 0; +} + +Audio::Mixer *OSystem_3DS::getMixer() { + assert(_mixer); + return _mixer; +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem-events.cpp b/backends/platform/3ds/osystem-events.cpp new file mode 100644 index 0000000000..ae8a9b8b2b --- /dev/null +++ b/backends/platform/3ds/osystem-events.cpp @@ -0,0 +1,302 @@ +/* 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/timer/default/default-timer.h" +#include "engines/engine.h" +#include "gui.h" +#include "options-dialog.h" +#include "config.h" +#include "osystem.h" + +namespace _3DS { + +static Common::Mutex *eventMutex; +static InputMode inputMode = MODE_DRAG; +static aptHookCookie cookie; +static bool optionMenuOpening = false; +static Common::String messageOSD; +static bool showMessageOSD = false; + +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; + float cursorX = 0; + float cursorY = 0; + float cursorDeltaX = 0; + float cursorDeltaY = 0; + int circleDeadzone = 20; + int borderSnapZone = 6; + Common::Event event; + + while (!osys->exiting) { + do { + osys->delayMillis(10); + } while (osys->sleeping && !osys->exiting); + + hidScanInput(); + touchPosition touch; + circlePosition circle; + u32 held = hidKeysHeld(); + u32 keysPressed = hidKeysDown(); + u32 keysReleased = hidKeysUp(); + + // C-Pad used to control the cursor + hidCircleRead(&circle); + if (circle.dx < circleDeadzone && circle.dx > -circleDeadzone) + circle.dx = 0; + if (circle.dy < circleDeadzone && circle.dy > -circleDeadzone) + circle.dy = 0; + cursorDeltaX = (0.0002f + config.sensitivity / 100000.f) * circle.dx * abs(circle.dx); + cursorDeltaY = (0.0002f + config.sensitivity / 100000.f) * circle.dy * abs(circle.dy); + + // Touch screen events + if (held & KEY_TOUCH) { + hidTouchRead(&touch); + if (config.snapToBorder) { + if (touch.px < borderSnapZone) + touch.px = 0; + if (touch.px > 319 - borderSnapZone) + touch.px = 319; + if (touch.py < borderSnapZone) + touch.py = 0; + if (touch.py > 239 - borderSnapZone) + touch.py = 239; + } + cursorX = touch.px; + cursorY = touch.py; + 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; + 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); + } + } else if (cursorDeltaX != 0 || cursorDeltaY != 0) { + cursorX += cursorDeltaX; + cursorY -= cursorDeltaY; + if (cursorX < 0) cursorX = 0; + if (cursorY < 0) cursorY = 0; + if (cursorX > 320) cursorX = 320; + if (cursorY > 240) cursorY = 240; + lastTouch.px = cursorX; + lastTouch.py = cursorY; + osys->transformPoint(lastTouch); + osys->warpMouse(lastTouch.px, lastTouch.py); + event.mouse.x = lastTouch.px; + event.mouse.y = lastTouch.py; + event.type = Common::EVENT_MOUSEMOVE; + pushEventQueue(eventQueue, event); + } + + // Button events + 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 || keysReleased & KEY_A || keysReleased & KEY_DLEFT) { + // SIMULATE LEFT CLICK + event.mouse.x = lastTouch.px; + event.mouse.y = lastTouch.py; + if (keysPressed & KEY_A || keysPressed & KEY_DLEFT) + event.type = Common::EVENT_LBUTTONDOWN; + else + event.type = Common::EVENT_LBUTTONUP; + pushEventQueue(eventQueue, event); + } + if (keysPressed & KEY_X || keysPressed & KEY_DUP || keysReleased & KEY_X || keysReleased & KEY_DUP) { + // SIMULATE RIGHT CLICK + event.mouse.x = lastTouch.px; + event.mouse.y = lastTouch.py; + if (keysPressed & KEY_X || keysPressed & KEY_DUP) + event.type = Common::EVENT_RBUTTONDOWN; + else + 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) { + if (!optionMenuOpened) + optionMenuOpening = true; + } + 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 + } +} + +static void aptHookFunc(APT_HookType hookType, void *param) { + OSystem_3DS *osys = (OSystem_3DS *)g_system; + + switch (hookType) { + case APTHOOK_ONSUSPEND: + case APTHOOK_ONSLEEP: + if (g_engine) + g_engine->pauseEngine(true); + osys->sleeping = true; + if (R_SUCCEEDED(gspLcdInit())) { + GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH); + gspLcdExit(); + } + break; + case APTHOOK_ONRESTORE: + case APTHOOK_ONWAKEUP: + if (g_engine) + g_engine->pauseEngine(false); + osys->sleeping = false; + loadConfig(); + break; + default: { + Common::StackLock lock(*eventMutex); + Common::Event event; + event.type = Common::EVENT_QUIT; + g_system->getEventManager()->pushEvent(event); + } + } +} + +static void timerThreadFunc(void *arg) { + OSystem_3DS *osys = (OSystem_3DS *)arg; + DefaultTimerManager *tm = (DefaultTimerManager *)osys->getTimerManager(); + while (!osys->exiting) { + g_system->delayMillis(10); + tm->handler(); + } +} + +void OSystem_3DS::initEvents() { + eventMutex = new Common::Mutex(); + s32 prio = 0; + svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); + _timerThread = threadCreate(&timerThreadFunc, this, 32 * 1024, prio - 1, -2, false); + _eventThread = threadCreate(&eventThreadFunc, &_eventQueue, 32 * 1024, prio - 1, -2, false); + + aptHook(&cookie, aptHookFunc, this); +} + +void OSystem_3DS::destroyEvents() { + threadJoin(_timerThread, U64_MAX); + threadFree(_timerThread); + + threadJoin(_eventThread, U64_MAX); + threadFree(_eventThread); + delete eventMutex; +} + +void OSystem_3DS::transformPoint(touchPosition &point) { + if (!_overlayVisible) { + point.px = static_cast<float>(point.px) / _gameBottomTexture.getScaleX() - _gameBottomX; + point.py = static_cast<float>(point.py) / _gameBottomTexture.getScaleY() - _gameBottomY; + } +} + +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(); + } + + aptMainLoop(); // Call apt hook when necessary + + if (optionMenuOpening) { + optionMenuOpening = false; + OptionsDialog dialog; + if (g_engine) + g_engine->pauseEngine(true); + dialog.runModal(); + if (g_engine) + g_engine->pauseEngine(false); + } + + Common::StackLock lock(*eventMutex); + + if (_eventQueue.empty()) + return false; + + event = _eventQueue.pop(); + return true; +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem-graphics.cpp b/backends/platform/3ds/osystem-graphics.cpp new file mode 100644 index 0000000000..0cfd70c9cd --- /dev/null +++ b/backends/platform/3ds/osystem-graphics.cpp @@ -0,0 +1,517 @@ +/* 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/osystem.h" +#include "backends/platform/3ds/shader_shbin.h" +#include "common/rect.h" +#include "options-dialog.h" +#include "config.h" + +// 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)) + +namespace _3DS { + +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(); + _gameTopTexture.free(); + _gameBottomTexture.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::kFeatureCursorPalette || + f == OSystem::kFeatureOverlaySupportsAlpha); +} + +void OSystem_3DS::setFeatureState(OSystem::Feature f, bool enable) { + switch (f) { + case OSystem::kFeatureCursorPalette: + _cursorPaletteEnabled = enable; + flushCursor(); + break; + default: + break; + } +} + +bool OSystem_3DS::getFeatureState(OSystem::Feature f) { + switch (f) { + 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() { + debug("resetGraphicsScale"); +} + +int OSystem_3DS::getGraphicsMode() const { + return GFX_LINEAR; +} +void OSystem_3DS::initSize(uint width, uint height, + const Graphics::PixelFormat *format) { + debug("3ds initsize w:%d h:%d", width, height); + _gameWidth = width; + _gameHeight = height; + _gameTopTexture.create(width, height, _pfGameTexture); + _overlay.create(getOverlayWidth(), getOverlayHeight(), _pfGameTexture); + + if (format) { + debug("pixelformat: %d %d %d %d %d", format->bytesPerPixel, format->rBits(), format->gBits(), format->bBits(), format->aBits());; + _pfGame = *format; + } + + _gameScreen.create(width, height, _pfGame); + + _focusDirty = true; + _focusRect = Common::Rect(_gameWidth, _gameHeight); + + updateSize(); +} + +void OSystem_3DS::updateSize() { + if (config.stretchToFit) { + _gameTopX = _gameTopY = _gameBottomX = _gameBottomY = 0; + _gameTopTexture.setScale(400.f / _gameWidth, 240.f / _gameHeight); + _gameBottomTexture.setScale(320.f / _gameWidth, 240.f / _gameHeight); + } else { + float ratio = static_cast<float>(_gameWidth) / _gameHeight; + + if (ratio > 400.f / 240.f) { + float r = 400.f / _gameWidth; + _gameTopTexture.setScale(r, r); + _gameTopX = 0; + _gameTopY = (240.f - r * _gameHeight) / 2.f; + } else { + float r = 240.f / _gameHeight; + _gameTopTexture.setScale(r, r); + _gameTopY = 0; + _gameTopX = (400.f - r * _gameWidth) / 2.f; + } + if (ratio > 320.f / 240.f) { + float r = 320.f / _gameWidth; + _gameBottomTexture.setScale(r, r); + _gameBottomX = 0; + _gameBottomY = (240.f - r * _gameHeight) / 2.f; + } else { + float r = 240.f / _gameHeight; + _gameBottomTexture.setScale(r, r); + _gameBottomY = 0; + _gameBottomX = (320.f - r * _gameWidth) / 2.f; + } + } + _gameTopTexture.setPosition(_gameTopX, _gameTopY); + _gameBottomTexture.setPosition(_gameBottomX, _gameBottomY); + if (_overlayVisible) + _cursorTexture.setScale(1.f, 1.f); + else if (config.screen == kScreenTop) + _cursorTexture.setScale(_gameTopTexture.getScaleX(), _gameTopTexture.getScaleY()); + else + _cursorTexture.setScale(_gameBottomTexture.getScaleX(), _gameBottomTexture.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) { + 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) { + 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); + _gameTopTexture.copyRectToSurface(*convertedSubSurface, x, y, Common::Rect(w, h)); + + convertedSubSurface->free(); + delete convertedSubSurface; + _gameTopTexture.markDirty(); +} + +void OSystem_3DS::flushGameScreen() { + Graphics::Surface *converted = _gameScreen.convertTo(_pfGameTexture, _palette); + _gameTopTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h)); + _gameTopTexture.markDirty(); + converted->free(); + delete converted; +} + +Graphics::Surface *OSystem_3DS::lockScreen() { + return &_gameScreen; +} +void OSystem_3DS::unlockScreen() { + flushGameScreen(); +} + +void OSystem_3DS::updateScreen() { + + if (sleeping || exiting) + return; + +// updateFocus(); + + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + // Render top screen + C3D_FrameDrawOn(_renderTargetTop); + if (config.screen == kScreenTop || config.screen == kScreenBoth) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionTop); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _gameTopTexture.getMatrix()); + _gameTopTexture.render(); + _gameTopTexture.render(); + if (_overlayVisible && config.screen == kScreenTop) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _overlay.getMatrix()); + _overlay.render(); + } + if (_cursorVisible && config.showCursor && config.screen == kScreenTop) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _cursorTexture.getMatrix()); + _cursorTexture.render(); + } + } + + // Render bottom screen + C3D_FrameDrawOn(_renderTargetBottom); + if (config.screen == kScreenBottom || config.screen == kScreenBoth) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionBottom); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _gameBottomTexture.getMatrix()); + _gameTopTexture.render(); + _gameTopTexture.render(); + if (_overlayVisible) { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _overlay.getMatrix()); + _overlay.render(); + } + if (_cursorVisible && config.showCursor) { + 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; + _gameTopTexture.setPosition(_gameTopX, _gameTopY + _gameTopTexture.getScaleY() * shakeOffset); + _gameBottomTexture.setPosition(_gameBottomX, _gameBottomY + _gameBottomTexture.getScaleY() * shakeOffset); +} + +void OSystem_3DS::setFocusRectangle(const Common::Rect &rect) { + debug("setfocus: %d %d %d %d", 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; + updateSize(); + updateScreen(); +} + +void OSystem_3DS::hideOverlay() { + _overlayVisible = false; + updateSize(); + 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; + warning("x:%d y:%d", x, y); + // TODO: adjust for _cursorScalable ? + int offsetx = 0; + int offsety = 0; + x -= _cursorHotspotX; + y -= _cursorHotspotY; + if (!_overlayVisible) { + offsetx += config.screen == kScreenTop ? _gameTopX : _gameBottomX; + offsety += config.screen == kScreenTop ? _gameTopY : _gameBottomY; + } + float scalex = config.screen == kScreenTop ? (float)_gameTopTexture.actualWidth / _gameWidth : 1.f; + float scaley = config.screen == kScreenTop ? (float)_gameTopTexture.actualHeight / _gameHeight : 1.f; + _cursorTexture.setPosition(scalex * x + offsetx, + scaley * y + offsety); +} + +void OSystem_3DS::setCursorDelta(float deltaX, float deltaY) { + _cursorDeltaX = deltaX; + _cursorDeltaY = deltaY; +} + +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; + } + } + } +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem.cpp b/backends/platform/3ds/osystem.cpp new file mode 100644 index 0000000000..f6278eb16b --- /dev/null +++ b/backends/platform/3ds/osystem.cpp @@ -0,0 +1,193 @@ +/* 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_EXCEPTION_printf +#define FORBIDDEN_SYMBOL_EXCEPTION_time_h +#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h + +#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 "config.h" + +#include "backends/fs/posix/posix-fs-factory.h" +#include "backends/fs/posix/posix-fs.h" +#include <unistd.h> +#include <time.h> + +namespace _3DS { + +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), + _cursorPaletteEnabled(false), + _cursorVisible(false), + _cursorScalable(false), + _cursorX(0), + _cursorY(0), + _cursorHotspotX(0), + _cursorHotspotY(0), + _gameTopX(0), + _gameTopY(0), + _gameBottomX(0), + _gameBottomY(0), + _gameWidth(320), + _gameHeight(240), + _overlayVisible(false), + exiting(false), + sleeping(false) +{ + chdir("sdmc:/"); + _fsFactory = new POSIXFilesystemFactory(); + Posix::assureDirectoryExists("/3ds/scummvm/saves/"); +} + +OSystem_3DS::~OSystem_3DS() { + exiting = true; + destroyEvents(); + destroyAudio(); + destroyGraphics(); + + delete _timerManager; + _timerManager = 0; +} + +void OSystem_3DS::quit() { + printf("OSystem_3DS::quit()\n"); +} + +void OSystem_3DS::initBackend() { + loadConfig(); + 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(); +} + +void OSystem_3DS::updateConfig() { + if (_gameScreen.getPixels()) { + updateSize(); + warpMouse(_cursorX, _cursorY); + } +} + +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); +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem.h b/backends/platform/3ds/osystem.h new file mode 100644 index 0000000000..478085acba --- /dev/null +++ b/backends/platform/3ds/osystem.h @@ -0,0 +1,221 @@ +/* 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 + +namespace _3DS { + +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(); + virtual ~OSystem_3DS(); + + volatile bool exiting; + volatile bool sleeping; + + 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); + + // Transform point from touchscreen coords into gamescreen coords + void transformPoint(touchPosition &point); + + void setCursorDelta(float deltaX, float deltaY); + + void updateFocus(); + void updateConfig(); + void updateSize(); + +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 _gameTopX, _gameTopY; + u16 _gameBottomX, _gameBottomY; + + // 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 _gameTopTexture; + Sprite _gameBottomTexture; + Sprite _overlay; + + int _screenShakeOffset; + bool _overlayVisible; + + 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; + Thread _timerThread; + Common::Queue<Common::Event> _eventQueue; + + // Cursor + Graphics::Surface _cursor; + Sprite _cursorTexture; + bool _cursorPaletteEnabled; + bool _cursorVisible; + bool _cursorScalable; + float _cursorX, _cursorY; + float _cursorDeltaX, _cursorDeltaY; + int _cursorHotspotX, _cursorHotspotY; + uint32 _cursorKeyColor; +}; + +} // namespace _3DS + +#endif diff --git a/backends/platform/3ds/shader.v.pica b/backends/platform/3ds/shader.v.pica new file mode 100644 index 0000000000..2d18985622 --- /dev/null +++ b/backends/platform/3ds/shader.v.pica @@ -0,0 +1,59 @@ +;* 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. +;* + +; 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..f97c611473 --- /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. + * + */ + +#include "backends/platform/3ds/sprite.h" +#include "common/util.h" +#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 = MAX(nextHigher2(width), 64u); + h = 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_LINEAR); + 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 |