dreamconn: detect dreamconn+ with VID/PID. Send maple data as text.

Create DreamConn gamepad when detected.
Send/receive maple data in ascii dump form.
Simplify maple device creation.

Issue #1305
This commit is contained in:
Flyinghead 2024-12-20 20:02:58 +01:00
parent 6c3d77b012
commit b9fdd5070b
10 changed files with 209 additions and 115 deletions

View File

@ -215,10 +215,7 @@ static void mcfg_Create(MapleDeviceType type, u32 bus, u32 port, s32 player_num
{
delete MapleDevices[bus][port];
maple_device* dev = maple_Create(type);
dev->Setup(maple_GetAddress(bus, port), player_num);
dev->config = new MapleConfigMap(dev);
dev->OnSetup();
MapleDevices[bus][port] = dev;
dev->Setup(bus, port, player_num);
}
static void createNaomiDevices()

View File

@ -32,15 +32,19 @@ const char* maple_densha_controller_name = "TAITO 001 Controller";
const char* maple_sega_brand = "Produced By or Under License From SEGA ENTERPRISES,LTD.";
//fill in the info
void maple_device::Setup(u32 port, int playerNum)
void maple_device::Setup(u32 bus, u32 port, int playerNum)
{
maple_port = port;
bus_port = maple_GetPort(port);
bus_id = maple_GetBusId(port);
maple_port = (bus << 6) | (1 << port);
bus_port = port;
bus_id = bus;
logical_port[0] = 'A' + bus_id;
logical_port[1] = bus_port == 5 ? 'x' : '1' + bus_port;
logical_port[2] = 0;
player_num = playerNum == -1 ? bus_id : playerNum;
config = new MapleConfigMap(this);
OnSetup();
MapleDevices[bus][port] = this;
}
maple_device::~maple_device()
{
@ -683,7 +687,7 @@ struct maple_sega_vmu: maple_base
case MFID_2_LCD:
{
DEBUG_LOG(MAPLE, "VMU %s LCD write", logical_port);
r32();
r32(); // PT, phase, block#
rptr(lcd_data,192);
u8 white=0xff,black=0x00;
@ -1719,7 +1723,7 @@ struct RFIDReaderWriter : maple_base
u32 resp = Dma(command, &buffer_in[1], buffer_in_len - 4, &buffer_out[1], outlen);
if (reci & 0x20)
reci |= maple_GetAttachedDevices(maple_GetBusId(reci));
reci |= maple_GetAttachedDevices(bus_id);
verify(u8(outlen / 4) * 4 == outlen);
buffer_out[0] = (resp << 0 ) | (reci << 8) | (send << 16) | ((outlen / 4) << 24);
@ -2107,63 +2111,94 @@ maple_device* maple_Create(MapleDeviceType type)
struct DreamConnVmu : public maple_sega_vmu
{
DreamConn& dreamconn;
std::shared_ptr<DreamConn> dreamconn;
DreamConnVmu(DreamConn& dreamconn) : dreamconn(dreamconn) {
DreamConnVmu(std::shared_ptr<DreamConn> dreamconn) : dreamconn(dreamconn) {
}
u32 dma(u32 cmd) override
{
if (cmd == MDCF_BlockWrite && *(u32 *)dma_buffer_in == MFID_2_LCD)
{
// send the raw maple msg
dreamconn.send(dma_buffer_in - 4, dma_count_in + 4);
const MapleMsg *msg = reinterpret_cast<const MapleMsg*>(dma_buffer_in - 4);
dreamconn->send(*msg);
}
return maple_sega_vmu::dma(cmd);
}
void copy(maple_sega_vmu *other)
{
memcpy(flash_data, other->flash_data, sizeof(flash_data));
memcpy(lcd_data, other->lcd_data, sizeof(lcd_data));
memcpy(lcd_data_decoded, other->lcd_data_decoded, sizeof(lcd_data_decoded));
fullSaveNeeded = other->fullSaveNeeded;
}
void updateScreen()
{
MapleMsg msg;
msg.command = MDCF_BlockWrite;
msg.destAP = maple_port;
msg.originAP = bus_id << 6;
msg.size = 2 + sizeof(lcd_data) / 4;
*(u32 *)&msg.data[0] = MFID_2_LCD;
*(u32 *)&msg.data[4] = 0; // PT, phase, block#
memcpy(&msg.data[8], lcd_data, sizeof(lcd_data));
dreamconn->send(msg);
}
};
struct DreamConnPurupuru : public maple_sega_purupuru
{
DreamConn& dreamconn;
std::shared_ptr<DreamConn> dreamconn;
DreamConnPurupuru(DreamConn& dreamconn) : dreamconn(dreamconn) {
DreamConnPurupuru(std::shared_ptr<DreamConn> dreamconn) : dreamconn(dreamconn) {
}
u32 dma(u32 cmd) override
{
const MapleMsg *msg = reinterpret_cast<const MapleMsg*>(dma_buffer_in - 4);
switch (cmd)
{
case MDCF_BlockWrite:
dreamconn.send(dma_buffer_in - 4, dma_count_in + 4);
dreamconn->send(*msg);
break;
case MDCF_SetCondition:
dreamconn.send(dma_buffer_in - 4, dma_count_in + 4);
dreamconn->send(*msg);
break;
}
return maple_sega_purupuru::dma(cmd);
}
};
void createDreamConnDevices(DreamConn& dreamconn)
void createDreamConnDevices(std::shared_ptr<DreamConn> dreamconn, bool gameStart)
{
const int bus = dreamconn.getBus();
if (dreamconn.hasVmu() && dynamic_cast<DreamConnVmu*>(MapleDevices[bus][0]) == nullptr)
const int bus = dreamconn->getBus();
if (dreamconn->hasVmu())
{
delete MapleDevices[bus][0];
DreamConnVmu *dev = new DreamConnVmu(dreamconn);
dev->Setup((bus << 6) | 1);
dev->config = new MapleConfigMap(dev);
dev->OnSetup();
MapleDevices[bus][0] = dev;
maple_device *dev = MapleDevices[bus][0];
if (gameStart || (dev != nullptr && dev->get_device_type() == MDT_SegaVMU))
{
DreamConnVmu *vmu = new DreamConnVmu(dreamconn);
vmu->Setup(bus, 0);
if (!gameStart) {
// if loading a state, copy data from the regular vmu and send a screen update
vmu->copy(static_cast<maple_sega_vmu*>(dev));
vmu->updateScreen();
}
delete dev;
}
}
if (dreamconn.hasRumble() && dynamic_cast<DreamConnPurupuru*>(MapleDevices[bus][1]) == nullptr)
if (dreamconn->hasRumble())
{
delete MapleDevices[bus][1];
DreamConnPurupuru *dev = new DreamConnPurupuru(dreamconn);
dev->Setup((bus << 6) | 2);
dev->config = new MapleConfigMap(dev);
dev->OnSetup();
MapleDevices[bus][1] = dev;
maple_device *dev = MapleDevices[bus][1];
if (gameStart || (dev != nullptr && dev->get_device_type() == MDT_PurupuruPack))
{
delete dev;
DreamConnPurupuru *rumble = new DreamConnPurupuru(dreamconn);
rumble->Setup(bus, 1);
}
}
}

View File

@ -131,7 +131,7 @@ struct maple_device
MapleConfigMap* config;
//fill in the info
void Setup(u32 port, int playerNum = -1);
void Setup(u32 bus, u32 port = 5, int playerNum = -1);
virtual void OnSetup() {};
virtual ~maple_device();
@ -246,7 +246,7 @@ struct maple_base: maple_device
u32 resp = Dma(command, &buffer_in[1], buffer_in_len - 4, &buffer_out[1], outlen);
if (reci & 0x20)
reci |= maple_GetAttachedDevices(maple_GetBusId(reci));
reci |= maple_GetAttachedDevices(bus_id);
verify(u8(outlen / 4) * 4 == outlen);
buffer_out[0] = (resp << 0 ) | (send << 8) | (reci << 16) | ((outlen / 4) << 24);

View File

@ -1,15 +1,6 @@
#include "maple_helper.h"
#include "maple_if.h"
u32 maple_GetPort(u32 addr)
{
for (int i=0;i<6;i++)
{
if ((1<<i)&addr)
return i;
}
return 5;
}
u32 maple_GetAttachedDevices(u32 bus)
{
verify(MapleDevices[bus][5]!=0);

View File

@ -1,19 +1,4 @@
#pragma once
#include "types.h"
static inline u32 maple_GetBusId(u32 addr)
{
return addr >> 6;
}
u32 maple_GetPort(u32 addr);
u32 maple_GetAttachedDevices(u32 bus);
//device : 0 .. 4 -> subdevice , 5 -> main device :)
static inline u32 maple_GetAddress(u32 bus, u32 port)
{
u32 rv = bus << 6;
rv |= 1 << port;
return rv;
}

View File

@ -124,6 +124,14 @@ static void maple_SB_MDSTAR_Write(u32 addr, u32 data)
}
#endif
static u32 getPort(u32 addr)
{
for (int i = 0; i < 6; i++)
if ((1 << i) & addr)
return i;
return 5;
}
static void maple_DoDma()
{
verify(SB_MDEN & 1);
@ -202,8 +210,8 @@ static void maple_DoDma()
//Number of additional words in frame
u32 inlen = (frame_header >> 24) & 0xFF;
u32 port = maple_GetPort(reci);
u32 bus = maple_GetBusId(reci);
u32 port = getPort(reci);
u32 bus = reci >> 6;
if (MapleDevices[bus][5] && MapleDevices[bus][port])
{

View File

@ -20,41 +20,42 @@
#if defined(_WIN32) && !defined(TARGET_UWP)
#include "hw/maple/maple_devs.h"
#include <SDL.h>
#include <iomanip>
#include <sstream>
void createDreamConnDevices(DreamConn& dreamconn);
void createDreamConnDevices(std::shared_ptr<DreamConn> dreamconn, bool gameStart);
struct MapleMsg
bool MapleMsg::send(sock_t sock) const
{
u8 command;
u8 destAP;
u8 originAP;
u8 size;
u8 data[1024];
std::ostringstream out;
out.fill('0');
out << std::hex << std::uppercase
<< std::setw(2) << (u32)command << " "
<< std::setw(2) << (u32)destAP << " "
<< std::setw(2) << (u32)originAP << " "
<< std::setw(2) << (u32)size;
const u32 sz = getDataSize();
for (u32 i = 0; i < sz; i++)
out << " " << std::setw(2) << (u32)data[i];
out << "\r\n";
std::string s = out.str();
return ::send(sock, s.c_str(), s.length(), 0) == (int)s.length();
}
u32 getDataSize() const {
return size * 4;
}
template<typename T>
void setData(const T& p) {
memcpy(data, &p, sizeof(T));
this->size = (sizeof(T) + 3) / 4;
}
bool send(sock_t sock) {
u32 sz = getDataSize() + 4;
return ::send(sock, (const char *)this, sz, 0) == sz;
}
bool receive(sock_t sock)
{
if (::recv(sock, (char *)this, 4, 0) != 4)
return false;
if (getDataSize() == 0)
return true;
return ::recv(sock, (char *)data, getDataSize(), 0) == getDataSize();
}
};
static_assert(sizeof(MapleMsg) == 1028);
bool MapleMsg::receive(sock_t sock)
{
std::string str(11, ' ');
if (::recv(sock, (char *)str.data(), str.length(), 0) != (int)str.length())
return false;
sscanf(str.c_str(), "%hhx %hhx %hhx %hhx", &command, &destAP, &originAP, &size);
str = std::string(getDataSize() * 3 + 2, ' ');
if (::recv(sock, (char *)str.data(), str.length(), 0) != (int)str.length())
return false;
for (unsigned i = 0; i < getDataSize(); i++)
sscanf(&str[i * 3 + 1], "%hhx", &data[i]);
return true;
}
void DreamConn::connect()
{
@ -91,13 +92,10 @@ void DreamConn::connect()
}
expansionDevs = msg.originAP & 0x1f;
NOTICE_LOG(INPUT, "Connected to DreamConn[%d]: VMU:%d, Rumble Pack:%d", bus, hasVmu(), hasRumble());
EventManager::listen(Event::Resume, handleEvent, this);
}
void DreamConn::disconnect()
{
EventManager::unlisten(Event::Resume, handleEvent, this);
if (VALID(sock)) {
NOTICE_LOG(INPUT, "Disconnected from DreamConn[%d]", bus);
closesocket(sock);
@ -105,16 +103,54 @@ void DreamConn::disconnect()
sock = INVALID_SOCKET;
}
bool DreamConn::send(const u8* data, int size)
bool DreamConn::send(const MapleMsg& msg)
{
if (VALID(sock))
return ::send(sock, (const char *)data, size, 0) == size;
return msg.send(sock);
else
return false;
}
void DreamConn::handleEvent(Event event, void *arg) {
createDreamConnDevices(*static_cast<DreamConn*>(arg));
bool DreamConnGamepad::isDreamConn(int deviceIndex)
{
char guid_str[33] {};
SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(deviceIndex), guid_str, sizeof(guid_str));
NOTICE_LOG(INPUT, "GUID: %s VID:%c%c%c%c PID:%c%c%c%c", guid_str,
guid_str[10], guid_str[11], guid_str[8], guid_str[9],
guid_str[18], guid_str[19], guid_str[16], guid_str[17]);
// DreamConn VID:4457 PID:4443
return memcmp("5744000043440000", guid_str + 8, 16) == 0;
}
DreamConnGamepad::DreamConnGamepad(int maple_port, int joystick_idx, SDL_Joystick* sdl_joystick)
: SDLGamepad(maple_port, joystick_idx, sdl_joystick)
{
EventManager::listen(Event::Start, handleEvent, this);
EventManager::listen(Event::LoadState, handleEvent, this);
}
DreamConnGamepad::~DreamConnGamepad() {
EventManager::unlisten(Event::Start, handleEvent, this);
EventManager::unlisten(Event::LoadState, handleEvent, this);
}
void DreamConnGamepad::set_maple_port(int port)
{
if (port < 0 || port >= 4) {
dreamconn.reset();
}
else if (dreamconn == nullptr || dreamconn->getBus() != port) {
dreamconn.reset();
dreamconn = std::make_shared<DreamConn>(port);
}
SDLGamepad::set_maple_port(port);
}
void DreamConnGamepad::handleEvent(Event event, void *arg)
{
DreamConnGamepad *gamepad = static_cast<DreamConnGamepad*>(arg);
if (gamepad->dreamconn != nullptr)
createDreamConnDevices(gamepad->dreamconn, event == Event::Start);
}
#else
@ -124,4 +160,15 @@ void DreamConn::connect() {
void DreamConn::disconnect() {
}
bool DreamConnGamepad::isDreamConn(int deviceIndex) {
return false;
}
DreamConnGamepad::DreamConnGamepad(int maple_port, int joystick_idx, SDL_Joystick* sdl_joystick)
: SDLGamepad(maple_port, joystick_idx, sdl_joystick) {
}
DreamConnGamepad::~DreamConnGamepad() {
}
void DreamConnGamepad::set_maple_port(int port) {
SDLGamepad::set_maple_port(port);
}
#endif

View File

@ -20,8 +20,31 @@
#include "types.h"
#include "network/net_platform.h"
#include "emulator.h"
#include "sdl_gamepad.h"
struct MapleMsg
{
u8 command;
u8 destAP;
u8 originAP;
u8 size;
u8 data[1024];
u32 getDataSize() const {
return size * 4;
}
template<typename T>
void setData(const T& p) {
memcpy(data, &p, sizeof(T));
this->size = (sizeof(T) + 3) / 4;
}
bool send(sock_t sock) const;
bool receive(sock_t sock);
};
static_assert(sizeof(MapleMsg) == 1028);
// TODO Need a way to detect DreamConn+ controllers
class DreamConn
{
const int bus;
@ -37,7 +60,7 @@ public:
disconnect();
}
bool send(const u8* data, int size);
bool send(const MapleMsg& msg);
int getBus() const {
return bus;
@ -52,5 +75,19 @@ public:
private:
void connect();
void disconnect();
static void handleEvent(Event event, void *arg);
};
class DreamConnGamepad : public SDLGamepad
{
public:
DreamConnGamepad(int maple_port, int joystick_idx, SDL_Joystick* sdl_joystick);
~DreamConnGamepad();
void set_maple_port(int port) override;
static bool isDreamConn(int deviceIndex);
private:
static void handleEvent(Event event, void *arg);
std::shared_ptr<DreamConn> dreamconn;
};

View File

@ -39,6 +39,7 @@ static u32 windowFlags;
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
std::map<SDL_JoystickID, std::shared_ptr<SDLGamepad>> SDLGamepad::sdl_gamepads;
static std::unordered_map<u64, std::shared_ptr<SDLMouse>> sdl_mice;
static std::shared_ptr<SDLKeyboardDevice> sdl_keyboard;
static bool window_fullscreen;
@ -49,7 +50,6 @@ static bool mouseCaptured;
static std::string clipboardText;
static std::string barcode;
static u64 lastBarcodeTime;
static std::unique_ptr<DreamConn> dreamconns[4];
static KeyboardLayout detectKeyboardLayout();
static bool handleBarcodeScanner(const SDL_Event& event);
@ -82,7 +82,11 @@ static void sdl_open_joystick(int index)
#ifdef __SWITCH__
std::shared_ptr<SDLGamepad> gamepad = std::make_shared<SwitchGamepad>(index < MAPLE_PORTS ? index : -1, index, pJoystick);
#else
std::shared_ptr<SDLGamepad> gamepad = std::make_shared<SDLGamepad>(index < MAPLE_PORTS ? index : -1, index, pJoystick);
std::shared_ptr<SDLGamepad> gamepad;
if (DreamConnGamepad::isDreamConn(index))
gamepad = std::make_shared<DreamConnGamepad>(index < MAPLE_PORTS ? index : -1, index, pJoystick);
else
gamepad = std::make_shared<SDLGamepad>(index < MAPLE_PORTS ? index : -1, index, pJoystick);
#endif
SDLGamepad::AddSDLGamepad(gamepad);
} catch (const FlycastException& e) {
@ -262,18 +266,10 @@ void input_sdl_init()
if (settings.input.keyboardLangId == KeyboardLayout::US)
settings.input.keyboardLangId = detectKeyboardLayout();
barcode.clear();
for (unsigned i = 0; i < std::size(dreamconns); i++)
{
std::string key = "DreamConn" + std::to_string(i);
if (cfgLoadBool("input", key.c_str(), false))
dreamconns[i] = std::make_unique<DreamConn>(i);
}
}
void input_sdl_quit()
{
for (auto& dc : dreamconns)
dc.reset();
EventManager::unlisten(Event::Terminate, emuEventCallback);
EventManager::unlisten(Event::Pause, emuEventCallback);
EventManager::unlisten(Event::Resume, emuEventCallback);

View File

@ -676,8 +676,6 @@ private:
int damperEffectId = -1;
};
std::map<SDL_JoystickID, std::shared_ptr<SDLGamepad>> SDLGamepad::sdl_gamepads;
class SDLMouse : public Mouse
{
public: