diff --git a/source/CmdLine.cpp b/source/CmdLine.cpp index bcac6e85..175ad8c2 100644 --- a/source/CmdLine.cpp +++ b/source/CmdLine.cpp @@ -36,6 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Keyboard.h" #include "Joystick.h" #include "SoundCore.h" +#include "SNESMAX.h" #include "ParallelPrinter.h" #include "Interface.h" @@ -48,6 +49,7 @@ bool g_bHookSystemKey = true; bool g_bHookAltTab = false; bool g_bHookAltGrControl = false; + static LPSTR GetCurrArg(LPSTR lpCmdLine) { if (*lpCmdLine == '\"') @@ -549,6 +551,20 @@ bool ProcessCmdLine(LPSTR lpCmdLine) { g_cmdLine.snesMaxAltControllerType[1] = true; } + else if (strcmp(lpCmdLine, "-snes-max-user-joy1") == 0 || strcmp(lpCmdLine, "-snes-max-user-joy2") == 0) + { + const unsigned int joyNum = (strcmp(lpCmdLine, "-snes-max-user-joy1") == 0) ? 0 : 1; + + lpCmdLine = GetCurrArg(lpNextArg); + lpNextArg = GetNextArg(lpNextArg); + + std::string errorMsg; + if (!SNESMAXCard::ParseControllerMappingFile(joyNum, lpCmdLine, errorMsg)) + { + LogFileOutput("%s", errorMsg.c_str()); + GetFrame().FrameMessageBox(errorMsg.c_str(), TEXT("AppleWin Error"), MB_OK); + } + } else if (strcmp(lpCmdLine, "-wav-speaker") == 0) { lpCmdLine = GetCurrArg(lpNextArg); diff --git a/source/SNESMAX.cpp b/source/SNESMAX.cpp index 788f85df..6115b135 100644 --- a/source/SNESMAX.cpp +++ b/source/SNESMAX.cpp @@ -43,11 +43,10 @@ Bit 6 = Controller 2, Active Low Bit 7 = Controller 1, Active Low - Once data is read, button presses (for each controller) should be stored in the following structure - Byte 0: B:Y:Sl:St:U:D:L:R - Byte 1: A:X:Fl:Fr:x:x:x:x + Once data is read, button presses (for each controller) are stored in the following structure: - The variable controllerXButtons will stored in the reverse order. + bit# 11 10 9 8 / 7 6 5 4 3 2 1 0 + m_controllerXButtons: x:x:x:x:Fr:Fl:X:A / R:L:D:U:St:Sl:Y:B Alex Lukacz Aug 2021 */ @@ -57,6 +56,16 @@ #include "Memory.h" #include "YamlHelper.h" +// AltControllerType defaults to "8BitDo NES30 PRO" +// b11,..,b0: Sr,Sl,R,L / -,-,-,Y,X,-,B,A +// +// infoEx.dwButtons bit definitions: +UINT SNESMAXCard::m_altControllerButtons[2][NUM_BUTTONS] = +{ + {A,B,UNUSED,X,Y,UNUSED,UNUSED,UNUSED, LB,RB,SELECT,START}, // bit0 -> A, bit1 -> B, bit2 -> unused, etc + {A,B,UNUSED,X,Y,UNUSED,UNUSED,UNUSED, LB,RB,SELECT,START} +}; + BYTE __stdcall SNESMAXCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles) { const UINT slot = ((addr & 0xff) >> 4) - 8; @@ -100,90 +109,12 @@ BYTE __stdcall SNESMAXCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE value, result = joyGetPosEx(JOYSTICKID1, &infoEx); if (result == JOYERR_NOERROR) - { - xAxis = (infoEx.dwXpos >> 8) & 0xFF; - yAxis = (infoEx.dwYpos >> 8) & 0xFF; - controller1Buttons = controller1Buttons | ((yAxis < 103 || infoEx.dwPOV == 0 || infoEx.dwPOV == 4500 || infoEx.dwPOV == 31500) << 4); // U Button - controller1Buttons = controller1Buttons | ((yAxis > 153 || (infoEx.dwPOV >= 13500 && infoEx.dwPOV <= 22500)) << 5); // D Button - controller1Buttons = controller1Buttons | ((xAxis < 103 || (infoEx.dwPOV >= 22500 && infoEx.dwPOV <= 31500)) << 6); // L Button - controller1Buttons = controller1Buttons | ((xAxis > 153 || (infoEx.dwPOV >= 4500 && infoEx.dwPOV <= 13500)) << 7); // R Button -// controller1Buttons = controller1Buttons | 0 * 0x1000; // spare Button -// controller1Buttons = controller1Buttons | 0 * 0x2000; // spare Button -// controller1Buttons = controller1Buttons | 0 * 0x4000; // spare Button -// controller1Buttons = controller1Buttons | 0 * 0x8000; // spare Button - - if (pCard->m_altControllerType[0]) - { - // 8BitDo NES30 PRO - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0002) >> 1); // B Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0010) >> 3); // Y Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0400) >> 8); // Sl Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0800) >> 8); // St Button - - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0001) << 8); // A Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0008) << 6); // X Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0100) << 2) | ((infoEx.dwButtons & 0x0040) << 4); // Fl Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0200) << 2) | ((infoEx.dwButtons & 0x0080) << 4); // Fr Button - } - else - { - // Logitech F310, Dualshock 4 - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0002) >> 1); // B Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0001) << 1); // Y Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0100) >> 6); // Sl Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0200) >> 6); // St Button - - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0004) << 6); // A Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0008) << 6); // X Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0010) << 6) | ((infoEx.dwButtons & 0x0040) << 4); // Fl Button - controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0020) << 6) | ((infoEx.dwButtons & 0x0080) << 4); // Fr Button - } - controller1Buttons = controller1Buttons | 0x10000; // Controller plugged in status. - } + controller1Buttons = pCard->GetControllerButtons(JOYSTICKID1, infoEx, pCard->m_altControllerType[0]); controller1Buttons = ~controller1Buttons; result = joyGetPosEx(JOYSTICKID2, &infoEx); if (result == JOYERR_NOERROR) - { - xAxis = (infoEx.dwXpos >> 8) & 0xFF; - yAxis = (infoEx.dwYpos >> 8) & 0xFF; - controller2Buttons = controller2Buttons | ((yAxis < 103 || infoEx.dwPOV == 0 || infoEx.dwPOV == 4500 || infoEx.dwPOV == 31500) << 4); // U Button - controller2Buttons = controller2Buttons | ((yAxis > 153 || (infoEx.dwPOV >= 13500 && infoEx.dwPOV <= 22500)) << 5); // D Button - controller2Buttons = controller2Buttons | ((xAxis < 103 || (infoEx.dwPOV >= 22500 && infoEx.dwPOV <= 31500)) << 6); // L Button - controller2Buttons = controller2Buttons | ((xAxis > 153 || (infoEx.dwPOV >= 4500 && infoEx.dwPOV <= 13500)) << 7); // R Button -// controller2Buttons = controller2Buttons | 0 * 0x1000; // spare Button -// controller2Buttons = controller2Buttons | 0 * 0x2000; // spare Button -// controller2Buttons = controller2Buttons | 0 * 0x4000; // spare Button -// controller2Buttons = controller2Buttons | 0 * 0x8000; // spare Button - - if (pCard->m_altControllerType[1]) - { - // 8BitDo NES30 PRO - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0002) >> 1); // B Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0010) >> 3); // Y Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0400) >> 8); // Sl Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0800) >> 8); // St Button - - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0001) << 8); // A Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0008) << 6); // X Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0100) << 2) | ((infoEx.dwButtons & 0x0040) << 4); // Fl Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0200) << 2) | ((infoEx.dwButtons & 0x0080) << 4); // Fr Button - } - else - { - // Logitech F310, Dualshock 4 - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0002) >> 1); // B Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0001) << 1); // Y Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0100) >> 6); // Sl Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0200) >> 6); // St Button - - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0004) << 6); // A Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0008) << 6); // X Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0010) << 6) | ((infoEx.dwButtons & 0x0040) << 4); // Fl Button - controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0020) << 6) | ((infoEx.dwButtons & 0x0080) << 4); // Fr Button - } - controller2Buttons = controller2Buttons | 0x10000; // Controller plugged in status. - } + controller2Buttons = pCard->GetControllerButtons(JOYSTICKID2, infoEx, pCard->m_altControllerType[1]); controller2Buttons = ~controller2Buttons; break; @@ -202,11 +133,126 @@ BYTE __stdcall SNESMAXCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE value, return 0; } +UINT SNESMAXCard::GetControllerButtons(UINT joyNum, JOYINFOEX& infoEx, bool altControllerType) +{ + UINT xAxis = (infoEx.dwXpos >> 8) & 0xFF; + UINT yAxis = (infoEx.dwYpos >> 8) & 0xFF; + + UINT controllerButtons = 0; + controllerButtons |= ((yAxis < 103 || infoEx.dwPOV == 0 || infoEx.dwPOV == 4500 || infoEx.dwPOV == 31500) << 4); // U Button + controllerButtons |= ((yAxis > 153 || (infoEx.dwPOV >= 13500 && infoEx.dwPOV <= 22500)) << 5); // D Button + controllerButtons |= ((xAxis < 103 || (infoEx.dwPOV >= 22500 && infoEx.dwPOV <= 31500)) << 6); // L Button + controllerButtons |= ((xAxis > 153 || (infoEx.dwPOV >= 4500 && infoEx.dwPOV <= 13500)) << 7); // R Button +// controllerButtons |= 0 * 0x1000; // spare Button +// controllerButtons |= 0 * 0x2000; // spare Button +// controllerButtons |= 0 * 0x4000; // spare Button +// controllerButtons |= 0 * 0x8000; // spare Button + + if (!altControllerType) + { + // Logitech F310, Sony DualShock 4 + // b11,..,b0: -,-,St,Sl / -,-,R,L,X,A,B,Y + controllerButtons |= ((infoEx.dwButtons & 0x0002) >> 1); // B Button + controllerButtons |= ((infoEx.dwButtons & 0x0001) << 1); // Y Button + controllerButtons |= ((infoEx.dwButtons & 0x0100) >> 6); // Sl Button + controllerButtons |= ((infoEx.dwButtons & 0x0200) >> 6); // St Button + + controllerButtons |= ((infoEx.dwButtons & 0x0004) << 6); // A Button + controllerButtons |= ((infoEx.dwButtons & 0x0008) << 6); // X Button + controllerButtons |= ((infoEx.dwButtons & 0x0010) << 6) | ((infoEx.dwButtons & 0x0040) << 4); // Fl Button + controllerButtons |= ((infoEx.dwButtons & 0x0020) << 6) | ((infoEx.dwButtons & 0x0080) << 4); // Fr Button + } + else + { + for (UINT i = 0; i < 12; i++) + { + if (infoEx.dwButtons & (1 << i)) + { + if (m_altControllerButtons[joyNum][i] != UNUSED) + controllerButtons |= (1 << m_altControllerButtons[joyNum][i]); + } + } + } + + return controllerButtons | 0x10000; // Controller plugged in status. +} + void SNESMAXCard::InitializeIO(LPBYTE pCxRomPeripheral) { RegisterIoHandler(m_slot, &SNESMAXCard::IORead, &SNESMAXCard::IOWrite, IO_Null, IO_Null, this, NULL); } +bool SNESMAXCard::ParseControllerMappingFile(UINT joyNum, const char* pathname, std::string& errorMsg) +{ + bool res = true; + YamlHelper yamlHelper; + + try + { + if (!yamlHelper.InitParser(pathname)) + throw std::runtime_error("Controller mapping file: Failed to initialize parser or open file"); + + if (yamlHelper.ParseFileHdr("AppleWin Controller Button Remapping") != 1) + throw std::runtime_error("Controller mapping file: Version mismatch"); + + std::string scalar; + while (yamlHelper.GetScalar(scalar)) + { + if (scalar == SS_YAML_KEY_UNIT) + { + yamlHelper.GetMapStartEvent(); + YamlLoadHelper yamlLoadHelper(yamlHelper); + std::string hid = yamlLoadHelper.LoadString("HID"); + std::string desc = yamlLoadHelper.LoadString("Description"); + for (UINT i = 0; i < SNESMAXCard::NUM_BUTTONS; i++) + { + char szButtonNum[3] = "00"; + sprintf_s(szButtonNum, "%d", i + 1); // +1 as 1-based + std::string buttonNum = szButtonNum; + bool found = false; + std::string buttonStr = yamlLoadHelper.LoadString_NoThrow(buttonNum, found); + SNESMAXCard::Button button = SNESMAXCard::UNUSED; + if (found) + { + if (buttonStr == "A") button = SNESMAXCard::A; + else if (buttonStr == "B") button = SNESMAXCard::B; + else if (buttonStr == "X") button = SNESMAXCard::X; + else if (buttonStr == "Y") button = SNESMAXCard::Y; + else if (buttonStr == "LB") button = SNESMAXCard::LB; + else if (buttonStr == "RB") button = SNESMAXCard::RB; + else if (buttonStr == "SELECT") button = SNESMAXCard::SELECT; + else if (buttonStr == "START") button = SNESMAXCard::START; + else if (buttonStr == "") button = SNESMAXCard::UNUSED; + else throw std::runtime_error("Controller mapping file: Unknown button: " + buttonStr); + } + m_altControllerButtons[joyNum][i] = button; + } + } + else + { + throw std::runtime_error("Unknown top-level scalar: " + scalar); + } + + break; // TODO: extend to support multiple controllers + } + } + catch (const std::exception& szMessage) + { + errorMsg = "Error with yaml file: "; + errorMsg += pathname; + errorMsg += "\n"; + errorMsg += szMessage.what(); + res = false; + } + + yamlHelper.FinaliseParser(); + + if (res) + g_cmdLine.snesMaxAltControllerType[joyNum] = true; // Enable the alt controller + + return res; +} + //=========================================================================== static const UINT kUNIT_VERSION = 1; diff --git a/source/SNESMAX.h b/source/SNESMAX.h index f188a0f7..99edda24 100644 --- a/source/SNESMAX.h +++ b/source/SNESMAX.h @@ -31,10 +31,17 @@ public: virtual void SaveSnapshot(YamlSaveHelper& yamlSaveHelper); virtual bool LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version); + static bool ParseControllerMappingFile(UINT joyNum, const char* pathname, std::string& errorMsg); + private: + UINT GetControllerButtons(UINT joyNum, JOYINFOEX& infoEx, bool altControllerType); + + enum Button { B, Y, SELECT, START, U, D, L, R, A, X, LB, RB, UNUSED1, UNUSED2, UNUSED3, UNUSED4, NUM_BUTTONS, UNUSED }; + UINT m_buttonIndex; UINT m_controller1Buttons; UINT m_controller2Buttons; bool m_altControllerType[2]; + static UINT m_altControllerButtons[2][NUM_BUTTONS]; }; diff --git a/source/SaveState.cpp b/source/SaveState.cpp index 063db8d1..c9e9f92a 100644 --- a/source/SaveState.cpp +++ b/source/SaveState.cpp @@ -237,33 +237,6 @@ static std::string GetApple2TypeAsString(void) //--- -static UINT ParseFileHdr(void) -{ - std::string scalar; - if (!yamlHelper.GetScalar(scalar)) - throw std::runtime_error(SS_YAML_KEY_FILEHDR ": Failed to find scalar"); - - if (scalar != SS_YAML_KEY_FILEHDR) - throw std::runtime_error("Failed to find file header"); - - yamlHelper.GetMapStartEvent(); - - YamlLoadHelper yamlLoadHelper(yamlHelper); - - // - - std::string value = yamlLoadHelper.LoadString(SS_YAML_KEY_TAG); - if (value != SS_YAML_VALUE_AWSS) - { - //printf("%s: Bad tag (%s) - expected %s\n", SS_YAML_KEY_FILEHDR, value.c_str(), SS_YAML_VALUE_AWSS); - throw std::runtime_error(SS_YAML_KEY_FILEHDR ": Bad tag"); - } - - return yamlLoadHelper.LoadUint(SS_YAML_KEY_VERSION); -} - -//--- - static void ParseUnitApple2(YamlLoadHelper& yamlLoadHelper, UINT version) { if (version == 0 || version > UNIT_APPLE2_VER) @@ -376,10 +349,10 @@ static void Snapshot_LoadState_v2(void) try { - if (!yamlHelper.InitParser( g_strSaveStatePathname.c_str() )) + if (!yamlHelper.InitParser(g_strSaveStatePathname.c_str())) throw std::runtime_error("Failed to initialize parser or open file: " + g_strSaveStatePathname); - if (ParseFileHdr() != SS_FILE_VER) + if (yamlHelper.ParseFileHdr(SS_YAML_VALUE_AWSS) != SS_FILE_VER) throw std::runtime_error("Version mismatch"); // diff --git a/source/YamlHelper.cpp b/source/YamlHelper.cpp index d42b991b..87fc37b2 100644 --- a/source/YamlHelper.cpp +++ b/source/YamlHelper.cpp @@ -58,6 +58,31 @@ void YamlHelper::FinaliseParser(void) yaml_parser_delete(&m_parser); } +UINT YamlHelper::ParseFileHdr(const char* tag) +{ + std::string scalar; + if (!GetScalar(scalar)) + throw std::runtime_error(SS_YAML_KEY_FILEHDR ": Failed to find scalar"); + + if (scalar != SS_YAML_KEY_FILEHDR) + throw std::runtime_error("Failed to find file header"); + + GetMapStartEvent(); + + YamlLoadHelper yamlLoadHelper(*this); + + // + + std::string value = yamlLoadHelper.LoadString(SS_YAML_KEY_TAG); + if (value != tag) + { + //printf("%s: Bad tag (%s) - expected %s\n", SS_YAML_KEY_FILEHDR, value.c_str(), tag); + throw std::runtime_error(SS_YAML_KEY_FILEHDR ": Bad tag"); + } + + return yamlLoadHelper.LoadUint(SS_YAML_KEY_VERSION); +} + void YamlHelper::GetNextEvent(void) { yaml_event_delete(&m_newEvent); diff --git a/source/YamlHelper.h b/source/YamlHelper.h index a95cdb3c..33345183 100644 --- a/source/YamlHelper.h +++ b/source/YamlHelper.h @@ -44,6 +44,8 @@ public: int InitParser(const char* pPathname); void FinaliseParser(void); + UINT ParseFileHdr(const char* tag); + int GetScalar(std::string& scalar); void GetMapStartEvent(void);