added auto detection for QuadTari controllers

This commit is contained in:
thrust26 2024-05-06 19:32:14 +02:00
parent 68e671169f
commit bede5cc0ca
9 changed files with 159 additions and 55 deletions

View File

@ -1111,7 +1111,7 @@ unique_ptr<Controller> Console::getControllerPort(
break;
case Controller::Type::QuadTari:
controller = make_unique<QuadTari>(port, myOSystem, *mySystem, myProperties);
controller = make_unique<QuadTari>(port, myOSystem, *mySystem, myProperties, *myCart);
break;
case Controller::Type::Joy2BPlus:

View File

@ -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)

View File

@ -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.

View File

@ -266,11 +266,11 @@ std::array<string, Properties::NUM_PROPS> 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

View File

@ -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);

View File

@ -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:

View File

@ -1559,7 +1559,7 @@ void GameInfoDialog::handleCommand(CommandSender* sender, int cmd,
if(!myQuadTariDialog)
myQuadTariDialog = make_unique<QuadTariDialog>
(this, _font, _font.getMaxCharWidth() * 37, _font.getFontHeight() * 8,
(this, _font, _font.getMaxCharWidth() * 42, _font.getFontHeight() * 10,
myGameProperties);
myQuadTariDialog->show(enableLeft, enableRight);
break;

View File

@ -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<QuadTari*>(
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()
{

View File

@ -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;