diff --git a/desmume/src/Makefile.am b/desmume/src/Makefile.am index f3185e918..ba3e442ef 100644 --- a/desmume/src/Makefile.am +++ b/desmume/src/Makefile.am @@ -88,6 +88,7 @@ libdesmume_a_SOURCES = \ utils/tinyxml/tinyxmlerror.cpp \ utils/tinyxml/tinyxmlparser.cpp \ utils/glcorearb.h \ + addons/slot2_analog.cpp \ addons/slot2_auto.cpp \ addons/slot2_mpcf.cpp \ addons/slot2_paddle.cpp \ diff --git a/desmume/src/addons/slot2_analog.cpp b/desmume/src/addons/slot2_analog.cpp new file mode 100644 index 000000000..4aebc4b02 --- /dev/null +++ b/desmume/src/addons/slot2_analog.cpp @@ -0,0 +1,100 @@ +/* + Copyright (C) 2021 LRFLEW + Copyright (C) 2021 DeSmuME team + + This file 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 file 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 the this software. If not, see . +*/ + +#include "../slot2.h" + +#include +#include + +#include "../debug.h" + +/* 2^15 / Pi */ +#define ANGLE_TO_SHORT 10430.37835047f +/* (float) 0x1000 */ +#define FLOAT_TO_FIXED 4096.0f +/* Open-Bus Value (mask) */ +#define OPEN_BUS 0xAFFF + +static u16 analog_x, analog_y, analog_magnitude, analog_angle; + +class Slot2_Analog : public ISlot2Interface { +public: + Slot2Info const* info() override { + static Slot2InfoSimple info("Analog Stick", "Analog Stick Input", 0x09); + return &info; + } + + void connect() override { + analog_x = analog_y = 0; + analog_magnitude = analog_angle = 0; + } + + u16 readWord(u8 PROCNUM, u32 addr) override { + if ((addr & 0xFF000000) != 0x09000000) return OPEN_BUS; + switch ((addr >> 8) & 0xFFFF) { + case 0: // Generic + switch (addr & 0xFF) { + // 0x00 and 0x01 are reserved to match the output + // of any physical device made for this purpose. + // 0x02 through 0x07 are reserved for future DeSmuME use + case 0x08: return analog_x; + case 0x0A: return analog_y; + default: return OPEN_BUS; + } + + case 1: // AM64DS + // Matches the layout of values used in SM64DS + switch (addr & 0xFF) { + case 0x00: return analog_magnitude; + case 0x02: return analog_x; + case 0x04: return analog_y; + case 0x06: return analog_angle; + default: return OPEN_BUS; + } + + default: + // Available for future game-specific interfaces + return OPEN_BUS; + } + } + + u8 readByte(u8 PROCNUM, u32 addr) override { + return (readWord(PROCNUM, addr & ~((u32) 1)) >> ((addr & 1) * 8)) & 0xFF; + } + + u32 readLong(u8 PROCNUM, u32 addr) override { + return (u32) readWord(PROCNUM, addr) | ((u32) readWord(PROCNUM, addr | 2) << 16); + } +}; + +ISlot2Interface* construct_Slot2_Analog() { return new Slot2_Analog(); } + +void analog_setValue(float x, float y) { + float mag = std::hypot(x, y); + float ang = std::atan2(x, y); + if (mag > 1.0f) { + x /= mag; + y /= mag; + mag = 1.0f; + } + + analog_x = static_cast(std::lround(x * FLOAT_TO_FIXED)); + analog_y = static_cast(std::lround(y * FLOAT_TO_FIXED)); + analog_magnitude = static_cast(std::lround(mag * FLOAT_TO_FIXED)); + analog_angle = static_cast(std::lround(ang * ANGLE_TO_SHORT)); +} diff --git a/desmume/src/frontend/cocoa/DeSmuME (Latest).xcodeproj/project.pbxproj b/desmume/src/frontend/cocoa/DeSmuME (Latest).xcodeproj/project.pbxproj index b4bfc2ae6..ec494a7f0 100755 --- a/desmume/src/frontend/cocoa/DeSmuME (Latest).xcodeproj/project.pbxproj +++ b/desmume/src/frontend/cocoa/DeSmuME (Latest).xcodeproj/project.pbxproj @@ -2553,6 +2553,13 @@ ABFEA8E81BB4FB3100B08C25 /* truetype.c in Sources */ = {isa = PBXBuildFile; fileRef = ABFEA7E61BB4EC1000B08C25 /* truetype.c */; }; ABFEA8E91BB4FB3100B08C25 /* truetype.c in Sources */ = {isa = PBXBuildFile; fileRef = ABFEA7E61BB4EC1000B08C25 /* truetype.c */; }; ABFEA8EA1BB4FB3200B08C25 /* truetype.c in Sources */ = {isa = PBXBuildFile; fileRef = ABFEA7E61BB4EC1000B08C25 /* truetype.c */; }; + D493A4F425F982C400282CE5 /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D493A4F325F982C400282CE5 /* slot2_analog.cpp */; }; + D493A4F525F982C400282CE5 /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D493A4F325F982C400282CE5 /* slot2_analog.cpp */; }; + D493A4F625F982C400282CE5 /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D493A4F325F982C400282CE5 /* slot2_analog.cpp */; }; + D493A4F725F982C400282CE5 /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D493A4F325F982C400282CE5 /* slot2_analog.cpp */; }; + D493A4F825F982C400282CE5 /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D493A4F325F982C400282CE5 /* slot2_analog.cpp */; }; + D493A4F925F982C400282CE5 /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D493A4F325F982C400282CE5 /* slot2_analog.cpp */; }; + D493A4FA25F982C400282CE5 /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D493A4F325F982C400282CE5 /* slot2_analog.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -3340,6 +3347,7 @@ ABFEA7C81BB4EC1000B08C25 /* sfnt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sfnt.c; path = sfnt/sfnt.c; sourceTree = ""; }; ABFEA7E41BB4EC1000B08C25 /* smooth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = smooth.c; path = smooth/smooth.c; sourceTree = ""; }; ABFEA7E61BB4EC1000B08C25 /* truetype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = truetype.c; path = truetype/truetype.c; sourceTree = ""; }; + D493A4F325F982C400282CE5 /* slot2_analog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = slot2_analog.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -4435,6 +4443,7 @@ AB9038AB17C5ED2200F410BD /* slot1comp_mc.cpp */, AB8B7AAB17CE8C440051CEBF /* slot1comp_protocol.cpp */, AB9038AD17C5ED2200F410BD /* slot1comp_rom.cpp */, + D493A4F325F982C400282CE5 /* slot2_analog.cpp */, AB29B16518313C14009B7982 /* slot2_auto.cpp */, ABD1FF031345AC9B00AF11D1 /* slot2_expMemory.cpp */, ABD1FF041345AC9B00AF11D1 /* slot2_gbagame.cpp */, @@ -6028,6 +6037,7 @@ AB564905186E6EBC002740F4 /* Slot2WindowDelegate.mm in Sources */, ABAD3E7813AF1D6D00502E1E /* SoundTouch.cpp in Sources */, ABFEA81D1BB4EC1000B08C25 /* ftfstype.c in Sources */, + D493A4F925F982C400282CE5 /* slot2_analog.cpp in Sources */, AB9038B017C5ED2200F410BD /* slot1_retail_auto.cpp in Sources */, ABD1FEFA1345AC8400AF11D1 /* SPU.cpp in Sources */, ABAD3E7913AF1D6D00502E1E /* sse_optimized.cpp in Sources */, @@ -6256,6 +6266,7 @@ AB7900A8215B84E50082AE82 /* slot1_retail_nand.cpp in Sources */, AB7900A9215B84E50082AE82 /* encoding_utf.c in Sources */, AB7900AA215B84E50082AE82 /* OGLDisplayOutput.cpp in Sources */, + D493A4F625F982C400282CE5 /* slot2_analog.cpp in Sources */, AB7900AB215B84E50082AE82 /* slot2_expMemory.cpp in Sources */, AB7900AC215B84E50082AE82 /* xbrz.cpp in Sources */, AB7900AD215B84E50082AE82 /* slot2_gbagame.cpp in Sources */, @@ -6558,6 +6569,7 @@ AB79024D215B84F20082AE82 /* MacBaseCaptureTool.mm in Sources */, AB79024E215B84F20082AE82 /* lq2x.cpp in Sources */, AB79024F215B84F20082AE82 /* xbrz.cpp in Sources */, + D493A4F725F982C400282CE5 /* slot2_analog.cpp in Sources */, AB790250215B84F20082AE82 /* OGLDisplayOutput_3_2.cpp in Sources */, AB790251215B84F20082AE82 /* scanline.cpp in Sources */, AB790252215B84F20082AE82 /* coreaudiosound.cpp in Sources */, @@ -6713,6 +6725,7 @@ AB796D2C15CDCBA200C59155 /* slot1_retail_nand.cpp in Sources */, AB35BD8F1DEBF40800844310 /* encoding_utf.c in Sources */, ABE6840C189E33BC007FD69C /* OGLDisplayOutput.cpp in Sources */, + D493A4F425F982C400282CE5 /* slot2_analog.cpp in Sources */, AB796D2D15CDCBA200C59155 /* slot2_expMemory.cpp in Sources */, AB47B52F18A45C35009A42AF /* xbrz.cpp in Sources */, AB796D2E15CDCBA200C59155 /* slot2_gbagame.cpp in Sources */, @@ -7015,6 +7028,7 @@ AB28625020AE3E7B00EAED43 /* MacBaseCaptureTool.mm in Sources */, AB8F3CEC1A53AC2600A80BF6 /* lq2x.cpp in Sources */, AB8F3CED1A53AC2600A80BF6 /* xbrz.cpp in Sources */, + D493A4F525F982C400282CE5 /* slot2_analog.cpp in Sources */, ABB24F6F1A81EE92006C1108 /* OGLDisplayOutput_3_2.cpp in Sources */, AB8F3CEE1A53AC2600A80BF6 /* scanline.cpp in Sources */, AB8F3CEF1A53AC2600A80BF6 /* coreaudiosound.cpp in Sources */, @@ -7223,6 +7237,7 @@ AB40567A169F5DCC0016AC3E /* x86assembler.cpp in Sources */, AB40567D169F5DCC0016AC3E /* x86compiler.cpp in Sources */, AB405680169F5DCC0016AC3E /* x86compilercontext.cpp in Sources */, + D493A4FA25F982C400282CE5 /* slot2_analog.cpp in Sources */, AB405683169F5DCC0016AC3E /* x86compilerfunc.cpp in Sources */, AB405686169F5DCC0016AC3E /* x86compileritem.cpp in Sources */, AB405689169F5DCC0016AC3E /* x86cpuinfo.cpp in Sources */, @@ -7609,6 +7624,7 @@ ABE145531FBBA71A0097A4A8 /* cocoa_rom.mm in Sources */, ABE145541FBBA71A0097A4A8 /* cocoa_util.mm in Sources */, ABE145551FBBA71A0097A4A8 /* cocoa_videofilter.mm in Sources */, + D493A4F825F982C400282CE5 /* slot2_analog.cpp in Sources */, ABE145561FBBA71A0097A4A8 /* OGLRender.cpp in Sources */, ABE145571FBBA71A0097A4A8 /* slot1comp_protocol.cpp in Sources */, AB28626320AE3E9F00EAED43 /* ClientAVCaptureObject.cpp in Sources */, diff --git a/desmume/src/frontend/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj b/desmume/src/frontend/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj index 7e8b7fd02..ce3e61fbc 100644 --- a/desmume/src/frontend/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj +++ b/desmume/src/frontend/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj @@ -1587,6 +1587,11 @@ ABFE151314C92FF5005D6699 /* hq4x.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABFE150014C92FF5005D6699 /* hq4x.cpp */; }; ABFE151514C92FF5005D6699 /* lq2x.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABFE150414C92FF5005D6699 /* lq2x.cpp */; }; ABFE151614C92FF5005D6699 /* scanline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABFE150614C92FF5005D6699 /* scanline.cpp */; }; + D4DB425C25F983C0008216EA /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D4DB425B25F983C0008216EA /* slot2_analog.cpp */; }; + D4DB425D25F983C0008216EA /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D4DB425B25F983C0008216EA /* slot2_analog.cpp */; }; + D4DB425E25F983C0008216EA /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D4DB425B25F983C0008216EA /* slot2_analog.cpp */; }; + D4DB425F25F983C0008216EA /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D4DB425B25F983C0008216EA /* slot2_analog.cpp */; }; + D4DB426025F983C0008216EA /* slot2_analog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D4DB425B25F983C0008216EA /* slot2_analog.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -2353,6 +2358,7 @@ ABFE150414C92FF5005D6699 /* lq2x.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lq2x.cpp; sourceTree = ""; }; ABFE150514C92FF5005D6699 /* lq2x.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lq2x.h; sourceTree = ""; }; ABFE150614C92FF5005D6699 /* scanline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scanline.cpp; sourceTree = ""; }; + D4DB425B25F983C0008216EA /* slot2_analog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = slot2_analog.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -3421,6 +3427,7 @@ AB4C808917C5D7780024D479 /* slot1comp_mc.cpp */, ABB9212017CEB4110049D4C5 /* slot1comp_protocol.cpp */, AB4C808B17C5D7780024D479 /* slot1comp_rom.cpp */, + D4DB425B25F983C0008216EA /* slot2_analog.cpp */, AB53517E18313E3100CCD532 /* slot2_auto.cpp */, ABD1FF031345AC9B00AF11D1 /* slot2_expMemory.cpp */, ABD1FF041345AC9B00AF11D1 /* slot2_gbagame.cpp */, @@ -4714,6 +4721,7 @@ ABD1266A20AE80DF00EFE1B2 /* MacBaseCaptureTool.mm in Sources */, ABD1267720AE812900EFE1B2 /* ClientAVCaptureObject.cpp in Sources */, ABD1267820AE812900EFE1B2 /* macOS_driver.cpp in Sources */, + D4DB426025F983C0008216EA /* slot2_analog.cpp in Sources */, AB4B5A22217E47E400381363 /* WifiSettingsPanel.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -4908,6 +4916,7 @@ ABD1266C20AE80DF00EFE1B2 /* MacBaseCaptureTool.mm in Sources */, ABD1267920AE812900EFE1B2 /* ClientAVCaptureObject.cpp in Sources */, ABD1267A20AE812900EFE1B2 /* macOS_driver.cpp in Sources */, + D4DB425F25F983C0008216EA /* slot2_analog.cpp in Sources */, AB4B5A23217E47E400381363 /* WifiSettingsPanel.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -5128,6 +5137,7 @@ ABC04DCA1F67A2AC00EA6ED7 /* macosx_10_5_compat.cpp in Sources */, ABAFD2751F7110E4007705BD /* gdbstub.cpp in Sources */, ABAB0AFF1F7C1BB70079EFD3 /* MacScreenshotCaptureTool.mm in Sources */, + D4DB425C25F983C0008216EA /* slot2_analog.cpp in Sources */, ABD1266720AE80DF00EFE1B2 /* MacAVCaptureTool.mm in Sources */, ABD1266820AE80DF00EFE1B2 /* MacBaseCaptureTool.mm in Sources */, ABD1267520AE812900EFE1B2 /* ClientAVCaptureObject.cpp in Sources */, @@ -5352,6 +5362,7 @@ ABC04DCE1F67A2AC00EA6ED7 /* macosx_10_5_compat.cpp in Sources */, ABAFD2761F7110E4007705BD /* gdbstub.cpp in Sources */, ABAB0B031F7C1BB70079EFD3 /* MacScreenshotCaptureTool.mm in Sources */, + D4DB425D25F983C0008216EA /* slot2_analog.cpp in Sources */, ABD1266F20AE80DF00EFE1B2 /* MacAVCaptureTool.mm in Sources */, ABD1267020AE80DF00EFE1B2 /* MacBaseCaptureTool.mm in Sources */, ABD1267D20AE812900EFE1B2 /* ClientAVCaptureObject.cpp in Sources */, @@ -5550,6 +5561,7 @@ ABD1266E20AE80DF00EFE1B2 /* MacBaseCaptureTool.mm in Sources */, ABD1267B20AE812900EFE1B2 /* ClientAVCaptureObject.cpp in Sources */, ABD1267C20AE812900EFE1B2 /* macOS_driver.cpp in Sources */, + D4DB425E25F983C0008216EA /* slot2_analog.cpp in Sources */, AB4B5A24217E47E400381363 /* WifiSettingsPanel.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/desmume/src/frontend/interface/meson.build b/desmume/src/frontend/interface/meson.build index 821e5d207..1837e4753 100644 --- a/desmume/src/frontend/interface/meson.build +++ b/desmume/src/frontend/interface/meson.build @@ -104,7 +104,7 @@ libdesmume_src += [ '../../utils/tinyxml/tinyxmlerror.cpp', '../../utils/tinyxml/tinyxmlparser.cpp', '../../utils/colorspacehandler/colorspacehandler.cpp', - '../../addons/slot2_auto.cpp', '../../addons/slot2_mpcf.cpp', '../../addons/slot2_paddle.cpp', '../../addons/slot2_gbagame.cpp', '../../addons/slot2_none.cpp', '../../addons/slot2_rumblepak.cpp', '../../addons/slot2_guitarGrip.cpp', '../../addons/slot2_expMemory.cpp', '../../addons/slot2_piano.cpp', '../../addons/slot2_passme.cpp', '../../addons/slot1_none.cpp', '../../addons/slot1_r4.cpp', '../../addons/slot1_retail_nand.cpp', '../../addons/slot1_retail_auto.cpp', '../../addons/slot1_retail_mcrom.cpp', '../../addons/slot1_retail_mcrom_debug.cpp', '../../addons/slot1comp_mc.cpp', '../../addons/slot1comp_rom.cpp', '../../addons/slot1comp_protocol.cpp', + '../../addons/slot2_analog.cpp', '../../addons/slot2_auto.cpp', '../../addons/slot2_mpcf.cpp', '../../addons/slot2_paddle.cpp', '../../addons/slot2_gbagame.cpp', '../../addons/slot2_none.cpp', '../../addons/slot2_rumblepak.cpp', '../../addons/slot2_guitarGrip.cpp', '../../addons/slot2_expMemory.cpp', '../../addons/slot2_piano.cpp', '../../addons/slot2_passme.cpp', '../../addons/slot1_none.cpp', '../../addons/slot1_r4.cpp', '../../addons/slot1_retail_nand.cpp', '../../addons/slot1_retail_auto.cpp', '../../addons/slot1_retail_mcrom.cpp', '../../addons/slot1_retail_mcrom_debug.cpp', '../../addons/slot1comp_mc.cpp', '../../addons/slot1comp_rom.cpp', '../../addons/slot1comp_protocol.cpp', '../../cheatSystem.cpp', '../../texcache.cpp', '../../rasterize.cpp', '../../metaspu/metaspu.cpp', diff --git a/desmume/src/frontend/interface/windows/DeSmuME_Interface.vcxproj b/desmume/src/frontend/interface/windows/DeSmuME_Interface.vcxproj index 4f1046fcb..0a66fd00b 100644 --- a/desmume/src/frontend/interface/windows/DeSmuME_Interface.vcxproj +++ b/desmume/src/frontend/interface/windows/DeSmuME_Interface.vcxproj @@ -95,6 +95,7 @@ + diff --git a/desmume/src/frontend/interface/windows/DeSmuME_Interface.vcxproj.filters b/desmume/src/frontend/interface/windows/DeSmuME_Interface.vcxproj.filters index db90b8e93..922096dec 100755 --- a/desmume/src/frontend/interface/windows/DeSmuME_Interface.vcxproj.filters +++ b/desmume/src/frontend/interface/windows/DeSmuME_Interface.vcxproj.filters @@ -581,6 +581,9 @@ frontend\interface + + addons + diff --git a/desmume/src/frontend/posix/meson.build b/desmume/src/frontend/posix/meson.build index 901c4defc..aca1a3825 100644 --- a/desmume/src/frontend/posix/meson.build +++ b/desmume/src/frontend/posix/meson.build @@ -104,7 +104,7 @@ libdesmume_src = [ '../../utils/tinyxml/tinyxmlerror.cpp', '../../utils/tinyxml/tinyxmlparser.cpp', '../../utils/colorspacehandler/colorspacehandler.cpp', - '../../addons/slot2_auto.cpp', '../../addons/slot2_mpcf.cpp', '../../addons/slot2_paddle.cpp', '../../addons/slot2_gbagame.cpp', '../../addons/slot2_none.cpp', '../../addons/slot2_rumblepak.cpp', '../../addons/slot2_guitarGrip.cpp', '../../addons/slot2_expMemory.cpp', '../../addons/slot2_piano.cpp', '../../addons/slot2_passme.cpp', '../../addons/slot1_none.cpp', '../../addons/slot1_r4.cpp', '../../addons/slot1_retail_nand.cpp', '../../addons/slot1_retail_auto.cpp', '../../addons/slot1_retail_mcrom.cpp', '../../addons/slot1_retail_mcrom_debug.cpp', '../../addons/slot1comp_mc.cpp', '../../addons/slot1comp_rom.cpp', '../../addons/slot1comp_protocol.cpp', + '../../addons/slot2_analog.cpp', '../../addons/slot2_auto.cpp', '../../addons/slot2_mpcf.cpp', '../../addons/slot2_paddle.cpp', '../../addons/slot2_gbagame.cpp', '../../addons/slot2_none.cpp', '../../addons/slot2_rumblepak.cpp', '../../addons/slot2_guitarGrip.cpp', '../../addons/slot2_expMemory.cpp', '../../addons/slot2_piano.cpp', '../../addons/slot2_passme.cpp', '../../addons/slot1_none.cpp', '../../addons/slot1_r4.cpp', '../../addons/slot1_retail_nand.cpp', '../../addons/slot1_retail_auto.cpp', '../../addons/slot1_retail_mcrom.cpp', '../../addons/slot1_retail_mcrom_debug.cpp', '../../addons/slot1comp_mc.cpp', '../../addons/slot1comp_rom.cpp', '../../addons/slot1comp_protocol.cpp', '../../cheatSystem.cpp', '../../texcache.cpp', '../../rasterize.cpp', '../../metaspu/metaspu.cpp', diff --git a/desmume/src/frontend/windows/DeSmuME.vcxproj b/desmume/src/frontend/windows/DeSmuME.vcxproj index f6aadc49c..d62507da8 100644 --- a/desmume/src/frontend/windows/DeSmuME.vcxproj +++ b/desmume/src/frontend/windows/DeSmuME.vcxproj @@ -58,6 +58,7 @@ + diff --git a/desmume/src/frontend/windows/DeSmuME.vcxproj.filters b/desmume/src/frontend/windows/DeSmuME.vcxproj.filters index 20bfe9dc2..7a68ade6e 100644 --- a/desmume/src/frontend/windows/DeSmuME.vcxproj.filters +++ b/desmume/src/frontend/windows/DeSmuME.vcxproj.filters @@ -930,6 +930,9 @@ frontend\Windows + + addons + diff --git a/desmume/src/frontend/windows/gbaslot_config.cpp b/desmume/src/frontend/windows/gbaslot_config.cpp index 75bb02053..485ddf706 100644 --- a/desmume/src/frontend/windows/gbaslot_config.cpp +++ b/desmume/src/frontend/windows/gbaslot_config.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "debug.h" #include "slot2.h" @@ -42,6 +43,7 @@ bool _OKbutton = false; SGuitar tmp_Guitar; SPiano tmp_Piano; SPaddle tmp_Paddle; +SAnalog tmp_Analog; //these are the remembered preset values for directory and filename //they are named very verbosely to distinguish them from the currently-configured values in addons.cpp @@ -467,6 +469,89 @@ INT_PTR CALLBACK GbaSlotPiano(HWND dialog, UINT msg,WPARAM wparam,LPARAM lparam) return FALSE; } +INT_PTR CALLBACK GbaSlotAnalog(HWND dialog, UINT msg, WPARAM wparam, LPARAM lparam) { + int which = 0; + + switch (msg) { + case WM_INITDIALOG: + { + _OKbutton = TRUE; + SendDlgItemMessage(dialog, IDC_ANALOG_X, WM_USER + 44, tmp_Analog.X, 0); + SendDlgItemMessage(dialog, IDC_ANALOG_Y, WM_USER + 44, tmp_Analog.Y, 0); + + std::string deadzone_val = std::to_string(tmp_Analog.Deadzone); + SendDlgItemMessage(dialog, IDC_ANALOG_DEADZONE, EM_SETLIMITTEXT, 3, 0); + SendDlgItemMessage(dialog, IDC_ANALOG_DEADZONE, EM_SETEVENTMASK, 0, ENM_UPDATE); + SetWindowTextA(GetDlgItem(dialog, IDC_ANALOG_DEADZONE), deadzone_val.c_str()); + + SendDlgItemMessage(dialog, IDC_ANALOG_DEADZONE_SLIDER, TBM_SETRANGE, FALSE, MAKELONG(0, 100)); + SendDlgItemMessage(dialog, IDC_ANALOG_DEADZONE_SLIDER, TBM_SETPOS, TRUE, tmp_Analog.Deadzone); + + CheckDlgButton(dialog, IDC_ANALOG_JOINED, tmp_Analog.Joined); + return TRUE; + } + + case WM_USER + 46: + SendDlgItemMessage(dialog, IDC_ANALOG_X, WM_USER + 44, tmp_Analog.X, 0); + SendDlgItemMessage(dialog, IDC_ANALOG_Y, WM_USER + 44, tmp_Analog.Y, 0); + return TRUE; + + case WM_USER + 43: + which = GetDlgCtrlID((HWND) lparam); + switch (which) { + case IDC_ANALOG_X: + tmp_Analog.X = wparam; + + break; + case IDC_ANALOG_Y: + tmp_Analog.Y = wparam; + + break; + } + + SendDlgItemMessage(dialog, IDC_ANALOG_X, WM_USER + 44, tmp_Analog.X, 0); + SendDlgItemMessage(dialog, IDC_ANALOG_Y, WM_USER + 44, tmp_Analog.Y, 0); + PostMessage(dialog, WM_NEXTDLGCTL, 0, 0); + return TRUE; + + case WM_COMMAND: + switch (wparam) { + case MAKELONG(IDC_ANALOG_DEADZONE, EN_UPDATE): + { + char text[4]; + WORD value = 0; + bool dirty = false; + HWND en = GetDlgItem(dialog, IDC_ANALOG_DEADZONE); + GetWindowTextA(en, text, 4); + if (text[0] != '\0') value = std::stoi(text); + if (value < 0) value = 0, dirty = true; + if (value > 100) value = 100, dirty = true; + if (dirty) SetWindowTextA(en, std::to_string(value).c_str()); + if (tmp_Analog.Deadzone != value) { + tmp_Analog.Deadzone = value; + SendDlgItemMessage(dialog, IDC_ANALOG_DEADZONE_SLIDER, TBM_SETPOS, TRUE, value); + } + return TRUE; + } + case MAKELONG(IDC_ANALOG_JOINED, BN_CLICKED): + tmp_Analog.Joined = IsDlgCheckboxChecked(dialog, IDC_ANALOG_JOINED); + return TRUE; + } + return FALSE; + + case WM_NOTIFY: + if (wparam == IDC_ANALOG_DEADZONE_SLIDER) { + HWND en = GetDlgItem(dialog, IDC_ANALOG_DEADZONE); + WORD value = (WORD) SendDlgItemMessage(dialog, IDC_ANALOG_DEADZONE_SLIDER, TBM_GETPOS, 0, 0); + if (tmp_Analog.Deadzone != value) { + tmp_Analog.Deadzone = value; + SetWindowTextA(en, std::to_string(value).c_str()); + } + } + } + return FALSE; +} + u32 GBAslot_IDDs[NDS_SLOT2_COUNT] = { IDD_GBASLOT_NONE, IDD_GBASLOT_NONE, @@ -478,6 +563,7 @@ u32 GBAslot_IDDs[NDS_SLOT2_COUNT] = { IDD_GBASLOT_PIANO, IDD_GBASLOT_PADDLE, //paddle IDD_GBASLOT_NONE, //PassME + IDD_GBASLOT_ANALOG, }; DLGPROC GBAslot_Procs[NDS_SLOT2_COUNT] = { @@ -490,7 +576,8 @@ DLGPROC GBAslot_Procs[NDS_SLOT2_COUNT] = { GbaSlotNone, //expmem GbaSlotPiano, GbaSlotPaddle, - GbaSlotNone // PassME + GbaSlotNone, // PassME + GbaSlotAnalog, }; @@ -569,6 +656,7 @@ void GBAslotDialog(HWND hwnd) memcpy(&tmp_Guitar, &Guitar, sizeof(Guitar)); memcpy(&tmp_Piano, &Piano, sizeof(Piano)); memcpy(&tmp_Paddle, &Paddle, sizeof(Paddle)); + memcpy(&tmp_Analog, &Analog, sizeof(Analog)); tmp_CFlashMode = CFlash_Mode; _OKbutton = false; @@ -633,6 +721,13 @@ void GBAslotDialog(HWND hwnd) break; case NDS_SLOT2_PASSME: break; + case NDS_SLOT2_ANALOG: + memcpy(&Analog, &tmp_Analog, sizeof(tmp_Analog)); + WritePrivateProfileInt("Slot2.Analog", "X", Analog.X, IniName); + WritePrivateProfileInt("Slot2.Analog", "Y", Analog.Y, IniName); + WritePrivateProfileInt("Slot2.Analog", "Deadzone", Analog.Deadzone, IniName); + WritePrivateProfileBool("Slot2.Analog", "Joined", Analog.Joined, IniName); + break; default: return; } @@ -641,9 +736,10 @@ void GBAslotDialog(HWND hwnd) WritePrivateProfileInt("Slot2", "id", slot2_List[(u8)slot2_GetCurrentType()]->info()->id(), IniName); - Guitar.Enabled = (slot2_GetCurrentType() == NDS_SLOT2_GUITARGRIP)?true:false; - Piano.Enabled = (slot2_GetCurrentType() == NDS_SLOT2_EASYPIANO)?true:false; - Paddle.Enabled = (slot2_GetCurrentType() == NDS_SLOT2_PADDLE)?true:false; + Guitar.Enabled = slot2_GetCurrentType() == NDS_SLOT2_GUITARGRIP; + Piano.Enabled = slot2_GetCurrentType() == NDS_SLOT2_EASYPIANO; + Paddle.Enabled = slot2_GetCurrentType() == NDS_SLOT2_PADDLE; + Analog.Enabled = slot2_GetCurrentType() == NDS_SLOT2_ANALOG; } } diff --git a/desmume/src/frontend/windows/inputdx.cpp b/desmume/src/frontend/windows/inputdx.cpp index 595a9744d..fbb31f64c 100644 --- a/desmume/src/frontend/windows/inputdx.cpp +++ b/desmume/src/frontend/windows/inputdx.cpp @@ -30,6 +30,8 @@ #include #include +#include +#include #include #if (((defined(_MSC_VER) && _MSC_VER >= 1300)) || defined(__MINGW32__)) @@ -97,8 +99,8 @@ // gaming buttons and axes #define GAMEDEVICE_JOYNUMPREFIX "(J%x)" // don't change this #define GAMEDEVICE_JOYBUTPREFIX "#[%d]" // don't change this -#define GAMEDEVICE_XNEG "Left" -#define GAMEDEVICE_XPOS "Right" +#define GAMEDEVICE_XPOS "Left" +#define GAMEDEVICE_XNEG "Right" #define GAMEDEVICE_YPOS "Up" #define GAMEDEVICE_YNEG "Down" #define GAMEDEVICE_POVLEFT "POV Left" @@ -119,12 +121,12 @@ #define GAMEDEVICE_VNEG "V Down" #define GAMEDEVICE_BUTTON "Button %d" -#define GAMEDEVICE_XROTPOS "X Rot Up" -#define GAMEDEVICE_XROTNEG "X Rot Down" +#define GAMEDEVICE_XROTPOS "X Rot Left" +#define GAMEDEVICE_XROTNEG "X Rot Right" #define GAMEDEVICE_YROTPOS "Y Rot Up" #define GAMEDEVICE_YROTNEG "Y Rot Down" -#define GAMEDEVICE_ZROTPOS "Z Rot Up" -#define GAMEDEVICE_ZROTNEG "Z Rot Down" +#define GAMEDEVICE_ZROTPOS "Z Rot +" +#define GAMEDEVICE_ZROTNEG "Z Rot -" // gaming general #define GAMEDEVICE_DISABLED "Disabled" @@ -206,11 +208,13 @@ static TCHAR szClassName[] = _T("InputCustom"); static TCHAR szHotkeysClassName[] = _T("InputCustomHot"); static TCHAR szGuitarClassName[] = _T("InputCustomGuitar"); static TCHAR szPaddleClassName[] = _T("InputCustomPaddle"); +static TCHAR szAnalogClassName[] = _T("InputCustomAnalog"); static LRESULT CALLBACK InputCustomWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK HotInputCustomWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK GuitarInputCustomWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK PaddleInputCustomWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); +static LRESULT CALLBACK AnalogInputCustomWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); SJoyState Joystick [16]; SJoyState JoystickF [16]; @@ -258,6 +262,9 @@ SPiano DefaultPiano = { false, 'Z', 'S', 'X', 'D', 'C', 'V', 'G', 'B', 'H', 'N', SPaddle Paddle; SPaddle DefaultPaddle = { false, 'K', 'L' }; +SAnalog Analog; +SAnalog DefaultAnalog = { false, 1, 2, 15, true }; + bool killStylusTopScreen = false; bool killStylusOffScreen = false; bool allowUpAndDown = false; @@ -390,6 +397,24 @@ static void ReadPaddleControl(const char* name, WORD& output) } } +static void ReadAnalogControl(const char* name, WORD& output) +{ + UINT temp; + temp = GetPrivateProfileInt("Slot2.Analog", name, -1, IniName); + if (temp != -1) { + output = temp; + } +} + +static void ReadAnalogBool(const char* name, BOOL& output) +{ + UINT temp; + temp = GetPrivateProfileInt("Slot2.Analog", name, -1, IniName); + if (temp != -1) { + output = (BOOL) temp; + } +} + void LoadHotkeyConfig() { SCustomKey *key = &CustomKeys.key(0); @@ -452,6 +477,16 @@ static void LoadPaddleConfig() ReadPaddleControl("INC", Paddle.INC); } +static void LoadAnalogConfig() +{ + memcpy(&Analog, &DefaultAnalog, sizeof(Analog)); + + ReadAnalogControl("X", Analog.X); + ReadAnalogControl("Y", Analog.Y); + ReadAnalogControl("Deadzone", Analog.Deadzone); + ReadAnalogBool("Joined", Analog.Joined); +} + static void LoadInputConfig() { @@ -607,7 +642,7 @@ HWND funky; //WPARAM tid; // -void JoystickChanged( short ID, short Movement) +void JoystickChanged( short ID, short Movement, short Axis) { // don't allow two changes to happen too close together in time { @@ -625,12 +660,19 @@ void JoystickChanged( short ID, short Movement) } WORD JoyKey; - - JoyKey = 0x8000; + JoyKey = 0x8000; JoyKey |= (WORD)(ID << 8); JoyKey |= Movement; SendMessage(funky,WM_USER+45,JoyKey,0); // KillTimer(funky,tid); + + if (Axis > 0) + { + WORD JoyAxis; + JoyAxis = (WORD)(ID << 8); + JoyAxis |= Axis; + SendMessage(funky,WM_USER+47,JoyAxis,0); + } } int FunkyNormalize(int cur, int min, int max) @@ -652,7 +694,7 @@ int FunkyNormalize(int cur, int min, int max) #define S9X_JOY_NEUTRAL 60 -void CheckAxis (short joy, short control, int val, +void CheckAxis (short joy, short control, short axis, int val, int min, int max, bool &first, bool &second) { @@ -665,7 +707,7 @@ void CheckAxis (short joy, short control, int val, second = false; if (!first) { - JoystickChanged (joy, control); + JoystickChanged (joy, control, axis); first = true; } @@ -678,7 +720,7 @@ void CheckAxis (short joy, short control, int val, first = false; if (!second) { - JoystickChanged (joy, (short) (control + 1)); + JoystickChanged (joy, (short) (control + 1), (short) (8 | axis)); second = true; } } @@ -733,6 +775,13 @@ void S9xUpdateJoyState() CheckAxis_game(JoyStatus.lRy,-10000,10000,Joystick[C].YRotMin,Joystick[C].YRotMax); CheckAxis_game(JoyStatus.lRz,-10000,10000,Joystick[C].ZRotMin,Joystick[C].ZRotMax); + Joystick[C].lX = JoyStatus.lX; + Joystick[C].lY = JoyStatus.lY; + Joystick[C].lZ = JoyStatus.lZ; + Joystick[C].lRx = JoyStatus.lRx; + Joystick[C].lRy = JoyStatus.lRy; + Joystick[C].lRz = JoyStatus.lRz; + switch (JoyStatus.rgdwPOV[0]) { case JOY_POVBACKWARD: @@ -805,18 +854,18 @@ void di_poll_scan() if (FAILED(hr)) hr=pJoystick->Acquire(); else { - CheckAxis(C,0,JoyStatus.lX,-10000,10000,Joystick[C].Left,Joystick[C].Right); - CheckAxis(C,2,JoyStatus.lY,-10000,10000,Joystick[C].Down,Joystick[C].Up); - CheckAxis(C,41,JoyStatus.lZ,-10000,10000,Joystick[C].ZNeg,Joystick[C].ZPos); - CheckAxis(C,53,JoyStatus.lRx,-10000,10000,Joystick[C].XRotMin,Joystick[C].XRotMax); - CheckAxis(C,55,JoyStatus.lRy,-10000,10000,Joystick[C].YRotMin,Joystick[C].YRotMax); - CheckAxis(C,57,JoyStatus.lRz,-10000,10000,Joystick[C].ZRotMin,Joystick[C].ZRotMax); + CheckAxis(C,0,1,JoyStatus.lX,-10000,10000,Joystick[C].Left,Joystick[C].Right); + CheckAxis(C,2,2,JoyStatus.lY,-10000,10000,Joystick[C].Down,Joystick[C].Up); + CheckAxis(C,41,3,JoyStatus.lZ,-10000,10000,Joystick[C].ZNeg,Joystick[C].ZPos); + CheckAxis(C,53,4,JoyStatus.lRx,-10000,10000,Joystick[C].XRotMin,Joystick[C].XRotMax); + CheckAxis(C,55,5,JoyStatus.lRy,-10000,10000,Joystick[C].YRotMin,Joystick[C].YRotMax); + CheckAxis(C,57,6,JoyStatus.lRz,-10000,10000,Joystick[C].ZRotMin,Joystick[C].ZRotMax); switch (JoyStatus.rgdwPOV[0]) { case JOY_POVBACKWARD: if( !JoystickF[C].PovDown) - { JoystickChanged( C, 7); } + { JoystickChanged( C, 7, -1); } JoystickF[C].PovDown = true; JoystickF[C].PovUp = false; @@ -829,7 +878,7 @@ void di_poll_scan() break; case 4500: if( !JoystickF[C].PovUpRight) - { JoystickChanged( C, 52); } + { JoystickChanged( C, 52, -1); } JoystickF[C].PovDown = false; JoystickF[C].PovUp = false; JoystickF[C].PovLeft = false; @@ -841,7 +890,7 @@ void di_poll_scan() break; case 13500: if( !JoystickF[C].PovDnRight) - { JoystickChanged( C, 50); } + { JoystickChanged( C, 50, -1); } JoystickF[C].PovDown = false; JoystickF[C].PovUp = false; JoystickF[C].PovLeft = false; @@ -853,7 +902,7 @@ void di_poll_scan() break; case 22500: if( !JoystickF[C].PovDnLeft) - { JoystickChanged( C, 49); } + { JoystickChanged( C, 49, -1); } JoystickF[C].PovDown = false; JoystickF[C].PovUp = false; JoystickF[C].PovLeft = false; @@ -865,7 +914,7 @@ void di_poll_scan() break; case 31500: if( !JoystickF[C].PovUpLeft) - { JoystickChanged( C, 51); } + { JoystickChanged( C, 51, -1); } JoystickF[C].PovDown = false; JoystickF[C].PovUp = false; JoystickF[C].PovLeft = false; @@ -878,7 +927,7 @@ void di_poll_scan() case JOY_POVFORWARD: if( !JoystickF[C].PovUp) - { JoystickChanged( C, 6); } + { JoystickChanged( C, 6, -1); } JoystickF[C].PovDown = false; JoystickF[C].PovUp = true; @@ -892,7 +941,7 @@ void di_poll_scan() case JOY_POVLEFT: if( !JoystickF[C].PovLeft) - { JoystickChanged( C, 4); } + { JoystickChanged( C, 4, -1); } JoystickF[C].PovDown = false; JoystickF[C].PovUp = false; @@ -906,7 +955,7 @@ void di_poll_scan() case JOY_POVRIGHT: if( !JoystickF[C].PovRight) - { JoystickChanged( C, 5); } + { JoystickChanged( C, 5, -1); } JoystickF[C].PovDown = false; JoystickF[C].PovUp = false; @@ -935,7 +984,7 @@ void di_poll_scan() { if( !JoystickF[C].Button[B]) { - JoystickChanged( C, (short)(8+B)); + JoystickChanged( C, (short)(8+B), -1); JoystickF[C].Button[B] = true; } } @@ -967,8 +1016,8 @@ void TranslateKey(WORD keyz,char *out) sprintf(out,GAMEDEVICE_JOYNUMPREFIX,((keyz>>8)&0xF)); switch(keyz&0xFF) { - case 0: strcat(out,GAMEDEVICE_XNEG); break; - case 1: strcat(out,GAMEDEVICE_XPOS); break; + case 0: strcat(out,GAMEDEVICE_XPOS); break; + case 1: strcat(out,GAMEDEVICE_XNEG); break; case 2: strcat(out,GAMEDEVICE_YPOS); break; case 3: strcat(out,GAMEDEVICE_YNEG); break; case 4: strcat(out,GAMEDEVICE_POVLEFT); break; @@ -1095,11 +1144,28 @@ void TranslateKey(WORD keyz,char *out) case VK_F11: sprintf(out,GAMEDEVICE_VK_F11); break; case VK_F12: sprintf(out,GAMEDEVICE_VK_F12); break; } +} - return ; - - - +void TranslateAnalog(WORD axis, char* out) +{ + sprintf(out, GAMEDEVICE_JOYNUMPREFIX, ((axis >> 8) & 0xF)); + switch (axis & 0xF) { + case 1: strcat(out, GAMEDEVICE_XPOS); break; + case 2: strcat(out, GAMEDEVICE_YPOS); break; + case 3: strcat(out, GAMEDEVICE_ZPOS); break; + case 4: strcat(out, GAMEDEVICE_XROTPOS); break; + case 5: strcat(out, GAMEDEVICE_YROTPOS); break; + case 6: strcat(out, GAMEDEVICE_XROTPOS); break; + + case 9: strcat(out, GAMEDEVICE_XNEG); break; + case 10: strcat(out, GAMEDEVICE_YNEG); break; + case 11: strcat(out, GAMEDEVICE_ZNEG); break; + case 12: strcat(out, GAMEDEVICE_XROTNEG); break; + case 13: strcat(out, GAMEDEVICE_YROTNEG); break; + case 14: strcat(out, GAMEDEVICE_ZROTNEG); break; + + default: strcat(out, "UNKNOWN"); break; + } } bool IsReserved (WORD Key, int modifiers) @@ -1177,8 +1243,8 @@ int GetNumButtonsAssignedTo (WORD Key) if(Key == Joypad[J].Y) count++; if(Key == Joypad[J].L) count++; if(Key == Joypad[J].R) count++; - if(Key == Joypad[J].Lid) count++; - if(Key == Joypad[J].Debug) count++; + if(Key == Joypad[J].Lid) count++; + if(Key == Joypad[J].Debug) count++; } return count; } @@ -1297,6 +1363,22 @@ static void InitCustomControls() RegisterClassEx(&wc); + + wc.cbSize = sizeof(wc); + wc.lpszClassName = szAnalogClassName; + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = AnalogInputCustomWndProc; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hIcon = 0; + wc.lpszMenuName = 0; + wc.hbrBackground = (HBRUSH) GetSysColorBrush(COLOR_BTNFACE); + wc.style = 0; + wc.cbClsExtra = 0; + wc.cbWndExtra = sizeof(InputCust*); + wc.hIconSm = 0; + + + RegisterClassEx(&wc); } InputCust * GetInputCustom(HWND hwnd) @@ -1782,6 +1864,136 @@ static LRESULT CALLBACK PaddleInputCustomWndProc(HWND hwnd, UINT msg, WPARAM wPa return DefWindowProc(hwnd, msg, wParam, lParam); } +static LRESULT CALLBACK AnalogInputCustomWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + // retrieve the custom structure POINTER for THIS window + InputCust* icp = GetInputCustom(hwnd); + HWND pappy = (HWND__*) GetWindowLongPtr(hwnd, GWLP_HWNDPARENT); + funky = hwnd; + + static HWND selectedItem = NULL; + + char temp[100]; + COLORREF col; + switch (msg) { + + case WM_GETDLGCODE: + return DLGC_WANTARROWS | DLGC_WANTALLKEYS | DLGC_WANTCHARS; + break; + + + case WM_NCCREATE: + + // Allocate a new CustCtrl structure for this window. + icp = (InputCust*) malloc(sizeof(InputCust)); + + // Failed to allocate, stop window creation. + if (icp == NULL) return FALSE; + + // Initialize the CustCtrl structure. + icp->hwnd = hwnd; + icp->crForeGnd = GetSysColor(COLOR_WINDOWTEXT); + icp->crBackGnd = GetSysColor(COLOR_WINDOW); + icp->hFont = (HFONT__*) GetStockObject(DEFAULT_GUI_FONT); + + // Assign the window text specified in the call to CreateWindow. + SetWindowText(hwnd, ((CREATESTRUCT*) lParam)->lpszName); + + // Attach custom structure to this window. + SetInputCustom(hwnd, icp); + + InvalidateRect(icp->hwnd, NULL, FALSE); + UpdateWindow(icp->hwnd); + + selectedItem = NULL; + + SetTimer(hwnd, 777, 125, NULL); + + // Continue with window creation. + return TRUE; + + // Clean up when the window is destroyed. + case WM_NCDESTROY: + free(icp); + break; + case WM_PAINT: + return InputCustom_OnPaint(icp, wParam, lParam); + break; + case WM_ERASEBKGND: + return 1; + case WM_USER+47: + TranslateAnalog(wParam, temp); + icp->crForeGnd = RGB(0, 0, 0); + icp->crBackGnd = RGB(255, 255, 255); + SetWindowText(hwnd, temp); + InvalidateRect(icp->hwnd, NULL, FALSE); + UpdateWindow(icp->hwnd); + SendMessage(pappy, WM_USER + 43, wParam, (LPARAM) hwnd); + + break; + + case WM_USER+44: + TranslateAnalog(wParam, temp); + if (IsWindowEnabled(hwnd)) { + col = RGB(255, 255, 255); + } else { + col = RGB(192, 192, 192); + } + icp->crForeGnd = ((~col) & 0x00ffffff); + icp->crBackGnd = col; + SetWindowText(hwnd, temp); + InvalidateRect(icp->hwnd, NULL, FALSE); + UpdateWindow(icp->hwnd); + + break; + + case WM_SETFOCUS: + { + selectedItem = hwnd; + col = RGB(0, 255, 0); + icp->crForeGnd = ((~col) & 0x00ffffff); + icp->crBackGnd = col; + InvalidateRect(icp->hwnd, NULL, FALSE); + UpdateWindow(icp->hwnd); + // tid = wParam; + + break; + } + case WM_KILLFOCUS: + { + selectedItem = NULL; + SendMessage(pappy, WM_USER + 46, wParam, (LPARAM) hwnd); // refresh fields on deselect + break; + } + + case WM_TIMER: + if (hwnd == selectedItem) { + FunkyJoyStickTimer(); + } + SetTimer(hwnd, 777, 125, NULL); + break; + case WM_LBUTTONDOWN: + SetFocus(hwnd); + break; + case WM_ENABLE: + COLORREF col; + if (wParam) { + col = RGB(255, 255, 255); + icp->crForeGnd = ((~col) & 0x00ffffff); + icp->crBackGnd = col; + } else { + col = RGB(192, 192, 192); + icp->crForeGnd = ((~col) & 0x00ffffff); + icp->crBackGnd = col; + } + InvalidateRect(icp->hwnd, NULL, FALSE); + UpdateWindow(icp->hwnd); + return true; + default: + break; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} + static void TranslateKeyWithModifiers(int wParam, int modifiers, char * outStr) { @@ -2481,6 +2693,32 @@ void S9xWinScanJoypads(const bool willAcceptInput) } } +static float get_analog_float(WORD axis) +{ + int J = axis >> 8; + LONG val; + + switch (axis & 0x7) + { + case 1: val = Joystick[J].lX; break; + case 2: val = Joystick[J].lY; break; + case 3: val = Joystick[J].lZ; break; + case 4: val = Joystick[J].lRx; break; + case 5: val = Joystick[J].lRy; break; + case 6: val = Joystick[J].lRz; break; + default: val = 0; break; + } + + if (axis & 8) val = -val; + return (float) val / 10000.0f; +} + +static float apply_deadzone(float value, float mag, float deadzone) { + if (mag <= deadzone) return 0.0f; + float cmag = std::min(mag, 1.0f); // clamp mag to 1.0f just in case + return value * (cmag - deadzone) / (1.0f - deadzone) / mag; +} + //void S9xOldAutofireAndStuff () //{ // // stuff ripped out of Snes9x that's no longer functional, at least for now @@ -2690,6 +2928,7 @@ void input_init() LoadGuitarConfig(); LoadPianoConfig(); LoadPaddleConfig(); + LoadAnalogConfig(); di_init(); FeedbackON = input_feedback; @@ -2816,6 +3055,25 @@ void input_acquire() if (inc) nds.paddle += 5; if (dec) nds.paddle -= 5; } + + if (Analog.Enabled) + { + float deadzone = Analog.Deadzone / 100.0f; + float x = get_analog_float(Analog.X); + float y = get_analog_float(Analog.Y); + + if (Analog.Joined) { + float mag = std::hypot(x, y); + x = apply_deadzone(x, mag, deadzone); + y = apply_deadzone(y, mag, deadzone); + } else { + // Faster approximation that prioritizes cardinal directions + x = apply_deadzone(x, x, deadzone); + y = apply_deadzone(y, y, deadzone); + } + + analog_setValue(x, y); + } } else { @@ -2828,6 +3086,10 @@ void input_acquire() { piano_setKey(false, false, false, false, false, false, false, false, false, false, false, false, false); } + + if (Analog.Enabled) { + analog_setValue(0.0f, 0.0f); + } } } diff --git a/desmume/src/frontend/windows/inputdx.h b/desmume/src/frontend/windows/inputdx.h index 4ed3bd45a..8720bec22 100644 --- a/desmume/src/frontend/windows/inputdx.h +++ b/desmume/src/frontend/windows/inputdx.h @@ -119,6 +119,13 @@ struct SJoyState{ bool Button[128]; bool FeedBack; LPDIRECTINPUTEFFECT pEffect; + + LONG lX; + LONG lY; + LONG lZ; + LONG lRx; + LONG lRy; + LONG lRz; }; extern SJoypad Joypad[16]; @@ -153,9 +160,19 @@ struct SPaddle { WORD INC; }; +struct SAnalog { + BOOL Enabled; + WORD X; + WORD Y; + + WORD Deadzone; + BOOL Joined; +}; + extern SGuitar Guitar; extern SPiano Piano; extern SPaddle Paddle; +extern SAnalog Analog; #endif diff --git a/desmume/src/frontend/windows/main.cpp b/desmume/src/frontend/windows/main.cpp index c236122c4..f46f56a42 100644 --- a/desmume/src/frontend/windows/main.cpp +++ b/desmume/src/frontend/windows/main.cpp @@ -1577,9 +1577,10 @@ static BOOL LoadROM(const char * filename, const char * physicalName, const char INFO("Loading %s was successful\n",logicalName); NDS_SLOT2_TYPE selectedSlot2Type = slot2_GetSelectedType(); - Guitar.Enabled = (selectedSlot2Type == NDS_SLOT2_GUITARGRIP)?true:false; - Piano.Enabled = (selectedSlot2Type == NDS_SLOT2_EASYPIANO)?true:false; - Paddle.Enabled = (selectedSlot2Type == NDS_SLOT2_PADDLE)?true:false; + Guitar.Enabled = selectedSlot2Type == NDS_SLOT2_GUITARGRIP; + Piano.Enabled = selectedSlot2Type == NDS_SLOT2_EASYPIANO; + Paddle.Enabled = selectedSlot2Type == NDS_SLOT2_PADDLE; + Analog.Enabled = selectedSlot2Type == NDS_SLOT2_ANALOG; LoadSaveStateInfo(); lagframecounter=0; @@ -2251,6 +2252,8 @@ int _main() break; case NDS_SLOT2_PASSME: break; + case NDS_SLOT2_ANALOG: + break; default: slot2_device_type = NDS_SLOT2_NONE; break; @@ -2258,9 +2261,10 @@ int _main() slot2_Change((NDS_SLOT2_TYPE)slot2_device_type); - Guitar.Enabled = (slot2_device_type == NDS_SLOT2_GUITARGRIP)?true:false; - Piano.Enabled = (slot2_device_type == NDS_SLOT2_EASYPIANO)?true:false; - Paddle.Enabled = (slot2_device_type == NDS_SLOT2_PADDLE)?true:false; + Guitar.Enabled = slot2_device_type == NDS_SLOT2_GUITARGRIP; + Piano.Enabled = slot2_device_type == NDS_SLOT2_EASYPIANO; + Paddle.Enabled = slot2_device_type == NDS_SLOT2_PADDLE; + Analog.Enabled = slot2_device_type == NDS_SLOT2_ANALOG; CommonSettings.WifiBridgeDeviceID = GetPrivateProfileInt("Wifi", "BridgeAdapter", 0, IniName); diff --git a/desmume/src/frontend/windows/resource.h b/desmume/src/frontend/windows/resource.h index a753958ae..6587d43d7 100644 --- a/desmume/src/frontend/windows/resource.h +++ b/desmume/src/frontend/windows/resource.h @@ -514,10 +514,14 @@ #define IDC_WIFI_ENABLED 1065 #define IDC_STATIC_RANGE 1066 #define IDC_WIFI_COMPAT 1066 +#define IDC_ANALOG_X 1066 #define IDC_TEXSCALE 1067 +#define IDC_ANALOG_Y 1067 #define IDC_BADD 1068 #define IDC_LIST 1069 +#define IDC_ANALOG_DEADZONE_SLIDER 1069 #define IDC_TEX_DEPOSTERIZE 1070 +#define IDC_ANALOG_DEADZONE 1070 #define IDC_SNUMBER 1071 #define IDC_TEX_SMOOTH 1072 #define IDC_CHECK1 1074 @@ -531,6 +535,7 @@ #define IDC_CHECK10 1079 #define IDC_CAP0_RUNNING 1079 #define IDC_BIG_ENDIAN 1079 +#define IDC_ANALOG_JOINED 1079 #define IDC_CHECK6 1080 #define IDC_CAP1_SRC 1080 #define IDC_CAP1_ONESHOT 1081 @@ -923,6 +928,7 @@ #define IDD_SLOT1_R4 10012 #define IDD_SLOT1_DEBUG 10013 #define IDD_GBASLOT_PADDLE 10014 +#define IDD_GBASLOT_ANALOG 10015 #define IDM_FILE_STOPAVI 40000 #define IDM_SCREENSEP_NONE 40000 #define IDM_FILE_STOPWAV 40001 @@ -1154,9 +1160,9 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 128 +#define _APS_NEXT_RESOURCE_VALUE 130 #define _APS_NEXT_COMMAND_VALUE 40159 -#define _APS_NEXT_CONTROL_VALUE 1066 +#define _APS_NEXT_CONTROL_VALUE 1071 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/desmume/src/frontend/windows/resources.rc b/desmume/src/frontend/windows/resources.rc index 2325fe3c3..79be4becb 100644 --- a/desmume/src/frontend/windows/resources.rc +++ b/desmume/src/frontend/windows/resources.rc @@ -1589,6 +1589,21 @@ BEGIN RTEXT "Decrease",-1,81,40,44,8 END +IDD_GBASLOT_ANALOG DIALOGEX 7, 48, 302, 109 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL " ",IDC_ANALOG_X,"InputCustomAnalog",WS_TABSTOP,75,17,71,12,WS_EX_CLIENTEDGE + CONTROL " ",IDC_ANALOG_Y,"InputCustomAnalog",WS_TABSTOP,180,17,71,12,WS_EX_CLIENTEDGE + RTEXT "Left",-1,47,20,24,8 + CTEXT "Analog Stick:",-1,137,2,51,12 + RTEXT "Up",-1,152,20,24,8 + CONTROL "",IDC_ANALOG_DEADZONE_SLIDER,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,147,41,100,15 + EDITTEXT IDC_ANALOG_DEADZONE,105,41,40,14,ES_AUTOHSCROLL | ES_NUMBER + RTEXT "Deadzone",-1,61,41,38,14,SS_CENTERIMAGE + CONTROL "Joined Deadzone",IDC_ANALOG_JOINED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,66,63,71,10 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -1968,11 +1983,23 @@ BEGIN BOTTOMMARGIN, 229 END + IDD_GBASLOT, DIALOG + BEGIN + END + IDD_GBASLOT_CFLASH, DIALOG BEGIN BOTTOMMARGIN, 106 END + IDD_GBASLOT_GBAGAME, DIALOG + BEGIN + END + + IDD_GBASLOT_GUITARGRIP, DIALOG + BEGIN + END + IDD_MEM_VIEW, DIALOG BEGIN RIGHTMARGIN, 438 @@ -2066,6 +2093,14 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 34 END + + IDD_GBASLOT_PADDLE, DIALOG + BEGIN + END + + IDD_GBASLOT_ANALOG, DIALOG + BEGIN + END END #endif // APSTUDIO_INVOKED diff --git a/desmume/src/slot2.cpp b/desmume/src/slot2.cpp index 8dd6ce993..ba8698143 100644 --- a/desmume/src/slot2.cpp +++ b/desmume/src/slot2.cpp @@ -58,6 +58,7 @@ void slot2_Init() extern TISlot2InterfaceConstructor construct_Slot2_EasyPiano; extern TISlot2InterfaceConstructor construct_Slot2_Paddle; extern TISlot2InterfaceConstructor construct_Slot2_PassME; + extern TISlot2InterfaceConstructor construct_Slot2_Analog; slot2_List[NDS_SLOT2_NONE] = construct_Slot2_None(); slot2_List[NDS_SLOT2_AUTO] = construct_Slot2_Auto(); @@ -69,6 +70,7 @@ void slot2_Init() slot2_List[NDS_SLOT2_EASYPIANO] = construct_Slot2_EasyPiano(); slot2_List[NDS_SLOT2_PADDLE] = construct_Slot2_Paddle(); slot2_List[NDS_SLOT2_PASSME] = construct_Slot2_PassME(); + slot2_List[NDS_SLOT2_ANALOG] = construct_Slot2_Analog(); } @@ -252,6 +254,7 @@ NDS_SLOT2_TYPE slot2_DetermineTypeByGameCode(const char *theGameCode) {"CV8", NDS_SLOT2_PADDLE}, // Space Invaders Extreme 2 {"AMH", NDS_SLOT2_RUMBLEPAK}, // Metroid Prime Hunters {"AP2", NDS_SLOT2_RUMBLEPAK}, // Metroid Prime Pinball + {"ASM", NDS_SLOT2_ANALOG}, // Super Mario 64 DS }; for(size_t i = 0; i < ARRAY_SIZE(gameCodeDeviceTypes); i++) diff --git a/desmume/src/slot2.h b/desmume/src/slot2.h index dd015fe23..fd1707de8 100644 --- a/desmume/src/slot2.h +++ b/desmume/src/slot2.h @@ -97,6 +97,7 @@ enum NDS_SLOT2_TYPE NDS_SLOT2_EASYPIANO, // 0x06 - Easy Piano NDS_SLOT2_PADDLE, // 0x07 - Arkanoids DS paddle NDS_SLOT2_PASSME, // 0x08 - PassME/Homebrew + NDS_SLOT2_ANALOG, // 0x09 - Analog Stick Hack NDS_SLOT2_COUNT // use for counter addons - MUST TO BE LAST!!! }; @@ -157,4 +158,5 @@ void Paddle_SetValue(u16 theValue); extern void guitarGrip_setKey(bool green, bool red, bool yellow, bool blue); // Guitar grip keys extern void piano_setKey(bool c, bool cs, bool d, bool ds, bool e, bool f, bool fs, bool g, bool gs, bool a, bool as, bool b, bool hic); //piano keys +extern void analog_setValue(float x, float y); // Analog Stick State #endif //__SLOT_H__