Initial commit
This commit is contained in:
104
backends/platform/3ds/3ds.mk
Normal file
104
backends/platform/3ds/3ds.mk
Normal file
@@ -0,0 +1,104 @@
|
||||
TARGET := scummvm
|
||||
|
||||
APP_TITLE := ScummVM
|
||||
APP_DESCRIPTION := Point-and-click adventure game engines
|
||||
APP_AUTHOR := ScummVM Team
|
||||
APP_ICON := $(srcdir)/backends/platform/3ds/app/icon.png
|
||||
|
||||
APP_RSF := $(srcdir)/backends/platform/3ds/app/scummvm.rsf
|
||||
APP_BANNER_IMAGE:= $(srcdir)/backends/platform/3ds/app/banner.png
|
||||
APP_BANNER_AUDIO:= $(srcdir)/backends/platform/3ds/app/banner.wav
|
||||
|
||||
BANNERTOOL ?= bannertool
|
||||
MAKEROM ?= makerom
|
||||
|
||||
.PHONY: clean_3ds dist_3ds
|
||||
|
||||
clean: clean_3ds
|
||||
|
||||
clean_3ds:
|
||||
$(RM) backends/platform/3ds/shader.shbin
|
||||
$(RM) backends/platform/3ds/shader_shbin.h
|
||||
$(RM) $(TARGET).smdh
|
||||
$(RM) $(TARGET).3dsx
|
||||
$(RM) $(TARGET).bnr
|
||||
$(RM) $(TARGET).cia
|
||||
$(RM) -rf romfs
|
||||
$(RM) -rf dist_3ds
|
||||
|
||||
romfs: $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_NETWORKING) $(DIST_FILES_VKEYBD) $(DIST_3DS_EXTRA_FILES) $(PLUGINS)
|
||||
@rm -rf romfs
|
||||
@mkdir -p romfs
|
||||
@cp $(DIST_FILES_THEMES) romfs/
|
||||
ifdef DIST_FILES_ENGINEDATA
|
||||
@cp $(DIST_FILES_ENGINEDATA) romfs/
|
||||
endif
|
||||
ifdef DIST_FILES_NETWORKING
|
||||
@cp $(DIST_FILES_NETWORKING) romfs/
|
||||
endif
|
||||
ifdef DIST_FILES_VKEYBD
|
||||
@cp $(DIST_FILES_VKEYBD) romfs/
|
||||
endif
|
||||
ifdef DIST_3DS_EXTRA_FILES
|
||||
@cp -a $(DIST_3DS_EXTRA_FILES) romfs/
|
||||
endif
|
||||
ifeq ($(DYNAMIC_MODULES),1)
|
||||
@mkdir -p romfs/plugins
|
||||
@for i in $(PLUGINS); do $(STRIP) --strip-debug $$i -o romfs/plugins/`basename $$i`; done
|
||||
endif
|
||||
|
||||
$(TARGET).smdh: $(APP_ICON)
|
||||
@$(DEVKITPRO)/tools/bin/smdhtool --create "$(APP_TITLE)" "$(APP_DESCRIPTION)" "$(APP_AUTHOR)" $(APP_ICON) $@
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
$(TARGET).3dsx: $(EXECUTABLE) $(TARGET).smdh romfs
|
||||
@$(DEVKITPRO)/tools/bin/3dsxtool $< $@ --smdh=$(TARGET).smdh --romfs=romfs
|
||||
@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 romfs
|
||||
@$(MAKEROM) -ver $(shell echo $$((($(VER_MAJOR)-2000)*256+$(VER_MINOR)*16+$(VER_PATCH)))) -f cia -target t -exefslogo -o $@ -elf $(EXECUTABLE) -rsf $(APP_RSF) -banner $(TARGET).bnr -icon $(TARGET).smdh -DAPP_ROMFS=romfs/
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
dist_3ds: $(TARGET).cia $(TARGET).3dsx $(DIST_FILES_DOCS)
|
||||
@rm -rf dist_3ds
|
||||
@mkdir -p dist_3ds
|
||||
@cp $(TARGET).3dsx $(TARGET).cia dist_3ds/
|
||||
@cp $(DIST_FILES_DOCS) dist_3ds/
|
||||
@cp $(srcdir)/backends/platform/3ds/README.md dist_3ds/README-3DS.md
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# rules for assembling GPU shaders
|
||||
#---------------------------------------------------------------------------------
|
||||
define shader-as
|
||||
$(eval FILEPATH := $(patsubst %.shbin.o,%.shbin,$@))
|
||||
$(eval FILE := $(patsubst %.shbin.o,%.shbin,$(notdir $@)))
|
||||
$(DEVKITPRO)/tools/bin/picasso -o $(FILEPATH) $1
|
||||
$(DEVKITPRO)/tools/bin/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
|
||||
|
||||
vpath %.v.pica $(srcdir)
|
||||
vpath %.g.pica $(srcdir)
|
||||
vpath %.shlist $(srcdir)
|
||||
|
||||
%.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)))
|
||||
|
||||
# osystem-graphics.cpp includes shader_shbin.h that is generated by the shader assembler
|
||||
backends/platform/3ds/osystem-graphics.o: backends/platform/3ds/shader.shbin.o
|
||||
259
backends/platform/3ds/README.md
Normal file
259
backends/platform/3ds/README.md
Normal file
@@ -0,0 +1,259 @@
|
||||
ScummVM 3DS README
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Table of Contents:
|
||||
------------------
|
||||
[1.0) Installation](#10-installation)
|
||||
* [1.1 3DSX installation](#11-3dsx-installation)
|
||||
* [1.2 CIA installation](#12-cia-installation)
|
||||
|
||||
[2.0) Controls](#20-controls)
|
||||
* [2.1 Default key mappings](#21-default-key-mappings)
|
||||
* [2.2 Hover mode](#22-hover-mode)
|
||||
* [2.3 Drag mode](#23-drag-mode)
|
||||
* [2.4 Magnify mode](#24-magnify-mode)
|
||||
|
||||
[3.0) Supported Games](#30-supported-games)
|
||||
|
||||
[4.0) Compiling](#40-compiling)
|
||||
* [4.1 Prerequisites](#41-prerequisites)
|
||||
* * [4.1.1 Compiling third-party libraries](#411-compiling-third-party-libraries)
|
||||
* * [4.1.2 Manually setting up the environment](#412-manually-setting-up-the-environment)
|
||||
* [4.2 Compiling ScummVM](#42-compiling-scummvm)
|
||||
|
||||
1.0) Installation
|
||||
-----------------
|
||||
There are two possible formats to be used: 3DSX and CIA.
|
||||
The 3DSX format is considered more ethical because it does not make use of
|
||||
invalid title IDs, which get logged. It is also compatible with homebrew loading
|
||||
methods that do not involve CFW.
|
||||
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.
|
||||
|
||||
1.1) 3DSX installation
|
||||
----------------
|
||||
You need to merely extract the ScummVM 3DSX files to your SD card so that all
|
||||
files reside in the `/3ds/scummvm/` directory. It can then be launched by Homebrew Launcher
|
||||
or a similar implementation.
|
||||
|
||||
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.
|
||||
|
||||
2.0) Controls
|
||||
-------------
|
||||
|
||||
2.1) Default key mappings
|
||||
-------------------------
|
||||
|
||||
The key mappings can be customized in the options dialog for the global mappings,
|
||||
and in the edit game dialog for per-game mappings. Per-game mappings overlay the
|
||||
global mappings, so if a button is bound to an action twice, the per-game mapping
|
||||
wins.
|
||||
|
||||
The default keymap is:
|
||||
|
||||
| Buttons | Function |
|
||||
|------------|--------------------------------|
|
||||
| A | Left-click |
|
||||
| B | Right-click |
|
||||
| X | . (skips the current line) |
|
||||
| Y | ESC (skips cutscenes and such) |
|
||||
| L | Toggle magnify mode on/off |
|
||||
| R | Toggle hover/drag modes |
|
||||
| Start | Open global main menu |
|
||||
| Select | Use virtual keyboard |
|
||||
| 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.
|
||||
|
||||
2.4) Magnify mode
|
||||
-----------------
|
||||
Due to the low resolutions of the 3DS's two screens (400x240 for the top, and 320x240
|
||||
for the bottom), games that run at a higher resolution will inevitably lose some visual
|
||||
detail from being scaled down. This can result in situations where essential information
|
||||
is undiscernable, such as text. Magnify mode increases the scale factor of the top screen
|
||||
back to 1; the bottom screen remains unchanged. The touchscreen can then be used to change
|
||||
which part of the game display is being magnified. This can all be done even in situations
|
||||
where the cursor is disabled, such as during full-motion video (FMV) segments.
|
||||
|
||||
When activating magnify mode, touchscreen controls are automatically switched to hover
|
||||
mode; this is to reduce the risk of the user accidentally inputting a click when changing
|
||||
the magnified area via dragging the stylus. Clicking can still be done at will as in normal
|
||||
hover mode. Turning off magnify mode will revert controls back to what was being used
|
||||
previously (ex: if drag mode was in use prior to activating magnify mode, drag mode will
|
||||
be reactivated upon exiting magnify mode), as well as restore the top screen's previous
|
||||
scale factor.
|
||||
|
||||
Currently magnify mode can only be used when the following conditions are met:
|
||||
- In the Backend tab in the options dialog, "Use Screen" is set to "Both"
|
||||
- A game is currently being played
|
||||
- The horizontal and/or vertical resolution in-game is greater than that of the top screen
|
||||
|
||||
Magnify mode cannot be used in the Launcher menu.
|
||||
|
||||
3.0) Supported Games
|
||||
--------------------
|
||||
The full game engine compatibility list can be found here:
|
||||
https://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
|
||||
------------------
|
||||
- Latest version of devkitPro, which comes with devkitARM and `libctru`
|
||||
- `citro3d` thorugh devkitPro's pacman
|
||||
- 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
|
||||
--------------------------------------
|
||||
It is strongly recommended that you use devkitPro's pacman in order to get the most recent
|
||||
portlibs for your build. Instructions for accessing these binaries can be found here:
|
||||
https://devkitpro.org/wiki/devkitPro_pacman
|
||||
|
||||
The following libraries can be downloaded with pacman:
|
||||
|
||||
| Library | Package |
|
||||
----------------|------------------------
|
||||
| zlib | 3ds-zlib |
|
||||
| libpng | 3ds-libpng |
|
||||
| libjpeg | 3ds-libjpeg-turbo |
|
||||
| freetype2 | 3ds-freetype |
|
||||
| libmad | 3ds-libmad |
|
||||
| libogg | 3ds-libogg |
|
||||
| tremor | 3ds-libvorbisidec |
|
||||
| flac | 3ds-flac |
|
||||
| curl | 3ds-curl |
|
||||
| libtheora | 3ds-libtheora |
|
||||
|
||||
At the moment of writing, the version of `freetype2` packaged by devkitPro has an issue
|
||||
where it allocates too much data on the stack when ScummVM loads GUI themes.
|
||||
As a workaround, an older version can be used. Version 2.6.5 is known to work well. The
|
||||
instructions below can be used to compile it.
|
||||
|
||||
At the moment of writing, `faad` is not in the devkitPro 3DS pacman repository. It
|
||||
can be compiled by following the instructions in the section below, in case it cannot
|
||||
be found through pacman.
|
||||
|
||||
The following pacman packages are also recommended:
|
||||
- `3ds-dev`
|
||||
- `dkp-toolchain-vars`
|
||||
|
||||
Once you have the `dkp-toolchain-vars` package, you should be able to find the following
|
||||
scripts in your `/opt/devkitpro` folder:
|
||||
- `devkitarm.sh`
|
||||
- `3dsvars.sh`
|
||||
|
||||
Run them one after the other with `source` in order to setup your environment variables
|
||||
for cross-compiling:
|
||||
```
|
||||
$ source /opt/devkitpro/devkitarm.sh
|
||||
$ source /opt/devkitpro/3dsvars.sh
|
||||
```
|
||||
|
||||
After that, you can download the libraries you want to cross compile, run any autoconf
|
||||
scripts that they may have, and then they can usually be built with the following steps
|
||||
from their source directory:
|
||||
```
|
||||
$ mkdir -p $PORTLIBS
|
||||
$ ./configure --prefix=$PORTLIBS --host=arm-none-eabi --disable-shared \
|
||||
--enable-static
|
||||
$ make
|
||||
$ make install
|
||||
```
|
||||
Most libraries used can be compiled with same commands and configuration flags.
|
||||
|
||||
4.1.2) Manually setting up the environment
|
||||
------------------------------------------
|
||||
In case you don't have the `dkp-toolchain-vars` package downloaded, you can use the
|
||||
following to set-up your environment variables.
|
||||
|
||||
It is assumed that you have these variables already set up. If not, then do so:
|
||||
- DEVKITPRO Your root devkitPro directory (usually /opt/devkitpro)
|
||||
- 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/3ds
|
||||
$ export PATH=$DEVKITPRO/tools/bin:$PORTLIBS/bin:$DEVKITARM/bin:$PATH
|
||||
$ export PKG_CONFIG_PATH=$PORTLIBS/lib/pkgconfig
|
||||
$ export PKG_CONFIG_LIBDIR=$PORTLIBS/lib/pkgconfig
|
||||
$ export CFLAGS="-g -march=armv6k -mtune=mpcore -mfloat-abi=hard -O2
|
||||
-mword-relocations -ffunction-sections -fdata-sections"
|
||||
$ export CXXFLAGS="$CFLAGS"
|
||||
$ export CPPFLAGS="-D_3DS -D__3DS__ -I$PORTLIBS/include -I$CTRULIB/include"
|
||||
$ export LDFLAGS="-L$PORTLIBS/lib -L$CTRULIB/lib"
|
||||
$ export TOOL_PREFIX=arm-none-eabi-
|
||||
$ export CC=${TOOL_PREFIX}gcc
|
||||
$ export CXX=${TOOL_PREFIX}g++
|
||||
$ export AR=${TOOL_PREFIX}gcc-ar
|
||||
$ export RANLIB=${TOOL_PREFIX}gcc-ranlib
|
||||
$ export LIBS="-lctru"
|
||||
```
|
||||
|
||||
4.2) Compiling ScummVM
|
||||
----------------------
|
||||
Do the following in a fresh terminal.
|
||||
|
||||
In case you get a "compiler not found" message, add the toolchain's executables to your PATH:
|
||||
```$ export PATH=$DEVKITARM/bin:$PATH```
|
||||
|
||||
Note: In more recent codebases of ScummVM, you may or may not need to set the following beforehand:
|
||||
```$ export PKG_CONFIG_LIBDIR=$PORTLIBS/lib/pkgconfig```
|
||||
See above for $PORTLIBS.
|
||||
|
||||
ScummVM doesn't provide the CA certificates bundle required by the cloud synchronization features.
|
||||
You need to download it from the curl website: https://curl.haxx.se/ca/cacert.pem, and instruct
|
||||
the build system to package it in the binary:
|
||||
```$ export DIST_3DS_EXTRA_FILES=/path/to/cacert.pem```
|
||||
The name of the file must be `cacert.pem`.
|
||||
|
||||
From the root of the scummvm repository:
|
||||
```
|
||||
$ ./configure --host=3ds --enable-plugins --default-dynamic
|
||||
$ make
|
||||
```
|
||||
Additionally compile to specific formats to be used on the 3DS:
|
||||
```
|
||||
$ make scummvm.3dsx
|
||||
$ make scummvm.cia
|
||||
```
|
||||
|
||||
Assuming everything was successful, you'll be able to find the binary
|
||||
files in the root of your scummvm folder.
|
||||
|
||||
Note: for the CIA format, you will need the 'makerom' and 'bannertool' tools which are
|
||||
not supplied with devkitPro.
|
||||
|
||||
Note: using dynamic plugins as suggested is required when building with most or all of the
|
||||
game engines enabled in order to keep the memory usage low and avoid stability issues.
|
||||
83
backends/platform/3ds/allocHeapsOverride.cpp
Normal file
83
backends/platform/3ds/allocHeapsOverride.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// This file largely reuses code from libctru
|
||||
// https://github.com/devkitPro/libctru/blob/33a570b1c335cc38e50b7fef6bf4ed840bbd4ab2/libctru/source/system/allocateHeaps.c
|
||||
#include <3ds.h>
|
||||
|
||||
#define HEAP_SPLIT_SIZE_CAP (24 << 20) // 24MB
|
||||
#define LINEAR_HEAP_SIZE_CAP (32 << 20) // 32MB
|
||||
|
||||
// Hack to get the hardware's FCRAM layout ID. It just werks.
|
||||
// Layouts 0 through 5 are used exclusively in Old 3DS systems.
|
||||
// Layouts 6 through 8 are used exclusively in New 3DS systems.
|
||||
#define APPMEMTYPE (*(u32*)0x1FF80030)
|
||||
|
||||
extern "C" void __system_allocateHeaps(void) {
|
||||
extern char* fake_heap_start;
|
||||
extern char* fake_heap_end;
|
||||
extern u32 __ctru_heap;
|
||||
extern u32 __ctru_linear_heap;
|
||||
extern u32 __ctru_heap_size;
|
||||
extern u32 __ctru_linear_heap_size;
|
||||
Result rc;
|
||||
|
||||
// Retrieve handle to the resource limit object for our process
|
||||
Handle reslimit = 0;
|
||||
rc = svcGetResourceLimit(&reslimit, CUR_PROCESS_HANDLE);
|
||||
if (R_FAILED(rc))
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
|
||||
// Retrieve information about total/used memory
|
||||
s64 maxCommit = 0, currentCommit = 0;
|
||||
ResourceLimitType reslimitType = RESLIMIT_COMMIT;
|
||||
svcGetResourceLimitLimitValues(&maxCommit, reslimit, &reslimitType, 1); // for APPLICATION this is equal to APPMEMALLOC at all times
|
||||
svcGetResourceLimitCurrentValues(¤tCommit, reslimit, &reslimitType, 1);
|
||||
svcCloseHandle(reslimit);
|
||||
|
||||
// Calculate how much remaining free memory is available
|
||||
u32 remaining = (u32)(maxCommit - currentCommit) &~ 0xFFF;
|
||||
|
||||
__ctru_heap_size = 0;
|
||||
// New 3DS needs more linear memory than Old 3DS to boot up ScummVM; app instantly crashes otherwise.
|
||||
// 0x00A00000 bytes = 10 MiB, for Old 3DS
|
||||
// 0x01400000 bytes = 20 MiB, for New 3DS
|
||||
__ctru_linear_heap_size = APPMEMTYPE < 6 ? 0x00A00000 : 0x01400000;
|
||||
__ctru_heap_size = remaining - __ctru_linear_heap_size;
|
||||
|
||||
// Allocate the application heap
|
||||
rc = svcControlMemory(&__ctru_heap, OS_HEAP_AREA_BEGIN, 0x0, __ctru_heap_size, MEMOP_ALLOC, static_cast<MemPerm>(static_cast<int>(MEMPERM_READ) | static_cast<int>(MEMPERM_WRITE)));
|
||||
if (R_FAILED(rc))
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
|
||||
// Allocate the linear heap
|
||||
rc = svcControlMemory(&__ctru_linear_heap, 0x0, 0x0, __ctru_linear_heap_size, MEMOP_ALLOC_LINEAR, static_cast<MemPerm>(static_cast<int>(MEMPERM_READ) | static_cast<int>(MEMPERM_WRITE)));
|
||||
if (R_FAILED(rc))
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
|
||||
// Mappable allocator init
|
||||
mappableInit(OS_MAP_AREA_BEGIN, OS_MAP_AREA_END);
|
||||
|
||||
// Set up newlib heap
|
||||
fake_heap_start = (char*)__ctru_heap;
|
||||
fake_heap_end = fake_heap_start + __ctru_heap_size;
|
||||
|
||||
}
|
||||
BIN
backends/platform/3ds/app/banner.png
Normal file
BIN
backends/platform/3ds/app/banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
backends/platform/3ds/app/banner.wav
Normal file
BIN
backends/platform/3ds/app/banner.wav
Normal file
Binary file not shown.
BIN
backends/platform/3ds/app/icon.png
Normal file
BIN
backends/platform/3ds/app/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
223
backends/platform/3ds/app/scummvm.rsf
Normal file
223
backends/platform/3ds/app/scummvm.rsf
Normal file
@@ -0,0 +1,223 @@
|
||||
BasicInfo:
|
||||
Title : ScummVM
|
||||
ProductCode : ScummVM
|
||||
Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem
|
||||
|
||||
TitleInfo:
|
||||
Category : Application
|
||||
UniqueId : 0xFF321
|
||||
|
||||
RomFs:
|
||||
# Specifies the root path of the read only file system to include in the ROM.
|
||||
RootPath : $(APP_ROMFS)
|
||||
|
||||
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
|
||||
85
backends/platform/3ds/main.cpp
Normal file
85
backends/platform/3ds/main.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/platform/3ds/osystem.h"
|
||||
#include "backends/plugins/3ds/3ds-provider.h"
|
||||
|
||||
#include <3ds.h>
|
||||
#include <malloc.h>
|
||||
|
||||
enum {
|
||||
SYSTEM_MODEL_2DS = 3
|
||||
};
|
||||
|
||||
// Set the size of the stack.
|
||||
u32 __stacksize__ = 64 * 1024;
|
||||
|
||||
// Set the size of the linear heap to allow a larger application heap.
|
||||
// We do this in backends/platform/3ds/allocHeapsOverride.cpp
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Initialize basic libctru stuff
|
||||
cfguInit();
|
||||
gfxInitDefault();
|
||||
|
||||
// 800px wide top screen is not available on old 2DS systems
|
||||
u8 systemModel = 0;
|
||||
CFGU_GetSystemModel(&systemModel);
|
||||
gfxSetWide(systemModel != SYSTEM_MODEL_2DS);
|
||||
|
||||
romfsInit();
|
||||
osSetSpeedupEnable(true);
|
||||
// consoleInit(GFX_TOP, NULL);
|
||||
gdbHioDevInit();
|
||||
gdbHioDevRedirectStdStreams(true, true, true);
|
||||
|
||||
#ifdef USE_LIBCURL
|
||||
const uint32 soc_sharedmem_size = 0x10000;
|
||||
void *soc_sharedmem = memalign(0x1000, soc_sharedmem_size);
|
||||
socInit((u32 *)soc_sharedmem, soc_sharedmem_size);
|
||||
#endif
|
||||
|
||||
g_system = new N3DS::OSystem_3DS();
|
||||
assert(g_system);
|
||||
|
||||
#ifdef DYNAMIC_MODULES
|
||||
PluginManager::instance().addPluginProvider(new CTRPluginProvider());
|
||||
#endif
|
||||
|
||||
int res = scummvm_main(argc, argv);
|
||||
|
||||
g_system->destroy();
|
||||
|
||||
// Turn on both screen backlights before exiting.
|
||||
if (R_SUCCEEDED(gspLcdInit())) {
|
||||
GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH);
|
||||
gspLcdExit();
|
||||
}
|
||||
|
||||
#ifdef USE_LIBCURL
|
||||
socExit();
|
||||
#endif
|
||||
gdbHioDevExit();
|
||||
romfsExit();
|
||||
gfxExit();
|
||||
cfguExit();
|
||||
return res;
|
||||
}
|
||||
17
backends/platform/3ds/module.mk
Normal file
17
backends/platform/3ds/module.mk
Normal file
@@ -0,0 +1,17 @@
|
||||
MODULE := backends/platform/3ds
|
||||
|
||||
MODULE_OBJS := \
|
||||
main.o \
|
||||
allocHeapsOverride.o \
|
||||
shader.shbin.o \
|
||||
sprite.o \
|
||||
options.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)))
|
||||
163
backends/platform/3ds/options.cpp
Normal file
163
backends/platform/3ds/options.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
|
||||
|
||||
#include "backends/platform/3ds/osystem.h"
|
||||
|
||||
#include "gui/gui-manager.h"
|
||||
#include "gui/ThemeEval.h"
|
||||
#include "gui/widget.h"
|
||||
#include "gui/widgets/list.h"
|
||||
#include "gui/widgets/popup.h"
|
||||
|
||||
#include "common/translation.h"
|
||||
|
||||
namespace N3DS {
|
||||
|
||||
class N3DSOptionsWidget : public GUI::OptionsContainerWidget {
|
||||
public:
|
||||
explicit N3DSOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain);
|
||||
~N3DSOptionsWidget() override;
|
||||
|
||||
// OptionsContainerWidget API
|
||||
void load() override;
|
||||
bool save() override;
|
||||
bool hasKeys() override;
|
||||
void setEnabled(bool e) override;
|
||||
|
||||
private:
|
||||
// OptionsContainerWidget API
|
||||
void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
|
||||
|
||||
GUI::CheckboxWidget *_showCursorCheckbox;
|
||||
GUI::CheckboxWidget *_snapToBorderCheckbox;
|
||||
GUI::CheckboxWidget *_stretchToFitCheckbox;
|
||||
|
||||
GUI::StaticTextWidget *_screenDesc;
|
||||
GUI::PopUpWidget *_screenPopUp;
|
||||
|
||||
bool _enabled;
|
||||
};
|
||||
|
||||
N3DSOptionsWidget::N3DSOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) :
|
||||
OptionsContainerWidget(boss, name, "N3DSOptionsDialog", domain), _enabled(true) {
|
||||
|
||||
_showCursorCheckbox = new GUI::CheckboxWidget(widgetsBoss(), "N3DSOptionsDialog.ShowCursor", _("Show mouse cursor"), Common::U32String(), 0, 'T');
|
||||
_snapToBorderCheckbox = new GUI::CheckboxWidget(widgetsBoss(), "N3DSOptionsDialog.SnapToBorder", _("Snap to edges"), Common::U32String(), 0, 'T');
|
||||
_stretchToFitCheckbox = new GUI::CheckboxWidget(widgetsBoss(), "N3DSOptionsDialog.StretchToFit", _("Stretch to fit"), Common::U32String(), 0, 'T');
|
||||
|
||||
_screenDesc = new GUI::StaticTextWidget(widgetsBoss(), "N3DSOptionsDialog.ScreenText", _("Use Screen:"));
|
||||
_screenPopUp = new GUI::PopUpWidget(widgetsBoss(), "N3DSOptionsDialog.Screen");
|
||||
_screenPopUp->appendEntry(_c("Top", "3ds-screen"), kScreenTop);
|
||||
_screenPopUp->appendEntry(_c("Bottom", "3ds-screen"), kScreenBottom);
|
||||
_screenPopUp->appendEntry(_c("Both", "3ds-screen"), kScreenBoth);
|
||||
}
|
||||
|
||||
N3DSOptionsWidget::~N3DSOptionsWidget() {
|
||||
}
|
||||
|
||||
void N3DSOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const {
|
||||
layouts.addDialog(layoutName, overlayedLayout)
|
||||
.addLayout(GUI::ThemeLayout::kLayoutVertical)
|
||||
.addPadding(0, 0, 0, 0)
|
||||
.addWidget("ShowCursor", "Checkbox")
|
||||
.addWidget("SnapToBorder", "Checkbox")
|
||||
.addWidget("StretchToFit", "Checkbox")
|
||||
.addLayout(GUI::ThemeLayout::kLayoutHorizontal)
|
||||
.addPadding(16, 16, 0, 0)
|
||||
.addWidget("ScreenText", "OptionsLabel")
|
||||
.addWidget("Screen", "PopUp")
|
||||
.closeLayout()
|
||||
.closeLayout()
|
||||
.closeDialog();
|
||||
}
|
||||
|
||||
void N3DSOptionsWidget::load() {
|
||||
_showCursorCheckbox->setState(ConfMan.getBool("3ds_showcursor", _domain));
|
||||
_snapToBorderCheckbox->setState(ConfMan.getBool("3ds_snaptoborder", _domain));
|
||||
_stretchToFitCheckbox->setState(ConfMan.getBool("3ds_stretchtofit", _domain));
|
||||
_screenPopUp->setSelectedTag(ConfMan.getInt("3ds_screen", _domain));
|
||||
}
|
||||
|
||||
bool N3DSOptionsWidget::save() {
|
||||
if (_enabled) {
|
||||
ConfMan.setBool("3ds_showcursor", _showCursorCheckbox->getState(), _domain);
|
||||
ConfMan.setBool("3ds_snaptoborder", _snapToBorderCheckbox->getState(), _domain);
|
||||
ConfMan.setBool("3ds_stretchtofit", _stretchToFitCheckbox->getState(), _domain);
|
||||
ConfMan.setInt("3ds_screen", _screenPopUp->getSelectedTag(), _domain);
|
||||
} else {
|
||||
ConfMan.removeKey("3ds_showcursor", _domain);
|
||||
ConfMan.removeKey("3ds_snaptoborder", _domain);
|
||||
ConfMan.removeKey("3ds_stretchtofit", _domain);
|
||||
ConfMan.removeKey("3ds_screen", _domain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool N3DSOptionsWidget::hasKeys() {
|
||||
return ConfMan.hasKey("3ds_showcursor", _domain) ||
|
||||
ConfMan.hasKey("3ds_snaptoborder", _domain) ||
|
||||
ConfMan.hasKey("3ds_stretchtofit", _domain) ||
|
||||
ConfMan.hasKey("3ds_screen", _domain);
|
||||
}
|
||||
|
||||
void N3DSOptionsWidget::setEnabled(bool e) {
|
||||
_enabled = e;
|
||||
|
||||
_showCursorCheckbox->setEnabled(e);
|
||||
_snapToBorderCheckbox->setEnabled(e);
|
||||
_stretchToFitCheckbox->setEnabled(e);
|
||||
_screenDesc->setEnabled(e);
|
||||
_screenPopUp->setEnabled(e);
|
||||
}
|
||||
|
||||
|
||||
GUI::OptionsContainerWidget *OSystem_3DS::buildBackendOptionsWidget(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const {
|
||||
return new N3DSOptionsWidget(boss, name, target);
|
||||
}
|
||||
|
||||
void OSystem_3DS::registerDefaultSettings(const Common::String &target) const {
|
||||
ConfMan.registerDefault("3ds_showcursor", true);
|
||||
ConfMan.registerDefault("3ds_snaptoborder", true);
|
||||
ConfMan.registerDefault("3ds_stretchtofit", false);
|
||||
ConfMan.registerDefault("3ds_screen", kScreenBoth);
|
||||
}
|
||||
|
||||
void OSystem_3DS::applyBackendSettings() {
|
||||
int oldScreen = _screen;
|
||||
|
||||
_showCursor = ConfMan.getBool("3ds_showcursor");
|
||||
_snapToBorder = ConfMan.getBool("3ds_snaptoborder");
|
||||
_stretchToFit = ConfMan.getBool("3ds_stretchtofit");
|
||||
_screen = (Screen)ConfMan.getInt("3ds_screen");
|
||||
|
||||
updateBacklight();
|
||||
updateConfig();
|
||||
|
||||
if (_screen != oldScreen) {
|
||||
_screenChangeId++;
|
||||
g_gui.checkScreenChange();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace N3DS
|
||||
107
backends/platform/3ds/osystem-audio.cpp
Normal file
107
backends/platform/3ds/osystem-audio.cpp
Normal file
@@ -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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/platform/3ds/osystem.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace N3DS {
|
||||
|
||||
static bool hasAudio = false;
|
||||
|
||||
static void audioThreadFunc(void *arg) {
|
||||
Audio::MixerImpl *mixer = (Audio::MixerImpl *)arg;
|
||||
OSystem_3DS *osys = dynamic_cast<OSystem_3DS *>(g_system);
|
||||
|
||||
const int channel = 0;
|
||||
int bufferIndex = 0;
|
||||
const int bufferCount = 2;
|
||||
const int sampleRate = mixer->getOutputRate();
|
||||
const int bufferSamples = 1024;
|
||||
const int bufferSize = bufferSamples * 4;
|
||||
|
||||
ndspWaveBuf buffers[bufferCount];
|
||||
|
||||
for (int 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) {
|
||||
svcSleepThread(5000 * 1000); // Wake up the thread every 5 ms
|
||||
|
||||
if (osys->sleeping) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ndspWaveBuf *buf = &buffers[bufferIndex];
|
||||
if (buf->status == NDSP_WBUF_FREE || buf->status == NDSP_WBUF_DONE) {
|
||||
buf->nsamples = mixer->mixCallback(buf->data_adpcm, bufferSize);
|
||||
if (buf->nsamples > 0) {
|
||||
DSP_FlushDataCache(buf->data_vaddr, bufferSize);
|
||||
ndspChnWaveBufAdd(channel, buf);
|
||||
}
|
||||
|
||||
bufferIndex++;
|
||||
bufferIndex %= bufferCount;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < bufferCount; ++i)
|
||||
linearFree(buffers[i].data_pcm16);
|
||||
}
|
||||
|
||||
void OSystem_3DS::initAudio() {
|
||||
_mixer = new Audio::MixerImpl(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 N3DS
|
||||
430
backends/platform/3ds/osystem-events.cpp
Normal file
430
backends/platform/3ds/osystem-events.cpp
Normal file
@@ -0,0 +1,430 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
|
||||
|
||||
#include "backends/platform/3ds/osystem.h"
|
||||
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymapper-defaults.h"
|
||||
#include "backends/keymapper/hardware-input.h"
|
||||
#include "backends/keymapper/keymap.h"
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
#include "backends/timer/default/default-timer.h"
|
||||
#include "common/translation.h"
|
||||
#include "engines/engine.h"
|
||||
#include "gui/gui-manager.h"
|
||||
|
||||
namespace N3DS {
|
||||
|
||||
static Common::Mutex *eventMutex;
|
||||
static InputMode inputMode = MODE_DRAG;
|
||||
static InputMode savedInputMode = MODE_DRAG;
|
||||
static aptHookCookie cookie;
|
||||
|
||||
static const Common::HardwareInputTableEntry ctrJoystickButtons[] = {
|
||||
{ "JOY_A", Common::JOYSTICK_BUTTON_A, _s("A") },
|
||||
{ "JOY_B", Common::JOYSTICK_BUTTON_B, _s("B") },
|
||||
{ "JOY_X", Common::JOYSTICK_BUTTON_X, _s("X") },
|
||||
{ "JOY_Y", Common::JOYSTICK_BUTTON_Y, _s("Y") },
|
||||
{ "JOY_BACK", Common::JOYSTICK_BUTTON_BACK, _s("Select") },
|
||||
{ "JOY_START", Common::JOYSTICK_BUTTON_START, _s("Start") },
|
||||
{ "JOY_LEFT_STICK", Common::JOYSTICK_BUTTON_LEFT_STICK, _s("ZL") },
|
||||
{ "JOY_RIGHT_STICK", Common::JOYSTICK_BUTTON_RIGHT_STICK, _s("ZR") },
|
||||
{ "JOY_LEFT_SHOULDER", Common::JOYSTICK_BUTTON_LEFT_SHOULDER, _s("L") },
|
||||
{ "JOY_RIGHT_SHOULDER", Common::JOYSTICK_BUTTON_RIGHT_SHOULDER, _s("R") },
|
||||
{ "JOY_UP", Common::JOYSTICK_BUTTON_DPAD_UP, _s("D-pad Up") },
|
||||
{ "JOY_DOWN", Common::JOYSTICK_BUTTON_DPAD_DOWN, _s("D-pad Down") },
|
||||
{ "JOY_LEFT", Common::JOYSTICK_BUTTON_DPAD_LEFT, _s("D-pad Left") },
|
||||
{ "JOY_RIGHT", Common::JOYSTICK_BUTTON_DPAD_RIGHT, _s("D-pad Right") },
|
||||
{ nullptr, 0, nullptr }
|
||||
};
|
||||
|
||||
static const Common::AxisTableEntry ctrJoystickAxes[] = {
|
||||
{ "JOY_LEFT_STICK_X", Common::JOYSTICK_AXIS_LEFT_STICK_X, Common::kAxisTypeFull, _s("C-Pad X") },
|
||||
{ "JOY_LEFT_STICK_Y", Common::JOYSTICK_AXIS_LEFT_STICK_Y, Common::kAxisTypeFull, _s("C-Pad Y") },
|
||||
{ nullptr, 0, Common::kAxisTypeFull, nullptr }
|
||||
};
|
||||
|
||||
const Common::HardwareInputTableEntry ctrMouseButtons[] = {
|
||||
{ "MOUSE_LEFT", Common::MOUSE_BUTTON_LEFT, _s("Touch") },
|
||||
{ nullptr, 0, nullptr }
|
||||
};
|
||||
|
||||
static const int16 CIRCLE_MAX = 160;
|
||||
|
||||
static void pushEventQueue(Common::Queue<Common::Event> *queue, Common::Event &event) {
|
||||
Common::StackLock lock(*eventMutex);
|
||||
queue->push(event);
|
||||
}
|
||||
|
||||
static void doJoyEvent(Common::Queue<Common::Event> *queue, u32 keysPressed, u32 keysReleased, u32 ctrKey, uint8 svmButton) {
|
||||
if (keysPressed & ctrKey || keysReleased & ctrKey) {
|
||||
Common::Event event;
|
||||
event.type = (keysPressed & ctrKey) ? Common::EVENT_JOYBUTTON_DOWN : Common::EVENT_JOYBUTTON_UP;
|
||||
event.joystick.button = svmButton;
|
||||
|
||||
pushEventQueue(queue, event);
|
||||
}
|
||||
}
|
||||
|
||||
static void eventThreadFunc(void *arg) {
|
||||
OSystem_3DS *osys = dynamic_cast<OSystem_3DS *>(g_system);
|
||||
Common::Queue<Common::Event> *eventQueue = (Common::Queue<Common::Event> *)arg;
|
||||
|
||||
uint32 touchStartTime = osys->getMillis();
|
||||
touchPosition lastTouch = {0, 0};
|
||||
circlePosition lastCircle = {0, 0};
|
||||
int borderSnapZone = 6;
|
||||
Common::Event event;
|
||||
|
||||
while (!osys->exiting) {
|
||||
do {
|
||||
osys->delayMillis(10);
|
||||
} while (osys->sleeping && !osys->exiting);
|
||||
|
||||
hidScanInput();
|
||||
u32 held = hidKeysHeld();
|
||||
u32 keysPressed = hidKeysDown();
|
||||
u32 keysReleased = hidKeysUp();
|
||||
|
||||
// Touch screen events
|
||||
if (held & KEY_TOUCH) {
|
||||
touchPosition touch;
|
||||
hidTouchRead(&touch);
|
||||
if (osys->_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;
|
||||
}
|
||||
}
|
||||
|
||||
osys->transformPoint(touch);
|
||||
|
||||
event.mouse.x = touch.px;
|
||||
event.mouse.y = touch.py;
|
||||
|
||||
if (keysPressed & KEY_TOUCH) {
|
||||
touchStartTime = osys->getMillis();
|
||||
if (inputMode == MODE_DRAG) {
|
||||
event.type = 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 = 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 = Common::EVENT_LBUTTONDOWN;
|
||||
pushEventQueue(eventQueue, event);
|
||||
event.type = Common::EVENT_LBUTTONUP;
|
||||
pushEventQueue(eventQueue, event);
|
||||
}
|
||||
}
|
||||
|
||||
// C-Pad events
|
||||
circlePosition circle;
|
||||
hidCircleRead(&circle);
|
||||
|
||||
if (circle.dx != lastCircle.dx) {
|
||||
int32 position = (int32)circle.dx * Common::JOYAXIS_MAX / CIRCLE_MAX;
|
||||
|
||||
event.type = Common::EVENT_JOYAXIS_MOTION;
|
||||
event.joystick.axis = Common::JOYSTICK_AXIS_LEFT_STICK_X;
|
||||
event.joystick.position = CLIP<int32>(position, Common::JOYAXIS_MIN, Common::JOYAXIS_MAX);
|
||||
pushEventQueue(eventQueue, event);
|
||||
}
|
||||
|
||||
if (circle.dy != lastCircle.dy) {
|
||||
int32 position = -(int32)circle.dy * Common::JOYAXIS_MAX / CIRCLE_MAX;
|
||||
|
||||
event.type = Common::EVENT_JOYAXIS_MOTION;
|
||||
event.joystick.axis = Common::JOYSTICK_AXIS_LEFT_STICK_Y;
|
||||
event.joystick.position = CLIP<int32>(position, Common::JOYAXIS_MIN, Common::JOYAXIS_MAX);
|
||||
pushEventQueue(eventQueue, event);
|
||||
}
|
||||
|
||||
lastCircle = circle;
|
||||
|
||||
// Button events
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_L, Common::JOYSTICK_BUTTON_LEFT_SHOULDER);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_R, Common::JOYSTICK_BUTTON_RIGHT_SHOULDER);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_A, Common::JOYSTICK_BUTTON_A);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_B, Common::JOYSTICK_BUTTON_B);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_X, Common::JOYSTICK_BUTTON_X);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_Y, Common::JOYSTICK_BUTTON_Y);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_DUP, Common::JOYSTICK_BUTTON_DPAD_UP);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_DDOWN, Common::JOYSTICK_BUTTON_DPAD_DOWN);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_DLEFT, Common::JOYSTICK_BUTTON_DPAD_LEFT);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_DRIGHT, Common::JOYSTICK_BUTTON_DPAD_RIGHT);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_START, Common::JOYSTICK_BUTTON_START);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_SELECT, Common::JOYSTICK_BUTTON_BACK);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_ZL, Common::JOYSTICK_BUTTON_LEFT_STICK);
|
||||
doJoyEvent(eventQueue, keysPressed, keysReleased, KEY_ZR, Common::JOYSTICK_BUTTON_RIGHT_STICK);
|
||||
}
|
||||
}
|
||||
|
||||
static void aptHookFunc(APT_HookType hookType, void *param) {
|
||||
OSystem_3DS *osys = dynamic_cast<OSystem_3DS *>(g_system);
|
||||
|
||||
switch (hookType) {
|
||||
case APTHOOK_ONSUSPEND:
|
||||
case APTHOOK_ONSLEEP:
|
||||
if (g_engine) {
|
||||
osys->_sleepPauseToken = g_engine->pauseEngine();
|
||||
}
|
||||
osys->sleeping = true;
|
||||
if (R_SUCCEEDED(gspLcdInit())) {
|
||||
GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH);
|
||||
gspLcdExit();
|
||||
}
|
||||
break;
|
||||
case APTHOOK_ONRESTORE:
|
||||
case APTHOOK_ONWAKEUP:
|
||||
if (g_engine) {
|
||||
osys->_sleepPauseToken.clear();
|
||||
}
|
||||
osys->sleeping = false;
|
||||
osys->updateBacklight();
|
||||
osys->updateConfig();
|
||||
break;
|
||||
case APTHOOK_ONEXIT:
|
||||
break;
|
||||
default:
|
||||
warning("Unhandled APT hook, type: %d", hookType);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Common::HardwareInputSet *OSystem_3DS::getHardwareInputSet() {
|
||||
using namespace Common;
|
||||
|
||||
CompositeHardwareInputSet *inputSet = new CompositeHardwareInputSet();
|
||||
// Touch input sends mouse events for now, so we need to declare we have a mouse...
|
||||
inputSet->addHardwareInputSet(new MouseHardwareInputSet(ctrMouseButtons));
|
||||
inputSet->addHardwareInputSet(new JoystickHardwareInputSet(ctrJoystickButtons, ctrJoystickAxes));
|
||||
|
||||
return inputSet;
|
||||
}
|
||||
|
||||
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);
|
||||
_eventManager->getEventDispatcher()->registerObserver(this, 10, false);
|
||||
}
|
||||
|
||||
void OSystem_3DS::destroyEvents() {
|
||||
_eventManager->getEventDispatcher()->unregisterObserver(this);
|
||||
|
||||
threadJoin(_timerThread, U64_MAX);
|
||||
threadFree(_timerThread);
|
||||
|
||||
threadJoin(_eventThread, U64_MAX);
|
||||
threadFree(_eventThread);
|
||||
delete eventMutex;
|
||||
}
|
||||
|
||||
void OSystem_3DS::transformPoint(touchPosition &point) {
|
||||
if (!_overlayInGUI) {
|
||||
point.px = static_cast<float>(point.px) / _gameBottomTexture.getScaleX() - _gameBottomTexture.getPosX();
|
||||
point.py = static_cast<float>(point.py) / _gameBottomTexture.getScaleY() - _gameBottomTexture.getPosY();
|
||||
}
|
||||
|
||||
clipPoint(point);
|
||||
}
|
||||
|
||||
void OSystem_3DS::clipPoint(touchPosition &point) {
|
||||
if (_overlayInGUI) {
|
||||
point.px = CLIP<uint16>(point.px, 0, getOverlayWidth() - 1);
|
||||
point.py = CLIP<uint16>(point.py, 0, getOverlayHeight() - 1);
|
||||
} else {
|
||||
point.px = CLIP<uint16>(point.px, 0, _gameTopTexture.actualWidth - 1);
|
||||
point.py = CLIP<uint16>(point.py, 0, _gameTopTexture.actualHeight - 1);
|
||||
}
|
||||
}
|
||||
|
||||
enum _3DSCustomEvent {
|
||||
k3DSEventToggleDragMode,
|
||||
k3DSEventToggleMagnifyMode
|
||||
};
|
||||
|
||||
Common::KeymapArray OSystem_3DS::getGlobalKeymaps() {
|
||||
using namespace Common;
|
||||
|
||||
Keymap *keymap = new Keymap(Keymap::kKeymapTypeGlobal, "3ds", "3DS");
|
||||
|
||||
Action *act;
|
||||
|
||||
act = new Action("DRAGM", _("Toggle Drag Mode"));
|
||||
act->setCustomBackendActionEvent(k3DSEventToggleDragMode);
|
||||
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
|
||||
keymap->addAction(act);
|
||||
|
||||
act = new Action("MAGM", _("Toggle Magnify Mode"));
|
||||
act->setCustomBackendActionEvent(k3DSEventToggleMagnifyMode);
|
||||
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
|
||||
keymap->addAction(act);
|
||||
|
||||
return Keymap::arrayOf(keymap);
|
||||
}
|
||||
|
||||
Common::KeymapperDefaultBindings *OSystem_3DS::getKeymapperDefaultBindings() {
|
||||
Common::KeymapperDefaultBindings *keymapperDefaultBindings = new Common::KeymapperDefaultBindings();
|
||||
|
||||
// Unmap the main menu standard action so LEFT_SHOULDER can be used for drag mode
|
||||
keymapperDefaultBindings->setDefaultBinding("engine-default", Common::kStandardActionOpenMainMenu, "");
|
||||
|
||||
return keymapperDefaultBindings;
|
||||
}
|
||||
|
||||
bool OSystem_3DS::pollEvent(Common::Event &event) {
|
||||
if (!aptMainLoop()) {
|
||||
// The system requested us to quit
|
||||
if (_sleepPauseToken.isActive()) {
|
||||
_sleepPauseToken.clear();
|
||||
}
|
||||
|
||||
event.type = Common::EVENT_QUIT;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If magnify mode is on when returning to Launcher, turn it off
|
||||
if (_eventManager->shouldReturnToLauncher()) {
|
||||
if (_magnifyMode == MODE_MAGON) {
|
||||
_magnifyMode = MODE_MAGOFF;
|
||||
updateSize();
|
||||
if (savedInputMode == MODE_DRAG) {
|
||||
inputMode = savedInputMode;
|
||||
displayMessageOnOSD(_("Magnify Mode Off. Reactivating Drag Mode.\nReturning to Launcher..."));
|
||||
} else {
|
||||
displayMessageOnOSD(_("Magnify Mode Off. Returning to Launcher..."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Common::StackLock lock(*eventMutex);
|
||||
|
||||
if (_eventQueue.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
event = _eventQueue.pop();
|
||||
|
||||
if (Common::isMouseEvent(event)) {
|
||||
warpMouse(event.mouse.x, event.mouse.y);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OSystem_3DS::notifyEvent(const Common::Event &event) {
|
||||
if (event.type != Common::EVENT_CUSTOM_BACKEND_ACTION_START
|
||||
&& event.type != Common::EVENT_CUSTOM_BACKEND_ACTION_END) {
|
||||
return false; // We're only interested in custom backend events
|
||||
}
|
||||
|
||||
if (event.type == Common::EVENT_CUSTOM_BACKEND_ACTION_END) {
|
||||
return true; // We'll say we have handled the event so it is not propagated
|
||||
}
|
||||
|
||||
switch ((_3DSCustomEvent)event.customType) {
|
||||
case k3DSEventToggleDragMode:
|
||||
if (inputMode == MODE_DRAG) {
|
||||
inputMode = savedInputMode = MODE_HOVER;
|
||||
displayMessageOnOSD(_("Hover Mode"));
|
||||
} else {
|
||||
if (_magnifyMode == MODE_MAGOFF) {
|
||||
inputMode = savedInputMode = MODE_DRAG;
|
||||
displayMessageOnOSD(_("Drag Mode"));
|
||||
} else {
|
||||
displayMessageOnOSD(_("Cannot Switch to Drag Mode while Magnify Mode is On"));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case k3DSEventToggleMagnifyMode:
|
||||
if (_overlayVisible) {
|
||||
displayMessageOnOSD(_("Magnify Mode cannot be activated in menus."));
|
||||
} else if (_screen != kScreenBoth && _magnifyMode == MODE_MAGOFF) {
|
||||
// TODO: Automatically enable both screens while magnify mode is on
|
||||
displayMessageOnOSD(_("Magnify Mode can only be activated\n when both screens are enabled."));
|
||||
} else if (_gameWidth <= 400 && _gameHeight <= 240) {
|
||||
displayMessageOnOSD(_("In-game resolution too small to magnify."));
|
||||
} else {
|
||||
if (_magnifyMode == MODE_MAGOFF) {
|
||||
_magnifyMode = MODE_MAGON;
|
||||
if (inputMode == MODE_DRAG) {
|
||||
inputMode = MODE_HOVER;
|
||||
displayMessageOnOSD(_("Magnify Mode On. Switching to Hover Mode..."));
|
||||
} else {
|
||||
displayMessageOnOSD(_("Magnify Mode On"));
|
||||
}
|
||||
} else {
|
||||
_magnifyMode = MODE_MAGOFF;
|
||||
updateSize();
|
||||
if (savedInputMode == MODE_DRAG) {
|
||||
inputMode = savedInputMode;
|
||||
displayMessageOnOSD(_("Magnify Mode Off. Reactivating Drag Mode..."));
|
||||
} else {
|
||||
displayMessageOnOSD(_("Magnify Mode Off"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace N3DS
|
||||
887
backends/platform/3ds/osystem-graphics.cpp
Normal file
887
backends/platform/3ds/osystem-graphics.cpp
Normal file
@@ -0,0 +1,887 @@
|
||||
/* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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 "graphics/blit.h"
|
||||
#include "graphics/fontman.h"
|
||||
#include "gui/gui-manager.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_RGB8) | \
|
||||
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \
|
||||
GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
|
||||
#define TEXTURE_TRANSFER_FLAGS(in, out) \
|
||||
(GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | \
|
||||
GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(in) | \
|
||||
GX_TRANSFER_OUT_FORMAT(out) | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
|
||||
#define DEFAULT_MODE _modeRGBA8
|
||||
|
||||
namespace N3DS {
|
||||
/* Group the various enums, values, etc. needed for
|
||||
* each graphics mode into instaces of GfxMode3DS */
|
||||
static const GfxMode3DS _modeRGBA8 = { Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0),
|
||||
GPU_RGBA8, TEXTURE_TRANSFER_FLAGS(GX_TRANSFER_FMT_RGBA8, GX_TRANSFER_FMT_RGBA8) };
|
||||
static const GfxMode3DS _modeRGBX8 = { Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0),
|
||||
GPU_RGB8, TEXTURE_TRANSFER_FLAGS(GX_TRANSFER_FMT_RGBA8, GX_TRANSFER_FMT_RGB8) };
|
||||
static const GfxMode3DS _modeRGB565 = { Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0),
|
||||
GPU_RGB565, TEXTURE_TRANSFER_FLAGS(GX_TRANSFER_FMT_RGB565, GX_TRANSFER_FMT_RGB565) };
|
||||
static const GfxMode3DS _modeRGB5A1 = { Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0),
|
||||
GPU_RGBA5551, TEXTURE_TRANSFER_FLAGS(GX_TRANSFER_FMT_RGB5A1, GX_TRANSFER_FMT_RGB5A1) };
|
||||
static const GfxMode3DS _modeRGBA4 = { Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0),
|
||||
GPU_RGBA4, TEXTURE_TRANSFER_FLAGS(GX_TRANSFER_FMT_RGBA4, GX_TRANSFER_FMT_RGBA4) };
|
||||
static const GfxMode3DS _modeCLUT8 = _modeRGBX8;
|
||||
|
||||
static const GfxMode3DS *gfxModes[] = { &_modeRGBX8, &_modeRGB565, &_modeRGB5A1, &_modeRGBA4, &_modeCLUT8 };
|
||||
|
||||
|
||||
void OSystem_3DS::init3DSGraphics() {
|
||||
_gfxState.gfxMode = gfxModes[CLUT8];
|
||||
_pfGame = Graphics::PixelFormat::createFormatCLUT8();
|
||||
_pfDefaultTexture = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
|
||||
|
||||
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
|
||||
|
||||
// Initialize the render targets
|
||||
|
||||
int topScreenWidth = gfxIsWide() ? 800 : 400;
|
||||
|
||||
_renderTargetTop =
|
||||
C3D_RenderTargetCreate(240, topScreenWidth, GPU_RB_RGB8, -1);
|
||||
C3D_RenderTargetClear(_renderTargetTop, C3D_CLEAR_ALL, 0x0000000, 0);
|
||||
C3D_RenderTargetSetOutput(_renderTargetTop, GFX_TOP, GFX_LEFT,
|
||||
DISPLAY_TRANSFER_FLAGS);
|
||||
|
||||
_renderTargetBottom =
|
||||
C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, -1);
|
||||
C3D_RenderTargetClear(_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*)const_cast<u8 *>(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, true);
|
||||
Mtx_OrthoTilt(&_projectionBottom, 0.0, 320.0, 240.0, 0.0, 0.0, 1.0, true);
|
||||
|
||||
C3D_TexEnv *env = C3D_GetTexEnv(0);
|
||||
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR);
|
||||
C3D_TexEnvOpRgb(env, GPU_TEVOP_RGB_SRC_COLOR, GPU_TEVOP_RGB_SRC_COLOR, GPU_TEVOP_RGB_SRC_COLOR);
|
||||
C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);
|
||||
|
||||
C3D_DepthTest(false, GPU_GEQUAL, GPU_WRITE_ALL);
|
||||
C3D_CullFace(GPU_CULL_NONE);
|
||||
|
||||
// _overlay initialized in updateSize()
|
||||
}
|
||||
|
||||
void OSystem_3DS::destroy3DSGraphics() {
|
||||
_gameScreen.free();
|
||||
_cursor.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::kFeatureCursorAlpha ||
|
||||
f == OSystem::kFeatureFilteringMode ||
|
||||
f == OSystem::kFeatureOverlaySupportsAlpha ||
|
||||
f == OSystem::kFeatureKbdMouseSpeed ||
|
||||
f == OSystem::kFeatureJoystickDeadzone);
|
||||
}
|
||||
|
||||
void OSystem_3DS::setFeatureState(OSystem::Feature f, bool enable) {
|
||||
switch (f) {
|
||||
case OSystem::kFeatureCursorPalette:
|
||||
_cursorPaletteEnabled = enable;
|
||||
flushCursor();
|
||||
break;
|
||||
case OSystem::kFeatureFilteringMode:
|
||||
_filteringEnabled = enable;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool OSystem_3DS::getFeatureState(OSystem::Feature f) {
|
||||
switch (f) {
|
||||
case OSystem::kFeatureCursorPalette:
|
||||
return _cursorPaletteEnabled;
|
||||
case OSystem::kFeatureFilteringMode:
|
||||
return _filteringEnabled;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GraphicsModeID OSystem_3DS::chooseMode(Graphics::PixelFormat *format) {
|
||||
if (format->bytesPerPixel > 2) {
|
||||
return RGBA8;
|
||||
} else if (format->bytesPerPixel > 1) {
|
||||
if (format->aBits() > 1) {
|
||||
return RGBA4;
|
||||
} else if (format->gBits() > 5) {
|
||||
return RGB565;
|
||||
} else {
|
||||
return RGB5A1;
|
||||
}
|
||||
}
|
||||
return CLUT8;
|
||||
}
|
||||
|
||||
bool OSystem_3DS::setGraphicsMode(GraphicsModeID modeID) {
|
||||
switch (modeID) {
|
||||
case RGBA8:
|
||||
case RGB565:
|
||||
case RGB5A1:
|
||||
case RGBA4:
|
||||
case CLUT8:
|
||||
_gfxState.gfxMode = gfxModes[modeID];
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void OSystem_3DS::initSize(uint width, uint height,
|
||||
const Graphics::PixelFormat *format) {
|
||||
debug("3ds initsize w:%d h:%d", width, height);
|
||||
updateBacklight();
|
||||
updateConfig();
|
||||
|
||||
_gameWidth = width;
|
||||
_gameHeight = height;
|
||||
_magCenterX = _magWidth / 2;
|
||||
_magCenterY = _magHeight / 2;
|
||||
|
||||
_oldPfGame = _pfGame;
|
||||
if (!format) {
|
||||
_pfGame = Graphics::PixelFormat::createFormatCLUT8();
|
||||
} else {
|
||||
debug("pixelformat: %d %d %d %d %d", format->bytesPerPixel, format->rBits(), format->gBits(), format->bBits(), format->aBits());
|
||||
_pfGame = *format;
|
||||
}
|
||||
|
||||
/* If the current graphics mode does not fit with the pixel
|
||||
* format being requested, choose one that does and switch to it */
|
||||
assert(_pfGame.bytesPerPixel > 0);
|
||||
if (_pfGame != _oldPfGame) {
|
||||
assert(_transactionState == kTransactionActive);
|
||||
_gfxState.gfxModeID = chooseMode(&_pfGame);
|
||||
_transactionDetails.formatChanged = true;
|
||||
}
|
||||
|
||||
_gameTopTexture.create(width, height, _gfxState.gfxMode, true);
|
||||
|
||||
if (_pfGame == _gameTopTexture.format)
|
||||
_gameScreen.free();
|
||||
else
|
||||
_gameScreen.create(width, height, _pfGame);
|
||||
|
||||
_focusDirty = true;
|
||||
_focusRect = Common::Rect(_gameWidth, _gameHeight);
|
||||
|
||||
updateSize();
|
||||
}
|
||||
|
||||
void OSystem_3DS::updateSize() {
|
||||
// Initialize _overlay here so that it can be reinitialized when _screen is changed.
|
||||
|
||||
// Overlay sprite must have a width matching or exceeding that of the screen to
|
||||
// which it's set to render, otherwise portions of the screen will not render.
|
||||
// _screen == kScreenTop
|
||||
// >>> overlay renders to top screen
|
||||
// >>> top screen is 400 pixels wide
|
||||
// _screen == (kScreenBottom | kScreenBoth)
|
||||
// >>> overlay renders to bottom screen
|
||||
// >>> bottom screen is 320 pixels wide
|
||||
_overlay.create(_screen == kScreenTop ? 400 : 320, 240, &DEFAULT_MODE, true);
|
||||
|
||||
if (_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);
|
||||
_gameTopTexture.setOffset(0, 0);
|
||||
_gameBottomTexture.setOffset(0, 0);
|
||||
if (_overlayInGUI) {
|
||||
_cursorTexture.setScale(1.f, 1.f);
|
||||
} else if (_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;
|
||||
// The following formats are supported natively by the GPU
|
||||
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(2, 5, 5, 5, 1, 11, 6, 1, 0)); // GPU_RGBA5551
|
||||
list.push_back(Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)); // GPU_RGBA4
|
||||
|
||||
// The following format requires software conversion
|
||||
list.push_back(Graphics::PixelFormat::createFormatCLUT8());
|
||||
return list;
|
||||
}
|
||||
|
||||
void OSystem_3DS::beginGFXTransaction() {
|
||||
assert(_transactionState == kTransactionNone);
|
||||
_transactionState = kTransactionActive;
|
||||
_transactionDetails.formatChanged = false;
|
||||
_oldGfxState = _gfxState;
|
||||
}
|
||||
|
||||
OSystem::TransactionError OSystem_3DS::endGFXTransaction() {
|
||||
int errors = OSystem::kTransactionSuccess;
|
||||
|
||||
assert(_transactionState != kTransactionNone);
|
||||
if (_transactionState == kTransactionRollback) {
|
||||
if (_gfxState.gfxModeID != _oldGfxState.gfxModeID) {
|
||||
errors |= OSystem::kTransactionModeSwitchFailed;
|
||||
_gfxState = _oldGfxState;
|
||||
} else if ((_gfxState.gfxMode != _oldGfxState.gfxMode) |
|
||||
(_gfxState.gfxMode != gfxModes[_gfxState.gfxModeID])) {
|
||||
errors |= OSystem::kTransactionFormatNotSupported;
|
||||
_gfxState = _oldGfxState;
|
||||
}
|
||||
|
||||
_oldGfxState.setup = false;
|
||||
}
|
||||
if (_transactionDetails.formatChanged) {
|
||||
if (!setGraphicsMode(_gfxState.gfxModeID)) {
|
||||
if (_oldGfxState.setup) {
|
||||
_transactionState = kTransactionRollback;
|
||||
errors |= endGFXTransaction();
|
||||
}
|
||||
} else if (_gfxState.gfxMode != gfxModes[_gfxState.gfxModeID]) {
|
||||
if (_oldGfxState.setup) {
|
||||
_transactionState = kTransactionRollback;
|
||||
errors |= endGFXTransaction();
|
||||
}
|
||||
} else {
|
||||
initSize(_gameWidth, _gameHeight, &_pfGame);
|
||||
clearOverlay();
|
||||
_gfxState.setup = true;
|
||||
_screenChangeId++;
|
||||
}
|
||||
}
|
||||
|
||||
_transactionState = kTransactionNone;
|
||||
return (OSystem::TransactionError)errors;
|
||||
}
|
||||
|
||||
float OSystem_3DS::getScaleRatio() const {
|
||||
if (_overlayInGUI) {
|
||||
return 1.0;
|
||||
} else if (_screen == kScreenTop) {
|
||||
return _gameTopTexture.getScaleX();
|
||||
} else {
|
||||
return _gameBottomTexture.getScaleX();
|
||||
}
|
||||
}
|
||||
|
||||
void OSystem_3DS::setPalette(const byte *colors, uint start, uint num) {
|
||||
assert(start + num <= 256);
|
||||
memcpy(_palette + 3 * start, colors, 3 * num);
|
||||
Graphics::convertPaletteToMap(_paletteMap + start, colors, num, _modeCLUT8.surfaceFormat);
|
||||
_gameTextureDirty = true;
|
||||
}
|
||||
|
||||
void OSystem_3DS::grabPalette(byte *colors, uint start, uint num) const {
|
||||
assert(start + num <= 256);
|
||||
memcpy(colors, _palette + 3 * start, 3 * num);
|
||||
}
|
||||
|
||||
// TODO: Move this into common code
|
||||
// TODO: Convert two pixels at once using 32-bit loads and stores
|
||||
static void copyRect555To5551(byte *dst, const byte *src, const uint dstPitch, const uint srcPitch,
|
||||
const uint w, const uint h) {
|
||||
// Faster, but larger, to provide optimized handling for each case.
|
||||
const uint srcDelta = (srcPitch - w * sizeof(uint16));
|
||||
const uint dstDelta = (dstPitch - w * sizeof(uint16));
|
||||
|
||||
for (uint y = 0; y < h; ++y) {
|
||||
for (uint x = 0; x < w; ++x) {
|
||||
uint16 col = *(const uint16 *)src;
|
||||
col = (col << 1) | 1;
|
||||
*(uint16 *)dst = col;
|
||||
|
||||
src += sizeof(uint16);
|
||||
dst += sizeof(uint16);
|
||||
}
|
||||
src += srcDelta;
|
||||
dst += dstDelta;
|
||||
}
|
||||
}
|
||||
|
||||
void OSystem_3DS::copyRectToScreen(const void *buf, int pitch, int x,
|
||||
int y, int w, int h) {
|
||||
if (_pfGame == _gameTopTexture.format) {
|
||||
_gameTopTexture.copyRectToSurface(buf, pitch, x, y, w, h);
|
||||
_gameTopTexture.markDirty();
|
||||
return;
|
||||
}
|
||||
|
||||
_gameScreen.copyRectToSurface(buf, pitch, x, y, w, h);
|
||||
|
||||
if (_pfGame == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) {
|
||||
byte *dst = (byte *)_gameTopTexture.getBasePtr(x, y);
|
||||
copyRect555To5551(dst, (const byte *)buf, _gameTopTexture.pitch, pitch, w, h);
|
||||
} else if (_gfxState.gfxMode == &_modeCLUT8) {
|
||||
byte *dst = (byte *)_gameTopTexture.getBasePtr(x, y);
|
||||
Graphics::crossBlitMap(dst, (const byte *)buf, _gameTopTexture.pitch, pitch,
|
||||
w, h, _gameTopTexture.format.bytesPerPixel, _paletteMap);
|
||||
} else {
|
||||
byte *dst = (byte *)_gameTopTexture.getBasePtr(x, y);
|
||||
Graphics::crossBlit(dst, (const byte *)buf, _gameTopTexture.pitch, pitch,
|
||||
w, h, _gameTopTexture.format, _pfGame);
|
||||
}
|
||||
|
||||
_gameTopTexture.markDirty();
|
||||
}
|
||||
|
||||
void OSystem_3DS::flushGameScreen() {
|
||||
if (_pfGame == _gameTopTexture.format) {
|
||||
return;
|
||||
} else if (_pfGame == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) {
|
||||
const byte *src = (const byte *)_gameScreen.getPixels();
|
||||
byte *dst = (byte *)_gameTopTexture.getPixels();
|
||||
copyRect555To5551(dst, src, _gameTopTexture.pitch, _gameScreen.pitch,
|
||||
_gameScreen.w, _gameScreen.h);
|
||||
} else if (_gfxState.gfxMode == &_modeCLUT8) {
|
||||
const byte *src = (const byte *)_gameScreen.getPixels();
|
||||
byte *dst = (byte *)_gameTopTexture.getPixels();
|
||||
Graphics::crossBlitMap(dst, src, _gameTopTexture.pitch, _gameScreen.pitch,
|
||||
_gameScreen.w, _gameScreen.h, _gameTopTexture.format.bytesPerPixel, _paletteMap);
|
||||
} else {
|
||||
const byte *src = (const byte *)_gameScreen.getPixels();
|
||||
byte *dst = (byte *)_gameTopTexture.getPixels();
|
||||
Graphics::crossBlit(dst, src, _gameTopTexture.pitch, _gameScreen.pitch,
|
||||
_gameScreen.w, _gameScreen.h, _gameTopTexture.format, _pfGame);
|
||||
}
|
||||
|
||||
_gameTopTexture.markDirty();
|
||||
}
|
||||
|
||||
Graphics::Surface *OSystem_3DS::lockScreen() {
|
||||
if (_pfGame == _gameTopTexture.format)
|
||||
return &_gameTopTexture;
|
||||
else
|
||||
return &_gameScreen;
|
||||
}
|
||||
void OSystem_3DS::unlockScreen() {
|
||||
_gameTextureDirty = true;
|
||||
}
|
||||
|
||||
void OSystem_3DS::updateScreen() {
|
||||
if (sleeping || exiting) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gameTextureDirty) {
|
||||
flushGameScreen();
|
||||
_gameTextureDirty = false;
|
||||
}
|
||||
|
||||
// updateFocus();
|
||||
updateMagnify();
|
||||
|
||||
if (_osdMessage.getPixels() && _osdMessageEndTime <= getMillis(true)) {
|
||||
_osdMessage.free();
|
||||
}
|
||||
|
||||
C3D_FrameBegin(0);
|
||||
_gameTopTexture.transfer();
|
||||
if (_overlayVisible) {
|
||||
_overlay.transfer();
|
||||
}
|
||||
if (_cursorVisible && _showCursor) {
|
||||
_cursorTexture.transfer();
|
||||
}
|
||||
_osdMessage.transfer();
|
||||
_activityIcon.transfer();
|
||||
C3D_FrameEnd(0);
|
||||
|
||||
C3D_FrameBegin(0);
|
||||
// Render top screen
|
||||
C3D_RenderTargetClear(_renderTargetTop, C3D_CLEAR_ALL, 0x00000000, 0);
|
||||
C3D_FrameDrawOn(_renderTargetTop);
|
||||
if (_screen == kScreenTop || _screen == kScreenBoth) {
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionTop);
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _gameTopTexture.getMatrix());
|
||||
_gameTopTexture.setFilteringMode(_magnifyMode != MODE_MAGON && _filteringEnabled);
|
||||
_gameTopTexture.render();
|
||||
if (_overlayVisible && _screen == kScreenTop) {
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _overlay.getMatrix());
|
||||
_overlay.render();
|
||||
}
|
||||
if (_activityIcon.getPixels() && _screen == kScreenTop) {
|
||||
_activityIcon.setPosition(400 - _activityIcon.actualWidth, 0);
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _activityIcon.getMatrix());
|
||||
_activityIcon.render();
|
||||
}
|
||||
if (_osdMessage.getPixels() && _screen == kScreenTop) {
|
||||
_osdMessage.setPosition((400 - _osdMessage.actualWidth) / 2, (240 - _osdMessage.actualHeight) / 2);
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _osdMessage.getMatrix());
|
||||
_osdMessage.render();
|
||||
}
|
||||
if (_cursorVisible && _showCursor && _screen == kScreenTop) {
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _cursorTexture.getMatrix());
|
||||
_cursorTexture.setFilteringMode(!_overlayInGUI && _filteringEnabled);
|
||||
_cursorTexture.render();
|
||||
}
|
||||
}
|
||||
|
||||
// Render bottom screen
|
||||
C3D_RenderTargetClear(_renderTargetBottom, C3D_CLEAR_ALL, 0x00000000, 0);
|
||||
C3D_FrameDrawOn(_renderTargetBottom);
|
||||
if (_screen == kScreenBottom || _screen == kScreenBoth) {
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionBottom);
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _gameBottomTexture.getMatrix());
|
||||
_gameTopTexture.setFilteringMode(_filteringEnabled);
|
||||
_gameTopTexture.render();
|
||||
if (_overlayVisible) {
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _overlay.getMatrix());
|
||||
_overlay.render();
|
||||
}
|
||||
if (_activityIcon.getPixels()) {
|
||||
_activityIcon.setPosition(320 - _activityIcon.actualWidth, 0);
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _activityIcon.getMatrix());
|
||||
_activityIcon.render();
|
||||
}
|
||||
if (_osdMessage.getPixels()) {
|
||||
_osdMessage.setPosition((320 - _osdMessage.actualWidth) / 2, (240 - _osdMessage.actualHeight) / 2);
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _osdMessage.getMatrix());
|
||||
_osdMessage.render();
|
||||
}
|
||||
if (_cursorVisible && _showCursor) {
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _cursorTexture.getMatrix());
|
||||
_cursorTexture.setFilteringMode(!_overlayInGUI && _filteringEnabled);
|
||||
_cursorTexture.render();
|
||||
}
|
||||
}
|
||||
C3D_FrameEnd(0);
|
||||
}
|
||||
|
||||
void OSystem_3DS::setShakePos(int shakeXOffset, int shakeYOffset) {
|
||||
// TODO: implement this in overlay, top screen, and mouse too
|
||||
_screenShakeXOffset = shakeXOffset;
|
||||
_screenShakeYOffset = shakeYOffset;
|
||||
int topX = _gameTopX + (_gameTopTexture.getScaleX() * shakeXOffset);
|
||||
int topY = _gameTopY + (_gameTopTexture.getScaleY() * shakeYOffset);
|
||||
_gameTopTexture.setPosition(topX, topY);
|
||||
int bottomX = _gameBottomX + (_gameBottomTexture.getScaleX() * shakeXOffset);
|
||||
int bottomY = _gameBottomY + (_gameBottomTexture.getScaleY() * shakeYOffset);
|
||||
_gameBottomTexture.setPosition(bottomX, bottomY);
|
||||
}
|
||||
|
||||
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, true);
|
||||
Mtx_Scale(&_focusMatrix, _focusScaleX, _focusScaleY, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
void OSystem_3DS::updateMagnify() {
|
||||
if (_magnifyMode == MODE_MAGON && _screen != kScreenBoth) {
|
||||
// Only allow to magnify when both screens are enabled
|
||||
_magnifyMode = MODE_MAGOFF;
|
||||
}
|
||||
|
||||
if (_magnifyMode == MODE_MAGON) {
|
||||
if (!_overlayVisible) {
|
||||
_magX = (_cursorScreenX < _magCenterX) ?
|
||||
0 : ((_cursorScreenX < (_gameWidth - _magCenterX)) ?
|
||||
_cursorScreenX - _magCenterX : _gameWidth - _magWidth);
|
||||
_magY = (_cursorScreenY < _magCenterY) ?
|
||||
0 : ((_cursorScreenY < _gameHeight - _magCenterY) ?
|
||||
_cursorScreenY - _magCenterY : _gameHeight - _magHeight);
|
||||
}
|
||||
_gameTopTexture.setScale(1.f,1.f);
|
||||
_gameTopTexture.setPosition(0,0);
|
||||
_gameTopTexture.setOffset(_magX, _magY);
|
||||
}
|
||||
}
|
||||
|
||||
void OSystem_3DS::showOverlay(bool inGUI) {
|
||||
_overlayInGUI = inGUI;
|
||||
_overlayVisible = true;
|
||||
updateSize();
|
||||
}
|
||||
|
||||
void OSystem_3DS::hideOverlay() {
|
||||
_overlayVisible = false;
|
||||
_overlayInGUI = false;
|
||||
updateSize();
|
||||
}
|
||||
|
||||
Graphics::PixelFormat OSystem_3DS::getOverlayFormat() const {
|
||||
return _overlay.format;
|
||||
}
|
||||
|
||||
void OSystem_3DS::clearOverlay() {
|
||||
_overlay.clear();
|
||||
}
|
||||
|
||||
void OSystem_3DS::grabOverlay(Graphics::Surface &surface) {
|
||||
assert(surface.w >= getOverlayWidth());
|
||||
assert(surface.h >= getOverlayHeight());
|
||||
assert(surface.format.bytesPerPixel == _overlay.format.bytesPerPixel);
|
||||
|
||||
byte *src = (byte *)_overlay.getPixels();
|
||||
byte *dst = (byte *)surface.getPixels();
|
||||
Graphics::copyBlit(dst, src, surface.pitch, _overlay.pitch,
|
||||
getOverlayWidth(), getOverlayHeight(), _overlay.format.bytesPerPixel);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void OSystem_3DS::displayMessageOnOSD(const Common::U32String &msg) {
|
||||
// The font we are going to use:
|
||||
const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kLocalizedFont);
|
||||
if (!font) {
|
||||
warning("No available font to render OSD messages");
|
||||
return;
|
||||
}
|
||||
|
||||
// Split the message into separate lines.
|
||||
Common::Array<Common::U32String> lines;
|
||||
Common::U32String::const_iterator strLineItrBegin = msg.begin();
|
||||
|
||||
for (Common::U32String::const_iterator itr = msg.begin(); itr != msg.end(); itr++) {
|
||||
if (*itr == '\n') {
|
||||
lines.push_back(Common::U32String(strLineItrBegin, itr));
|
||||
strLineItrBegin = itr + 1;
|
||||
}
|
||||
}
|
||||
if (strLineItrBegin != msg.end())
|
||||
lines.push_back(Common::U32String(strLineItrBegin, msg.end()));
|
||||
|
||||
// Determine a rect which would contain the message string (clipped to the
|
||||
// screen dimensions).
|
||||
const int vOffset = 6;
|
||||
const int lineSpacing = 1;
|
||||
const int lineHeight = font->getFontHeight() + 2 * lineSpacing;
|
||||
int width = 0;
|
||||
int height = lineHeight * lines.size() + 2 * vOffset;
|
||||
uint i;
|
||||
for (i = 0; i < lines.size(); i++) {
|
||||
width = MAX(width, font->getStringWidth(lines[i]) + 14);
|
||||
}
|
||||
|
||||
// Clip the rect
|
||||
if (width > getOverlayWidth()) {
|
||||
width = getOverlayWidth();
|
||||
}
|
||||
if (height > getOverlayHeight()) {
|
||||
height = getOverlayHeight();
|
||||
}
|
||||
|
||||
_osdMessage.create(width, height, &DEFAULT_MODE);
|
||||
_osdMessage.fillRect(Common::Rect(width, height), _pfDefaultTexture.ARGBToColor(200, 0, 0, 0));
|
||||
|
||||
// Render the message, centered, and in white
|
||||
for (i = 0; i < lines.size(); i++) {
|
||||
font->drawString(&_osdMessage, lines[i],
|
||||
0, 0 + i * lineHeight + vOffset + lineSpacing, width,
|
||||
_pfDefaultTexture.RGBToColor(255, 255, 255),
|
||||
Graphics::kTextAlignCenter, 0, true);
|
||||
}
|
||||
|
||||
_osdMessageEndTime = getMillis(true) + kOSDMessageDuration;
|
||||
}
|
||||
|
||||
void OSystem_3DS::displayActivityIconOnOSD(const Graphics::Surface *icon) {
|
||||
if (!icon) {
|
||||
_activityIcon.free();
|
||||
} else {
|
||||
if (!_activityIcon.getPixels() || icon->w != _activityIcon.w || icon->h != _activityIcon.h) {
|
||||
_activityIcon.create(icon->w, icon->h, &DEFAULT_MODE);
|
||||
}
|
||||
|
||||
if (icon->format == _activityIcon.format) {
|
||||
_activityIcon.copyRectToSurface(*icon, 0, 0, Common::Rect(icon->w, icon->h));
|
||||
} else {
|
||||
Graphics::Surface *converted = icon->convertTo(_activityIcon.format);
|
||||
_activityIcon.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h));
|
||||
converted->free();
|
||||
delete converted;
|
||||
}
|
||||
|
||||
_activityIcon.markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
int16 OSystem_3DS::getOverlayHeight() const {
|
||||
return 240;
|
||||
}
|
||||
|
||||
int16 OSystem_3DS::getOverlayWidth() const {
|
||||
return _screen == kScreenTop ? 400 : 320;
|
||||
}
|
||||
|
||||
bool OSystem_3DS::showMouse(bool visible) {
|
||||
_cursorVisible = visible;
|
||||
flushCursor();
|
||||
return !visible;
|
||||
}
|
||||
|
||||
void OSystem_3DS::warpMouse(int x, int y) {
|
||||
if (!_overlayVisible) {
|
||||
_cursorScreenX = x;
|
||||
_cursorScreenY = y;
|
||||
} else {
|
||||
_cursorOverlayX = x;
|
||||
_cursorOverlayY = y;
|
||||
}
|
||||
|
||||
// TODO: adjust for _cursorScalable ?
|
||||
x -= _cursorHotspotX;
|
||||
y -= _cursorHotspotY;
|
||||
|
||||
int offsetx = 0;
|
||||
int offsety = 0;
|
||||
if (!_overlayVisible) {
|
||||
offsetx = _screen == kScreenTop ? _gameTopTexture.getPosX() : _gameBottomTexture.getPosX();
|
||||
offsety = _screen == kScreenTop ? _gameTopTexture.getPosY() : _gameBottomTexture.getPosY();
|
||||
}
|
||||
|
||||
_cursorTexture.setPosition(x + offsetx, 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, const byte *mask) {
|
||||
_cursorScalable = !dontScale;
|
||||
_cursorHotspotX = hotspotX;
|
||||
_cursorHotspotY = hotspotY;
|
||||
_cursorKeyColor = keycolor;
|
||||
_pfCursor = !format ? Graphics::PixelFormat::createFormatCLUT8() : *format;
|
||||
|
||||
if (mask)
|
||||
warning("OSystem_3DS::setMouseCursor: Masks are not supported");
|
||||
|
||||
if (w != (uint)_cursor.w || h != (uint)_cursor.h || _cursor.format != _pfCursor) {
|
||||
_cursor.create(w, h, _pfCursor);
|
||||
_cursorTexture.create(w, h, &DEFAULT_MODE);
|
||||
}
|
||||
|
||||
if ( w != 0 && h != 0 ) {
|
||||
_cursor.copyRectToSurface(buf, w * _pfCursor.bytesPerPixel, 0, 0, w, h);
|
||||
}
|
||||
|
||||
flushCursor();
|
||||
|
||||
if (!_overlayVisible) {
|
||||
warpMouse(_cursorScreenX, _cursorScreenY);
|
||||
} else {
|
||||
warpMouse(_cursorOverlayX, _cursorOverlayY);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
namespace {
|
||||
template<typename SrcColor>
|
||||
void applyKeyColor(Graphics::Surface *src, Graphics::Surface *dst, const SrcColor keyColor) {
|
||||
assert(dst->format.bytesPerPixel == 4);
|
||||
assert((dst->w >= src->w) && (dst->h >= src->h));
|
||||
|
||||
for (uint y = 0; y < (uint)src->h; ++y) {
|
||||
SrcColor *srcPtr = (SrcColor *)src->getBasePtr(0, y);
|
||||
uint32 *dstPtr = (uint32 *)dst->getBasePtr(0, y);
|
||||
|
||||
for (uint x = 0; x < (uint)src->w; ++x) {
|
||||
const SrcColor color = *srcPtr++;
|
||||
|
||||
if (color == keyColor) {
|
||||
*dstPtr = 0;
|
||||
}
|
||||
|
||||
dstPtr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // End of anonymous namespace
|
||||
|
||||
void OSystem_3DS::flushCursor() {
|
||||
if (_cursor.getPixels()) {
|
||||
Graphics::Surface *converted = _cursor.convertTo(_cursorTexture.format, _cursorPaletteEnabled ? _cursorPalette : _palette);
|
||||
_cursorTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h));
|
||||
_cursorTexture.markDirty();
|
||||
converted->free();
|
||||
delete converted;
|
||||
|
||||
if (_pfCursor.bytesPerPixel == 1) {
|
||||
applyKeyColor<byte>(&_cursor, &_cursorTexture, _cursorKeyColor);
|
||||
} else if (_pfCursor.bytesPerPixel == 2) {
|
||||
applyKeyColor<uint16>(&_cursor, &_cursorTexture, _cursorKeyColor);
|
||||
} else if (_pfCursor.bytesPerPixel == 4) {
|
||||
applyKeyColor<uint32>(&_cursor, &_cursorTexture, _cursorKeyColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace N3DS
|
||||
275
backends/platform/3ds/osystem.cpp
Normal file
275
backends/platform/3ds/osystem.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
|
||||
|
||||
#include <3ds.h>
|
||||
#include "osystem.h"
|
||||
|
||||
#include "backends/mutex/3ds/3ds-mutex.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-drives/posix-drives-fs-factory.h"
|
||||
#include "backends/fs/posix-drives/posix-drives-fs.h"
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace N3DS {
|
||||
|
||||
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),
|
||||
_cursorScreenX(0),
|
||||
_cursorScreenY(0),
|
||||
_cursorOverlayX(0),
|
||||
_cursorOverlayY(0),
|
||||
_cursorHotspotX(0),
|
||||
_cursorHotspotY(0),
|
||||
_gameTopX(0),
|
||||
_gameTopY(0),
|
||||
_gameBottomX(0),
|
||||
_gameBottomY(0),
|
||||
_gameWidth(320),
|
||||
_gameHeight(240),
|
||||
_magX(0),
|
||||
_magY(0),
|
||||
_magWidth(400),
|
||||
_magHeight(240),
|
||||
_gameTextureDirty(false),
|
||||
_filteringEnabled(true),
|
||||
_overlayVisible(false),
|
||||
_overlayInGUI(false),
|
||||
_screenChangeId(0),
|
||||
_magnifyMode(MODE_MAGOFF),
|
||||
exiting(false),
|
||||
sleeping(false),
|
||||
_logger(0),
|
||||
_showCursor(true),
|
||||
_snapToBorder(true),
|
||||
_stretchToFit(false),
|
||||
_screen(kScreenBoth)
|
||||
{
|
||||
chdir("sdmc:/");
|
||||
|
||||
DrivesPOSIXFilesystemFactory *fsFactory = new DrivesPOSIXFilesystemFactory();
|
||||
fsFactory->addDrive("sdmc:");
|
||||
fsFactory->addDrive("romfs:");
|
||||
|
||||
//
|
||||
// Disable newlib's buffered IO, and use ScummVM's own buffering stream wrappers.
|
||||
//
|
||||
// The newlib version in use in devkitPro has performance issues
|
||||
// when seeking with a relative offset. See:
|
||||
// https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=commit;h=59362c80e3a02c011fd0ef3d7f07a20098d2a9d5
|
||||
//
|
||||
// devKitPro has a patch to newlib that can cause data corruption when
|
||||
// seeking back in files and then reading. See:
|
||||
// https://github.com/devkitPro/newlib/issues/16
|
||||
//
|
||||
fsFactory->configureBuffering(DrivePOSIXFilesystemNode::kBufferingModeScummVM, 2048);
|
||||
|
||||
_fsFactory = fsFactory;
|
||||
|
||||
Posix::assureDirectoryExists("/3ds/scummvm/saves/");
|
||||
}
|
||||
|
||||
OSystem_3DS::~OSystem_3DS() {
|
||||
exiting = true;
|
||||
destroyEvents();
|
||||
destroyAudio();
|
||||
destroy3DSGraphics();
|
||||
|
||||
delete _logger;
|
||||
_logger = 0;
|
||||
|
||||
delete _timerManager;
|
||||
_timerManager = 0;
|
||||
}
|
||||
|
||||
void OSystem_3DS::quit() {
|
||||
printf("OSystem_3DS::quit()\n");
|
||||
}
|
||||
|
||||
void OSystem_3DS::initBackend() {
|
||||
if (!_logger)
|
||||
_logger = new Backends::Log::Log(this);
|
||||
|
||||
if (_logger) {
|
||||
Common::WriteStream *logFile = createLogFile();
|
||||
if (logFile)
|
||||
_logger->open(logFile);
|
||||
}
|
||||
|
||||
updateBacklight();
|
||||
updateConfig();
|
||||
ConfMan.registerDefault("fullscreen", true);
|
||||
ConfMan.registerDefault("aspect_ratio", true);
|
||||
ConfMan.registerDefault("filtering", true);
|
||||
if (!ConfMan.hasKey("vkeybd_pack_name")) {
|
||||
ConfMan.set("vkeybd_pack_name", "vkeybd_small");
|
||||
}
|
||||
|
||||
_timerManager = new DefaultTimerManager();
|
||||
_savefileManager = new DefaultSaveFileManager("sdmc:/3ds/scummvm/saves/");
|
||||
|
||||
init3DSGraphics();
|
||||
initAudio();
|
||||
EventsBaseBackend::initBackend();
|
||||
initEvents();
|
||||
}
|
||||
|
||||
void OSystem_3DS::updateBacklight() {
|
||||
// Turn off the backlight of any screen not used
|
||||
if (R_SUCCEEDED(gspLcdInit())) {
|
||||
if (_screen == kScreenTop) {
|
||||
GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_TOP);
|
||||
GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_BOTTOM);
|
||||
} else if (_screen == kScreenBottom) {
|
||||
GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTTOM);
|
||||
GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_TOP);
|
||||
} else {
|
||||
GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH);
|
||||
}
|
||||
gspLcdExit();
|
||||
}
|
||||
}
|
||||
|
||||
void OSystem_3DS::updateConfig() {
|
||||
if (_gameScreen.getPixels()) {
|
||||
updateSize();
|
||||
(!_overlayVisible) ? warpMouse(_cursorScreenX, _cursorScreenY) :
|
||||
warpMouse(_cursorOverlayX, _cursorOverlayY);
|
||||
}
|
||||
}
|
||||
|
||||
Common::Path OSystem_3DS::getDefaultConfigFileName() {
|
||||
return "sdmc:/3ds/scummvm/scummvm.ini";
|
||||
}
|
||||
|
||||
Common::Path OSystem_3DS::getDefaultLogFileName() {
|
||||
return "sdmc:/3ds/scummvm/scummvm.log";
|
||||
}
|
||||
|
||||
void OSystem_3DS::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
|
||||
s.add("RomFS", new Common::FSDirectory(DATA_PATH"/"), priority);
|
||||
}
|
||||
|
||||
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, bool skipRecord) const {
|
||||
time_t curTime = time(0);
|
||||
struct tm t = *localtime(&curTime);
|
||||
td.tm_sec = t.tm_sec;
|
||||
td.tm_min = t.tm_min;
|
||||
td.tm_hour = t.tm_hour;
|
||||
td.tm_mday = t.tm_mday;
|
||||
td.tm_mon = t.tm_mon;
|
||||
td.tm_year = t.tm_year;
|
||||
td.tm_wday = t.tm_wday;
|
||||
}
|
||||
|
||||
Common::MutexInternal *OSystem_3DS::createMutex() {
|
||||
return create3DSMutexInternal();
|
||||
}
|
||||
|
||||
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("%s", message);
|
||||
|
||||
// Then log into file (via the logger)
|
||||
if (_logger)
|
||||
_logger->print(message);
|
||||
}
|
||||
|
||||
Common::WriteStream *OSystem_3DS::createLogFile() {
|
||||
// Start out by resetting _logFilePath, so that in case
|
||||
// of a failure, we know that no log file is open.
|
||||
_logFilePath.clear();
|
||||
|
||||
Common::Path logFile;
|
||||
if (ConfMan.hasKey("logfile"))
|
||||
logFile = ConfMan.getPath("logfile");
|
||||
else
|
||||
logFile = getDefaultLogFileName();
|
||||
if (logFile.empty())
|
||||
return nullptr;
|
||||
|
||||
Common::FSNode file(logFile);
|
||||
Common::WriteStream *stream = file.createWriteStream(false);
|
||||
if (stream)
|
||||
_logFilePath = logFile;
|
||||
return stream;
|
||||
}
|
||||
|
||||
} // namespace N3DS
|
||||
316
backends/platform/3ds/osystem.h
Normal file
316
backends/platform/3ds/osystem.h
Normal file
@@ -0,0 +1,316 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PLATFORM_3DS_H
|
||||
#define PLATFORM_3DS_H
|
||||
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
|
||||
|
||||
#include "backends/base-backend.h"
|
||||
#include "graphics/paletteman.h"
|
||||
#include "base/main.h"
|
||||
#include "audio/mixer_intern.h"
|
||||
#include "backends/graphics/graphics.h"
|
||||
#include "backends/log/log.h"
|
||||
#include "backends/platform/3ds/sprite.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/queue.h"
|
||||
#include "common/ustr.h"
|
||||
#include "engines/engine.h"
|
||||
|
||||
#define TICKS_PER_MSEC 268123
|
||||
|
||||
namespace N3DS {
|
||||
|
||||
enum MagnifyMode {
|
||||
MODE_MAGON,
|
||||
MODE_MAGOFF,
|
||||
};
|
||||
|
||||
enum InputMode {
|
||||
MODE_HOVER,
|
||||
MODE_DRAG,
|
||||
};
|
||||
|
||||
enum GraphicsModeID {
|
||||
RGBA8,
|
||||
RGB565,
|
||||
RGB5A1,
|
||||
RGBA4,
|
||||
CLUT8
|
||||
};
|
||||
|
||||
enum Screen {
|
||||
kScreenTop = 0x10000002,
|
||||
kScreenBottom,
|
||||
kScreenBoth,
|
||||
};
|
||||
|
||||
enum TransactionState {
|
||||
kTransactionNone = 0,
|
||||
kTransactionActive = 1,
|
||||
kTransactionRollback = 2
|
||||
};
|
||||
|
||||
|
||||
struct TransactionDetails {
|
||||
bool formatChanged, modeChanged;
|
||||
|
||||
TransactionDetails() {
|
||||
formatChanged = false;
|
||||
modeChanged = false;
|
||||
}
|
||||
};
|
||||
|
||||
typedef struct GfxMode3DS {
|
||||
Graphics::PixelFormat surfaceFormat;
|
||||
GPU_TEXCOLOR textureFormat;
|
||||
uint32 textureTransferFlags;
|
||||
} GfxMode3DS;
|
||||
|
||||
struct GfxState {
|
||||
bool setup;
|
||||
GraphicsModeID gfxModeID;
|
||||
const GfxMode3DS *gfxMode;
|
||||
|
||||
GfxState() {
|
||||
setup = false;
|
||||
gfxModeID = CLUT8;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class OSystem_3DS : public EventsBaseBackend, public PaletteManager, public Common::EventObserver {
|
||||
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);
|
||||
|
||||
bool pollEvent(Common::Event &event) override;
|
||||
bool notifyEvent(const Common::Event &event) override;
|
||||
Common::HardwareInputSet *getHardwareInputSet() override;
|
||||
Common::KeymapArray getGlobalKeymaps() override;
|
||||
Common::KeymapperDefaultBindings *getKeymapperDefaultBindings() override;
|
||||
|
||||
void registerDefaultSettings(const Common::String &target) const override;
|
||||
GUI::OptionsContainerWidget *buildBackendOptionsWidget(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const override;
|
||||
void applyBackendSettings() override;
|
||||
|
||||
virtual uint32 getMillis(bool skipRecord = false);
|
||||
virtual void delayMillis(uint msecs);
|
||||
virtual void getTimeAndDate(TimeDate &td, bool skipRecord = false) const;
|
||||
|
||||
virtual Common::MutexInternal *createMutex();
|
||||
|
||||
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::Path getDefaultConfigFileName();
|
||||
void addSysArchivesToSearchSet(Common::SearchSet &s, int priority) override;
|
||||
|
||||
// Graphics
|
||||
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 _screenChangeId; };
|
||||
GraphicsModeID chooseMode(Graphics::PixelFormat *format);
|
||||
bool setGraphicsMode(GraphicsModeID modeID);
|
||||
|
||||
void beginGFXTransaction();
|
||||
OSystem::TransactionError endGFXTransaction();
|
||||
int16 getHeight(){ return _gameHeight; }
|
||||
int16 getWidth(){ return _gameWidth; }
|
||||
float getScaleRatio() const;
|
||||
void setPalette(const byte *colors, uint start, uint num);
|
||||
void grabPalette(byte *colors, uint start, uint num) const;
|
||||
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 shakeXOffset, int shakeYOffset);
|
||||
void setFocusRectangle(const Common::Rect &rect);
|
||||
void clearFocusRectangle();
|
||||
void showOverlay(bool inGUI);
|
||||
void hideOverlay();
|
||||
bool isOverlayVisible() const { return _overlayVisible; }
|
||||
Graphics::PixelFormat getOverlayFormat() const;
|
||||
void clearOverlay();
|
||||
void grabOverlay(Graphics::Surface &surface);
|
||||
void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w,
|
||||
int h);
|
||||
virtual int16 getOverlayHeight() const;
|
||||
virtual int16 getOverlayWidth() const;
|
||||
void displayMessageOnOSD(const Common::U32String &msg) override;
|
||||
void displayActivityIconOnOSD(const Graphics::Surface *icon) override;
|
||||
|
||||
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, const byte *mask = NULL);
|
||||
void setCursorPalette(const byte *colors, uint start, uint num);
|
||||
|
||||
// Transform point from touchscreen coords into gamescreen coords
|
||||
void transformPoint(touchPosition &point);
|
||||
// Clip point to gamescreen coords
|
||||
void clipPoint(touchPosition &point);
|
||||
|
||||
void setCursorDelta(float deltaX, float deltaY);
|
||||
|
||||
void updateFocus();
|
||||
void updateMagnify();
|
||||
void updateBacklight();
|
||||
void updateConfig();
|
||||
void updateSize();
|
||||
|
||||
private:
|
||||
void init3DSGraphics();
|
||||
void destroy3DSGraphics();
|
||||
void initAudio();
|
||||
void destroyAudio();
|
||||
void initEvents();
|
||||
void destroyEvents();
|
||||
|
||||
void flushGameScreen();
|
||||
void flushCursor();
|
||||
|
||||
virtual Common::Path getDefaultLogFileName();
|
||||
virtual Common::WriteStream *createLogFile();
|
||||
|
||||
protected:
|
||||
Audio::MixerImpl *_mixer;
|
||||
Backends::Log::Log *_logger;
|
||||
|
||||
private:
|
||||
u16 _gameWidth, _gameHeight;
|
||||
u16 _gameTopX, _gameTopY;
|
||||
u16 _gameBottomX, _gameBottomY;
|
||||
|
||||
// Audio
|
||||
Thread audioThread;
|
||||
|
||||
// Graphics
|
||||
GraphicsModeID _graphicsModeID;
|
||||
TransactionState _transactionState;
|
||||
TransactionDetails _transactionDetails;
|
||||
|
||||
GfxState _gfxState, _oldGfxState;
|
||||
Graphics::PixelFormat _pfDefaultTexture;
|
||||
Graphics::PixelFormat _pfGame, _oldPfGame;
|
||||
Graphics::PixelFormat _pfCursor;
|
||||
byte _palette[3 * 256];
|
||||
byte _cursorPalette[3 * 256];
|
||||
uint32 _paletteMap[256];
|
||||
|
||||
Graphics::Surface _gameScreen;
|
||||
bool _gameTextureDirty;
|
||||
Sprite _gameTopTexture;
|
||||
Sprite _gameBottomTexture;
|
||||
Sprite _overlay;
|
||||
Sprite _activityIcon;
|
||||
Sprite _osdMessage;
|
||||
bool _filteringEnabled;
|
||||
|
||||
enum {
|
||||
kOSDMessageDuration = 800
|
||||
};
|
||||
uint32 _osdMessageEndTime;
|
||||
|
||||
int _screenShakeXOffset;
|
||||
int _screenShakeYOffset;
|
||||
bool _overlayVisible;
|
||||
bool _overlayInGUI;
|
||||
int _screenChangeId;
|
||||
|
||||
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 _cursorScreenX, _cursorScreenY;
|
||||
float _cursorOverlayX, _cursorOverlayY;
|
||||
float _cursorDeltaX, _cursorDeltaY;
|
||||
int _cursorHotspotX, _cursorHotspotY;
|
||||
uint32 _cursorKeyColor;
|
||||
|
||||
// Magnify
|
||||
MagnifyMode _magnifyMode;
|
||||
u16 _magX, _magY;
|
||||
u16 _magWidth, _magHeight;
|
||||
u16 _magCenterX, _magCenterY;
|
||||
|
||||
Common::Path _logFilePath;
|
||||
|
||||
public:
|
||||
// Pause
|
||||
PauseToken _sleepPauseToken;
|
||||
|
||||
bool _showCursor;
|
||||
bool _snapToBorder;
|
||||
bool _stretchToFit;
|
||||
Screen _screen;
|
||||
};
|
||||
|
||||
} // namespace N3DS
|
||||
|
||||
#endif
|
||||
56
backends/platform/3ds/shader.v.pica
Normal file
56
backends/platform/3ds/shader.v.pica
Normal file
@@ -0,0 +1,56 @@
|
||||
;* 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 3 of the License, or
|
||||
;* 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
; 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
|
||||
175
backends/platform/3ds/sprite.cpp
Normal file
175
backends/platform/3ds/sprite.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/platform/3ds/osystem.h"
|
||||
#include "backends/platform/3ds/sprite.h"
|
||||
#include "common/algorithm.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace N3DS {
|
||||
|
||||
Sprite::Sprite()
|
||||
: textureTransferFlags(0)
|
||||
, dirtyPixels(true)
|
||||
, dirtyMatrix(true)
|
||||
, actualWidth(0)
|
||||
, actualHeight(0)
|
||||
, posX(0)
|
||||
, posY(0)
|
||||
, offsetX(0)
|
||||
, offsetY(0)
|
||||
, scaleX(1.f)
|
||||
, scaleY(1.f)
|
||||
{
|
||||
Mtx_Identity(&modelview);
|
||||
|
||||
vertices = (vertex *)linearAlloc(sizeof(vertex) * 4);
|
||||
}
|
||||
|
||||
Sprite::~Sprite() {
|
||||
free();
|
||||
linearFree(vertices);
|
||||
}
|
||||
|
||||
void Sprite::create(uint16 width, uint16 height, const GfxMode3DS *mode, bool vram) {
|
||||
int16 wPow = MAX<uint16>(Common::nextHigher2(width), 64u);
|
||||
int16 hPow = MAX<uint16>(Common::nextHigher2(height), 64u);
|
||||
|
||||
bool pwrW_hChanged = (wPow != w) || (hPow != h);
|
||||
bool sfcBppChanged = (mode->surfaceFormat.bytesPerPixel != format.bytesPerPixel);
|
||||
bool texFmtChanged = (mode->textureFormat != texture.fmt);
|
||||
|
||||
bool srfDataReinitNeeded = (pwrW_hChanged || sfcBppChanged || !pixels);
|
||||
bool textureReinitNeeded = (pwrW_hChanged || texFmtChanged || !texture.data);
|
||||
|
||||
actualWidth = width;
|
||||
actualHeight = height;
|
||||
format = mode->surfaceFormat;
|
||||
textureTransferFlags = mode->textureTransferFlags;
|
||||
w = wPow;
|
||||
h = hPow;
|
||||
pitch = w * format.bytesPerPixel;
|
||||
dirtyPixels = true;
|
||||
|
||||
if (width && height) {
|
||||
// Don't needlessly reinitialize surface pixels.
|
||||
if (srfDataReinitNeeded) {
|
||||
linearFree(pixels);
|
||||
pixels = linearAlloc(h * pitch);
|
||||
}
|
||||
// Don't needlessly reinitialize C3D_Tex data.
|
||||
if (textureReinitNeeded) {
|
||||
C3D_TexDelete(&texture);
|
||||
if (vram) {
|
||||
if (!C3D_TexInitVRAM(&texture, w, h, mode->textureFormat))
|
||||
C3D_TexInit(&texture, w, h, mode->textureFormat);
|
||||
} else
|
||||
C3D_TexInit(&texture, w, h, mode->textureFormat);
|
||||
}
|
||||
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(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::transfer() {
|
||||
if (pixels && dirtyPixels) {
|
||||
dirtyPixels = false;
|
||||
GSPGPU_FlushDataCache(pixels, w * h * format.bytesPerPixel);
|
||||
C3D_SyncDisplayTransfer((u32*)pixels, GX_BUFFER_DIM(w, h), (u32*)texture.data, GX_BUFFER_DIM(w, h), textureTransferFlags);
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite::render() {
|
||||
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) {
|
||||
if (x != scaleX || y != scaleY) {
|
||||
scaleX = x;
|
||||
scaleY = y;
|
||||
dirtyMatrix = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite::setPosition(int x, int y) {
|
||||
if (x != posX || y != posY) {
|
||||
posX = x;
|
||||
posY = y;
|
||||
dirtyMatrix = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite::setOffset(uint16 x, uint16 y) {
|
||||
offsetX = x;
|
||||
offsetY = 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 - offsetX, posY - offsetY, 0, true);
|
||||
}
|
||||
return &modelview;
|
||||
}
|
||||
|
||||
void Sprite::setFilteringMode(bool enableLinearFiltering) {
|
||||
GPU_TEXTURE_FILTER_PARAM filteringMode = enableLinearFiltering ? GPU_LINEAR : GPU_NEAREST;
|
||||
C3D_TexSetFilter(&texture, filteringMode, filteringMode);
|
||||
}
|
||||
|
||||
} // namespace N3DS
|
||||
83
backends/platform/3ds/sprite.h
Normal file
83
backends/platform/3ds/sprite.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRAPHICS_SPRITE_3DS_H
|
||||
#define GRAPHICS_SPRITE_3DS_H
|
||||
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include <3ds.h>
|
||||
#include <citro3d.h>
|
||||
|
||||
namespace N3DS {
|
||||
|
||||
typedef struct {
|
||||
float position[3];
|
||||
float texcoord[2];
|
||||
} vertex;
|
||||
|
||||
struct GfxMode3DS;
|
||||
|
||||
class Sprite : public Graphics::Surface {
|
||||
public:
|
||||
Sprite();
|
||||
~Sprite();
|
||||
void create(uint16 width, uint16 height, const GfxMode3DS *mode, bool vram = false);
|
||||
void free();
|
||||
void convertToInPlace(const Graphics::PixelFormat &dstFormat, const byte *palette = 0);
|
||||
void transfer();
|
||||
void render();
|
||||
void clear(uint32 color = 0);
|
||||
void markDirty(){ dirtyPixels = true; }
|
||||
|
||||
void setPosition(int x, int y);
|
||||
void setOffset(uint16 x, uint16 y);
|
||||
void setScale(float x, float y);
|
||||
float getScaleX() const { return scaleX; }
|
||||
float getScaleY() const { return scaleY; }
|
||||
int getPosX() const { return posX; }
|
||||
int getPosY() const { return posY; }
|
||||
C3D_Mtx* getMatrix();
|
||||
|
||||
void setFilteringMode(bool enableLinearFiltering);
|
||||
|
||||
uint16 actualWidth;
|
||||
uint16 actualHeight;
|
||||
|
||||
private:
|
||||
uint32 textureTransferFlags;
|
||||
bool dirtyPixels;
|
||||
bool dirtyMatrix;
|
||||
C3D_Mtx modelview;
|
||||
C3D_Tex texture;
|
||||
vertex* vertices;
|
||||
int posX;
|
||||
int posY;
|
||||
uint16 offsetX;
|
||||
uint16 offsetY;
|
||||
float scaleX;
|
||||
float scaleY;
|
||||
};
|
||||
|
||||
} // namespace N3DS
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user