From bede5cc0cac35fa741a37d25c93ffb1b9b9655ac Mon Sep 17 00:00:00 2001 From: thrust26 Date: Mon, 6 May 2024 19:32:14 +0200 Subject: [PATCH] added auto detection for QuadTari controllers --- src/emucore/Console.cxx | 2 +- src/emucore/ControllerDetector.cxx | 31 ++++++---- src/emucore/ControllerDetector.hxx | 37 ++++++------ src/emucore/Props.cxx | 8 +-- src/emucore/QuadTari.cxx | 32 ++++++++--- src/emucore/QuadTari.hxx | 4 +- src/gui/GameInfoDialog.cxx | 2 +- src/gui/QuadTariDialog.cxx | 92 ++++++++++++++++++++++++++---- src/gui/QuadTariDialog.hxx | 6 ++ 9 files changed, 159 insertions(+), 55 deletions(-) diff --git a/src/emucore/Console.cxx b/src/emucore/Console.cxx index f760ae2b4..dd2e456fd 100644 --- a/src/emucore/Console.cxx +++ b/src/emucore/Console.cxx @@ -1111,7 +1111,7 @@ unique_ptr Console::getControllerPort( break; case Controller::Type::QuadTari: - controller = make_unique(port, myOSystem, *mySystem, myProperties); + controller = make_unique(port, myOSystem, *mySystem, myProperties, *myCart); break; case Controller::Type::Joy2BPlus: diff --git a/src/emucore/ControllerDetector.cxx b/src/emucore/ControllerDetector.cxx index cce87137e..2f750996b 100644 --- a/src/emucore/ControllerDetector.cxx +++ b/src/emucore/ControllerDetector.cxx @@ -24,11 +24,12 @@ Controller::Type ControllerDetector::detectType( const ByteBuffer& image, size_t size, const Controller::Type type, const Controller::Jack port, - const Settings& settings) + const Settings& settings, bool isQuadTari) { if(type == Controller::Type::Unknown || settings.getBool("rominfo")) { - const Controller::Type detectedType = autodetectPort(image, size, port, settings); + const Controller::Type detectedType + = autodetectPort(image, size, port, settings, isQuadTari); if(type != Controller::Type::Unknown && type != detectedType) { @@ -46,22 +47,22 @@ Controller::Type ControllerDetector::detectType( // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - string ControllerDetector::detectName(const ByteBuffer& image, size_t size, const Controller::Type type, const Controller::Jack port, - const Settings& settings) + const Settings& settings, bool isQuadTari) { - return Controller::getName(detectType(image, size, type, port, settings)); + return Controller::getName(detectType(image, size, type, port, settings, isQuadTari)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Controller::Type ControllerDetector::autodetectPort( const ByteBuffer& image, size_t size, - Controller::Jack port, const Settings& settings) + Controller::Jack port, const Settings& settings, bool isQuadTari) { // default type joystick Controller::Type type = Controller::Type::Joystick; if(isProbablySaveKey(image, size, port)) type = Controller::Type::SaveKey; - else if(isProbablyQuadTari(image, size, port)) + else if(!isQuadTari && isProbablyQuadTari(image, size, port)) type = Controller::Type::QuadTari; else if(usesJoystickButton(image, size, port)) { @@ -89,6 +90,8 @@ Controller::Type ControllerDetector::autodetectPort( type = Controller::Type::Paddles; else if(isProbablyKidVid(image, size, port)) type = Controller::Type::KidVid; + else if (isQuadTari) // currently most likely assumption + type = Controller::Type::Paddles; } // TODO: BOOSTERGRIP, DRIVING, COMPUMATE, MINDLINK, ATARIVOX // not detectable: PADDLES_IAXIS, PADDLES_IAXDR @@ -126,7 +129,7 @@ bool ControllerDetector::usesJoystickButton(const ByteBuffer& image, size_t size if(port == Controller::Jack::Left) { // check for INPT4 access - static constexpr int NUM_SIGS_0 = 24; + static constexpr int NUM_SIGS_0 = 25; static constexpr int SIG_SIZE_0 = 3; static constexpr uInt8 signature_0[NUM_SIGS_0][SIG_SIZE_0] = { { 0x24, 0x0c, 0x10 }, // bit INPT4; bpl (joystick games only) @@ -152,9 +155,10 @@ bool ControllerDetector::usesJoystickButton(const ByteBuffer& image, size_t size { 0xa5, 0x0c, 0x25 }, // lda INPT4; and (joystick games only) { 0xa6, 0x3c, 0x30 }, // ldx INPT4|$30; bmi (joystick games only) { 0xa6, 0x0c, 0x30 }, // ldx INPT4; bmi - { 0xa5, 0x0c, 0x0a } // lda INPT4; asl (joystick games only) + { 0xa5, 0x0c, 0x0a }, // lda INPT4; asl (joystick games only) + { 0xb5, 0x0c, 0x4a } // lda INPT4,x; lsr (joystick games only) }; - static constexpr int NUM_SIGS_1 = 9; + static constexpr int NUM_SIGS_1 = 11; static constexpr int SIG_SIZE_1 = 4; static constexpr uInt8 signature_1[NUM_SIGS_1][SIG_SIZE_1] = { { 0xb9, 0x0c, 0x00, 0x10 }, // lda INPT4,y; bpl (joystick games only) @@ -165,9 +169,11 @@ bool ControllerDetector::usesJoystickButton(const ByteBuffer& image, size_t size { 0xb5, 0x0c, 0x29, 0x80 }, // lda INPT4,x; and #$80 (joystick games only) { 0xb5, 0x3c, 0x29, 0x80 }, // lda INPT4|$30,x; and #$80 (joystick games only) { 0xa5, 0x0c, 0x29, 0x80 }, // lda INPT4; and #$80 (joystick games only) - { 0xa5, 0x3c, 0x29, 0x80 } // lda INPT4|$30; and #$80 (joystick games only) + { 0xa5, 0x3c, 0x29, 0x80 }, // lda INPT4|$30; and #$80 (joystick games only) + { 0xa5, 0x0c, 0x49, 0x80 }, // lda INPT4; eor #$80 (Lady Bug Arcade only) + { 0xb9, 0x0c, 0x00, 0x4a } // lda INPT4,y; lsr (Wizard of Wor Arcade only) }; - static constexpr int NUM_SIGS_2 = 9; + static constexpr int NUM_SIGS_2 = 10; static constexpr int SIG_SIZE_2 = 5; static constexpr uInt8 signature_2[NUM_SIGS_2][SIG_SIZE_2] = { { 0xa5, 0x0c, 0x25, 0x0d, 0x10 }, // lda INPT4; and INPT5; bpl (joystick games only) @@ -178,7 +184,8 @@ bool ControllerDetector::usesJoystickButton(const ByteBuffer& image, size_t size { 0xa9, 0x80, 0x24, 0x0c, 0xd0 }, // lda #$80; bit INPT4; bne (bBasic) { 0xa5, 0x0c, 0x29, 0x80, 0xd0 }, // lda INPT4; and #$80; bne (joystick games only) { 0xa5, 0x3c, 0x29, 0x80, 0xd0 }, // lda INPT4|$30; and #$80; bne (joystick games only) - { 0xad, 0x0c, 0x00, 0x29, 0x80 } // lda.w INPT4|$30; and #$80 (joystick games only) + { 0xad, 0x0c, 0x00, 0x29, 0x80 }, // lda.w INPT4; and #$80 (joystick games only) + { 0xb9, 0x0c, 0x00, 0x29, 0x80 } // lda.w INPT4,y; and #$80 (joystick games only) }; for(const auto* const sig: signature_0) diff --git a/src/emucore/ControllerDetector.hxx b/src/emucore/ControllerDetector.hxx index ce9d5e1ae..38b098113 100644 --- a/src/emucore/ControllerDetector.hxx +++ b/src/emucore/ControllerDetector.hxx @@ -34,46 +34,49 @@ class ControllerDetector /** Detects the controller type at the given port if no controller is provided. - @param image A reference to the ROM image - @param size The size of the ROM image - @param type The provided controller type of the ROM image - @param port The port to be checked - @param settings A reference to the various settings (read-only) + @param image A reference to the ROM image + @param size The size of the ROM image + @param type The provided controller type of the ROM image + @param port The port to be checked + @param settings A reference to the various settings (read-only) + @param isQuadTari If true, try to detect the QuadTari's controllers @return The detected controller type */ static Controller::Type detectType(const ByteBuffer& image, size_t size, const Controller::Type type, const Controller::Jack port, - const Settings& settings); + const Settings& settings, bool isQuadTari = false); /** Detects the controller type at the given port if no controller is provided and returns its name. - @param image A reference to the ROM image - @param size The size of the ROM image - @param type The provided controller type of the ROM image - @param port The port to be checked - @param settings A reference to the various settings (read-only) + @param image A reference to the ROM image + @param size The size of the ROM image + @param type The provided controller type of the ROM image + @param port The port to be checked + @param settings A reference to the various settings (read-only) + @param isQuadTari If true, try to detect the QuadTari's controllers @return The (detected) controller name */ static string detectName(const ByteBuffer& image, size_t size, const Controller::Type type, const Controller::Jack port, - const Settings& settings); + const Settings& settings, bool isQuadTari = false); private: /** Detects the controller type at the given port. - @param image A reference to the ROM image - @param size The size of the ROM image - @param port The port to be checked - @param settings A reference to the various settings (read-only) + @param image A reference to the ROM image + @param size The size of the ROM image + @param port The port to be checked + @param settings A reference to the various settings (read-only) + @param isQuadTari If true, try to detect the QuadTari's controllers @return The detected controller type */ static Controller::Type autodetectPort(const ByteBuffer& image, size_t size, - Controller::Jack port, const Settings& settings); + Controller::Jack port, const Settings& settings, bool isQuadTari); /** Search the image for the specified byte signature. diff --git a/src/emucore/Props.cxx b/src/emucore/Props.cxx index 5b020e45f..1a88d56c3 100644 --- a/src/emucore/Props.cxx +++ b/src/emucore/Props.cxx @@ -266,11 +266,11 @@ std::array Properties::ourDefaultProperties = "COLOR", // Console.TVType "NO", // Console.SwapPorts "AUTO", // Controller.Left - "", // Controller.Left1 - "", // Controller.Left2 + "AUTO", // Controller.Left1 + "AUTO", // Controller.Left2 "AUTO", // Controller.Right - "", // Controller.Right1 - "", // Controller.Right2 + "AUTO", // Controller.Right1 + "AUTO", // Controller.Right2 "NO", // Controller.SwapPaddles "12", // Controller.PaddlesXCenter "12", // Controller.PaddlesYCenter diff --git a/src/emucore/QuadTari.cxx b/src/emucore/QuadTari.cxx index ff8a1444d..3c3beefcf 100644 --- a/src/emucore/QuadTari.cxx +++ b/src/emucore/QuadTari.cxx @@ -21,6 +21,8 @@ #include "System.hxx" #include "TIA.hxx" #include "FrameBuffer.hxx" +#include "ControllerDetector.hxx" +#include "Cart.hxx" #include "AtariVox.hxx" #include "Driving.hxx" #include "Joystick.hxx" @@ -30,14 +32,13 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - QuadTari::QuadTari(Jack jack, const OSystem& osystem, const System& system, - const Properties& properties) + const Properties& properties, Cartridge& cart) : Controller(jack, osystem.eventHandler().event(), system, Controller::Type::QuadTari), myOSystem{osystem}, myProperties{properties} { - Controller::Type firstType = Controller::Type::Joystick, - secondType = Controller::Type::Joystick; + Controller::Type firstType, secondType; string first, second; if(jack == Controller::Jack::Left) @@ -50,11 +51,28 @@ QuadTari::QuadTari(Jack jack, const OSystem& osystem, const System& system, first = properties.get(PropType::Controller_Right1); second = properties.get(PropType::Controller_Right2); } + firstType = Controller::getType(first); + secondType = Controller::getType(second); - if(!first.empty()) - firstType = Controller::getType(first); - if(!second.empty()) - secondType = Controller::getType(second); + // Autodetect QuadTari controllers: + // This will detect the same controller for 1st and 2nd controller + size_t size = 0; + const ByteBuffer& image = cart.getImage(size); + + if(image != nullptr && size != 0) + { + if(firstType == Controller::Type::Unknown || secondType == Controller::Type::Unknown) + { + Controller::Type autodetected = Controller::Type::Unknown; + autodetected = ControllerDetector::detectType(image, size, autodetected, + jack, myOSystem.settings(), true); + + if(firstType == Controller::Type::Unknown) + firstType = autodetected; + if(secondType == Controller::Type::Unknown) + secondType = autodetected; + } + } myFirstController = addController(firstType, false); mySecondController = addController(secondType, true); diff --git a/src/emucore/QuadTari.hxx b/src/emucore/QuadTari.hxx index fad5d04e9..238ecc8df 100644 --- a/src/emucore/QuadTari.hxx +++ b/src/emucore/QuadTari.hxx @@ -20,6 +20,7 @@ class Controller; class Event; +class Cartridge; /** The QuadTari controller. @@ -45,7 +46,8 @@ class QuadTari : public Controller @param system The system using this controller @param properties The properties to use for the current ROM */ - QuadTari(Jack jack, const OSystem& osystem, const System& system, const Properties& properties); + QuadTari(Jack jack, const OSystem& osystem, const System& system, + const Properties& properties, Cartridge& cart); ~QuadTari() override = default; public: diff --git a/src/gui/GameInfoDialog.cxx b/src/gui/GameInfoDialog.cxx index 0898bc3a3..af8126297 100644 --- a/src/gui/GameInfoDialog.cxx +++ b/src/gui/GameInfoDialog.cxx @@ -1559,7 +1559,7 @@ void GameInfoDialog::handleCommand(CommandSender* sender, int cmd, if(!myQuadTariDialog) myQuadTariDialog = make_unique - (this, _font, _font.getMaxCharWidth() * 37, _font.getFontHeight() * 8, + (this, _font, _font.getMaxCharWidth() * 42, _font.getFontHeight() * 10, myGameProperties); myQuadTariDialog->show(enableLeft, enableRight); break; diff --git a/src/gui/QuadTariDialog.cxx b/src/gui/QuadTariDialog.cxx index 3249772ec..cd83b238f 100644 --- a/src/gui/QuadTariDialog.cxx +++ b/src/gui/QuadTariDialog.cxx @@ -16,6 +16,7 @@ //============================================================================ #include "OSystem.hxx" +#include "Console.hxx" #include "EventHandler.hxx" #include "Widget.hxx" #include "PopUpWidget.hxx" @@ -23,6 +24,9 @@ #include "Variant.hxx" #include "Props.hxx" #include "PropsSet.hxx" +#include "Launcher.hxx" +#include "ControllerDetector.hxx" +#include "QuadTari.hxx" #include "QuadTariDialog.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -31,7 +35,9 @@ QuadTariDialog::QuadTariDialog(GuiObject* boss, const GUI::Font& font, int max_w : Dialog(boss->instance(), boss->parent(), font, "QuadTari controllers", 0, 0, max_w, max_h), myGameProperties{properties} { + const GUI::Font& ifont = instance().frameBuffer().infoFont(); const int lineHeight = Dialog::lineHeight(), + fontWidth = Dialog::fontWidth(), VBORDER = Dialog::vBorder(), HBORDER = Dialog::hBorder(), VGAP = Dialog::vGap(); @@ -41,7 +47,7 @@ QuadTariDialog::QuadTariDialog(GuiObject* boss, const GUI::Font& font, int max_w int xpos = HBORDER, ypos = VBORDER + _th; ctrls.clear(); - //VarList::push_back(ctrls, "Auto-detect", "AUTO"); + VarList::push_back(ctrls, "Auto-detect", "AUTO"); VarList::push_back(ctrls, "Joystick", "JOYSTICK"); VarList::push_back(ctrls, "Paddles", "PADDLES"); //VarList::push_back(ctrls, "Paddles_IAxis", "PADDLES_IAXIS"); @@ -61,7 +67,7 @@ QuadTariDialog::QuadTariDialog(GuiObject* boss, const GUI::Font& font, int max_w //VarList::push_back(ctrls, "MindLink", "MINDLINK"); //VarList::push_back(ctrls, "QuadTari", "QUADTARI"); - const int pwidth = font.getStringWidth("Joystick12"); // a bit wider looks better overall + const int pwidth = font.getStringWidth("Auto-detect "); // a bit wider looks better overall myLeftPortLabel = new StaticTextWidget(this, font, xpos, ypos + 1, "Left port"); @@ -69,11 +75,19 @@ QuadTariDialog::QuadTariDialog(GuiObject* boss, const GUI::Font& font, int max_w myLeft1Port = new PopUpWidget(this, font, xpos, ypos, pwidth, lineHeight, ctrls, "P1 "); wid.push_back(myLeft1Port); + ypos += lineHeight + VGAP; + + myLeft1PortDetected = new StaticTextWidget(this, ifont, + myLeft1Port->getLeft() + fontWidth * 3, ypos, "AtariVox detected"); + ypos += lineHeight + VGAP; - ypos += lineHeight + VGAP * 2; myLeft2Port = new PopUpWidget(this, font, xpos, ypos, pwidth, lineHeight, ctrls, "P3 "); wid.push_back(myLeft2Port); + ypos += lineHeight + VGAP; + + myLeft2PortDetected = new StaticTextWidget(this, ifont, + myLeft2Port->getLeft() + fontWidth * 3, ypos, "AtariVox detected"); xpos = _w - HBORDER - myLeft1Port->getWidth(); // aligned right ypos = myLeftPortLabel->getTop() - 1; @@ -83,11 +97,20 @@ QuadTariDialog::QuadTariDialog(GuiObject* boss, const GUI::Font& font, int max_w myRight1Port = new PopUpWidget(this, font, xpos, ypos, pwidth, lineHeight, ctrls, "P2 "); wid.push_back(myRight1Port); + ypos += lineHeight + VGAP; - ypos += lineHeight + VGAP * 2; + myRight1PortDetected = new StaticTextWidget(this, ifont, + myRight1Port->getLeft() + fontWidth * 3, ypos, "AtariVox detected"); + ypos += lineHeight + VGAP; + + //ypos += lineHeight + VGAP * 2; myRight2Port = new PopUpWidget(this, font, xpos, ypos, pwidth, lineHeight, ctrls, "P4 "); wid.push_back(myRight2Port); + ypos += lineHeight + VGAP; + + myRight2PortDetected = new StaticTextWidget(this, ifont, + myRight2Port->getLeft() + fontWidth * 3, ypos, "AtariVox detected"); addDefaultsOKCancelBGroup(wid, _font); addBGroupToFocusList(wid); @@ -115,21 +138,66 @@ void QuadTariDialog::loadControllerProperties(const Properties& props) if(myLeftPortLabel->isEnabled()) { - controller = props.get(PropType::Controller_Left1); - myLeft1Port->setSelected(controller, "Joystick"); - controller = props.get(PropType::Controller_Left2); - myLeft2Port->setSelected(controller, "Joystick"); + defineController(props, PropType::Controller_Left1, Controller::Jack::Left, + myLeft1Port, myLeft1PortDetected); + defineController(props, PropType::Controller_Left2, Controller::Jack::Left, + myLeft2Port, myLeft2PortDetected, false); } if(myRightPortLabel->isEnabled()) { - controller = props.get(PropType::Controller_Right1); - myRight1Port->setSelected(controller, "Joystick"); - controller = props.get(PropType::Controller_Right2); - myRight2Port->setSelected(controller, "Joystick"); + defineController(props, PropType::Controller_Right1, Controller::Jack::Right, + myRight1Port, myRight1PortDetected); + defineController(props, PropType::Controller_Right2, Controller::Jack::Right, + myRight2Port, myRight2PortDetected, false); } } +void QuadTariDialog::defineController(const Properties& props, PropType key, + Controller::Jack jack, PopUpWidget* popupWidget, StaticTextWidget* labelWidget, bool first) +{ + bool autoDetect = false; + ByteBuffer image; + size_t size = 0; + + string controller = props.get(key); + popupWidget->setSelected(controller, "AUTO"); + + // try to load the image for auto detection + if(!instance().hasConsole()) + { + const FSNode& node = FSNode(instance().launcher().selectedRom()); + string md5 = myGameProperties.get(PropType::Cart_MD5); + + autoDetect = node.exists() && !node.isDirectory() + && (image = instance().openROM(node, md5, size)) != nullptr; + } + string label; + Controller::Type type = Controller::getType(popupWidget->getSelectedTag().toString()); + + if(type == Controller::Type::Unknown) + { + if(instance().hasConsole()) + { + const QuadTari* qt = dynamic_cast( + jack == Controller::Jack::Left + ? &instance().console().leftController() + : &instance().console().rightController()); + if(qt != nullptr) + label = (first + ? qt->firstController().name() + : qt->secondController().name()) + + " detected"; + else + label = "nothing detected"; + } + else if(autoDetect) + label = ControllerDetector::detectName( + image, size, type, jack, instance().settings(), true) + " detected"; + } + labelWidget->setLabel(label); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void QuadTariDialog::loadConfig() { diff --git a/src/gui/QuadTariDialog.hxx b/src/gui/QuadTariDialog.hxx index 44f6cb1f2..bb1485dde 100644 --- a/src/gui/QuadTariDialog.hxx +++ b/src/gui/QuadTariDialog.hxx @@ -44,15 +44,21 @@ class QuadTariDialog: public Dialog void setDefaults() override; void loadControllerProperties(const Properties& props); + void QuadTariDialog::defineController(const Properties& props, PropType key, + Controller::Jack jack, PopUpWidget* popup, StaticTextWidget* label, bool first = true); private: StaticTextWidget* myLeftPortLabel{nullptr}; PopUpWidget* myLeft1Port{nullptr}; + StaticTextWidget* myLeft1PortDetected{nullptr}; PopUpWidget* myLeft2Port{nullptr}; + StaticTextWidget* myLeft2PortDetected{nullptr}; StaticTextWidget* myRightPortLabel{nullptr}; PopUpWidget* myRight1Port{nullptr}; + StaticTextWidget* myRight1PortDetected{nullptr}; PopUpWidget* myRight2Port{nullptr}; + StaticTextWidget* myRight2PortDetected{nullptr}; // Game properties for currently loaded ROM Properties& myGameProperties;