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__