diff options
| author | Eugene Sandulenko | 2016-05-16 10:05:00 +0200 | 
|---|---|---|
| committer | Eugene Sandulenko | 2016-05-16 10:05:00 +0200 | 
| commit | 2cd0a99e2bc28fa8ec17f782405c4abfd047f5e1 (patch) | |
| tree | 96328591d401d3edc68a4b62ceb44e7d9781c6bf /backends/platform/3ds | |
| parent | 9178fba4d7dda6b3d8669c318568d46a07e50d28 (diff) | |
| parent | 1531b4ddbf685f22970b7365515318b847e4f4f3 (diff) | |
| download | scummvm-rg350-2cd0a99e2bc28fa8ec17f782405c4abfd047f5e1.tar.gz scummvm-rg350-2cd0a99e2bc28fa8ec17f782405c4abfd047f5e1.tar.bz2 scummvm-rg350-2cd0a99e2bc28fa8ec17f782405c4abfd047f5e1.zip  | |
Merge pull request #745 from Cruel/3ds
3DS: New Backend
Diffstat (limited to 'backends/platform/3ds')
22 files changed, 2544 insertions, 0 deletions
diff --git a/backends/platform/3ds/3ds.mk b/backends/platform/3ds/3ds.mk new file mode 100644 index 0000000000..7ab58995f6 --- /dev/null +++ b/backends/platform/3ds/3ds.mk @@ -0,0 +1,64 @@ +TARGET := scummvm + +APP_TITLE       := ScummVM +APP_DESCRIPTION := Point-and-click adventure game engines +APP_AUTHOR      := ScummVM Team +APP_ICON        := backends/platform/3ds/app/icon.png + +APP_RSF         := backends/platform/3ds/app/scummvm.rsf +APP_BANNER_IMAGE:= backends/platform/3ds/app/banner.png +APP_BANNER_AUDIO:= backends/platform/3ds/app/banner.wav + +ARCH     := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft +CXXFLAGS += -std=gnu++11 +ASFLAGS  += -mfloat-abi=hard +LDFLAGS  += -specs=3dsx.specs $(ARCH) -L$(DEVKITPRO)/libctru/lib -L$(DEVKITPRO)/portlibs/3ds/lib + +.PHONY: clean_3ds + +clean: clean_3ds + +clean_3ds: +	$(RM) $(TARGET).3dsx +	$(RM) $(TARGET).cia + +$(TARGET).smdh: $(APP_ICON) +	@bannertool makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i $(APP_ICON) -o $@ +	@echo built ... $(notdir $@) + +$(TARGET).3dsx: $(EXECUTABLE) $(TARGET).smdh +	@3dsxtool $< $@ --smdh=$(TARGET).smdh +	@echo built ... $(notdir $@) +	 +$(TARGET).bnr: $(APP_BANNER_IMAGE) $(APP_BANNER_AUDIO) +	@bannertool makebanner -o $@ -i $(APP_BANNER_IMAGE) -a $(APP_BANNER_AUDIO) +	@echo built ... $(notdir $@) +	 +$(TARGET).cia: $(EXECUTABLE) $(APP_RSF) $(TARGET).smdh $(TARGET).bnr +	@makerom -f cia -target t -exefslogo -o $@ -elf $(EXECUTABLE) -rsf $(APP_RSF) -banner $(TARGET).bnr -icon $(TARGET).smdh +	@echo built ... $(notdir $@) + +#--------------------------------------------------------------------------------- +# rules for assembling GPU shaders +#--------------------------------------------------------------------------------- +define shader-as +	$(eval FILEPATH := $(patsubst %.shbin.o,%.shbin,$@)) +	$(eval FILE := $(patsubst %.shbin.o,%.shbin,$(notdir $@))) +	picasso -o $(FILEPATH) $1 +	bin2s $(FILEPATH) | $(AS) -o $@ +	echo "extern const u8" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(FILEPATH) | tr . _)`.h +	echo "extern const u8" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(FILEPATH) | tr . _)`.h +	echo "extern const u32" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(FILEPATH) | tr . _)`.h +endef + +%.shbin.o : %.v.pica %.g.pica +	@echo $(notdir $^) +	@$(call shader-as,$^) + +%.shbin.o : %.v.pica +	@echo $(notdir $<) +	@$(call shader-as,$<) + +%.shbin.o : %.shlist +	@echo $(notdir $<) +	@$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file))) diff --git a/backends/platform/3ds/README b/backends/platform/3ds/README new file mode 100644 index 0000000000..516e694f64 --- /dev/null +++ b/backends/platform/3ds/README @@ -0,0 +1,185 @@ +ScummVM 3DS README +------------------------------------------------------------------------ + +Table of Contents: +------------------ +1.0) Installation + * 1.1 3DSX installation + * 1.2 CIA installation +2.0) Controls + * 2.1 Default key mappings + * 2.2 Hover mode + * 2.3 Drag mode +3.0) Supported Games +4.0) Compiling + * 4.1 Prerequisites + * * 4.1.1 Compiling third-party libraries + * 4.2 Compiling ScummVM + * 4.3 Warning for 3DSX build + + + +1.0) Installation +----------------- +There are two possible formats to be used: 3DSX and CIA (recommended). +The 3DSX format is exclusively used by the Homebrew Launcher and its derivatives. +The CIA format can be installed directly to the 3DS home menu and can be launched +using any CFW (Custom Firmware) of your choice. + +Installing the Homebrew Launcher or any CFW is beyond the scope of this README. +Look elsewhere to see how to install those if you do not already have them set up. + + +1.1) 3DSX installation +---------------- +The CIA format is recommended for stability and maximum game support. If that is +not an option, you will need one of a collection of 3DS titles installed on your +system in order to properly launch ScummVM as a 3DSX. This is because the +Homebrew Launcher hijacks other processes to run 3DSX homebrew, and ScummVM is a +particularly large homebrew that can't be launched with the resources provided +by standard system applications. + +You will need one of the following (installed or physically in cart slot): + +- Youtube +- Monster Hunter 4 Ultimate Special Demo +- Monster Hunter 4 Ultimate +- Monster Hunter 4G +- Super Smash Bros. for Nintendo 3DS Demo +- Super Smash Bros. for Nintendo 3DS Special Demo +- Super Smash Bros. for Nintendo 3DS + +Once you have one of the above, you need to merely extract all ScummVM 3DS files +to the root of your SD card so that all files reside in the /3ds/scummvm/ directory. + + +1.2) CIA installation +--------------------- +The CIA format requires a DSP binary dump saved on your SD card as /3ds/dspfirm.cdc +for proper audio support. You can search online to find software to dump this. +Not having this file will cause many problems with games that need audio, sometimes +even crashing, so this is NOT considered optional. + +Using any CIA installation software (search elsewhere for that), you need to install +the scummvm.cia file. Then, just like what is done with the 3DSX installation, you +need to extract all ScummVM 3DS files (scummvm.cia excluded) to the root of your SD +card so that all files reside in the /3ds/scummvm/ directory. + + + +2.0) Controls +------------- + +2.1) Default key mappings +------------------------- +The D-Pad and A/B/X/Y buttons have mirrored usage. So they do the same things +depending on if you're right or left-handed. + +|  Buttons   |   Function                     | +|------------|--------------------------------| +| A / D-left | Left-click                     | +| X / D-up   | Right-click                    | +| B / D-down | ESC (skips cutscenes and such) | +| L          | Use virtual keyboard           | +| R          | Toggle hover/drag modes        | +| Start      | Open game menu                 | +| Select     | Open 3DS config menu           | +| Circle Pad | Move the cursor                | + + +2.2) Hover mode +--------------- +When you use the touchscreen, you are simulating the mere moving of the mouse. You +can click only with taps, meaning it is impossible to drag stuff or hold down a +mouse button without using buttons mapped to right/left-click. + + +2.3) Drag mode +-------------- +Every time you touch and release the touchscreen, you are simulating the click and +release of the mouse buttons. At the moment, this is only a left-click. + + + +3.0) Supported Games +-------------------- +The full game engine compatibility list can be found here: +http://scummvm.org/compatibility/ + +While all the above games should run on the 3DS (report if they do not), there are +many games which are unplayable due to the lack of CPU speed on the 3DS. So if +you play any games that run really slow, this is not considered a bug, but rather +a hardware limitation. Though possible GPU optimizations are always in the works. +The New 3DS console has much better performance, but there are still many newer and +high-resolution games that cannot be played. A list of these unplayable games and +game engines will eventually be listed here. + + + +4.0) Compiling +-------------- + +4.1) Prerequisites +------------------ + - devkitARM (presumably with libctru, picasso and such) + - citro3d + - Optional: You should compile third-party libraries for the 3ds (commonly referred +   to as portlibs in the devkitPRO community). Some games requires these to operate +   properly. + + +4.1.1) Compiling third-party libraries +-------------------------------------- +Most libraries used can be compiled with same commands and configuration flags. + +It is assumed that you have these environment variables defined: + - DEVKITPRO    Your root devkitPro directory + - DEVKITARM    Your root devkitARM directory (probably same as $DEVKITPRO/devkitARM) + - CTRULIB      Your root libctru directory (probably same as $DEVKITPRO/libctru) + +In the source directory of the library: + - $ export PORTLIBS=$DEVKITPRO/portlibs/armv6k + - $ export PATH=$DEVKITARM/bin:$PATH + - $ export PKG_CONFIG_PATH=$PORTLIBS/lib/pkgconfig + - $ export CFLAGS="-g -march=armv6k -mtune=mpcore -mfloat-abi=hard -O2 +                    -mword-relocations -ffunction-sections -fdata-sections" + - $ export CPPFLAGS="-I$PORTLIBS/include -I$CTRULIB/include" + - $ export LDFLAGS="-L$PORTLIBS/lib" + - $ mkdir -p $PORTLIBS + - $ ./configure --prefix=$PORTLIBS --host=arm-none-eabi --disable-shared +     --enable-static + - $ make + - $ make install + +Useful libraries (and special config flags needed): + - zlib + - libpng + - libjpeg + - freetype2   --without-bzip2 --without-harfbuzz + - libmad + - tremor + - flac        --disable-cpplibs --without-flac + - faad + + +4.2) Compiling ScummVM +---------------------- + - $ ./configure --host=3ds + - $ make + +Additionally compile to specific formats to be used on the 3ds: + - $ make scummvm.3dsx + - $ make scummvm.cia + + +4.3) Warning for 3DSX build +--------------------------- +The above configuration command will include all game engines by default and will +likely be too massive to run using the 3DSX format. Until dynamic modules are figured +out, you should configure engines like this for 3DSX builds: + + - $ ./configure --host=3ds --disable-all-engines--enable-engine=scumm-7-8,myst,riven, +     sword1,sword2,sword25,sci,lure,sky,agi,agos + +Choose whatever engines you want, but if the ELF's .text section exceeds ~10MB, it +won't be playable unless it's a CIA. diff --git a/backends/platform/3ds/app/banner.png b/backends/platform/3ds/app/banner.png Binary files differnew file mode 100644 index 0000000000..a3b02150ec --- /dev/null +++ b/backends/platform/3ds/app/banner.png diff --git a/backends/platform/3ds/app/banner.wav b/backends/platform/3ds/app/banner.wav Binary files differnew file mode 100644 index 0000000000..e0b684b62f --- /dev/null +++ b/backends/platform/3ds/app/banner.wav diff --git a/backends/platform/3ds/app/icon.png b/backends/platform/3ds/app/icon.png Binary files differnew file mode 100644 index 0000000000..07022fbac1 --- /dev/null +++ b/backends/platform/3ds/app/icon.png diff --git a/backends/platform/3ds/app/scummvm.rsf b/backends/platform/3ds/app/scummvm.rsf new file mode 100644 index 0000000000..a4518949bb --- /dev/null +++ b/backends/platform/3ds/app/scummvm.rsf @@ -0,0 +1,219 @@ +BasicInfo: +  Title                   : ScummVM +  ProductCode             : ScummVM +  Logo                    : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem + +TitleInfo: +  Category                : Application +  UniqueId                : 0xFF321 + +Option: +  UseOnSD                 : true # true if App is to be installed to SD +  FreeProductCode         : true # Removes limitations on ProductCode +  MediaFootPadding        : false # If true CCI files are created with padding +  EnableCrypt             : false # Enables encryption for NCCH and CIA +  EnableCompress          : false # Compresses where applicable (currently only exefs:/.code) + +AccessControlInfo: +  CoreVersion                   : 2 + +  # Exheader Format Version +  DescVersion                   : 2 + +  # Minimum Required Kernel Version (below is for 4.5.0) +  ReleaseKernelMajor            : "02" +  ReleaseKernelMinor            : "33" + +  # ExtData +  UseExtSaveData                : false # enables ExtData +  #ExtSaveDataId                : 0x300 # only set this when the ID is different to the UniqueId + +  # FS:USER Archive Access Permissions +  # Uncomment as required +  FileSystemAccess: +   #- CategorySystemApplication +   #- CategoryHardwareCheck +   #- CategoryFileSystemTool +   #- Debug +   #- TwlCardBackup +   #- TwlNandData +   #- Boss +   - DirectSdmc +   #- Core +   #- CtrNandRo +   #- CtrNandRw +   #- CtrNandRoWrite +   #- CategorySystemSettings +   #- CardBoard +   #- ExportImportIvs +   #- DirectSdmcWrite +   #- SwitchCleanup +   #- SaveDataMove +   #- Shop +   #- Shell +   #- CategoryHomeMenu + +  # Process Settings +  MemoryType                    : Application # Application/System/Base +  SystemMode                    : 64MB # 64MB(Default)/96MB/80MB/72MB/32MB +  IdealProcessor                : 0 +  AffinityMask                  : 1 +  Priority                      : 16 +  MaxCpu                        : 0 # Let system decide +  HandleTableSize               : 0x200 +  DisableDebug                  : false +  EnableForceDebug              : false +  CanWriteSharedPage            : true +  CanUsePrivilegedPriority      : false +  CanUseNonAlphabetAndNumber    : true +  PermitMainFunctionArgument    : true +  CanShareDeviceMemory          : true +  RunnableOnSleep               : false +  SpecialMemoryArrange          : true + +  # New3DS Exclusive Process Settings +  SystemModeExt                 : 124MB # Legacy(Default)/124MB/178MB  Legacy:Use Old3DS SystemMode +  CpuSpeed                      : 804MHz # 268MHz(Default)/804MHz +  EnableL2Cache                 : true # false(default)/true +  CanAccessCore2                : true + +  # Virtual Address Mappings +  IORegisterMapping: +   - 1ff00000-1ff7ffff   # DSP memory +  MemoryMapping: +   - 1f000000-1f5fffff:r # VRAM + +  # Accessible SVCs, <Name>:<ID> +  SystemCallAccess: +    ArbitrateAddress: 34 +    Break: 60 +    CancelTimer: 28 +    ClearEvent: 25 +    ClearTimer: 29 +    CloseHandle: 35 +    ConnectToPort: 45 +    ControlMemory: 1 +    CreateAddressArbiter: 33 +    CreateEvent: 23 +    CreateMemoryBlock: 30 +    CreateMutex: 19 +    CreateSemaphore: 21 +    CreateThread: 8 +    CreateTimer: 26 +    DuplicateHandle: 39 +    ExitProcess: 3 +    ExitThread: 9 +    GetCurrentProcessorNumber: 17 +    GetHandleInfo: 41 +    GetProcessId: 53 +    GetProcessIdOfThread: 54 +    GetProcessIdealProcessor: 6 +    GetProcessInfo: 43 +    GetResourceLimit: 56 +    GetResourceLimitCurrentValues: 58 +    GetResourceLimitLimitValues: 57 +    GetSystemInfo: 42 +    GetSystemTick: 40 +    GetThreadContext: 59 +    GetThreadId: 55 +    GetThreadIdealProcessor: 15 +    GetThreadInfo: 44 +    GetThreadPriority: 11 +    MapMemoryBlock: 31 +    OutputDebugString: 61 +    QueryMemory: 2 +    ReleaseMutex: 20 +    ReleaseSemaphore: 22 +    SendSyncRequest1: 46 +    SendSyncRequest2: 47 +    SendSyncRequest3: 48 +    SendSyncRequest4: 49 +    SendSyncRequest: 50 +    SetThreadPriority: 12 +    SetTimer: 27 +    SignalEvent: 24 +    SleepThread: 10 +    UnmapMemoryBlock: 32 +    WaitSynchronization1: 36 +    WaitSynchronizationN: 37 +    Backdoor: 123 + +  # Service List +  # Maximum 34 services (32 if firmware is prior to 9.3.0) +  ServiceAccessControl: +   - cfg:u +   - fs:USER +   - gsp::Gpu +   - hid:USER +   - ndm:u +   - pxi:dev +   - APT:U +   - ac:u +   - act:u +   - am:net +   - boss:U +   - cam:u +   - cecd:u +   - dsp::DSP +   - frd:u +   - http:C +   - ir:USER +   - ir:u +   - ir:rst +   - ldr:ro +   - mic:u +   - news:u +   - nim:aoc +   - nwm::UDS +   - ptm:u +   - qtm:u +   - soc:U +   - ssl:C +   - y2r:u +   - gsp::Lcd + + +SystemControlInfo: +  SaveDataSize: 0K +  RemasterVersion: 0 +  StackSize: 0x40000 + +  # Modules that run services listed above should be included below +  # Maximum 48 dependencies +  # If a module is listed that isn't present on the 3DS, the title will get stuck at the logo (3ds waves) +  # So act, nfc and qtm are commented for 4.x support. Uncomment if you need these. +  # <module name>:<module titleid> +  Dependency: +    ac: 0x0004013000002402 +    #act: 0x0004013000003802 +    am: 0x0004013000001502 +    boss: 0x0004013000003402 +    camera: 0x0004013000001602 +    cecd: 0x0004013000002602 +    cfg: 0x0004013000001702 +    codec: 0x0004013000001802 +    csnd: 0x0004013000002702 +    dlp: 0x0004013000002802 +    dsp: 0x0004013000001a02 +    friends: 0x0004013000003202 +    gpio: 0x0004013000001b02 +    gsp: 0x0004013000001c02 +    hid: 0x0004013000001d02 +    http: 0x0004013000002902 +    i2c: 0x0004013000001e02 +    ir: 0x0004013000003302 +    mcu: 0x0004013000001f02 +    mic: 0x0004013000002002 +    ndm: 0x0004013000002b02 +    news: 0x0004013000003502 +    #nfc: 0x0004013000004002 +    nim: 0x0004013000002c02 +    nwm: 0x0004013000002d02 +    pdn: 0x0004013000002102 +    ps: 0x0004013000003102 +    ptm: 0x0004013000002202 +    #qtm: 0x0004013020004202 +    ro: 0x0004013000003702 +    socket: 0x0004013000002e02 +    spi: 0x0004013000002302 +    ssl: 0x0004013000002f02 diff --git a/backends/platform/3ds/config.cpp b/backends/platform/3ds/config.cpp new file mode 100644 index 0000000000..117b979d9f --- /dev/null +++ b/backends/platform/3ds/config.cpp @@ -0,0 +1,87 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "osystem.h" +#include "options-dialog.h" +#include "common/config-manager.h" +#include <3ds.h> + +namespace _3DS { + +Config config; +static Common::String prefix = "3ds_"; + +static bool confGetBool(Common::String key, bool defaultVal) { +	if (ConfMan.hasKey(prefix + key)) +		return ConfMan.getBool(prefix + key); +	return defaultVal; +} + +static void confSetBool(Common::String key, bool val) { +	ConfMan.setBool(prefix + key, val); +} + +static int confGetInt(Common::String key, int defaultVal) { +	if (ConfMan.hasKey(prefix + key)) +		return ConfMan.getInt(prefix + key); +	return defaultVal; +} + +static void confSetInt(Common::String key, int val) { +	ConfMan.setInt(prefix + key, val); +} + +void loadConfig() { +	config.showCursor = confGetBool("showcursor", true); +	config.snapToBorder = confGetBool("snaptoborder", true); +	config.stretchToFit = confGetBool("stretchtofit", false); +	config.sensitivity = confGetInt("sensitivity", -5); +	config.screen = confGetInt("screen", kScreenBoth); +	 +	// Turn off the backlight of any screen not used +	if (R_SUCCEEDED(gspLcdInit())) { +		if (config.screen == kScreenTop) { +			GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_TOP); +			GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_BOTTOM); +		} else if (config.screen == kScreenBottom) { +			GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTTOM); +			GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_TOP); +		} else +			GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH); +		gspLcdExit(); +	} +	 +	OSystem_3DS *osys = (OSystem_3DS *)g_system; +	osys->updateConfig(); +} + +void saveConfig() { +	confSetBool("showcursor", config.showCursor); +	confSetBool("snaptoborder", config.snapToBorder); +	confSetBool("stretchtofit", config.stretchToFit); +	confSetInt("sensitivity", config.sensitivity); +	confSetInt("screen", config.screen); +	ConfMan.flushToDisk(); +} + +} // namespace _3DS diff --git a/backends/platform/3ds/config.h b/backends/platform/3ds/config.h new file mode 100644 index 0000000000..c8b75736ad --- /dev/null +++ b/backends/platform/3ds/config.h @@ -0,0 +1,45 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef CONFIG_3DS_H +#define CONFIG_3DS_H + +#include "common/str.h" + +namespace _3DS { +	 +struct Config { +	bool showCursor; +	bool snapToBorder; +	bool stretchToFit; +	int sensitivity; +	int screen; +}; + +extern Config config; + +void loadConfig(); +void saveConfig(); + +} // namespace _3DS + +#endif // CONFIG_3DS_H diff --git a/backends/platform/3ds/gui.cpp b/backends/platform/3ds/gui.cpp new file mode 100644 index 0000000000..0883d5a102 --- /dev/null +++ b/backends/platform/3ds/gui.cpp @@ -0,0 +1,46 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/platform/3ds/gui.h" +#include "common/system.h" + +StatusMessageDialog* StatusMessageDialog::_opened = 0; + +StatusMessageDialog::StatusMessageDialog(const Common::String &message, uint32 duration) +	: MessageDialog(message, 0, 0) { +	_timer = g_system->getMillis() + duration; +	if (_opened) +		_opened->close(); +	_opened = this; +} + +void StatusMessageDialog::handleTickle() { +	MessageDialog::handleTickle(); +	if (g_system->getMillis() > _timer) +		close(); +} + +void StatusMessageDialog::close() { +    GUI::Dialog::close(); +	if (_opened) +		_opened = 0; +} diff --git a/backends/platform/3ds/gui.h b/backends/platform/3ds/gui.h new file mode 100644 index 0000000000..66c6547139 --- /dev/null +++ b/backends/platform/3ds/gui.h @@ -0,0 +1,41 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GUI_3DS_H +#define GUI_3DS_H + +#include "gui/message.h" + +class StatusMessageDialog : public GUI::MessageDialog { +public: +	StatusMessageDialog(const Common::String &message, uint32 duration); + +	void handleTickle(); + +protected: +	virtual void close(); +	 +	uint32 _timer; +	static StatusMessageDialog* _opened; +}; + +#endif // GUI_3DS_H diff --git a/backends/platform/3ds/main.cpp b/backends/platform/3ds/main.cpp new file mode 100644 index 0000000000..6cc2c5cf5d --- /dev/null +++ b/backends/platform/3ds/main.cpp @@ -0,0 +1,54 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "osystem.h" +#include <3ds.h> + +int main(int argc, char *argv[]) { +	// Initialize basic libctru stuff +	gfxInitDefault(); +	cfguInit(); +	osSetSpeedupEnable(true); +// 	consoleInit(GFX_TOP, NULL); + +	g_system = new _3DS::OSystem_3DS(); +	assert(g_system); + +	// Invoke the actual ScummVM main entry point +// 	if (argc > 2) +// 		res = scummvm_main(argc-2, &argv[2]); +// 	else +// 		res = scummvm_main(argc, argv); +	scummvm_main(0, nullptr); + +	delete dynamic_cast<_3DS::OSystem_3DS*>(g_system); +	 +	// Turn on both screen backlights before exiting. +	if (R_SUCCEEDED(gspLcdInit())) { +		GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH); +		gspLcdExit(); +	} +	 +	cfguExit(); +	gfxExit(); +	return 0; +} diff --git a/backends/platform/3ds/module.mk b/backends/platform/3ds/module.mk new file mode 100644 index 0000000000..3eb15aef81 --- /dev/null +++ b/backends/platform/3ds/module.mk @@ -0,0 +1,18 @@ +MODULE := backends/platform/3ds + +MODULE_OBJS := \ +	main.o \ +	shader.shbin.o \ +	sprite.o \ +	gui.o \ +	config.o \ +	options-dialog.o \ +	osystem.o \ +	osystem-graphics.o \ +	osystem-audio.o \ +	osystem-events.o + +# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS. +MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) +OBJS := $(MODULE_OBJS) $(OBJS) +MODULE_DIRS += $(sort $(dir $(MODULE_OBJS))) diff --git a/backends/platform/3ds/options-dialog.cpp b/backends/platform/3ds/options-dialog.cpp new file mode 100644 index 0000000000..0f8bfd0c66 --- /dev/null +++ b/backends/platform/3ds/options-dialog.cpp @@ -0,0 +1,98 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "options-dialog.h" +#include "config.h" +#include "gui/dialog.h" +#include "gui/gui-manager.h" +#include "gui/widgets/list.h" +#include "gui/widgets/tab.h" +#include "osystem.h" +#include "engines/scumm/scumm.h" +#include "gui/widgets/popup.h" + +#include "common/translation.h" + +namespace _3DS { +	 +bool optionMenuOpened = false; + +OptionsDialog::OptionsDialog() : GUI::Dialog(20, 20, 280, 200) { +	 +	optionMenuOpened = true; + +	new GUI::ButtonWidget(this, 120, 180, 72, 16, _("~C~lose"), 0, GUI::kCloseCmd); +	new GUI::ButtonWidget(this, 200, 180, 72, 16, _("~S~ave"), 0, GUI::kOKCmd); + +	_showCursorCheckbox = new GUI::CheckboxWidget(this, 5, 5, 130, 20, _("Show mouse cursor"), 0, 0, 'T'); +	_showCursorCheckbox->setState(config.showCursor); +	 +	_snapToBorderCheckbox = new GUI::CheckboxWidget(this, 5, 22, 130, 20, _("Snap to edges"), 0, 0, 'T'); +	_snapToBorderCheckbox->setState(config.snapToBorder); +	 +	_stretchToFitCheckbox = new GUI::CheckboxWidget(this, 140, 5, 130, 20, _("Stretch to fit"), 0, 0, 'T'); +	_stretchToFitCheckbox->setState(config.stretchToFit); + +	new GUI::StaticTextWidget(this, 0, 60, 110, 15, _("Use Screen:"), Graphics::kTextAlignRight); +	_screenRadioGroup = new GUI::RadiobuttonGroup(this, kScreenRadioGroup); +	_screenTopRadioWidget = new GUI::RadiobuttonWidget(this, 120, 50, 60, 20, _screenRadioGroup, kScreenTop, _("Top")); +	_screenBottomRadioWidget = new GUI::RadiobuttonWidget(this, 190, 50, 80, 20, _screenRadioGroup, kScreenBottom, _("Bottom")); +	_screenBothRadioWidget = new GUI::RadiobuttonWidget(this, 155, 70, 80, 20, _screenRadioGroup, kScreenBoth, _("Both")); +	_screenRadioGroup->setValue(config.screen); + +	new GUI::StaticTextWidget(this, 0, 100, 110, 15, _("C-Pad Sensitivity:"), Graphics::kTextAlignRight); +	_sensitivity = new GUI::SliderWidget(this, 115, 100, 160, 15, "TODO: Add tooltip", 1); +	_sensitivity->setMinValue(-15); +	_sensitivity->setMaxValue(30); +	_sensitivity->setValue(config.sensitivity); +	_sensitivity->setFlags(GUI::WIDGET_CLEARBG); +} + +OptionsDialog::~OptionsDialog() { +	optionMenuOpened = false; +} + +void OptionsDialog::updateConfigManager() { +	config.showCursor = _showCursorCheckbox->getState(); +	config.snapToBorder = _snapToBorderCheckbox->getState(); +	config.stretchToFit = _stretchToFitCheckbox->getState(); +	config.sensitivity = _sensitivity->getValue(); +	config.screen = _screenRadioGroup->getValue(); +	saveConfig(); +	loadConfig(); +} + +void OptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { +	switch(cmd) { +	case GUI::kOKCmd: +		updateConfigManager(); +		// Fall through +	case GUI::kCloseCmd: +		close(); +		break; +	default: +		Dialog::handleCommand(sender, cmd, data); +		break; +	} +} + +} // namespace _3DS diff --git a/backends/platform/3ds/options-dialog.h b/backends/platform/3ds/options-dialog.h new file mode 100644 index 0000000000..6673b88e7b --- /dev/null +++ b/backends/platform/3ds/options-dialog.h @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef OPTIONS_DIALOG_3DS_H +#define OPTIONS_DIALOG_3DS_H + + +#include "common/scummsys.h" +#include "common/str.h" +#include "gui/object.h" +#include "gui/widget.h" +#include "gui/dialog.h" +#include "gui/widgets/tab.h" +#include "scumm/dialogs.h" + +namespace _3DS { +	 +enum { +	kSave = 0x10000000, +	kScreenRadioGroup, +	kScreenTop, +	kScreenBottom, +	kScreenBoth, +}; +	 +extern bool optionMenuOpened; + +class OptionsDialog : public GUI::Dialog { + +public: +	OptionsDialog(); +	~OptionsDialog(); + +protected: +	virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); +	void updateConfigManager(); + +	GUI::SliderWidget *_sensitivity; +	GUI::CheckboxWidget *_showCursorCheckbox; +	GUI::CheckboxWidget *_snapToBorderCheckbox; +	GUI::CheckboxWidget *_stretchToFitCheckbox; + +	GUI::RadiobuttonGroup *_screenRadioGroup; +	GUI::RadiobuttonWidget *_screenTopRadioWidget; +	GUI::RadiobuttonWidget *_screenBottomRadioWidget; +	GUI::RadiobuttonWidget *_screenBothRadioWidget; +}; + +} // namespace _3DS + +#endif // OPTIONS_DIALOG_3DS_H diff --git a/backends/platform/3ds/osystem-audio.cpp b/backends/platform/3ds/osystem-audio.cpp new file mode 100644 index 0000000000..17e419c36d --- /dev/null +++ b/backends/platform/3ds/osystem-audio.cpp @@ -0,0 +1,110 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "osystem.h" +#include "audio/mixer.h" + +namespace _3DS { + +static bool hasAudio = false; + +static void audioThreadFunc(void *arg) { +	Audio::MixerImpl *mixer = (Audio::MixerImpl *)arg; +	OSystem_3DS *osys = (OSystem_3DS *)g_system; + +	int i; +	const int channel = 0; +	int bufferIndex = 0; +	const int bufferCount = 3; +	const int bufferSize = 80000; // Can't be too small, based on delayMillis duration +	const int sampleRate = mixer->getOutputRate(); +	int sampleLen = 0; +	uint32 lastTime = osys->getMillis(true); +	uint32 time = lastTime; +	ndspWaveBuf buffers[bufferCount]; +	 +	for (i = 0; i < bufferCount; ++i) { +		memset(&buffers[i], 0, sizeof(ndspWaveBuf)); +		buffers[i].data_vaddr = linearAlloc(bufferSize); +		buffers[i].looping = false; +		buffers[i].status = NDSP_WBUF_FREE; +	} +	 +	ndspChnReset(channel); +	ndspChnSetInterp(channel, NDSP_INTERP_LINEAR); +	ndspChnSetRate(channel, sampleRate); +	ndspChnSetFormat(channel, NDSP_FORMAT_STEREO_PCM16); + +	while (!osys->exiting) {		 +		osys->delayMillis(100); // Note: Increasing the delay requires a bigger buffer +		 +		time = osys->getMillis(true); +		sampleLen = (time - lastTime) * 22 * 4; // sampleRate / 1000 * channelCount * sizeof(int16); +		lastTime = time; +		 +		if (!osys->sleeping && sampleLen > 0) { +			bufferIndex++; +			bufferIndex %= bufferCount; +			ndspWaveBuf *buf = &buffers[bufferIndex]; +		 +			buf->nsamples = mixer->mixCallback(buf->data_adpcm, sampleLen); +			if (buf->nsamples > 0) { +				DSP_FlushDataCache(buf->data_vaddr, bufferSize); +				ndspChnWaveBufAdd(channel, buf); +			} +		} +	} +	 +	for (i = 0; i < bufferCount; ++i) +		linearFree(buffers[i].data_pcm8); +} + +void OSystem_3DS::initAudio() { +	_mixer = new Audio::MixerImpl(this, 22050); +	 +	hasAudio = R_SUCCEEDED(ndspInit()); +	_mixer->setReady(false); + +	if (hasAudio) { +		s32 prio = 0; +		svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); +		audioThread = threadCreate(&audioThreadFunc, _mixer, 32 * 1048, prio - 1, -2, false); +	} +} + +void OSystem_3DS::destroyAudio() { +	if (hasAudio) { +		threadJoin(audioThread, U64_MAX); +		threadFree(audioThread); +		ndspExit(); +	} +	 +	delete _mixer; +	_mixer = 0; +} + +Audio::Mixer *OSystem_3DS::getMixer() { +	assert(_mixer); +	return _mixer; +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem-events.cpp b/backends/platform/3ds/osystem-events.cpp new file mode 100644 index 0000000000..ae8a9b8b2b --- /dev/null +++ b/backends/platform/3ds/osystem-events.cpp @@ -0,0 +1,302 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/timer/default/default-timer.h" +#include "engines/engine.h" +#include "gui.h" +#include "options-dialog.h" +#include "config.h" +#include "osystem.h" + +namespace _3DS { + +static Common::Mutex *eventMutex; +static InputMode inputMode = MODE_DRAG; +static aptHookCookie cookie; +static bool optionMenuOpening = false; +static Common::String messageOSD; +static bool showMessageOSD = false; + +static void pushEventQueue(Common::Queue<Common::Event> *queue, Common::Event &event) { +	Common::StackLock lock(*eventMutex); +	queue->push(event); +} + +static void eventThreadFunc(void *arg) { +	OSystem_3DS *osys = (OSystem_3DS *)g_system; +	auto eventQueue = (Common::Queue<Common::Event> *)arg; +	 +	uint32 touchStartTime = osys->getMillis(); +	touchPosition lastTouch = {0, 0}; +	bool isRightClick = false; +	float cursorX = 0; +	float cursorY = 0; +	float cursorDeltaX = 0; +	float cursorDeltaY = 0; +	int circleDeadzone = 20; +	int borderSnapZone = 6; +	Common::Event event; +	 +	while (!osys->exiting) { +		do { +			osys->delayMillis(10); +		} while (osys->sleeping && !osys->exiting); +		 +		hidScanInput(); +		touchPosition touch; +		circlePosition circle; +		u32 held = hidKeysHeld(); +		u32 keysPressed = hidKeysDown(); +		u32 keysReleased = hidKeysUp(); +		 +		// C-Pad used to control the cursor +		hidCircleRead(&circle); +		if (circle.dx < circleDeadzone && circle.dx > -circleDeadzone) +			circle.dx = 0; +		if (circle.dy < circleDeadzone && circle.dy > -circleDeadzone) +			circle.dy = 0; +		cursorDeltaX = (0.0002f + config.sensitivity / 100000.f) * circle.dx * abs(circle.dx); +		cursorDeltaY = (0.0002f + config.sensitivity / 100000.f) * circle.dy * abs(circle.dy); +		 +		// Touch screen events +		if (held & KEY_TOUCH) { +			hidTouchRead(&touch); +			if (config.snapToBorder) { +				if (touch.px < borderSnapZone) +					touch.px = 0; +				if (touch.px > 319 - borderSnapZone) +					touch.px = 319; +				if (touch.py < borderSnapZone) +					touch.py = 0; +				if (touch.py > 239 - borderSnapZone) +					touch.py = 239; +			} +			cursorX = touch.px; +			cursorY = touch.py; +			osys->transformPoint(touch); + +			osys->warpMouse(touch.px, touch.py); +			event.mouse.x = touch.px; +			event.mouse.y = touch.py; +			 +			if (keysPressed & KEY_TOUCH) { +				touchStartTime = osys->getMillis(); +				isRightClick = (held & KEY_X || held & KEY_DUP); +				if (inputMode == MODE_DRAG) { +					event.type = isRightClick ? Common::EVENT_RBUTTONDOWN : Common::EVENT_LBUTTONDOWN; +					pushEventQueue(eventQueue, event); +				} +			} else if (touch.px != lastTouch.px || touch.py != lastTouch.py) { +				event.type = Common::EVENT_MOUSEMOVE; +				pushEventQueue(eventQueue, event); +			} +			 +			lastTouch = touch; +		} else if (keysReleased & KEY_TOUCH) { +			event.mouse.x = lastTouch.px; +			event.mouse.y = lastTouch.py; +			if (inputMode == MODE_DRAG) { +				event.type = isRightClick ? Common::EVENT_RBUTTONUP : Common::EVENT_LBUTTONUP; +				pushEventQueue(eventQueue, event); +			} else if (osys->getMillis() - touchStartTime < 200) { +				// Process click in MODE_HOVER +				event.type = Common::EVENT_MOUSEMOVE; +				pushEventQueue(eventQueue, event); +				event.type = isRightClick ? Common::EVENT_RBUTTONDOWN : Common::EVENT_LBUTTONDOWN; +				pushEventQueue(eventQueue, event); +				event.type = isRightClick ? Common::EVENT_RBUTTONUP : Common::EVENT_LBUTTONUP; +				pushEventQueue(eventQueue, event); +			} +		} else if (cursorDeltaX != 0 || cursorDeltaY != 0) { +			cursorX += cursorDeltaX; +			cursorY -= cursorDeltaY; +			if (cursorX < 0) cursorX = 0; +			if (cursorY < 0) cursorY = 0; +			if (cursorX > 320) cursorX = 320; +			if (cursorY > 240) cursorY = 240; +			lastTouch.px = cursorX; +			lastTouch.py = cursorY; +			osys->transformPoint(lastTouch); +			osys->warpMouse(lastTouch.px, lastTouch.py);  +			event.mouse.x = lastTouch.px; +			event.mouse.y = lastTouch.py; +			event.type = Common::EVENT_MOUSEMOVE; +			pushEventQueue(eventQueue, event); +		} +		 +		// Button events +		if (keysPressed & KEY_R) { +			if (inputMode == MODE_DRAG) { +				inputMode = MODE_HOVER; +				osys->displayMessageOnOSD("Hover Mode"); +			} else { +				inputMode = MODE_DRAG; +				osys->displayMessageOnOSD("Drag Mode"); +			} +		} +		if (keysPressed & KEY_A || keysPressed & KEY_DLEFT || keysReleased & KEY_A || keysReleased & KEY_DLEFT) { +			// SIMULATE LEFT CLICK +			event.mouse.x = lastTouch.px; +			event.mouse.y = lastTouch.py; +			if (keysPressed & KEY_A || keysPressed & KEY_DLEFT) +				event.type = Common::EVENT_LBUTTONDOWN; +			else +				event.type = Common::EVENT_LBUTTONUP; +			pushEventQueue(eventQueue, event); +		} +		if (keysPressed & KEY_X || keysPressed & KEY_DUP || keysReleased & KEY_X || keysReleased & KEY_DUP) { +			// SIMULATE RIGHT CLICK +			event.mouse.x = lastTouch.px; +			event.mouse.y = lastTouch.py; +			if (keysPressed & KEY_X || keysPressed & KEY_DUP) +				event.type = Common::EVENT_RBUTTONDOWN; +			else +				event.type = Common::EVENT_RBUTTONUP; +			pushEventQueue(eventQueue, event); +		} +		if (keysPressed & KEY_L) { +			event.type = Common::EVENT_VIRTUAL_KEYBOARD; +			pushEventQueue(eventQueue, event); +		} +		if (keysPressed & KEY_START) { +			event.type = Common::EVENT_MAINMENU; +			pushEventQueue(eventQueue, event); +		} +		if (keysPressed & KEY_SELECT) { +			if (!optionMenuOpened) +				optionMenuOpening = true; +		} +		if (keysPressed & KEY_B || keysReleased & KEY_B || keysPressed & KEY_DDOWN || keysReleased & KEY_DDOWN) { +			if (keysPressed & KEY_B || keysPressed & KEY_DDOWN) +				event.type = Common::EVENT_KEYDOWN; +			else +				event.type = Common::EVENT_KEYUP; +			event.kbd.keycode = Common::KEYCODE_ESCAPE; +			event.kbd.ascii = Common::ASCII_ESCAPE; +			event.kbd.flags = 0; +			pushEventQueue(eventQueue, event); +		} +		 +		// TODO: EVENT_PREDICTIVE_DIALOG +		// EVENT_SCREEN_CHANGED +	} +} + +static void aptHookFunc(APT_HookType hookType, void *param) { +	OSystem_3DS *osys = (OSystem_3DS *)g_system; +	 +	switch (hookType) { +		case APTHOOK_ONSUSPEND: +		case APTHOOK_ONSLEEP: +			if (g_engine) +				g_engine->pauseEngine(true); +			osys->sleeping = true; +			if (R_SUCCEEDED(gspLcdInit())) { +				GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH); +				gspLcdExit(); +			} +			break; +		case APTHOOK_ONRESTORE: +		case APTHOOK_ONWAKEUP: +			if (g_engine) +				g_engine->pauseEngine(false); +			osys->sleeping = false; +			loadConfig(); +			break; +		default: { +			Common::StackLock lock(*eventMutex); +			Common::Event event; +			event.type = Common::EVENT_QUIT; +			g_system->getEventManager()->pushEvent(event); +		} +	} +} + +static void timerThreadFunc(void *arg) { +	OSystem_3DS *osys = (OSystem_3DS *)arg; +	DefaultTimerManager *tm = (DefaultTimerManager *)osys->getTimerManager(); +	while (!osys->exiting) { +		g_system->delayMillis(10); +		tm->handler(); +	} +} + +void OSystem_3DS::initEvents() { +	eventMutex = new Common::Mutex(); +	s32 prio = 0; +	svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); +	_timerThread = threadCreate(&timerThreadFunc, this, 32 * 1024, prio - 1, -2, false); +	_eventThread = threadCreate(&eventThreadFunc, &_eventQueue, 32 * 1024, prio - 1, -2, false); +	 +	aptHook(&cookie, aptHookFunc, this); +} + +void OSystem_3DS::destroyEvents() { +	threadJoin(_timerThread, U64_MAX); +	threadFree(_timerThread); + +	threadJoin(_eventThread, U64_MAX); +	threadFree(_eventThread); +	delete eventMutex; +} + +void OSystem_3DS::transformPoint(touchPosition &point) { +	if (!_overlayVisible) { +		point.px = static_cast<float>(point.px) / _gameBottomTexture.getScaleX() - _gameBottomX; +		point.py = static_cast<float>(point.py) / _gameBottomTexture.getScaleY() - _gameBottomY; +	} +} + +void OSystem_3DS::displayMessageOnOSD(const char *msg) { +	messageOSD = msg; +	showMessageOSD = true; +} + +bool OSystem_3DS::pollEvent(Common::Event &event) { +	if (showMessageOSD) { +		showMessageOSD = false; +		StatusMessageDialog dialog(messageOSD, 800); +		dialog.runModal(); +	} +	 +	aptMainLoop(); // Call apt hook when necessary + +	if (optionMenuOpening) { +		optionMenuOpening = false; +		OptionsDialog dialog; +		if (g_engine) +			g_engine->pauseEngine(true); +		dialog.runModal(); +		if (g_engine) +			g_engine->pauseEngine(false); +	} +	 +	Common::StackLock lock(*eventMutex); +	 +	if (_eventQueue.empty()) +		return false; +	 +	event = _eventQueue.pop(); +	return true; +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem-graphics.cpp b/backends/platform/3ds/osystem-graphics.cpp new file mode 100644 index 0000000000..0cfd70c9cd --- /dev/null +++ b/backends/platform/3ds/osystem-graphics.cpp @@ -0,0 +1,517 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This _program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This _program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this _program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + */ + +#include "backends/platform/3ds/osystem.h" +#include "backends/platform/3ds/shader_shbin.h" +#include "common/rect.h" +#include "options-dialog.h" +#include "config.h" + +// Used to transfer the final rendered display to the framebuffer +#define DISPLAY_TRANSFER_FLAGS                                                 \ +	(GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) |                     \ +	 GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) |  \ +	 GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) |                            \ +	 GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) + +namespace _3DS { +	 +void OSystem_3DS::initGraphics() { +	_pfGame = Graphics::PixelFormat::createFormatCLUT8(); +	_pfGameTexture = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + +	C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + +	// Initialize the render targets +	_renderTargetTop = +	    C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); +	C3D_RenderTargetSetClear(_renderTargetTop, C3D_CLEAR_ALL, 0x0000000, 0); +	C3D_RenderTargetSetOutput(_renderTargetTop, GFX_TOP, GFX_LEFT, +	                          DISPLAY_TRANSFER_FLAGS); + +	_renderTargetBottom = +	    C3D_RenderTargetCreate(240, 320, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); +	C3D_RenderTargetSetClear(_renderTargetBottom, C3D_CLEAR_ALL, 0x00000000, 0); +	C3D_RenderTargetSetOutput(_renderTargetBottom, GFX_BOTTOM, GFX_LEFT, +	                          DISPLAY_TRANSFER_FLAGS); + +	// Load and bind simple default shader (shader.v.pica) +	_dvlb = DVLB_ParseFile((u32*)shader_shbin, shader_shbin_size); +	shaderProgramInit(&_program); +	shaderProgramSetVsh(&_program, &_dvlb->DVLE[0]); +	C3D_BindProgram(&_program); +	 +	_projectionLocation = shaderInstanceGetUniformLocation(_program.vertexShader, "projection"); +	_modelviewLocation = shaderInstanceGetUniformLocation(_program.vertexShader, "modelView"); +	 +	C3D_AttrInfo *attrInfo = C3D_GetAttrInfo(); +	AttrInfo_Init(attrInfo); +	AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position +	AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord +	 +	Mtx_OrthoTilt(&_projectionTop, 0.0, 400.0, 240.0, 0.0, 0.0, 1.0); +	Mtx_OrthoTilt(&_projectionBottom, 0.0, 320.0, 240.0, 0.0, 0.0, 1.0); +	 +	C3D_TexEnv *env = C3D_GetTexEnv(0); +	C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, 0, 0); +	C3D_TexEnvOp(env, C3D_Both, 0, 0, 0); +	C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); +	 +	C3D_DepthTest(false, GPU_GEQUAL, GPU_WRITE_ALL); +	C3D_CullFace(GPU_CULL_NONE); +} + +void OSystem_3DS::destroyGraphics() { +	_gameScreen.free(); +	_gameTopTexture.free(); +	_gameBottomTexture.free(); +	_overlay.free(); + +	shaderProgramFree(&_program); +	DVLB_Free(_dvlb); + +	C3D_RenderTargetDelete(_renderTargetTop); +	C3D_RenderTargetDelete(_renderTargetBottom); + +	C3D_Fini(); +} + +bool OSystem_3DS::hasFeature(OSystem::Feature f) { +	return (f == OSystem::kFeatureCursorPalette || +	        f == OSystem::kFeatureOverlaySupportsAlpha); +} + +void OSystem_3DS::setFeatureState(OSystem::Feature f, bool enable) { +	switch (f) { +	case OSystem::kFeatureCursorPalette: +		_cursorPaletteEnabled = enable; +		flushCursor(); +		break; +	default: +		break; +	} +} + +bool OSystem_3DS::getFeatureState(OSystem::Feature f) { +	switch (f) { +	case OSystem::kFeatureCursorPalette: +		return _cursorPaletteEnabled; +	default: +		return false; +	} +} + +const OSystem::GraphicsMode * +OSystem_3DS::getSupportedGraphicsModes() const { +	return s_graphicsModes; +} + +int OSystem_3DS::getDefaultGraphicsMode() const { +	return GFX_LINEAR; +} + +bool OSystem_3DS::setGraphicsMode(int mode) { +	return true; +} + +void OSystem_3DS::resetGraphicsScale() { +	debug("resetGraphicsScale"); +} + +int OSystem_3DS::getGraphicsMode() const { +	return GFX_LINEAR; +} +void OSystem_3DS::initSize(uint width, uint height, +                                   const Graphics::PixelFormat *format) { +	debug("3ds initsize w:%d h:%d", width, height); +	_gameWidth = width; +	_gameHeight = height; +	_gameTopTexture.create(width, height, _pfGameTexture); +	_overlay.create(getOverlayWidth(), getOverlayHeight(), _pfGameTexture); +	 +	if (format) { +		debug("pixelformat: %d %d %d %d %d", format->bytesPerPixel, format->rBits(), format->gBits(), format->bBits(), format->aBits());; +		_pfGame = *format; +	} + +	_gameScreen.create(width, height, _pfGame); +	 +	_focusDirty = true; +	_focusRect = Common::Rect(_gameWidth, _gameHeight); + +	updateSize(); +} + +void OSystem_3DS::updateSize() { +	if (config.stretchToFit) { +		_gameTopX = _gameTopY = _gameBottomX = _gameBottomY = 0; +		_gameTopTexture.setScale(400.f / _gameWidth, 240.f / _gameHeight); +		_gameBottomTexture.setScale(320.f / _gameWidth, 240.f / _gameHeight); +	} else { +		float ratio = static_cast<float>(_gameWidth) / _gameHeight; +		 +		if (ratio > 400.f / 240.f) { +			float r = 400.f / _gameWidth; +			_gameTopTexture.setScale(r, r); +			_gameTopX = 0; +			_gameTopY = (240.f - r * _gameHeight) / 2.f; +		} else { +			float r = 240.f / _gameHeight; +			_gameTopTexture.setScale(r, r); +			_gameTopY = 0; +			_gameTopX = (400.f - r * _gameWidth) / 2.f; +		} +		if (ratio > 320.f / 240.f) { +			float r = 320.f / _gameWidth; +			_gameBottomTexture.setScale(r, r); +			_gameBottomX = 0; +			_gameBottomY = (240.f - r * _gameHeight) / 2.f; +		} else { +			float r = 240.f / _gameHeight; +			_gameBottomTexture.setScale(r, r); +			_gameBottomY = 0; +			_gameBottomX = (320.f - r * _gameWidth) / 2.f; +		} +	} +	_gameTopTexture.setPosition(_gameTopX, _gameTopY); +	_gameBottomTexture.setPosition(_gameBottomX, _gameBottomY); +	if (_overlayVisible) +		_cursorTexture.setScale(1.f, 1.f); +	else if (config.screen == kScreenTop) +		_cursorTexture.setScale(_gameTopTexture.getScaleX(), _gameTopTexture.getScaleY()); +	else +		_cursorTexture.setScale(_gameBottomTexture.getScaleX(), _gameBottomTexture.getScaleY()); +} + +Common::List<Graphics::PixelFormat> OSystem_3DS::getSupportedFormats() const { +	Common::List<Graphics::PixelFormat> list; +	list.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); // GPU_RGBA8 +	list.push_back(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); // GPU_RGB565 +// 		list.push_back(Graphics::PixelFormat(3, 0, 0, 0, 8, 0, 8, 16, 0)); // GPU_RGB8 +	list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); // RGB555 (needed for FMTOWNS?) +	list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)); // GPU_RGBA5551 +	list.push_back(Graphics::PixelFormat::createFormatCLUT8()); +	return list; +} + +void OSystem_3DS::beginGFXTransaction() { +	// +} +OSystem::TransactionError OSystem_3DS::endGFXTransaction() { +	return OSystem::kTransactionSuccess; +} + +void OSystem_3DS::setPalette(const byte *colors, uint start, uint num) { +	assert(start + num <= 256); +	memcpy(_palette + 3 * start, colors, 3 * num); +	 +	// Manually update all color that were changed +	if (_gameScreen.format.bytesPerPixel == 1) { +		flushGameScreen(); +	} +} +void OSystem_3DS::grabPalette(byte *colors, uint start, uint num) { +	assert(start + num <= 256); +	memcpy(colors, _palette + 3 * start, 3 * num); +} + +void OSystem_3DS::copyRectToScreen(const void *buf, int pitch, int x, +                                           int y, int w, int h) { +	Common::Rect rect(x, y, x+w, y+h); +	_gameScreen.copyRectToSurface(buf, pitch, x, y, w, h); +	Graphics::Surface subSurface = _gameScreen.getSubArea(rect); +	 +	Graphics::Surface *convertedSubSurface = subSurface.convertTo(_pfGameTexture, _palette); +	_gameTopTexture.copyRectToSurface(*convertedSubSurface, x, y, Common::Rect(w, h)); +	 +	convertedSubSurface->free(); +	delete convertedSubSurface; +	_gameTopTexture.markDirty(); +} + +void OSystem_3DS::flushGameScreen() { +	Graphics::Surface *converted = _gameScreen.convertTo(_pfGameTexture, _palette); +	_gameTopTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h)); +	_gameTopTexture.markDirty(); +	converted->free(); +	delete converted; +} + +Graphics::Surface *OSystem_3DS::lockScreen() { +	return &_gameScreen; +} +void OSystem_3DS::unlockScreen() { +	flushGameScreen(); +} + +void OSystem_3DS::updateScreen() { + +	if (sleeping || exiting) +		return; +	 +// 	updateFocus(); + +	C3D_FrameBegin(C3D_FRAME_SYNCDRAW); +		// Render top screen +		C3D_FrameDrawOn(_renderTargetTop); +		if (config.screen == kScreenTop || config.screen == kScreenBoth) { +			C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionTop); +			C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _gameTopTexture.getMatrix()); +			_gameTopTexture.render(); +			_gameTopTexture.render(); +			if (_overlayVisible && config.screen == kScreenTop) { +				C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _overlay.getMatrix()); +				_overlay.render(); +			} +			if (_cursorVisible && config.showCursor && config.screen == kScreenTop) { +				C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _cursorTexture.getMatrix()); +				_cursorTexture.render(); +			} +		} +		 +		// Render bottom screen +		C3D_FrameDrawOn(_renderTargetBottom); +		if (config.screen == kScreenBottom || config.screen == kScreenBoth) { +			C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionBottom); +			C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _gameBottomTexture.getMatrix()); +			_gameTopTexture.render(); +			_gameTopTexture.render(); +			if (_overlayVisible) { +				C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _overlay.getMatrix()); +				_overlay.render(); +			} +			if (_cursorVisible && config.showCursor) { +				C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _cursorTexture.getMatrix()); +				_cursorTexture.render(); +			} +		} +	C3D_FrameEnd(0); +} + +void OSystem_3DS::setShakePos(int shakeOffset) { +	// TODO: implement this in overlay, top screen, and mouse too +	_screenShakeOffset = shakeOffset; +	_gameTopTexture.setPosition(_gameTopX, _gameTopY + _gameTopTexture.getScaleY() * shakeOffset); +	_gameBottomTexture.setPosition(_gameBottomX, _gameBottomY + _gameBottomTexture.getScaleY() * shakeOffset); +} + +void OSystem_3DS::setFocusRectangle(const Common::Rect &rect) { +	debug("setfocus: %d %d %d %d", rect.left, rect.top, rect.width(), rect.height()); +	_focusRect = rect; +	_focusDirty = true; +	_focusClearTime = 0; +} + +void OSystem_3DS::clearFocusRectangle() { +	_focusClearTime = getMillis(); +} + +void OSystem_3DS::updateFocus() { +	 +	if (_focusClearTime && getMillis() - _focusClearTime > 5000) { +		_focusClearTime = 0; +		_focusDirty = true; +		_focusRect = Common::Rect(_gameWidth, _gameHeight); +	} + +	if (_focusDirty) { +		float duration = 1.f / 20.f; // Focus animation in frame duration +		float w = 400.f; +		float h = 240.f; +		float ratio = _focusRect.width() / _focusRect.height(); +		if (ratio > w/h) { +			_focusTargetScaleX = w / _focusRect.width(); +			float newHeight = (float)_focusRect.width() / w/h; +			_focusTargetScaleY = h / newHeight; +			_focusTargetPosX = _focusTargetScaleX * _focusRect.left; +			_focusTargetPosY = _focusTargetScaleY * ((float)_focusRect.top - (newHeight - _focusRect.height())/2.f); +		} else { +			_focusTargetScaleY = h / _focusRect.height(); +			float newWidth = (float)_focusRect.height() * w/h; +			_focusTargetScaleX = w / newWidth; +			_focusTargetPosY = _focusTargetScaleY * _focusRect.top; +			_focusTargetPosX = _focusTargetScaleX * ((float)_focusRect.left - (newWidth - _focusRect.width())/2.f); +		} +		if (_focusTargetPosX < 0 && _focusTargetScaleY != 240.f / _gameHeight) +			_focusTargetPosX = 0; +		if (_focusTargetPosY < 0 && _focusTargetScaleX != 400.f / _gameWidth) +			_focusTargetPosY = 0; +		_focusStepPosX = duration * (_focusTargetPosX - _focusPosX); +		_focusStepPosY = duration * (_focusTargetPosY - _focusPosY); +		_focusStepScaleX = duration * (_focusTargetScaleX - _focusScaleX); +		_focusStepScaleY = duration * (_focusTargetScaleY - _focusScaleY); +	} +	 +	if (_focusDirty || _focusPosX != _focusTargetPosX || _focusPosY != _focusTargetPosY || +			_focusScaleX != _focusTargetScaleX || _focusScaleY != _focusTargetScaleY) { +		_focusDirty = false; +	 +		if ((_focusStepPosX > 0 && _focusPosX > _focusTargetPosX) || (_focusStepPosX < 0 && _focusPosX < _focusTargetPosX)) +			_focusPosX = _focusTargetPosX; +		else if (_focusPosX != _focusTargetPosX) +			_focusPosX += _focusStepPosX; +		 +		if ((_focusStepPosY > 0 && _focusPosY > _focusTargetPosY) || (_focusStepPosY < 0 && _focusPosY < _focusTargetPosY)) +			_focusPosY = _focusTargetPosY; +		else if (_focusPosY != _focusTargetPosY) +			_focusPosY += _focusStepPosY; +		 +		if ((_focusStepScaleX > 0 && _focusScaleX > _focusTargetScaleX) || (_focusStepScaleX < 0 && _focusScaleX < _focusTargetScaleX)) +			_focusScaleX = _focusTargetScaleX; +		else if (_focusScaleX != _focusTargetScaleX) +			_focusScaleX += _focusStepScaleX; +		 +		if ((_focusStepScaleY > 0 && _focusScaleY > _focusTargetScaleY) || (_focusStepScaleY < 0 && _focusScaleY < _focusTargetScaleY)) +			_focusScaleY = _focusTargetScaleY; +		else if (_focusScaleY != _focusTargetScaleY) +			_focusScaleY += _focusStepScaleY; + +		Mtx_Identity(&_focusMatrix); +		Mtx_Translate(&_focusMatrix, -_focusPosX, -_focusPosY, 0); +		Mtx_Scale(&_focusMatrix, _focusScaleX, _focusScaleY, 1.f); +	} +} + +void OSystem_3DS::showOverlay() { +	_overlayVisible = true; +	updateSize(); +	updateScreen(); +} + +void OSystem_3DS::hideOverlay() { +	_overlayVisible = false; +	updateSize(); +	updateScreen(); +} + +Graphics::PixelFormat OSystem_3DS::getOverlayFormat() const { +	return _pfGameTexture; +} + +void OSystem_3DS::clearOverlay() { +	_overlay.clear(); +} + +void OSystem_3DS::grabOverlay(void *buf, int pitch) { +	for (int y = 0; y < getOverlayHeight(); ++y) { +		memcpy(buf, _overlay.getBasePtr(0, y), pitch); +	} +} + +void OSystem_3DS::copyRectToOverlay(const void *buf, int pitch, int x, +                                            int y, int w, int h) { +	_overlay.copyRectToSurface(buf, pitch, x, y, w, h); +	_overlay.markDirty(); +} + +int16 OSystem_3DS::getOverlayHeight() { +	return 240; +} + +int16 OSystem_3DS::getOverlayWidth() { +	return 320; +} + +bool OSystem_3DS::showMouse(bool visible) { +	_cursorVisible = visible; +	flushCursor(); +	return !visible; +} + +void OSystem_3DS::warpMouse(int x, int y) { +	_cursorX = x; +	_cursorY = y; +	warning("x:%d y:%d", x, y); +	// TODO: adjust for _cursorScalable ? +	int offsetx = 0; +	int offsety = 0; +	x -= _cursorHotspotX; +	y -= _cursorHotspotY; +	if (!_overlayVisible) { +		offsetx += config.screen == kScreenTop ? _gameTopX : _gameBottomX; +		offsety += config.screen == kScreenTop ? _gameTopY : _gameBottomY; +	} +	float scalex = config.screen == kScreenTop ? (float)_gameTopTexture.actualWidth / _gameWidth : 1.f; +	float scaley = config.screen == kScreenTop ? (float)_gameTopTexture.actualHeight / _gameHeight : 1.f; +	_cursorTexture.setPosition(scalex * x + offsetx, +							   scaley * y + offsety); +} + +void OSystem_3DS::setCursorDelta(float deltaX, float deltaY) { +	_cursorDeltaX = deltaX; +	_cursorDeltaY = deltaY; +} + +void OSystem_3DS::setMouseCursor(const void *buf, uint w, uint h, +                                         int hotspotX, int hotspotY, +                                         uint32 keycolor, bool dontScale, +                                         const Graphics::PixelFormat *format) { +	_cursorScalable = !dontScale; +	_cursorHotspotX = hotspotX; +	_cursorHotspotY = hotspotY; +	_cursorKeyColor = keycolor; +	_pfCursor = !format ? Graphics::PixelFormat::createFormatCLUT8() : *format; + +	if (w != _cursor.w || h != _cursor.h || _cursor.format != _pfCursor) { +		_cursor.create(w, h, _pfCursor); +		_cursorTexture.create(w, h, _pfGameTexture); +	} +	 +	_cursor.copyRectToSurface(buf, w, 0, 0, w, h); +	flushCursor(); +	 +	warpMouse(_cursorX, _cursorY); +} + +void OSystem_3DS::setCursorPalette(const byte *colors, uint start, uint num) { +	assert(start + num <= 256); +	memcpy(_cursorPalette + 3 * start, colors, 3 * num); +	_cursorPaletteEnabled = true; +	flushCursor(); +} + +void OSystem_3DS::flushCursor() { +	if (_cursor.getPixels()) { +		Graphics::Surface *converted = _cursor.convertTo(_pfGameTexture, _cursorPaletteEnabled ? _cursorPalette : _palette); +		_cursorTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h)); +		_cursorTexture.markDirty(); +		converted->free(); +		delete converted; +	 +		if (_pfCursor.bytesPerPixel == 1) { +			uint* dest = (uint*) _cursorTexture.getPixels(); +			byte* src = (byte*) _cursor.getPixels(); +			for (int y = 0; y < _cursor.h; ++y) { +				for (int x = 0; x < _cursor.w; ++x) { +					if (*src++ == _cursorKeyColor) +						*dest++ = 0; +					else +						dest++; +				} +				dest += _cursorTexture.w - _cursorTexture.actualWidth; +			} +		} +	} +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem.cpp b/backends/platform/3ds/osystem.cpp new file mode 100644 index 0000000000..f6278eb16b --- /dev/null +++ b/backends/platform/3ds/osystem.cpp @@ -0,0 +1,193 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#define FORBIDDEN_SYMBOL_EXCEPTION_printf +#define FORBIDDEN_SYMBOL_EXCEPTION_time_h +#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h + +#include "osystem.h" + +#include "backends/saves/default/default-saves.h" +#include "backends/timer/default/default-timer.h" +#include "backends/events/default/default-events.h" +#include "audio/mixer_intern.h" +#include "common/scummsys.h" +#include "common/config-manager.h" +#include "common/str.h" +#include "config.h" + +#include "backends/fs/posix/posix-fs-factory.h" +#include "backends/fs/posix/posix-fs.h" +#include <unistd.h> +#include <time.h> + +namespace _3DS { + +OSystem_3DS::OSystem_3DS(): +	_focusDirty(true), +	_focusRect(Common::Rect(1, 1)), +	_focusPosX(0), +	_focusPosY(0), +	_focusTargetPosX(0), +	_focusTargetPosY(0), +	_focusStepPosX(0), +	_focusStepPosY(0), +	_focusScaleX(1.f), +	_focusScaleY(1.f), +	_focusTargetScaleX(1.f), +	_focusTargetScaleY(1.f), +	_focusStepScaleX(0.f), +	_focusStepScaleY(0.f), +	_focusClearTime(0), +	_cursorPaletteEnabled(false), +	_cursorVisible(false), +	_cursorScalable(false), +	_cursorX(0), +	_cursorY(0), +	_cursorHotspotX(0), +	_cursorHotspotY(0), +	_gameTopX(0), +	_gameTopY(0), +	_gameBottomX(0), +	_gameBottomY(0), +	_gameWidth(320), +	_gameHeight(240), +	_overlayVisible(false), +	exiting(false), +	sleeping(false) +{ +	chdir("sdmc:/"); +	_fsFactory = new POSIXFilesystemFactory(); +	Posix::assureDirectoryExists("/3ds/scummvm/saves/"); +} + +OSystem_3DS::~OSystem_3DS() { +	exiting = true; +	destroyEvents(); +	destroyAudio(); +	destroyGraphics(); +	 +	delete _timerManager; +	_timerManager = 0; +} + +void OSystem_3DS::quit() { +	printf("OSystem_3DS::quit()\n"); +} + +void OSystem_3DS::initBackend() { +	loadConfig(); +	ConfMan.registerDefault("fullscreen", true); +	ConfMan.registerDefault("aspect_ratio", true); +	if (!ConfMan.hasKey("vkeybd_pack_name")) +		ConfMan.set("vkeybd_pack_name", "vkeybd_small"); +	if (!ConfMan.hasKey("vkeybdpath")) +		ConfMan.set("vkeybdpath", "/3ds/scummvm/kb"); +	if (!ConfMan.hasKey("themepath")) +		ConfMan.set("themepath", "/3ds/scummvm"); +	if (!ConfMan.hasKey("gui_theme")) +		ConfMan.set("gui_theme", "builtin"); + +	_timerManager = new DefaultTimerManager(); +	_savefileManager = new DefaultSaveFileManager("/3ds/scummvm/saves/"); +	 +	initGraphics(); +	initAudio(); +	initEvents(); +	EventsBaseBackend::initBackend(); +} + +void OSystem_3DS::updateConfig() { +	if (_gameScreen.getPixels()) { +		updateSize(); +		warpMouse(_cursorX, _cursorY); +	} +} + +Common::String OSystem_3DS::getDefaultConfigFileName() { +	return "/3ds/scummvm/scummvm.ini"; +} + +uint32 OSystem_3DS::getMillis(bool skipRecord) { +	return svcGetSystemTick() / TICKS_PER_MSEC; +} + +void OSystem_3DS::delayMillis(uint msecs) { +	svcSleepThread(msecs * 1000000); +} + +void OSystem_3DS::getTimeAndDate(TimeDate& td) const { +	time_t curTime = time(0); +	struct tm t = *localtime(&curTime); +	td.tm_sec = t.tm_sec; +	td.tm_min = t.tm_min; +	td.tm_hour = t.tm_hour; +	td.tm_mday = t.tm_mday; +	td.tm_mon = t.tm_mon; +	td.tm_year = t.tm_year; +	td.tm_wday = t.tm_wday; +} + +OSystem::MutexRef OSystem_3DS::createMutex() { +	RecursiveLock *mutex = new RecursiveLock(); +	RecursiveLock_Init(mutex); +	return (OSystem::MutexRef) mutex; +} +void OSystem_3DS::lockMutex(MutexRef mutex) { +	RecursiveLock_Lock((RecursiveLock*)mutex); +} +void OSystem_3DS::unlockMutex(MutexRef mutex) { +	RecursiveLock_Unlock((RecursiveLock*)mutex); +} +void OSystem_3DS::deleteMutex(MutexRef mutex) { +	delete (RecursiveLock*)mutex; +} + +Common::String OSystem_3DS::getSystemLanguage() const { +	u8 langcode; +	CFGU_GetSystemLanguage(&langcode); +	switch (langcode) { +		case CFG_LANGUAGE_JP: return "ja_JP"; +		case CFG_LANGUAGE_EN: return "en_US"; +		case CFG_LANGUAGE_FR: return "fr_FR"; +		case CFG_LANGUAGE_DE: return "de_DE"; +		case CFG_LANGUAGE_IT: return "it_IT"; +		case CFG_LANGUAGE_ES: return "es_ES"; +		case CFG_LANGUAGE_ZH: return "zh_CN"; +		case CFG_LANGUAGE_KO: return "ko_KR"; +		case CFG_LANGUAGE_NL: return "nl_NL"; +		case CFG_LANGUAGE_PT: return "pt_BR"; +		case CFG_LANGUAGE_RU: return "ru_RU"; +		case CFG_LANGUAGE_TW: return "zh_HK"; +		default:              return "en_US"; +	} +} + +void OSystem_3DS::fatalError() { +	printf("FatalError!\n"); +} + +void OSystem_3DS::logMessage(LogMessageType::Type type, const char *message) { +	printf("3DS log: %s\n", message); +} + +} // namespace _3DS diff --git a/backends/platform/3ds/osystem.h b/backends/platform/3ds/osystem.h new file mode 100644 index 0000000000..478085acba --- /dev/null +++ b/backends/platform/3ds/osystem.h @@ -0,0 +1,221 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PLATFORM_3DS_H +#define PLATFORM_3DS_H + +#include <citro3d.h> +#include "backends/mutex/mutex.h" +#include "backends/base-backend.h" +#include "graphics/palette.h" +#include "base/main.h" +#include "audio/mixer_intern.h" +#include "backends/graphics/graphics.h" +#include "backends/platform/3ds/sprite.h" +#include "common/rect.h" +#include "common/queue.h" + +#define TICKS_PER_MSEC 268123 + +namespace _3DS { + +enum { +	GFX_LINEAR = 0, +	GFX_NEAREST = 1 +}; + +enum InputMode { +	MODE_HOVER, +	MODE_DRAG, +}; + +static const OSystem::GraphicsMode s_graphicsModes[] = { +	{"default", "Default Test", GFX_LINEAR}, +	{ 0, 0, 0 } +}; + +class OSystem_3DS : public EventsBaseBackend, public PaletteManager { +public: +	OSystem_3DS(); +	virtual ~OSystem_3DS(); +	 +	volatile bool exiting; +	volatile bool sleeping; + +	virtual void initBackend(); +	 +	virtual bool hasFeature(OSystem::Feature f); +	virtual void setFeatureState(OSystem::Feature f, bool enable); +	virtual bool getFeatureState(OSystem::Feature f); + +	virtual bool pollEvent(Common::Event &event); + +	virtual uint32 getMillis(bool skipRecord = false); +	virtual void delayMillis(uint msecs); +	virtual void getTimeAndDate(TimeDate &t) const; +	 +	virtual MutexRef createMutex(); +	virtual void lockMutex(MutexRef mutex); +	virtual void unlockMutex(MutexRef mutex); +	virtual void deleteMutex(MutexRef mutex); + +	virtual void logMessage(LogMessageType::Type type, const char *message); +	 +	virtual Audio::Mixer *getMixer(); +	virtual PaletteManager *getPaletteManager() { return this; } +	virtual Common::String getSystemLanguage() const; +	virtual void fatalError(); +	virtual void quit(); +	 +	virtual Common::String getDefaultConfigFileName(); +	 +	// Graphics +	virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const; +	int getDefaultGraphicsMode() const; +	bool setGraphicsMode(int mode); +	void resetGraphicsScale(); +	int getGraphicsMode() const; +	inline Graphics::PixelFormat getScreenFormat() const { return _pfGame; } +	virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const; +	void initSize(uint width, uint height, +	              const Graphics::PixelFormat *format = NULL); +	virtual int getScreenChangeID() const { return 0; }; + +	void beginGFXTransaction(); +	OSystem::TransactionError endGFXTransaction(); +	int16 getHeight(){ return _gameHeight; } +	int16 getWidth(){ return _gameWidth; } +	void setPalette(const byte *colors, uint start, uint num); +	void grabPalette(byte *colors, uint start, uint num); +	void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, +	                      int h); +	Graphics::Surface *lockScreen(); +	void unlockScreen(); +	void updateScreen(); +	void setShakePos(int shakeOffset); +	void setFocusRectangle(const Common::Rect &rect); +	void clearFocusRectangle(); +	void showOverlay(); +	void hideOverlay(); +	Graphics::PixelFormat getOverlayFormat() const; +	void clearOverlay(); +	void grabOverlay(void *buf, int pitch); +	void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, +	                       int h); +	virtual int16 getOverlayHeight(); +	virtual int16 getOverlayWidth(); +	virtual void displayMessageOnOSD(const char *msg); + +	bool showMouse(bool visible); +	void warpMouse(int x, int y); +	void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, +	                    int hotspotY, uint32 keycolor, bool dontScale = false, +	                    const Graphics::PixelFormat *format = NULL); +	void setCursorPalette(const byte *colors, uint start, uint num); +	 +	// Transform point from touchscreen coords into gamescreen coords +	void transformPoint(touchPosition &point); +	 +	void setCursorDelta(float deltaX, float deltaY); +	 +	void updateFocus(); +	void updateConfig(); +	void updateSize(); +	 +private: +	void initGraphics(); +	void destroyGraphics(); +	void initAudio(); +	void destroyAudio(); +	void initEvents(); +	void destroyEvents(); +	 +	void flushGameScreen(); +	void flushCursor(); +	 +protected: +	Audio::MixerImpl *_mixer; +	 +private: +	u16 _gameWidth, _gameHeight; +	u16 _gameTopX, _gameTopY; +	u16 _gameBottomX, _gameBottomY; +	 +	// Audio +	Thread audioThread; + +	// Graphics +	Graphics::PixelFormat _pfGame; +	Graphics::PixelFormat _pfGameTexture; +	Graphics::PixelFormat _pfCursor; +	byte _palette[3 * 256]; +	byte _cursorPalette[3 * 256]; +	 +	Graphics::Surface _gameScreen; +	Sprite _gameTopTexture; +	Sprite _gameBottomTexture; +	Sprite _overlay; +	 +	int _screenShakeOffset; +	bool _overlayVisible; +	 +	DVLB_s *_dvlb; +	shaderProgram_s _program; +	int _projectionLocation; +	int _modelviewLocation; +	C3D_Mtx _projectionTop; +	C3D_Mtx _projectionBottom; +	C3D_RenderTarget* _renderTargetTop; +	C3D_RenderTarget* _renderTargetBottom; +	 +	// Focus +	Common::Rect _focusRect; +	bool _focusDirty; +	C3D_Mtx _focusMatrix; +	int _focusPosX, _focusPosY; +	int _focusTargetPosX, _focusTargetPosY; +	float _focusStepPosX, _focusStepPosY; +	float _focusScaleX, _focusScaleY; +	float _focusTargetScaleX, _focusTargetScaleY; +	float _focusStepScaleX, _focusStepScaleY; +	uint32 _focusClearTime; +	 +	// Events +	Thread _eventThread; +	Thread _timerThread; +	Common::Queue<Common::Event> _eventQueue; +	 +	// Cursor +	Graphics::Surface _cursor; +	Sprite _cursorTexture; +	bool _cursorPaletteEnabled; +	bool _cursorVisible; +	bool _cursorScalable; +	float _cursorX, _cursorY; +	float _cursorDeltaX, _cursorDeltaY; +	int _cursorHotspotX, _cursorHotspotY; +	uint32 _cursorKeyColor; +}; + +} // namespace _3DS + +#endif diff --git a/backends/platform/3ds/shader.v.pica b/backends/platform/3ds/shader.v.pica new file mode 100644 index 0000000000..2d18985622 --- /dev/null +++ b/backends/platform/3ds/shader.v.pica @@ -0,0 +1,59 @@ +;* ScummVM - Graphic Adventure Engine +;* +;* ScummVM is the legal property of its developers, whose names +;* are too numerous to list here. Please refer to the COPYRIGHT +;* file distributed with this source distribution. +;* +;* This program is free software; you can redistribute it and/or +;* modify it under the terms of the GNU General Public License +;* as published by the Free Software Foundation; either version 2 +;* of the License, or (at your option) any later version. +;* +;* This program is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +;* GNU General Public License for more details. +;* +;* You should have received a copy of the GNU General Public License +;* along with this program; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +;* + +; Uniforms +.fvec projection[4], modelView[4] + +; Constants +.constf myconst(0.0, 1.0, -1.0, 0.1) +.alias  zeros myconst.xxxx ; Vector full of zeros +.alias  ones  myconst.yyyy ; Vector full of ones + +; Outputs +.out outpos position +.out outtex texcoord0 + +; Inputs (defined as aliases for convenience) +.alias inpos v0 +.alias intex v1 + +.proc main +	; Force the w component of inpos to be 1.0 +	mov r0.xyz, inpos +	mov r0.w,   ones +	 +	; r1 = modelView * inpos +	dp4 r1.x, modelView[0], r0 +	dp4 r1.y, modelView[1], r0 +	dp4 r1.z, modelView[2], r0 +	dp4 r1.w, modelView[3], r0 + +	; outpos = projection * r1 +	dp4 outpos.x, projection[0], r1 +	dp4 outpos.y, projection[1], r1 +	dp4 outpos.z, projection[2], r1 +	dp4 outpos.w, projection[3], r1 + + 	mov outtex, intex + +	end +.end + diff --git a/backends/platform/3ds/sprite.cpp b/backends/platform/3ds/sprite.cpp new file mode 100644 index 0000000000..a0aee385a9 --- /dev/null +++ b/backends/platform/3ds/sprite.cpp @@ -0,0 +1,144 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/platform/3ds/sprite.h" +#include "common/util.h" +#include <3ds.h> + +static uint nextHigher2(uint v) { +	if (v == 0) +		return 1; +	v--; +	v |= v >> 1; +	v |= v >> 2; +	v |= v >> 4; +	v |= v >> 8; +	v |= v >> 16; +	return ++v; +} + +Sprite::Sprite() +	: dirtyPixels(true) +	, dirtyMatrix(true) +	, actualWidth(0) +	, actualHeight(0) +	, posX(0) +	, posY(0) +	, scaleX(1.f) +	, scaleY(1.f) +{ +	Mtx_Identity(&modelview); + +	vertices = (vertex *)linearAlloc(sizeof(vertex) * 4); +} + +Sprite::~Sprite() { +	// +} + +void Sprite::create(uint16 width, uint16 height, const Graphics::PixelFormat &f) { +	free(); +	 +	actualWidth = width; +	actualHeight = height; +	format = f; +	w = MAX(nextHigher2(width), 64u); +	h = MAX(nextHigher2(height), 64u);; +	pitch = w * format.bytesPerPixel; +	dirtyPixels = true; + +	if (width && height) { +		pixels = linearAlloc(h * pitch); +		C3D_TexInit(&texture, w, h, GPU_RGBA8); +		C3D_TexSetFilter(&texture, GPU_LINEAR, GPU_LINEAR); +		assert(pixels && texture.data); +		clear(); +	} +	 +	float x = 0.f, y = 0.f; +	float u = (float)width/w; +	float v = (float)height/h; +	vertex tmp[4] = { +		{{x,       y,        0.5f}, {0, 0}}, +		{{x+width, y,        0.5f}, {u, 0}}, +		{{x,       y+height, 0.5f}, {0, v}}, +		{{x+width, y+height, 0.5f}, {u, v}}, +	}; +	memcpy(vertices, tmp, sizeof(vertex) * 4); +} + + +void Sprite::free() { +	linearFree(vertices); +	linearFree(pixels); +	C3D_TexDelete(&texture); +	pixels = 0; +	w = h = pitch = 0; +	actualWidth = actualHeight = 0; +	format = Graphics::PixelFormat(); +} + +void Sprite::convertToInPlace(const Graphics::PixelFormat &dstFormat, const byte *palette) { +	// +} + +void Sprite::render() { +	if (dirtyPixels) { +		dirtyPixels = false; +		GSPGPU_FlushDataCache(pixels, w * h * format.bytesPerPixel); +		C3D_SafeDisplayTransfer((u32*)pixels, GX_BUFFER_DIM(w, h), (u32*)texture.data, GX_BUFFER_DIM(w, h), TEXTURE_TRANSFER_FLAGS); +		gspWaitForPPF(); +	} +	C3D_TexBind(0, &texture); + +	C3D_BufInfo *bufInfo = C3D_GetBufInfo(); +	BufInfo_Init(bufInfo); +	BufInfo_Add(bufInfo, vertices, sizeof(vertex), 2, 0x10); +	C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4); +} + +void Sprite::clear(uint32 color) { +	dirtyPixels = true; +	memset(pixels, color, w * h * format.bytesPerPixel); +} + +void Sprite::setScale (float x, float y) { +	scaleX = x; +	scaleY = y; +	dirtyMatrix = true; +} + +void Sprite::setPosition(int x, int y) { +	posX = x; +	posY = y; +	dirtyMatrix = true; +} + +C3D_Mtx* Sprite::getMatrix() { +	if (dirtyMatrix) { +		dirtyMatrix = false; +		Mtx_Identity(&modelview); +		Mtx_Scale(&modelview, scaleX, scaleY, 1.f); +		Mtx_Translate(&modelview, posX, posY, 0); +	} +	return &modelview; +} diff --git a/backends/platform/3ds/sprite.h b/backends/platform/3ds/sprite.h new file mode 100644 index 0000000000..6d88ae4ce1 --- /dev/null +++ b/backends/platform/3ds/sprite.h @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GRAPHICS_SPRITE_3DS_H +#define GRAPHICS_SPRITE_3DS_H + +#include <citro3d.h> +#include "graphics/surface.h" + +#define TEXTURE_TRANSFER_FLAGS \ +	(GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | \ +	GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) | \ +	GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) + +typedef struct { +	float position[3]; +	float texcoord[2]; +} vertex; + +class Sprite : public Graphics::Surface { +public: +	Sprite(); +	~Sprite(); +	void create(uint16 width, uint16 height, const Graphics::PixelFormat &format); +	void free(); +	void convertToInPlace(const Graphics::PixelFormat &dstFormat, const byte *palette = 0); +	void render(); +	void clear(uint32 color = 0); +	void markDirty(){ dirtyPixels = true; } +	 +	void setPosition(int x, int y); +	void setScale(float x, float y); +	float getScaleX(){ return scaleX; } +	float getScaleY(){ return scaleY; } +	C3D_Mtx* getMatrix(); +	 +	uint16 actualWidth; +	uint16 actualHeight; +	 +private: +	bool dirtyPixels; +	bool dirtyMatrix; +	C3D_Mtx modelview; +	C3D_Tex texture; +	vertex* vertices; +	int posX; +	int posY; +	float scaleX; +	float scaleY; +}; + +#endif  | 
