2013-12-19 17:10:14 +00:00
|
|
|
#include "maple_devs.h"
|
|
|
|
#include "maple_cfg.h"
|
2020-03-28 16:58:01 +00:00
|
|
|
#include "maple_helper.h"
|
2023-04-29 15:50:56 +00:00
|
|
|
#include "maple_if.h"
|
2018-10-01 18:34:35 +00:00
|
|
|
#include "hw/pvr/spg.h"
|
2020-12-05 17:47:09 +00:00
|
|
|
#include "oslib/audiostream.h"
|
2021-07-05 17:44:08 +00:00
|
|
|
#include "oslib/oslib.h"
|
2021-10-10 14:33:37 +00:00
|
|
|
#include "hw/aica/sgc_if.h"
|
2023-05-16 07:43:21 +00:00
|
|
|
#include "cfg/option.h"
|
2019-09-04 16:40:18 +00:00
|
|
|
#include <zlib.h>
|
2023-04-29 15:50:56 +00:00
|
|
|
#include <time.h>
|
2023-06-14 23:09:19 +00:00
|
|
|
#include <errno.h>
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
const char* maple_sega_controller_name = "Dreamcast Controller";
|
2023-05-29 10:45:34 +00:00
|
|
|
const char* maple_sega_vmu_name = "Visual Memory";
|
|
|
|
const char* maple_sega_kbd_name = "Emulated Dreamcast Keyboard";
|
|
|
|
const char* maple_sega_mouse_name = "Emulated Dreamcast Mouse";
|
|
|
|
const char* maple_sega_dreameye_name_1 = "Dreamcast Camera Flash Device";
|
|
|
|
const char* maple_sega_dreameye_name_2 = "Dreamcast Camera Flash LDevice";
|
|
|
|
const char* maple_sega_mic_name = "MicDevice for Dreameye";
|
|
|
|
const char* maple_sega_purupuru_name = "Puru Puru Pack";
|
|
|
|
const char* maple_sega_lightgun_name = "Dreamcast Gun";
|
|
|
|
const char* maple_sega_twinstick_name = "Twin Stick";
|
|
|
|
const char* maple_ascii_stick_name = "ASCII STICK";
|
|
|
|
const char* maple_maracas_controller_name = "Maracas Controller";
|
|
|
|
const char* maple_fishing_controller_name = "Dreamcast Fishing Controller";
|
|
|
|
const char* maple_popnmusic_controller_name = "pop'n music controller";
|
|
|
|
const char* maple_racing_controller_name = "Racing Controller";
|
|
|
|
const char* maple_densha_controller_name = "TAITO 001 Controller";
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
const char* maple_sega_brand = "Produced By or Under License From SEGA ENTERPRISES,LTD.";
|
|
|
|
|
|
|
|
//fill in the info
|
2021-01-22 14:11:04 +00:00
|
|
|
void maple_device::Setup(u32 port, int playerNum)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2021-01-22 14:11:04 +00:00
|
|
|
maple_port = port;
|
|
|
|
bus_port = maple_GetPort(port);
|
|
|
|
bus_id = maple_GetBusId(port);
|
2013-12-19 17:10:14 +00:00
|
|
|
logical_port[0] = 'A' + bus_id;
|
|
|
|
logical_port[1] = bus_port == 5 ? 'x' : '1' + bus_port;
|
|
|
|
logical_port[2] = 0;
|
2021-01-22 14:11:04 +00:00
|
|
|
player_num = playerNum == -1 ? bus_id : playerNum;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
maple_device::~maple_device()
|
|
|
|
{
|
2021-03-14 19:20:38 +00:00
|
|
|
delete config;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
2021-09-07 08:54:00 +00:00
|
|
|
static inline void mutualExclusion(u32& keycode, u32 mask)
|
|
|
|
{
|
|
|
|
if ((keycode & mask) == 0)
|
|
|
|
keycode |= mask;
|
|
|
|
}
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
/*
|
|
|
|
Sega Dreamcast Controller
|
2013-12-24 00:56:44 +00:00
|
|
|
No error checking of any kind, but works just fine
|
2013-12-19 17:10:14 +00:00
|
|
|
*/
|
|
|
|
struct maple_sega_controller: maple_base
|
|
|
|
{
|
2019-04-08 15:09:10 +00:00
|
|
|
virtual u32 get_capabilities() {
|
|
|
|
// byte 0: 0 0 0 0 0 0 0 0
|
|
|
|
// byte 1: 0 0 a5 a4 a3 a2 a1 a0
|
|
|
|
// byte 2: R2 L2 D2 U2 D X Y Z
|
|
|
|
// byte 3: R L D U St A B C
|
|
|
|
|
|
|
|
return 0xfe060f00; // 4 analog axes (0-3) X Y A B Start U D L R
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
virtual u16 getButtonState(const PlainJoystickState &pjs)
|
2021-09-07 08:54:00 +00:00
|
|
|
{
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 kcode = pjs.kcode;
|
2021-09-07 08:54:00 +00:00
|
|
|
mutualExclusion(kcode, DC_DPAD_UP | DC_DPAD_DOWN);
|
|
|
|
mutualExclusion(kcode, DC_DPAD_LEFT | DC_DPAD_RIGHT);
|
2019-08-30 21:37:43 +00:00
|
|
|
return kcode | 0xF901; // mask off DPad2, C, D and Z;
|
2019-04-08 15:09:10 +00:00
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
virtual u32 getAnalogAxis(int index, const PlainJoystickState &pjs)
|
2019-12-17 22:41:32 +00:00
|
|
|
{
|
|
|
|
if (index == 2 || index == 3)
|
2019-04-08 15:09:10 +00:00
|
|
|
{
|
2019-12-17 22:41:32 +00:00
|
|
|
// Limit the magnitude of the analog axes to 128
|
2020-01-27 21:08:39 +00:00
|
|
|
s8 xaxis = pjs.joy[PJAI_X1] - 128;
|
|
|
|
s8 yaxis = pjs.joy[PJAI_Y1] - 128;
|
|
|
|
limit_joystick_magnitude<128>(xaxis, yaxis);
|
|
|
|
if (index == 2)
|
|
|
|
return xaxis + 128;
|
2019-12-17 22:41:32 +00:00
|
|
|
else
|
2020-01-27 21:08:39 +00:00
|
|
|
return yaxis + 128;
|
2019-12-17 22:41:32 +00:00
|
|
|
}
|
|
|
|
else if (index == 0)
|
2019-04-08 15:09:10 +00:00
|
|
|
return pjs.trigger[PJTI_R]; // Right trigger
|
2019-12-17 22:41:32 +00:00
|
|
|
else if (index == 1)
|
2019-04-08 15:09:10 +00:00
|
|
|
return pjs.trigger[PJTI_L]; // Left trigger
|
2019-12-17 22:41:32 +00:00
|
|
|
else
|
2019-04-08 15:09:10 +00:00
|
|
|
return 0x80; // unused
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
MapleDeviceType get_device_type() override
|
2018-09-29 14:13:07 +00:00
|
|
|
{
|
|
|
|
return MDT_SegaController;
|
|
|
|
}
|
|
|
|
|
2019-07-12 17:18:39 +00:00
|
|
|
virtual const char *get_device_name()
|
|
|
|
{
|
|
|
|
return maple_sega_controller_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual const char *get_device_brand()
|
|
|
|
{
|
|
|
|
return maple_sega_brand;
|
|
|
|
}
|
|
|
|
|
2023-05-29 10:45:34 +00:00
|
|
|
virtual u32 get_device_current(int get_max_current)
|
|
|
|
{
|
|
|
|
return get_max_current ? 0x01F4 : 0x01AE; // Max. 50 mA, standby: 43 mA
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 dma(u32 cmd) override
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2019-05-08 20:31:59 +00:00
|
|
|
//printf("maple_sega_controller::dma Called 0x%X;Command %d\n", bus_id, cmd);
|
2013-12-19 17:10:14 +00:00
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case MDC_DeviceRequest:
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_AllStatusReq:
|
2023-05-29 10:45:34 +00:00
|
|
|
// Fixed Device Status
|
|
|
|
// (Device ID)
|
2013-12-19 17:10:14 +00:00
|
|
|
//caps
|
|
|
|
//4
|
|
|
|
w32(MFID_0_Input);
|
|
|
|
|
|
|
|
//struct data
|
|
|
|
//3*4
|
2019-04-08 15:09:10 +00:00
|
|
|
w32(get_capabilities());
|
|
|
|
w32(0);
|
|
|
|
w32(0);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2023-05-29 10:45:34 +00:00
|
|
|
//1 area code (Country specification)
|
2013-12-19 17:10:14 +00:00
|
|
|
w8(0xFF);
|
|
|
|
|
2023-05-29 10:45:34 +00:00
|
|
|
//1 direction (Connection method)
|
2013-12-19 17:10:14 +00:00
|
|
|
w8(0);
|
2018-08-27 11:50:59 +00:00
|
|
|
|
2023-05-29 10:45:34 +00:00
|
|
|
//30 (Model name)
|
2019-07-12 17:18:39 +00:00
|
|
|
wstr(get_device_name(), 30);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2023-05-29 10:45:34 +00:00
|
|
|
//60 (License)
|
2019-07-12 17:18:39 +00:00
|
|
|
wstr(get_device_brand(), 60);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2023-05-29 10:45:34 +00:00
|
|
|
//2 (Standby current consumption)
|
|
|
|
w16(get_device_current(0));
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2023-05-29 10:45:34 +00:00
|
|
|
//2 (Maximum current consumption)
|
|
|
|
w16(get_device_current(1));
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
return cmd == MDC_DeviceRequest ? MDRS_DeviceStatus : MDRS_DeviceStatusAll;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
//controller condition
|
|
|
|
case MDCF_GetCondition:
|
|
|
|
{
|
|
|
|
PlainJoystickState pjs;
|
|
|
|
config->GetInput(&pjs);
|
|
|
|
//caps
|
|
|
|
//4
|
|
|
|
w32(MFID_0_Input);
|
|
|
|
|
|
|
|
//state data
|
|
|
|
//2 key code
|
2023-05-31 15:01:54 +00:00
|
|
|
w16(getButtonState(pjs));
|
|
|
|
|
|
|
|
// analog axes
|
|
|
|
for (int axis = 0; axis < 6; axis++)
|
|
|
|
w8(getAnalogAxis(axis, pjs));
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
2018-09-08 12:35:46 +00:00
|
|
|
return MDRS_DataTransfer;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_DeviceReset:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
|
|
|
case MDC_DeviceKill:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
default:
|
2020-06-15 15:04:04 +00:00
|
|
|
INFO_LOG(MAPLE, "maple_sega_controller: Unknown maple command %d", cmd);
|
2018-09-08 12:35:46 +00:00
|
|
|
return MDRE_UnknownCmd;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
2018-08-27 11:50:59 +00:00
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
};
|
|
|
|
|
2019-04-08 15:09:10 +00:00
|
|
|
struct maple_atomiswave_controller: maple_sega_controller
|
|
|
|
{
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 get_capabilities() override {
|
2019-04-08 15:09:10 +00:00
|
|
|
// byte 0: 0 0 0 0 0 0 0 0
|
|
|
|
// byte 1: 0 0 a5 a4 a3 a2 a1 a0
|
|
|
|
// byte 2: R2 L2 D2 U2 D X Y Z
|
|
|
|
// byte 3: R L D U St A B C
|
|
|
|
|
|
|
|
return 0xff663f00; // 6 analog axes, X Y L2/D2(?) A B C Start U D L R
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u16 getButtonState(const PlainJoystickState &pjs) override
|
2021-09-07 08:54:00 +00:00
|
|
|
{
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 kcode = pjs.kcode;
|
2021-09-07 08:54:00 +00:00
|
|
|
mutualExclusion(kcode, AWAVE_UP_KEY | AWAVE_DOWN_KEY);
|
|
|
|
mutualExclusion(kcode, AWAVE_LEFT_KEY | AWAVE_RIGHT_KEY);
|
2019-04-08 15:09:10 +00:00
|
|
|
return kcode | AWAVE_TRIGGER_KEY;
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 getAnalogAxis(int index, const PlainJoystickState &pjs) override {
|
2020-01-27 21:08:39 +00:00
|
|
|
if (index < 2 || index > 5)
|
2019-04-08 15:09:10 +00:00
|
|
|
return 0x80;
|
2020-01-27 21:08:39 +00:00
|
|
|
index -= 2;
|
2020-12-02 13:40:50 +00:00
|
|
|
return pjs.joy[index];
|
2019-04-08 15:09:10 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-07-12 17:18:39 +00:00
|
|
|
/*
|
|
|
|
Sega Twin Stick Controller
|
|
|
|
*/
|
|
|
|
struct maple_sega_twinstick: maple_sega_controller
|
|
|
|
{
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 get_capabilities() override {
|
2019-07-12 17:18:39 +00:00
|
|
|
// byte 0: 0 0 0 0 0 0 0 0
|
|
|
|
// byte 1: 0 0 a5 a4 a3 a2 a1 a0
|
|
|
|
// byte 2: R2 L2 D2 U2 D X Y Z
|
|
|
|
// byte 3: R L D U St A B C
|
|
|
|
|
|
|
|
return 0xfefe0000; // no analog axes, X Y A B D Start U/D/L/R U2/D2/L2/R2
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u16 getButtonState(const PlainJoystickState &pjs) override
|
2021-09-07 08:54:00 +00:00
|
|
|
{
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 kcode = pjs.kcode;
|
2021-09-07 08:54:00 +00:00
|
|
|
mutualExclusion(kcode, DC_DPAD_UP | DC_DPAD_DOWN);
|
|
|
|
mutualExclusion(kcode, DC_DPAD_LEFT | DC_DPAD_RIGHT);
|
|
|
|
mutualExclusion(kcode, DC_DPAD2_UP | DC_DPAD2_DOWN);
|
|
|
|
mutualExclusion(kcode, DC_DPAD2_LEFT | DC_DPAD2_RIGHT);
|
2019-07-12 17:18:39 +00:00
|
|
|
return kcode | 0x0101;
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
MapleDeviceType get_device_type() override {
|
2019-07-12 17:18:39 +00:00
|
|
|
return MDT_TwinStick;
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 getAnalogAxis(int index, const PlainJoystickState &pjs) override {
|
2019-07-12 17:18:39 +00:00
|
|
|
return 0x80;
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
const char *get_device_name() override {
|
2019-07-12 17:18:39 +00:00
|
|
|
return maple_sega_twinstick_name;
|
|
|
|
}
|
2023-05-29 10:45:34 +00:00
|
|
|
|
|
|
|
u32 get_device_current(int get_max_current) override {
|
|
|
|
return get_max_current ? 0x012C : 0x00DC; // Max. 30 mA, standby: 22 mA
|
|
|
|
}
|
2019-07-12 17:18:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Ascii Stick (Arcade/FT Stick)
|
|
|
|
*/
|
|
|
|
struct maple_ascii_stick: maple_sega_controller
|
|
|
|
{
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 get_capabilities() override {
|
2019-07-12 17:18:39 +00:00
|
|
|
// byte 0: 0 0 0 0 0 0 0 0
|
|
|
|
// byte 1: 0 0 a5 a4 a3 a2 a1 a0
|
|
|
|
// byte 2: R2 L2 D2 U2 D X Y Z
|
|
|
|
// byte 3: R L D U St A B C
|
|
|
|
|
|
|
|
return 0xff070000; // no analog axes, X Y Z A B C Start U/D/L/R
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u16 getButtonState(const PlainJoystickState &pjs) override
|
2021-09-07 08:54:00 +00:00
|
|
|
{
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 kcode = pjs.kcode;
|
2021-09-07 08:54:00 +00:00
|
|
|
mutualExclusion(kcode, DC_DPAD_UP | DC_DPAD_DOWN);
|
|
|
|
mutualExclusion(kcode, DC_DPAD_LEFT | DC_DPAD_RIGHT);
|
2019-07-12 17:18:39 +00:00
|
|
|
return kcode | 0xF800;
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
MapleDeviceType get_device_type() override {
|
2019-07-12 17:18:39 +00:00
|
|
|
return MDT_AsciiStick;
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 getAnalogAxis(int index, const PlainJoystickState &pjs) override {
|
2019-07-12 17:18:39 +00:00
|
|
|
return 0x80;
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
const char *get_device_name() override {
|
2019-07-12 17:18:39 +00:00
|
|
|
return maple_ascii_stick_name;
|
|
|
|
}
|
2023-05-29 10:45:34 +00:00
|
|
|
|
|
|
|
u32 get_device_current(int get_max_current) override {
|
|
|
|
return get_max_current ? 0x0172 : 0x010E; // Max. 37 mA, standby: 27 mA
|
|
|
|
}
|
2019-07-12 17:18:39 +00:00
|
|
|
};
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
/*
|
|
|
|
Sega Dreamcast Visual Memory Unit
|
|
|
|
This is pretty much done (?)
|
|
|
|
*/
|
2014-01-03 20:17:37 +00:00
|
|
|
u8 vmu_default[] = {
|
2019-05-09 09:25:17 +00:00
|
|
|
0x78,0x9c,0xed,0xd2,0x31,0x4e,0x02,0x61,0x10,0x06,0xd0,0x8f,0x04,0x28,0x4c,0x2c,
|
|
|
|
0x28,0x2d,0x0c,0xa5,0x57,0xe0,0x16,0x56,0x16,0x76,0x14,0x1e,0xc4,0x03,0x50,0x98,
|
|
|
|
0x50,0x40,0x69,0xc1,0x51,0x28,0xbc,0x8e,0x8a,0x0a,0xeb,0xc2,0xcf,0x66,0x13,0x1a,
|
|
|
|
0x13,0xa9,0x30,0x24,0xe6,0xbd,0xc9,0x57,0xcc,0x4c,0x33,0xc5,0x2c,0xb3,0x48,0x6e,
|
|
|
|
0x67,0x01,0x00,0x00,0x00,0x00,0x00,0x4e,0xaf,0xdb,0xe4,0x7a,0xd2,0xcf,0x53,0x16,
|
|
|
|
0x6d,0x46,0x99,0xb6,0xc9,0x78,0x9e,0x3c,0x5f,0x9c,0xfb,0x3c,0x00,0x00,0x00,0x00,
|
|
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
|
|
0x00,0x00,0x00,0x80,0x5f,0xd5,0x45,0xfd,0xef,0xaa,0xca,0x6b,0xde,0xf2,0x9e,0x55,
|
|
|
|
0x3e,0xf2,0x99,0xaf,0xac,0xb3,0x49,0x95,0xef,0xd4,0xa9,0x9a,0xdd,0xdd,0x0f,0x9d,
|
|
|
|
0x52,0xca,0xc3,0x91,0x7f,0xb9,0x9a,0x0f,0x6e,0x92,0xfb,0xee,0xa1,0x2f,0x6d,0x76,
|
|
|
|
0xe9,0x64,0x9b,0xcb,0xf4,0xf2,0x92,0x61,0x33,0x79,0xfc,0xeb,0xb7,0xe5,0x44,0xf6,
|
|
|
|
0x77,0x19,0x06,0xef,
|
2014-01-03 20:17:37 +00:00
|
|
|
};
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
struct maple_sega_vmu: maple_base
|
|
|
|
{
|
|
|
|
FILE* file;
|
|
|
|
u8 flash_data[128*1024];
|
|
|
|
u8 lcd_data[192];
|
|
|
|
u8 lcd_data_decoded[48*32];
|
2018-08-27 11:50:59 +00:00
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
MapleDeviceType get_device_type() override
|
2018-09-29 14:13:07 +00:00
|
|
|
{
|
|
|
|
return MDT_SegaVMU;
|
|
|
|
}
|
|
|
|
|
2021-11-13 14:56:42 +00:00
|
|
|
void serialize(Serializer& ser) const override
|
2018-09-02 13:49:23 +00:00
|
|
|
{
|
2021-11-13 14:56:42 +00:00
|
|
|
maple_base::serialize(ser);
|
|
|
|
ser << flash_data;
|
|
|
|
ser << lcd_data;
|
|
|
|
ser << lcd_data_decoded;
|
2018-09-02 13:49:23 +00:00
|
|
|
}
|
2021-11-13 14:56:42 +00:00
|
|
|
void deserialize(Deserializer& deser) override
|
2018-09-02 13:49:23 +00:00
|
|
|
{
|
2021-11-13 14:56:42 +00:00
|
|
|
maple_base::deserialize(deser);
|
|
|
|
deser >> flash_data;
|
|
|
|
deser >> lcd_data;
|
|
|
|
deser >> lcd_data_decoded;
|
2021-07-11 10:37:21 +00:00
|
|
|
for (u8 b : lcd_data)
|
|
|
|
if (b != 0)
|
|
|
|
{
|
|
|
|
config->SetImage(lcd_data_decoded);
|
|
|
|
break;
|
|
|
|
}
|
2018-09-02 13:49:23 +00:00
|
|
|
}
|
2021-05-27 15:58:33 +00:00
|
|
|
|
|
|
|
void initializeVmu()
|
|
|
|
{
|
|
|
|
INFO_LOG(MAPLE, "Initialising empty VMU...");
|
|
|
|
|
|
|
|
uLongf dec_sz = sizeof(flash_data);
|
|
|
|
int rv = uncompress(flash_data, &dec_sz, vmu_default, sizeof(vmu_default));
|
|
|
|
|
|
|
|
verify(rv == Z_OK);
|
|
|
|
verify(dec_sz == sizeof(flash_data));
|
|
|
|
|
|
|
|
if (file != nullptr)
|
|
|
|
{
|
|
|
|
if (std::fwrite(flash_data, sizeof(flash_data), 1, file) != 1)
|
|
|
|
WARN_LOG(MAPLE, "Failed to write the VMU to disk");
|
|
|
|
if (std::fseek(file, 0, SEEK_SET) != 0)
|
|
|
|
WARN_LOG(MAPLE, "VMU: I/O error");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
void OnSetup() override
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-08-27 11:50:59 +00:00
|
|
|
memset(flash_data, 0, sizeof(flash_data));
|
|
|
|
memset(lcd_data, 0, sizeof(lcd_data));
|
2021-07-05 17:44:08 +00:00
|
|
|
std::string apath = hostfs::getVmuPath(logical_port);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2021-01-19 10:11:01 +00:00
|
|
|
file = nowide::fopen(apath.c_str(), "rb+");
|
2021-05-27 15:58:33 +00:00
|
|
|
if (file == nullptr)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2019-06-30 21:38:58 +00:00
|
|
|
INFO_LOG(MAPLE, "Unable to open VMU save file \"%s\", creating new file", apath.c_str());
|
2021-05-27 15:58:33 +00:00
|
|
|
file = nowide::fopen(apath.c_str(), "wb+");
|
|
|
|
if (file == nullptr)
|
2020-11-26 15:45:57 +00:00
|
|
|
ERROR_LOG(MAPLE, "Failed to create VMU save file \"%s\"", apath.c_str());
|
2021-05-27 15:58:33 +00:00
|
|
|
initializeVmu();
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
2020-11-26 15:45:57 +00:00
|
|
|
if (file != nullptr)
|
2021-05-27 15:58:33 +00:00
|
|
|
if (std::fread(flash_data, sizeof(flash_data), 1, file) != 1)
|
|
|
|
WARN_LOG(MAPLE, "Failed to read the VMU from disk");
|
2014-01-03 20:17:37 +00:00
|
|
|
|
|
|
|
u8 sum = 0;
|
2020-03-20 15:57:50 +00:00
|
|
|
for (u32 i = 0; i < sizeof(flash_data); i++)
|
2018-08-27 11:50:59 +00:00
|
|
|
sum |= flash_data[i];
|
2014-01-03 20:17:37 +00:00
|
|
|
|
2021-05-27 15:58:33 +00:00
|
|
|
if (sum == 0)
|
2018-08-27 11:50:59 +00:00
|
|
|
// This means the existing VMU file is completely empty and needs to be recreated
|
2021-05-27 15:58:33 +00:00
|
|
|
initializeVmu();
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
2021-05-27 15:58:33 +00:00
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
~maple_sega_vmu() override
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2021-05-27 15:58:33 +00:00
|
|
|
if (file != nullptr)
|
|
|
|
std::fclose(file);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
2021-05-27 15:58:33 +00:00
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 dma(u32 cmd) override
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2019-05-08 20:31:59 +00:00
|
|
|
//printf("maple_sega_vmu::dma Called for port %d:%d, Command %d\n", bus_id, bus_port, cmd);
|
2013-12-19 17:10:14 +00:00
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case MDC_DeviceRequest:
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_AllStatusReq:
|
2020-05-29 12:44:44 +00:00
|
|
|
//caps
|
|
|
|
//4
|
|
|
|
w32(MFID_1_Storage | MFID_2_LCD | MFID_3_Clock);
|
|
|
|
|
|
|
|
//struct data
|
|
|
|
//3*4
|
|
|
|
w32( 0x403f7e7e); // for clock
|
|
|
|
w32( 0x00100500); // for LCD
|
|
|
|
w32( 0x00410f00); // for storage
|
|
|
|
//1 area code
|
|
|
|
w8(0xFF);
|
|
|
|
//1 direction
|
|
|
|
w8(0);
|
|
|
|
//30
|
|
|
|
wstr(maple_sega_vmu_name,30);
|
|
|
|
|
|
|
|
//60
|
|
|
|
wstr(maple_sega_brand,60);
|
|
|
|
|
|
|
|
//2
|
|
|
|
w16(0x007c); // 12.4 mA
|
|
|
|
|
|
|
|
//2
|
|
|
|
w16(0x0082); // 13 mA
|
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
return cmd == MDC_DeviceRequest ? MDRS_DeviceStatus : MDRS_DeviceStatusAll;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
//in[0] is function used
|
|
|
|
//out[0] is function used
|
|
|
|
case MDCF_GetMediaInfo:
|
|
|
|
{
|
|
|
|
u32 function=r32();
|
|
|
|
switch(function)
|
|
|
|
{
|
|
|
|
case MFID_1_Storage:
|
2020-05-29 12:44:44 +00:00
|
|
|
DEBUG_LOG(MAPLE, "VMU %s GetMediaInfo storage", logical_port);
|
|
|
|
w32(MFID_1_Storage);
|
2018-08-27 11:50:59 +00:00
|
|
|
|
2020-05-29 12:44:44 +00:00
|
|
|
if (*(u16*)&flash_data[0xFF * 512 + 0x40] != 0xFF)
|
|
|
|
{
|
|
|
|
// Unformatted state: return predetermined media information
|
|
|
|
//total_size;
|
|
|
|
w16(0xff);
|
|
|
|
//partition_number;
|
|
|
|
w16(0);
|
|
|
|
//system_area_block;
|
|
|
|
w16(0xFF);
|
|
|
|
//fat_area_block;
|
|
|
|
w16(0xfe);
|
|
|
|
//number_fat_areas_block;
|
|
|
|
w16(1);
|
|
|
|
//file_info_block;
|
|
|
|
w16(0xfd);
|
|
|
|
//number_info_blocks;
|
|
|
|
w16(0xd);
|
|
|
|
//volume_icon;
|
|
|
|
w8(0);
|
|
|
|
//reserved1;
|
|
|
|
w8(0);
|
|
|
|
//save_area_block;
|
|
|
|
w16(0xc8);
|
|
|
|
//number_of_save_blocks;
|
|
|
|
w16(0x1f);
|
|
|
|
//reserverd0 (something for execution files?)
|
|
|
|
w32(0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-05-09 09:25:17 +00:00
|
|
|
// Get data from the vmu system area (block 0xFF)
|
|
|
|
wptr(flash_data + 0xFF * 512 + 0x40, 24);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
2020-05-29 12:44:44 +00:00
|
|
|
return MDRS_DataTransfer;//data transfer
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
case MFID_2_LCD:
|
|
|
|
{
|
|
|
|
u32 pt=r32();
|
|
|
|
if (pt!=0)
|
|
|
|
{
|
2019-06-30 21:38:58 +00:00
|
|
|
INFO_LOG(MAPLE, "VMU: MDCF_GetMediaInfo -> bad input |%08X|, returning MDRE_UnknownCmd", pt);
|
2013-12-19 17:10:14 +00:00
|
|
|
return MDRE_UnknownCmd;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-05-29 12:44:44 +00:00
|
|
|
DEBUG_LOG(MAPLE, "VMU %s GetMediaInfo LCD", logical_port);
|
2013-12-19 17:10:14 +00:00
|
|
|
w32(MFID_2_LCD);
|
|
|
|
|
2013-12-24 00:56:44 +00:00
|
|
|
w8(47); //X dots -1
|
|
|
|
w8(31); //Y dots -1
|
|
|
|
w8(((1)<<4) | (0)); //1 Color, 0 contrast levels
|
2019-05-08 20:31:59 +00:00
|
|
|
w8(2); //Padding
|
2018-08-27 11:50:59 +00:00
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
return MDRS_DataTransfer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2019-06-30 21:38:58 +00:00
|
|
|
INFO_LOG(MAPLE, "VMU: MDCF_GetMediaInfo -> Bad function used |%08X|, returning -2", function);
|
2013-12-19 17:10:14 +00:00
|
|
|
return MDRE_UnknownFunction;//bad function
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MDCF_BlockRead:
|
|
|
|
{
|
|
|
|
u32 function=r32();
|
|
|
|
switch(function)
|
|
|
|
{
|
|
|
|
case MFID_1_Storage:
|
|
|
|
{
|
|
|
|
w32(MFID_1_Storage);
|
|
|
|
u32 xo=r32();
|
|
|
|
u32 Block = (SWAP32(xo))&0xffff;
|
|
|
|
w32(xo);
|
|
|
|
|
|
|
|
if (Block>255)
|
|
|
|
{
|
2019-06-30 21:38:58 +00:00
|
|
|
DEBUG_LOG(MAPLE, "Block read : %d", Block);
|
|
|
|
DEBUG_LOG(MAPLE, "BLOCK READ ERROR");
|
2013-12-19 17:10:14 +00:00
|
|
|
Block&=255;
|
|
|
|
}
|
2020-05-29 12:44:44 +00:00
|
|
|
else
|
|
|
|
DEBUG_LOG(MAPLE, "VMU %s block read: Block %d addr %x len %d", logical_port, Block, Block*512, 512);
|
2013-12-19 17:10:14 +00:00
|
|
|
wptr(flash_data+Block*512,512);
|
|
|
|
}
|
2020-05-29 12:44:44 +00:00
|
|
|
return MDRS_DataTransfer;//data transfer
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
case MFID_2_LCD:
|
2020-05-29 12:44:44 +00:00
|
|
|
DEBUG_LOG(MAPLE, "VMU %s read LCD", logical_port);
|
|
|
|
w32(MFID_2_LCD);
|
|
|
|
w32(r32()); // mnn ?
|
|
|
|
wptr(flash_data,192);
|
2018-08-27 11:50:59 +00:00
|
|
|
|
2020-05-29 12:44:44 +00:00
|
|
|
return MDRS_DataTransfer;//data transfer
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
case MFID_3_Clock:
|
2020-05-29 12:44:44 +00:00
|
|
|
if (r32()!=0)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2020-05-29 12:44:44 +00:00
|
|
|
INFO_LOG(MAPLE, "VMU: Block read: MFID_3_Clock : invalid params");
|
|
|
|
return MDRE_TransmitAgain; //invalid params
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
w32(MFID_3_Clock);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2020-05-29 12:44:44 +00:00
|
|
|
time_t now;
|
|
|
|
time(&now);
|
|
|
|
tm* timenow=localtime(&now);
|
2018-08-27 11:50:59 +00:00
|
|
|
|
2020-05-29 12:44:44 +00:00
|
|
|
u8* timebuf=dma_buffer_out;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2020-05-29 12:44:44 +00:00
|
|
|
w8((timenow->tm_year+1900)%256);
|
|
|
|
w8((timenow->tm_year+1900)/256);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2020-05-29 12:44:44 +00:00
|
|
|
w8(timenow->tm_mon+1);
|
|
|
|
w8(timenow->tm_mday);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2020-05-29 12:44:44 +00:00
|
|
|
w8(timenow->tm_hour);
|
|
|
|
w8(timenow->tm_min);
|
|
|
|
w8(timenow->tm_sec);
|
|
|
|
w8(0);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2020-05-29 12:44:44 +00:00
|
|
|
DEBUG_LOG(MAPLE, "VMU: CLOCK Read-> datetime is %04d/%02d/%02d ~ %02d:%02d:%02d!",
|
|
|
|
timebuf[0] + timebuf[1] * 256,
|
|
|
|
timebuf[2],
|
|
|
|
timebuf[3],
|
|
|
|
timebuf[4],
|
|
|
|
timebuf[5],
|
|
|
|
timebuf[6]);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2020-05-29 12:44:44 +00:00
|
|
|
return MDRS_DataTransfer;//transfer reply ...
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2019-06-30 21:38:58 +00:00
|
|
|
INFO_LOG(MAPLE, "VMU: cmd MDCF_BlockRead -> Bad function |%08X| used, returning -2", function);
|
2013-12-19 17:10:14 +00:00
|
|
|
return MDRE_UnknownFunction;//bad function
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MDCF_BlockWrite:
|
|
|
|
{
|
2021-05-27 15:58:33 +00:00
|
|
|
u32 function = r32();
|
|
|
|
switch (function)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
case MFID_1_Storage:
|
|
|
|
{
|
|
|
|
u32 bph=r32();
|
|
|
|
u32 Block = (SWAP32(bph))&0xffff;
|
2018-08-27 11:50:59 +00:00
|
|
|
u32 Phase = ((SWAP32(bph))>>16)&0xff;
|
2013-12-19 17:10:14 +00:00
|
|
|
u32 write_adr=Block*512+Phase*(512/4);
|
|
|
|
u32 write_len=r_count();
|
2020-05-29 12:44:44 +00:00
|
|
|
DEBUG_LOG(MAPLE, "VMU %s block write: Block %d Phase %d addr %x len %d", logical_port, Block, Phase, write_adr, write_len);
|
2019-10-21 17:09:01 +00:00
|
|
|
if (write_adr + write_len > sizeof(flash_data))
|
2020-05-29 12:44:44 +00:00
|
|
|
{
|
|
|
|
INFO_LOG(MAPLE, "Failed to write VMU %s: overflow", logical_port);
|
2021-05-27 15:58:33 +00:00
|
|
|
skip(write_len);
|
|
|
|
return MDRE_FileError; //invalid params
|
2020-05-29 12:44:44 +00:00
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
rptr(&flash_data[write_adr],write_len);
|
|
|
|
|
2021-05-27 15:58:33 +00:00
|
|
|
if (file != nullptr)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2021-05-27 15:58:33 +00:00
|
|
|
if (std::fseek(file, write_adr, SEEK_SET) != 0
|
|
|
|
|| std::fwrite(&flash_data[write_adr], write_len, 1, file) != 1)
|
|
|
|
{
|
|
|
|
WARN_LOG(MAPLE, "Failed to save VMU %s: I/O error", logical_port);
|
|
|
|
return MDRE_FileError; // I/O error
|
|
|
|
}
|
2021-01-19 10:11:01 +00:00
|
|
|
std::fflush(file);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-06-30 21:38:58 +00:00
|
|
|
INFO_LOG(MAPLE, "Failed to save VMU %s data", logical_port);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
2021-05-27 15:58:33 +00:00
|
|
|
return MDRS_DeviceReply;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case MFID_2_LCD:
|
|
|
|
{
|
2020-05-29 12:44:44 +00:00
|
|
|
DEBUG_LOG(MAPLE, "VMU %s LCD write", logical_port);
|
2020-03-20 15:57:50 +00:00
|
|
|
r32();
|
2013-12-19 17:10:14 +00:00
|
|
|
rptr(lcd_data,192);
|
2018-08-27 11:50:59 +00:00
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
u8 white=0xff,black=0x00;
|
|
|
|
|
|
|
|
for(int y=0;y<32;++y)
|
|
|
|
{
|
|
|
|
u8* dst=lcd_data_decoded+y*48;
|
|
|
|
u8* src=lcd_data+6*y+5;
|
|
|
|
for(int x=0;x<6;++x)
|
|
|
|
{
|
|
|
|
u8 col=*src--;
|
|
|
|
for(int l=0;l<8;l++)
|
|
|
|
{
|
|
|
|
*dst++=col&1?black:white;
|
|
|
|
col>>=1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
config->SetImage(lcd_data_decoded);
|
2021-01-21 20:00:46 +00:00
|
|
|
|
|
|
|
return MDRS_DeviceReply;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case MFID_3_Clock:
|
|
|
|
if (r32()!=0 || r_count()!=8)
|
2020-05-29 12:44:44 +00:00
|
|
|
{
|
|
|
|
INFO_LOG(MAPLE, "VMU %s clock write invalid params: rcount %d", logical_port, r_count());
|
2018-10-11 08:09:28 +00:00
|
|
|
return MDRE_TransmitAgain; //invalid params ...
|
2020-05-29 12:44:44 +00:00
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
u8 timebuf[8];
|
|
|
|
rptr(timebuf,8);
|
2019-06-30 21:38:58 +00:00
|
|
|
DEBUG_LOG(MAPLE, "VMU: CLOCK Write-> datetime is %04d/%02d/%02d ~ %02d:%02d:%02d! Nothing set tho ...",
|
|
|
|
timebuf[0]+timebuf[1]*256,timebuf[2],timebuf[3],timebuf[4],timebuf[5],timebuf[6]);
|
2013-12-19 17:10:14 +00:00
|
|
|
return MDRS_DeviceReply;//ok !
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2021-05-27 15:58:33 +00:00
|
|
|
INFO_LOG(MAPLE, "VMU: command MDCF_BlockWrite -> Unknown function %x", function);
|
2013-12-19 17:10:14 +00:00
|
|
|
return MDRE_UnknownFunction;//bad function
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MDCF_GetLastError:
|
|
|
|
return MDRS_DeviceReply;//just ko
|
|
|
|
|
|
|
|
case MDCF_SetCondition:
|
|
|
|
{
|
|
|
|
switch(r32())
|
|
|
|
{
|
|
|
|
case MFID_3_Clock:
|
|
|
|
{
|
2023-01-21 10:54:34 +00:00
|
|
|
u8 alw = r8();
|
|
|
|
u8 ald = r8();
|
2021-10-10 14:33:37 +00:00
|
|
|
r16(); // Alarm 2
|
2023-01-21 10:54:34 +00:00
|
|
|
INFO_LOG(MAPLE, "BEEP: %d/%d", alw, ald);
|
2023-01-30 09:40:07 +00:00
|
|
|
aica::sgc::vmuBeep(alw, ald);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
2021-10-10 14:33:37 +00:00
|
|
|
return MDRS_DeviceReply;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
default:
|
2020-12-02 13:40:50 +00:00
|
|
|
INFO_LOG(MAPLE, "VMU: command MDCF_SetCondition -> Bad function used, returning MDRE_UnknownFunction");
|
|
|
|
return MDRE_UnknownFunction; //bad function
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-02 13:40:50 +00:00
|
|
|
break;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_DeviceReset:
|
2023-01-30 09:40:07 +00:00
|
|
|
aica::sgc::vmuBeep(0, 0);
|
2020-06-15 15:04:04 +00:00
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
|
|
|
case MDC_DeviceKill:
|
2023-01-30 09:40:07 +00:00
|
|
|
aica::sgc::vmuBeep(0, 0);
|
2020-06-15 15:04:04 +00:00
|
|
|
return MDRS_DeviceReply;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
default:
|
2019-06-30 21:38:58 +00:00
|
|
|
DEBUG_LOG(MAPLE, "Unknown MAPLE COMMAND %d", cmd);
|
2013-12-19 17:10:14 +00:00
|
|
|
return MDRE_UnknownCmd;
|
|
|
|
}
|
2018-08-27 11:50:59 +00:00
|
|
|
}
|
2021-10-16 15:56:21 +00:00
|
|
|
|
|
|
|
const void *getData(size_t& size) const override
|
|
|
|
{
|
|
|
|
size = sizeof(flash_data);
|
|
|
|
return flash_data;
|
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
};
|
2015-08-09 04:34:02 +00:00
|
|
|
|
2014-01-13 17:12:26 +00:00
|
|
|
struct maple_microphone: maple_base
|
|
|
|
{
|
2020-12-05 17:47:09 +00:00
|
|
|
u32 gain;
|
|
|
|
bool sampling;
|
|
|
|
bool eight_khz;
|
2014-01-18 21:54:18 +00:00
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
~maple_microphone() override
|
2020-12-05 17:47:09 +00:00
|
|
|
{
|
|
|
|
if (sampling)
|
|
|
|
StopAudioRecording();
|
|
|
|
}
|
2021-03-13 11:44:59 +00:00
|
|
|
MapleDeviceType get_device_type() override
|
2018-09-29 14:13:07 +00:00
|
|
|
{
|
|
|
|
return MDT_Microphone;
|
|
|
|
}
|
|
|
|
|
2021-11-13 14:56:42 +00:00
|
|
|
void serialize(Serializer& ser) const override
|
2018-09-02 13:49:23 +00:00
|
|
|
{
|
2021-11-13 14:56:42 +00:00
|
|
|
maple_base::serialize(ser);
|
|
|
|
ser << gain;
|
|
|
|
ser << sampling;
|
|
|
|
ser << eight_khz;
|
2018-09-02 13:49:23 +00:00
|
|
|
}
|
2021-11-13 14:56:42 +00:00
|
|
|
void deserialize(Deserializer& deser) override
|
2018-09-02 13:49:23 +00:00
|
|
|
{
|
2020-12-05 17:47:09 +00:00
|
|
|
if (sampling)
|
|
|
|
StopAudioRecording();
|
2021-11-13 14:56:42 +00:00
|
|
|
maple_base::deserialize(deser);
|
|
|
|
deser >> gain;
|
|
|
|
deser >> sampling;
|
|
|
|
deser >> eight_khz;
|
|
|
|
deser.skip(480 - sizeof(u32) - sizeof(bool) * 2, Deserializer::V23);
|
2020-12-05 17:47:09 +00:00
|
|
|
if (sampling)
|
|
|
|
StartAudioRecording(eight_khz);
|
|
|
|
}
|
2021-11-13 14:56:42 +00:00
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
void OnSetup() override
|
2014-01-18 21:54:18 +00:00
|
|
|
{
|
2020-12-05 17:47:09 +00:00
|
|
|
gain = 0xf;
|
|
|
|
sampling = false;
|
|
|
|
eight_khz = false;
|
2014-01-18 21:54:18 +00:00
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 dma(u32 cmd) override
|
2014-01-13 17:12:26 +00:00
|
|
|
{
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case MDC_DeviceRequest:
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_AllStatusReq:
|
2019-06-30 21:38:58 +00:00
|
|
|
DEBUG_LOG(MAPLE, "maple_microphone::dma MDC_DeviceRequest");
|
2014-01-16 06:36:04 +00:00
|
|
|
|
2014-01-13 17:12:26 +00:00
|
|
|
//caps
|
|
|
|
//4
|
|
|
|
w32(MFID_4_Mic);
|
|
|
|
|
|
|
|
//struct data
|
|
|
|
//3*4
|
2020-12-05 17:47:09 +00:00
|
|
|
w32(0xf0000000);
|
|
|
|
w32(0);
|
|
|
|
w32(0);
|
2014-01-13 17:12:26 +00:00
|
|
|
|
|
|
|
//1 area code
|
|
|
|
w8(0xFF);
|
|
|
|
|
|
|
|
//1 direction
|
|
|
|
w8(0);
|
2018-08-27 11:50:59 +00:00
|
|
|
|
2014-01-13 17:12:26 +00:00
|
|
|
//30
|
2020-12-05 17:47:09 +00:00
|
|
|
wstr(maple_sega_mic_name, 30);
|
2014-01-13 17:12:26 +00:00
|
|
|
|
|
|
|
//60
|
2020-12-05 17:47:09 +00:00
|
|
|
wstr(maple_sega_brand, 60);
|
2014-01-13 17:12:26 +00:00
|
|
|
|
|
|
|
//2
|
2020-12-05 17:47:09 +00:00
|
|
|
w16(0x012C); // 30 mA
|
2014-01-13 17:12:26 +00:00
|
|
|
|
|
|
|
//2
|
2020-12-05 17:47:09 +00:00
|
|
|
w16(0x012C); // 30 mA
|
2014-01-13 17:12:26 +00:00
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
return cmd == MDC_DeviceRequest ? MDRS_DeviceStatus : MDRS_DeviceStatusAll;
|
2014-01-13 17:12:26 +00:00
|
|
|
|
2014-01-13 21:30:42 +00:00
|
|
|
case MDC_DeviceReset:
|
2019-06-30 21:38:58 +00:00
|
|
|
DEBUG_LOG(MAPLE, "maple_microphone::dma MDC_DeviceReset");
|
2020-12-05 17:47:09 +00:00
|
|
|
if (sampling)
|
|
|
|
StopAudioRecording();
|
|
|
|
OnSetup();
|
2014-01-14 05:38:59 +00:00
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
|
|
|
case MDCF_MICControl:
|
|
|
|
{
|
|
|
|
u32 function=r32();
|
|
|
|
|
|
|
|
switch(function)
|
|
|
|
{
|
|
|
|
case MFID_4_Mic:
|
|
|
|
{
|
2020-12-05 17:47:09 +00:00
|
|
|
u32 subcommand = r8();
|
|
|
|
u32 dt1 = r8();
|
|
|
|
u16 dt23 = r16();
|
|
|
|
|
2014-01-16 07:05:39 +00:00
|
|
|
switch(subcommand)
|
2014-01-14 05:38:59 +00:00
|
|
|
{
|
2020-12-05 17:47:09 +00:00
|
|
|
case 0x01: // Get_Sampling_Data
|
2014-01-16 06:36:04 +00:00
|
|
|
{
|
2014-01-19 17:35:52 +00:00
|
|
|
w32(MFID_4_Mic);
|
|
|
|
|
2020-12-05 17:47:09 +00:00
|
|
|
u8 micdata[240 * 2];
|
|
|
|
u32 samples = RecordAudio(micdata, 240);
|
2014-01-19 17:35:52 +00:00
|
|
|
|
|
|
|
//32 bit header
|
2020-12-05 17:47:09 +00:00
|
|
|
//status: bit 7 6 5 4 3 2 1 0
|
|
|
|
// name EX_BIT SBFOV 0 14LSB1 14LSB0 SMPL ulaw Fs
|
|
|
|
w8((sampling << 2) | eight_khz);
|
|
|
|
w8(gain); // gain
|
|
|
|
w8(0); //(unused)
|
|
|
|
w8(samples); // sample count (max 240)
|
|
|
|
|
|
|
|
wptr(micdata, ((samples + 1) >> 1) << 2);
|
2014-01-18 21:54:18 +00:00
|
|
|
|
2014-01-14 05:38:59 +00:00
|
|
|
return MDRS_DataTransfer;
|
2014-01-16 06:36:04 +00:00
|
|
|
}
|
2020-12-05 17:47:09 +00:00
|
|
|
|
|
|
|
case 0x02: // Basic_Control
|
|
|
|
DEBUG_LOG(MAPLE, "maple_microphone::dma MDCF_MICControl Basic_Control DT1 %02x", dt1);
|
|
|
|
eight_khz = ((dt1 >> 2) & 3) == 1;
|
|
|
|
if (((dt1 & 0x80) == 0x80) != sampling)
|
|
|
|
{
|
|
|
|
if (sampling)
|
|
|
|
StopAudioRecording();
|
|
|
|
else
|
|
|
|
StartAudioRecording(eight_khz);
|
|
|
|
sampling = (dt1 & 0x80) == 0x80;
|
|
|
|
}
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
|
|
|
case 0x03: // AMP_GAIN
|
|
|
|
gain = dt1;
|
|
|
|
DEBUG_LOG(MAPLE, "maple_microphone::dma MDCF_MICControl set gain %x", gain);
|
2014-01-14 05:38:59 +00:00
|
|
|
return MDRS_DeviceReply;
|
2020-12-05 17:47:09 +00:00
|
|
|
|
|
|
|
case 0x04: // EXTU_BIT
|
|
|
|
DEBUG_LOG(MAPLE, "maple_microphone::dma MDCF_MICControl EXTU_BIT %#010x", dt1);
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
|
|
|
case 0x05: // Volume_Mode
|
|
|
|
DEBUG_LOG(MAPLE, "maple_microphone::dma MDCF_MICControl Volume_Mode %#010x", dt1);
|
2014-01-14 05:38:59 +00:00
|
|
|
return MDRS_DeviceReply;
|
2020-12-05 17:47:09 +00:00
|
|
|
|
2018-10-11 08:09:28 +00:00
|
|
|
case MDRE_TransmitAgain:
|
2019-06-30 21:38:58 +00:00
|
|
|
WARN_LOG(MAPLE, "maple_microphone::dma MDCF_MICControl MDRE_TransmitAgain");
|
2014-01-19 17:35:52 +00:00
|
|
|
//apparently this doesnt matter
|
|
|
|
//wptr(micdata, SIZE_OF_MIC_DATA);
|
|
|
|
return MDRS_DeviceReply;//MDRS_DataTransfer;
|
2020-12-05 17:47:09 +00:00
|
|
|
|
2014-01-14 05:38:59 +00:00
|
|
|
default:
|
2020-12-05 17:47:09 +00:00
|
|
|
INFO_LOG(MAPLE, "maple_microphone::dma UNHANDLED DT1 %02x DT23 %04x", dt1, dt23);
|
2018-09-08 12:35:46 +00:00
|
|
|
return MDRE_UnknownFunction;
|
2014-01-14 05:38:59 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-05 17:47:09 +00:00
|
|
|
|
2014-01-14 05:38:59 +00:00
|
|
|
default:
|
2019-06-30 21:38:58 +00:00
|
|
|
INFO_LOG(MAPLE, "maple_microphone::dma UNHANDLED function %#010x", function);
|
2018-09-08 12:35:46 +00:00
|
|
|
return MDRE_UnknownFunction;
|
2014-01-14 05:38:59 +00:00
|
|
|
}
|
2020-12-05 17:47:09 +00:00
|
|
|
break;
|
2014-01-14 05:38:59 +00:00
|
|
|
}
|
2014-01-13 17:12:26 +00:00
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_DeviceKill:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
2014-01-13 17:12:26 +00:00
|
|
|
default:
|
2019-06-30 21:38:58 +00:00
|
|
|
INFO_LOG(MAPLE, "maple_microphone::dma UNHANDLED MAPLE COMMAND %d", cmd);
|
2018-09-08 12:35:46 +00:00
|
|
|
return MDRE_UnknownCmd;
|
2014-01-13 17:12:26 +00:00
|
|
|
}
|
2018-08-27 11:50:59 +00:00
|
|
|
}
|
2014-01-13 17:12:26 +00:00
|
|
|
};
|
|
|
|
|
2015-12-08 00:21:03 +00:00
|
|
|
struct maple_sega_purupuru : maple_base
|
|
|
|
{
|
2019-02-22 18:23:03 +00:00
|
|
|
u16 AST = 19, AST_ms = 5000;
|
2015-12-08 00:21:03 +00:00
|
|
|
u32 VIBSET;
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
MapleDeviceType get_device_type() override
|
2018-09-29 14:13:07 +00:00
|
|
|
{
|
|
|
|
return MDT_PurupuruPack;
|
|
|
|
}
|
|
|
|
|
2021-11-13 14:56:42 +00:00
|
|
|
void serialize(Serializer& ser) const override
|
|
|
|
{
|
|
|
|
maple_base::serialize(ser);
|
|
|
|
ser << AST;
|
|
|
|
ser << AST_ms;
|
|
|
|
ser << VIBSET;
|
|
|
|
}
|
|
|
|
void deserialize(Deserializer& deser) override
|
|
|
|
{
|
|
|
|
maple_base::deserialize(deser);
|
|
|
|
deser >> AST;
|
|
|
|
deser >> AST_ms;
|
|
|
|
deser >> VIBSET;
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 dma(u32 cmd) override
|
2015-12-08 00:21:03 +00:00
|
|
|
{
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case MDC_DeviceRequest:
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_AllStatusReq:
|
2015-12-08 00:21:03 +00:00
|
|
|
//caps
|
|
|
|
//4
|
|
|
|
w32(MFID_8_Vibration);
|
|
|
|
|
|
|
|
//struct data
|
|
|
|
//3*4
|
|
|
|
w32(0x00000101);
|
|
|
|
w32(0);
|
|
|
|
w32(0);
|
|
|
|
|
|
|
|
//1 area code
|
|
|
|
w8(0xFF);
|
|
|
|
|
|
|
|
//1 direction
|
|
|
|
w8(0);
|
|
|
|
|
|
|
|
//30
|
|
|
|
wstr(maple_sega_purupuru_name, 30);
|
|
|
|
|
|
|
|
//60
|
|
|
|
wstr(maple_sega_brand, 60);
|
|
|
|
|
|
|
|
//2
|
2018-09-29 14:13:07 +00:00
|
|
|
w16(0x00C8); // 20 mA
|
2015-12-08 00:21:03 +00:00
|
|
|
|
|
|
|
//2
|
2018-09-29 14:13:07 +00:00
|
|
|
w16(0x0640); // 160 mA
|
2015-12-08 00:21:03 +00:00
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
return cmd == MDC_DeviceRequest ? MDRS_DeviceStatus : MDRS_DeviceStatusAll;
|
2015-12-08 00:21:03 +00:00
|
|
|
|
|
|
|
//get last vibration
|
|
|
|
case MDCF_GetCondition:
|
2018-08-27 11:50:59 +00:00
|
|
|
|
2015-12-08 00:21:03 +00:00
|
|
|
w32(MFID_8_Vibration);
|
|
|
|
|
|
|
|
w32(VIBSET);
|
|
|
|
|
|
|
|
return MDRS_DataTransfer;
|
|
|
|
|
|
|
|
case MDCF_GetMediaInfo:
|
|
|
|
|
|
|
|
w32(MFID_8_Vibration);
|
|
|
|
|
|
|
|
// PuruPuru vib specs
|
2018-08-27 11:50:59 +00:00
|
|
|
w32(0x3B07E010);
|
2015-12-08 00:21:03 +00:00
|
|
|
|
|
|
|
return MDRS_DataTransfer;
|
|
|
|
|
|
|
|
case MDCF_BlockRead:
|
|
|
|
|
|
|
|
w32(MFID_8_Vibration);
|
|
|
|
w32(0);
|
|
|
|
|
|
|
|
w16(2);
|
|
|
|
w16(AST);
|
|
|
|
|
|
|
|
return MDRS_DataTransfer;
|
|
|
|
|
|
|
|
case MDCF_BlockWrite:
|
2018-08-27 11:50:59 +00:00
|
|
|
|
2015-12-08 00:21:03 +00:00
|
|
|
//Auto-stop time
|
|
|
|
AST = dma_buffer_in[10];
|
|
|
|
AST_ms = AST * 250 + 250;
|
|
|
|
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
|
|
|
case MDCF_SetCondition:
|
2018-08-27 11:50:59 +00:00
|
|
|
|
2015-12-08 00:21:03 +00:00
|
|
|
VIBSET = *(u32*)&dma_buffer_in[4];
|
2019-02-22 18:23:03 +00:00
|
|
|
{
|
|
|
|
//Do the rumble thing!
|
|
|
|
u8 POW_POS = (VIBSET >> 8) & 0x7;
|
|
|
|
u8 POW_NEG = (VIBSET >> 12) & 0x7;
|
|
|
|
u8 FREQ = (VIBSET >> 16) & 0xFF;
|
|
|
|
s16 INC = (VIBSET >> 24) & 0xFF;
|
|
|
|
if (VIBSET & 0x8000) // INH
|
|
|
|
INC = -INC;
|
|
|
|
else if (!(VIBSET & 0x0800)) // EXH
|
|
|
|
INC = 0;
|
|
|
|
bool CNT = VIBSET & 1;
|
|
|
|
|
2020-03-29 17:29:14 +00:00
|
|
|
float power = std::min((POW_POS + POW_NEG) / 7.0, 1.0);
|
2019-02-22 18:53:26 +00:00
|
|
|
|
|
|
|
u32 duration_ms;
|
|
|
|
if (FREQ > 0 && (!CNT || INC))
|
2020-03-29 17:29:14 +00:00
|
|
|
duration_ms = std::min((int)(1000 * (INC ? abs(INC) * std::max(POW_POS, POW_NEG) : 1) / FREQ), (int)AST_ms);
|
2019-02-22 18:53:26 +00:00
|
|
|
else
|
|
|
|
duration_ms = AST_ms;
|
|
|
|
float inclination;
|
|
|
|
if (INC == 0 || power == 0)
|
|
|
|
inclination = 0.0;
|
|
|
|
else
|
2020-03-29 17:29:14 +00:00
|
|
|
inclination = FREQ / (1000.0 * INC * std::max(POW_POS, POW_NEG));
|
2019-02-22 18:53:26 +00:00
|
|
|
config->SetVibration(power, inclination, duration_ms);
|
2019-02-22 18:23:03 +00:00
|
|
|
}
|
2015-12-08 00:21:03 +00:00
|
|
|
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_DeviceReset:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
|
|
|
case MDC_DeviceKill:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
2015-12-08 00:21:03 +00:00
|
|
|
default:
|
2019-06-30 21:38:58 +00:00
|
|
|
INFO_LOG(MAPLE, "UNKOWN MAPLE COMMAND %d", cmd);
|
2018-09-08 12:35:46 +00:00
|
|
|
return MDRE_UnknownCmd;
|
2015-12-08 00:21:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-09-06 18:17:28 +00:00
|
|
|
struct maple_keyboard : maple_base
|
|
|
|
{
|
2021-03-13 11:44:59 +00:00
|
|
|
MapleDeviceType get_device_type() override
|
2018-09-29 14:13:07 +00:00
|
|
|
{
|
|
|
|
return MDT_Keyboard;
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 dma(u32 cmd) override
|
2018-09-06 18:17:28 +00:00
|
|
|
{
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case MDC_DeviceRequest:
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_AllStatusReq:
|
2018-09-06 18:17:28 +00:00
|
|
|
//caps
|
|
|
|
//4
|
2018-09-08 12:35:46 +00:00
|
|
|
w32(MFID_6_Keyboard);
|
2018-09-06 18:17:28 +00:00
|
|
|
|
|
|
|
//struct data
|
|
|
|
//3*4
|
2021-03-02 17:45:18 +00:00
|
|
|
w8((u8)settings.input.keyboardLangId);
|
|
|
|
switch (settings.input.keyboardLangId)
|
|
|
|
{
|
|
|
|
case KeyboardLayout::JP:
|
|
|
|
w8(2); // 92 keys
|
|
|
|
break;
|
|
|
|
case KeyboardLayout::US:
|
|
|
|
w8(5); // 104 keys
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
w8(6); // 105 keys
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
w8(0);
|
|
|
|
w8(0x80); // keyboard-controlled LEDs
|
|
|
|
|
2018-09-08 12:35:46 +00:00
|
|
|
w32(0);
|
|
|
|
w32(0);
|
2018-09-06 18:17:28 +00:00
|
|
|
//1 area code
|
|
|
|
w8(0xFF);
|
|
|
|
//1 direction
|
|
|
|
w8(0);
|
2018-09-08 12:35:46 +00:00
|
|
|
// Product name (30)
|
2020-06-23 16:10:22 +00:00
|
|
|
wstr(maple_sega_kbd_name, 30);
|
2018-09-06 18:17:28 +00:00
|
|
|
|
2018-09-08 12:35:46 +00:00
|
|
|
// License (60)
|
2020-06-23 16:10:22 +00:00
|
|
|
wstr(maple_sega_brand, 60);
|
2018-09-06 18:17:28 +00:00
|
|
|
|
2018-09-08 12:35:46 +00:00
|
|
|
// Low-consumption standby current (2)
|
2018-09-29 14:13:07 +00:00
|
|
|
w16(0x01AE); // 43 mA
|
2018-09-06 18:17:28 +00:00
|
|
|
|
2018-09-08 12:35:46 +00:00
|
|
|
// Maximum current consumption (2)
|
2018-09-29 14:13:07 +00:00
|
|
|
w16(0x01F5); // 50.1 mA
|
2018-09-06 18:17:28 +00:00
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
return cmd == MDC_DeviceRequest ? MDRS_DeviceStatus : MDRS_DeviceStatusAll;
|
2018-09-06 18:17:28 +00:00
|
|
|
|
|
|
|
case MDCF_GetCondition:
|
2021-10-19 14:56:46 +00:00
|
|
|
{
|
|
|
|
u8 shift;
|
|
|
|
u8 keys[6];
|
|
|
|
config->GetKeyboardInput(shift, keys);
|
|
|
|
|
|
|
|
w32(MFID_6_Keyboard);
|
|
|
|
//struct data
|
|
|
|
//int8 shift ; shift keys pressed (bitmask) //1
|
|
|
|
w8(shift);
|
|
|
|
//int8 led ; leds currently lit //1
|
|
|
|
w8(0);
|
|
|
|
//int8 key[6] ; normal keys pressed //6
|
2023-02-18 12:24:34 +00:00
|
|
|
for (std::size_t i = 0; i < std::size(keys); i++)
|
2021-10-19 14:56:46 +00:00
|
|
|
w8(keys[i]);
|
|
|
|
}
|
2018-09-08 12:35:46 +00:00
|
|
|
return MDRS_DataTransfer;
|
2018-09-06 18:17:28 +00:00
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_DeviceReset:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
|
|
|
case MDC_DeviceKill:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
2018-09-06 18:17:28 +00:00
|
|
|
default:
|
2019-07-01 08:28:31 +00:00
|
|
|
INFO_LOG(MAPLE, "Keyboard: unknown MAPLE COMMAND %d", cmd);
|
2018-09-08 12:35:46 +00:00
|
|
|
return MDRE_UnknownCmd;
|
2018-09-06 18:17:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-09-18 07:27:16 +00:00
|
|
|
struct maple_mouse : maple_base
|
|
|
|
{
|
2021-03-13 11:44:59 +00:00
|
|
|
MapleDeviceType get_device_type() override
|
2018-09-29 14:13:07 +00:00
|
|
|
{
|
|
|
|
return MDT_Mouse;
|
|
|
|
}
|
|
|
|
|
2021-01-21 20:00:46 +00:00
|
|
|
static u16 mo_cvt(int delta)
|
2018-09-18 07:27:16 +00:00
|
|
|
{
|
2021-01-21 20:00:46 +00:00
|
|
|
return (u16)std::min(0x3FF, std::max(0, delta + 0x200));
|
2018-09-18 07:27:16 +00:00
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 dma(u32 cmd) override
|
2018-09-18 07:27:16 +00:00
|
|
|
{
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case MDC_DeviceRequest:
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_AllStatusReq:
|
2018-09-18 07:27:16 +00:00
|
|
|
//caps
|
|
|
|
//4
|
|
|
|
w32(MFID_9_Mouse);
|
|
|
|
|
|
|
|
//struct data
|
|
|
|
//3*4
|
2018-09-26 20:37:51 +00:00
|
|
|
w32(0x00070E00); // Mouse, 3 buttons, 3 axes
|
2018-09-18 07:27:16 +00:00
|
|
|
w32(0);
|
|
|
|
w32(0);
|
|
|
|
//1 area code
|
|
|
|
w8(0xFF);
|
|
|
|
//1 direction
|
|
|
|
w8(0);
|
|
|
|
// Product name (30)
|
2020-06-23 16:10:22 +00:00
|
|
|
wstr(maple_sega_mouse_name, 30);
|
2018-09-18 07:27:16 +00:00
|
|
|
|
|
|
|
// License (60)
|
2020-06-23 16:10:22 +00:00
|
|
|
wstr(maple_sega_brand, 60);
|
2018-09-18 07:27:16 +00:00
|
|
|
|
|
|
|
// Low-consumption standby current (2)
|
2021-11-13 14:56:42 +00:00
|
|
|
w16(0x0190); // 40 mA
|
2018-09-18 07:27:16 +00:00
|
|
|
|
|
|
|
// Maximum current consumption (2)
|
2021-11-13 14:56:42 +00:00
|
|
|
w16(0x01f4); // 50 mA
|
2018-09-18 07:27:16 +00:00
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
return cmd == MDC_DeviceRequest ? MDRS_DeviceStatus : MDRS_DeviceStatusAll;
|
2018-09-18 07:27:16 +00:00
|
|
|
|
|
|
|
case MDCF_GetCondition:
|
2021-01-21 20:00:46 +00:00
|
|
|
{
|
2021-05-19 16:13:52 +00:00
|
|
|
u8 buttons;
|
2021-01-21 20:00:46 +00:00
|
|
|
int x, y, wheel;
|
|
|
|
config->GetMouseInput(buttons, x, y, wheel);
|
|
|
|
|
|
|
|
w32(MFID_9_Mouse);
|
2021-05-19 16:13:52 +00:00
|
|
|
// buttons (RLDUSABC, where A is left btn, B is right, and S is middle/scrollwheel)
|
|
|
|
w8(buttons);
|
|
|
|
// options
|
|
|
|
w8(0);
|
|
|
|
// axes overflow
|
|
|
|
w8(0);
|
|
|
|
// reserved
|
|
|
|
w8(0);
|
2021-01-21 20:00:46 +00:00
|
|
|
//int16 axis1 ; horizontal movement (0-$3FF) (little endian)
|
|
|
|
w16(mo_cvt(x));
|
|
|
|
//int16 axis2 ; vertical movement (0-$3FF) (little endian)
|
|
|
|
w16(mo_cvt(y));
|
|
|
|
//int16 axis3 ; mouse wheel movement (0-$3FF) (little endian)
|
|
|
|
w16(mo_cvt(wheel));
|
|
|
|
//int16 axis4 ; ? movement (0-$3FF) (little endian)
|
|
|
|
w16(mo_cvt(0));
|
|
|
|
//int16 axis5 ; ? movement (0-$3FF) (little endian)
|
|
|
|
w16(mo_cvt(0));
|
|
|
|
//int16 axis6 ; ? movement (0-$3FF) (little endian)
|
|
|
|
w16(mo_cvt(0));
|
|
|
|
//int16 axis7 ; ? movement (0-$3FF) (little endian)
|
|
|
|
w16(mo_cvt(0));
|
|
|
|
//int16 axis8 ; ? movement (0-$3FF) (little endian)
|
|
|
|
w16(mo_cvt(0));
|
|
|
|
}
|
2018-09-18 07:27:16 +00:00
|
|
|
|
|
|
|
return MDRS_DataTransfer;
|
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_DeviceReset:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
|
|
|
case MDC_DeviceKill:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
2018-09-18 07:27:16 +00:00
|
|
|
default:
|
2019-07-01 08:28:31 +00:00
|
|
|
INFO_LOG(MAPLE, "Mouse: unknown MAPLE COMMAND %d", cmd);
|
2018-09-18 07:27:16 +00:00
|
|
|
return MDRE_UnknownCmd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-10-01 18:34:35 +00:00
|
|
|
struct maple_lightgun : maple_base
|
|
|
|
{
|
2021-09-07 08:54:00 +00:00
|
|
|
virtual u32 transform_kcode(u32 kcode)
|
|
|
|
{
|
|
|
|
mutualExclusion(kcode, DC_DPAD_UP | DC_DPAD_DOWN);
|
|
|
|
mutualExclusion(kcode, DC_DPAD_LEFT | DC_DPAD_RIGHT);
|
2020-12-02 13:40:50 +00:00
|
|
|
if ((kcode & DC_BTN_RELOAD) == 0)
|
|
|
|
kcode &= ~DC_BTN_A; // trigger
|
2019-04-08 17:14:55 +00:00
|
|
|
return kcode | 0xFF01;
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
MapleDeviceType get_device_type() override
|
2018-10-01 18:34:35 +00:00
|
|
|
{
|
|
|
|
return MDT_LightGun;
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 dma(u32 cmd) override
|
2018-10-01 18:34:35 +00:00
|
|
|
{
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case MDC_DeviceRequest:
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_AllStatusReq:
|
2018-10-01 18:34:35 +00:00
|
|
|
//caps
|
|
|
|
//4
|
|
|
|
w32(MFID_7_LightGun | MFID_0_Input);
|
|
|
|
|
|
|
|
//struct data
|
|
|
|
//3*4
|
|
|
|
w32(0); // Light gun
|
|
|
|
w32(0xFE000000); // Controller
|
|
|
|
w32(0);
|
|
|
|
//1 area code
|
2020-06-23 16:10:22 +00:00
|
|
|
w8(1); // FF: Worldwide, 01: North America
|
2018-10-01 18:34:35 +00:00
|
|
|
//1 direction
|
|
|
|
w8(0);
|
|
|
|
// Product name (30)
|
2020-06-23 16:10:22 +00:00
|
|
|
wstr(maple_sega_lightgun_name, 30);
|
2018-10-01 18:34:35 +00:00
|
|
|
|
|
|
|
// License (60)
|
2020-06-23 16:10:22 +00:00
|
|
|
wstr(maple_sega_brand, 60);
|
2018-10-01 18:34:35 +00:00
|
|
|
|
|
|
|
// Low-consumption standby current (2)
|
|
|
|
w16(0x0069); // 10.5 mA
|
|
|
|
|
|
|
|
// Maximum current consumption (2)
|
|
|
|
w16(0x0120); // 28.8 mA
|
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
return cmd == MDC_DeviceRequest ? MDRS_DeviceStatus : MDRS_DeviceStatusAll;
|
2018-10-01 18:34:35 +00:00
|
|
|
|
|
|
|
case MDCF_GetCondition:
|
|
|
|
{
|
|
|
|
PlainJoystickState pjs;
|
|
|
|
config->GetInput(&pjs);
|
|
|
|
|
|
|
|
//caps
|
|
|
|
//4
|
|
|
|
w32(MFID_0_Input);
|
|
|
|
|
|
|
|
//state data
|
|
|
|
//2 key code
|
2019-04-10 09:31:08 +00:00
|
|
|
w16(transform_kcode(pjs.kcode));
|
2018-10-01 18:34:35 +00:00
|
|
|
|
|
|
|
//not used
|
|
|
|
//2
|
|
|
|
w16(0xFFFF);
|
|
|
|
|
|
|
|
//not used
|
|
|
|
//4
|
|
|
|
w32(0x80808080);
|
|
|
|
}
|
|
|
|
return MDRS_DataTransfer;
|
|
|
|
|
2020-06-15 15:04:04 +00:00
|
|
|
case MDC_DeviceReset:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
|
|
|
case MDC_DeviceKill:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
2018-10-01 18:34:35 +00:00
|
|
|
default:
|
2019-07-01 08:28:31 +00:00
|
|
|
INFO_LOG(MAPLE, "Light gun: unknown MAPLE COMMAND %d", cmd);
|
2018-10-01 18:34:35 +00:00
|
|
|
return MDRE_UnknownCmd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
bool get_lightgun_pos() override
|
2018-10-01 18:34:35 +00:00
|
|
|
{
|
2020-12-02 13:40:50 +00:00
|
|
|
PlainJoystickState pjs;
|
|
|
|
config->GetInput(&pjs);
|
2021-01-21 20:00:46 +00:00
|
|
|
int x, y;
|
|
|
|
config->GetAbsCoordinates(x, y);
|
2020-12-02 13:40:50 +00:00
|
|
|
|
|
|
|
if ((pjs.kcode & DC_BTN_RELOAD) == 0)
|
|
|
|
read_lightgun_position(-1, -1);
|
|
|
|
else
|
2021-01-21 20:00:46 +00:00
|
|
|
read_lightgun_position(x, y);
|
2020-11-30 20:31:50 +00:00
|
|
|
return true;
|
2018-10-01 18:34:35 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-04-08 17:14:55 +00:00
|
|
|
struct atomiswave_lightgun : maple_lightgun
|
|
|
|
{
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 transform_kcode(u32 kcode) override {
|
2021-09-07 08:54:00 +00:00
|
|
|
mutualExclusion(kcode, AWAVE_UP_KEY | AWAVE_DOWN_KEY);
|
|
|
|
mutualExclusion(kcode, AWAVE_LEFT_KEY | AWAVE_RIGHT_KEY);
|
2020-12-02 13:40:50 +00:00
|
|
|
// No need for reload on AW
|
2019-04-08 17:14:55 +00:00
|
|
|
return (kcode & AWAVE_TRIGGER_KEY) == 0 ? ~AWAVE_BTN0_KEY : ~0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-05-29 10:45:34 +00:00
|
|
|
struct maple_maracas_controller: maple_sega_controller
|
|
|
|
{
|
|
|
|
u32 get_capabilities() override {
|
|
|
|
// byte 0: 0 0 0 0 0 0 0 0
|
|
|
|
// byte 1: 0 0 a5 a4 a3 a2 a1 a0
|
|
|
|
// byte 2: R2 L2 D2 U2 D X Y Z
|
|
|
|
// byte 3: R L D U St A B C
|
|
|
|
|
|
|
|
return 0x0f093c00; // 4 analog axes (2-5) A B C D Z Start
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u16 getButtonState(const PlainJoystickState &pjs) override {
|
|
|
|
return pjs.kcode | 0xf6f0; // mask off DPad2, X, Y, DPad;
|
2023-05-29 10:45:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MapleDeviceType get_device_type() override {
|
|
|
|
return MDT_MaracasController;
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 getAnalogAxis(int index, const PlainJoystickState &pjs) override {
|
2023-05-29 10:45:34 +00:00
|
|
|
if (index < 2 || index > 5)
|
|
|
|
return 0;
|
|
|
|
return pjs.joy[index -2];
|
|
|
|
/* // This should be tested with real maracas to see if it is worth implementing or not
|
|
|
|
u8 maracas_saturation_reduction = 2;
|
|
|
|
s32 axis_val = (pjs.joy[index -2] - 0x80) / maracas_saturation_reduction + 0x80;
|
|
|
|
if (axis_val < 0) axis_val = 0;
|
|
|
|
else if (axis_val > 0xff) axis_val = 0xFF;
|
|
|
|
return axis_val; */
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *get_device_name() override {
|
|
|
|
return maple_maracas_controller_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 get_device_current(int get_max_current) override {
|
|
|
|
return get_max_current ? 0x0546 : 0x044C; // Max. 130 mA, standby: 100 mA
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct maple_fishing_controller: maple_sega_controller
|
|
|
|
{
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 analogToDPad = ~0;
|
|
|
|
|
2023-05-29 10:45:34 +00:00
|
|
|
u32 get_capabilities() override {
|
|
|
|
// byte 0: 0 0 0 0 0 0 0 0
|
|
|
|
// byte 1: 0 0 a5 a4 a3 a2 a1 a0
|
|
|
|
// byte 2: R2 L2 D2 U2 D X Y Z
|
|
|
|
// byte 3: R L D U St A B C
|
|
|
|
|
|
|
|
return 0x0fe063f00; // Ra,La,Da,Ua,A,B,X,Y,Start,A1,A2,A3,A4,A5,A6
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u16 getButtonState(const PlainJoystickState &pjs) override
|
|
|
|
{
|
|
|
|
// Analog to DPad handling
|
|
|
|
if (pjs.joy[PJAI_X1] < 0x30) {
|
|
|
|
analogToDPad &= ~DC_DPAD_LEFT;
|
|
|
|
analogToDPad |= DC_DPAD_RIGHT;
|
|
|
|
}
|
|
|
|
else if (pjs.joy[PJAI_X1] > 0xd0) {
|
|
|
|
analogToDPad &= ~DC_DPAD_RIGHT;
|
|
|
|
analogToDPad |= DC_DPAD_LEFT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pjs.joy[PJAI_X1] >= 0x40)
|
|
|
|
analogToDPad |= DC_DPAD_LEFT;
|
|
|
|
if (pjs.joy[PJAI_X1] <= 0xc0)
|
|
|
|
analogToDPad |= DC_DPAD_RIGHT;
|
|
|
|
}
|
|
|
|
if (pjs.joy[PJAI_Y1] < 0x30) {
|
|
|
|
analogToDPad &= ~DC_DPAD_UP;
|
|
|
|
analogToDPad |= DC_DPAD_DOWN;
|
|
|
|
}
|
|
|
|
else if (pjs.joy[PJAI_Y1] > 0xd0) {
|
|
|
|
analogToDPad &= ~DC_DPAD_DOWN;
|
|
|
|
analogToDPad |= DC_DPAD_UP;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pjs.joy[PJAI_Y1] >= 0x40)
|
|
|
|
analogToDPad |= DC_DPAD_UP;
|
|
|
|
if (pjs.joy[PJAI_Y1] <= 0xc0)
|
|
|
|
analogToDPad |= DC_DPAD_DOWN;
|
|
|
|
}
|
|
|
|
u32 kcode = pjs.kcode & analogToDPad;
|
2023-05-29 10:45:34 +00:00
|
|
|
mutualExclusion(kcode, DC_DPAD_UP | DC_DPAD_DOWN);
|
|
|
|
mutualExclusion(kcode, DC_DPAD_LEFT | DC_DPAD_RIGHT);
|
|
|
|
return kcode | 0xf901; // mask off DPad2, D, Z, C;
|
|
|
|
}
|
|
|
|
|
|
|
|
MapleDeviceType get_device_type() override {
|
|
|
|
return MDT_FishingController;
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 getAnalogAxis(int index, const PlainJoystickState &pjs) override
|
|
|
|
{
|
2023-05-29 10:45:34 +00:00
|
|
|
// In the XYZ axes, acceleration sensor outputs 80 ± 8H (home position)
|
|
|
|
// in the static state (± 0G), F0h or greater for maximum force (+10G)
|
|
|
|
// in the positive direction and 11h or less
|
|
|
|
// for the maximum force (-10G) applied in the negative direction
|
|
|
|
// From the perspective of the player operating the controller:
|
|
|
|
// X: Right is positive, left is negative
|
|
|
|
// Y: Down is positive, up is negative
|
|
|
|
// Z: Forward is positive, backward is negative
|
2023-05-31 15:01:54 +00:00
|
|
|
switch (index)
|
|
|
|
{
|
|
|
|
case 0:
|
2023-05-29 10:45:34 +00:00
|
|
|
return pjs.trigger[PJTI_R]; // A1: Reel handle output
|
2023-05-31 15:01:54 +00:00
|
|
|
case 1:
|
|
|
|
return pjs.joy[PJAI_X3]; // A2: acceleration sensor Z
|
|
|
|
case 2:
|
|
|
|
return pjs.joy[PJAI_X1]; // A3: analog stick X
|
|
|
|
case 3:
|
|
|
|
return pjs.joy[PJAI_Y1]; // A4: analog stick Y
|
|
|
|
case 4:
|
|
|
|
return pjs.joy[PJAI_X2]; // A5: acceleration sensor X
|
|
|
|
case 5:
|
|
|
|
return pjs.joy[PJAI_Y2]; // A6: acceleration sensor Y
|
|
|
|
default:
|
|
|
|
return 0x80;
|
|
|
|
}
|
2023-05-29 10:45:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *get_device_name() override {
|
|
|
|
return maple_fishing_controller_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 get_device_current(int get_max_current) override {
|
|
|
|
return get_max_current ? 0x0960 : 0x0258; // Max. 240 mA, standby: 60 mA
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct maple_popnmusic_controller: maple_sega_controller
|
|
|
|
{
|
|
|
|
u32 get_capabilities() override {
|
|
|
|
// byte 0: 0 0 0 0 0 0 0 0
|
|
|
|
// byte 1: 0 0 a5 a4 a3 a2 a1 a0
|
|
|
|
// byte 2: R2 L2 D2 U2 D X Y Z
|
|
|
|
// byte 3: R L D U St A B C
|
|
|
|
|
|
|
|
return 0xff060000; // no analog axes, X Y A B C Start U/D/L/R
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u16 getButtonState(const PlainJoystickState &pjs) override {
|
|
|
|
return pjs.kcode | 0xf100; // mask off DPad2 and Z
|
2023-05-29 10:45:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MapleDeviceType get_device_type() override {
|
|
|
|
return MDT_PopnMusicController;
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 getAnalogAxis(int index, const PlainJoystickState &pjs) override {
|
2023-05-29 10:45:34 +00:00
|
|
|
if (index == 0 || index == 1)
|
|
|
|
return 0; // Right and left triggers
|
|
|
|
return 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *get_device_name() override {
|
|
|
|
return maple_popnmusic_controller_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 get_device_current(int get_max_current) override {
|
|
|
|
return get_max_current ? 0x012C : 0x00AA; // Max. 30 mA, standby: 17 mA
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct maple_racing_controller: maple_sega_controller
|
|
|
|
{
|
|
|
|
u32 get_capabilities() override {
|
|
|
|
// byte 0: 0 0 0 0 0 0 0 0
|
|
|
|
// byte 1: 0 0 a5 a4 a3 a2 a1 a0
|
|
|
|
// byte 2: R2 L2 D2 U2 D X Y Z
|
|
|
|
// byte 3: R L D U St A B C
|
|
|
|
|
|
|
|
return 0xfe003700; // Steering + accelerator/brake unit: Ra,La,Da,Ua,A,B,Start,A1,A2,A3,A5,A6
|
|
|
|
// (A5 & A6 only valid when the accelerator/brake unit is connected.)
|
|
|
|
//return 0xfe000700; // Steering only
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u16 getButtonState(const PlainJoystickState &pjs) override
|
|
|
|
{
|
2023-05-29 10:45:34 +00:00
|
|
|
// Ra, La are ON when A3 threshold values (La: 40h, Ra: BEh) are exceeded
|
|
|
|
u32 kcode = pjs.kcode;
|
|
|
|
if (pjs.joy[PJAI_X1] < 0x40)
|
2023-05-31 15:01:54 +00:00
|
|
|
kcode &= ~DC_DPAD_LEFT;
|
2023-05-29 10:45:34 +00:00
|
|
|
else if (pjs.joy[PJAI_X1] > 0xBE)
|
2023-05-31 15:01:54 +00:00
|
|
|
kcode &= ~DC_DPAD_RIGHT;
|
2023-05-29 10:45:34 +00:00
|
|
|
mutualExclusion(kcode, DC_DPAD_UP | DC_DPAD_DOWN);
|
|
|
|
mutualExclusion(kcode, DC_DPAD_LEFT | DC_DPAD_RIGHT);
|
|
|
|
return kcode | 0xff01; // mask off DPad2, D, X, Y, Z, C
|
|
|
|
}
|
|
|
|
|
|
|
|
MapleDeviceType get_device_type() override {
|
|
|
|
return MDT_RacingController;
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 getAnalogAxis(int index, const PlainJoystickState &pjs) override
|
|
|
|
{
|
|
|
|
switch (index)
|
|
|
|
{
|
|
|
|
case 0: return pjs.trigger[PJTI_R]; // A1: lever, 0 at rest
|
|
|
|
case 1: return pjs.trigger[PJTI_L]; // A2: lever, 0 at rest
|
|
|
|
case 2: return pjs.joy[PJAI_X1]; // A3: 0-0xff, 0x80 at rest
|
|
|
|
// (A5 and A6 are only valid when the accelerator/brake unit is connected)
|
|
|
|
case 4: return pjs.trigger[PJTI_R2]; // A5: lever, 0 at rest
|
|
|
|
case 5: return pjs.trigger[PJTI_L2]; // A6: lever, 0 at rest
|
|
|
|
default: return 0x80; // unused
|
|
|
|
}
|
2023-05-29 10:45:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *get_device_name() override {
|
|
|
|
return maple_racing_controller_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 get_device_current(int get_max_current) override {
|
|
|
|
return get_max_current ? 0x0226 : 0x01B8; // Max. 55 mA, standby: 44 mA
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct maple_densha_controller: maple_sega_controller
|
|
|
|
{
|
|
|
|
u32 get_capabilities() override {
|
|
|
|
// byte 0: 0 0 0 0 0 0 0 0
|
|
|
|
// byte 1: 0 0 a5 a4 a3 a2 a1 a0
|
|
|
|
// byte 2: R2 L2 D2 U2 D X Y Z
|
|
|
|
// byte 3: R L D U St A B C
|
|
|
|
|
|
|
|
return 0xff0f3f00; // Ra,La,Da,Ua A,B,C,D,X,Y,Z,Start Xa,Ya,Xb,Yb Analog levers R,L
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u16 getButtonState(const PlainJoystickState &pjs) override {
|
2023-05-29 10:45:34 +00:00
|
|
|
// Ra,La,Da,Ua are used together, corresponding to the brake lever.
|
2023-05-31 15:01:54 +00:00
|
|
|
return pjs.kcode | 0xF000; // mask off DPad2
|
2023-05-29 10:45:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MapleDeviceType get_device_type() override {
|
|
|
|
return MDT_DenshaDeGoController;
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:01:54 +00:00
|
|
|
u32 getAnalogAxis(int index, const PlainJoystickState &pjs) override {
|
2023-05-29 10:45:34 +00:00
|
|
|
if (index == 2 || index == 3)
|
|
|
|
return 0;
|
|
|
|
if (index == 0 || index == 1 || index == 4 || index == 5)
|
|
|
|
return 0xff;
|
|
|
|
return 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *get_device_name() override {
|
|
|
|
return maple_densha_controller_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 get_device_current(int get_max_current) override {
|
|
|
|
return get_max_current ? 0x01F4 : 0x00DC; // Max. 50 mA, standby: 22 mA
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2023-04-29 15:50:56 +00:00
|
|
|
// Emulates a 838-14245-92 maple to RS232 converter
|
|
|
|
// wired to a 838-14243 RFID reader/writer (apparently Saxa HW210)
|
|
|
|
struct RFIDReaderWriter : maple_base
|
|
|
|
{
|
|
|
|
u32 getStatus() const
|
|
|
|
{
|
|
|
|
// b0: !card switch
|
|
|
|
// b1: state=4 errors?
|
|
|
|
// b2: state=5
|
|
|
|
// b3: state=6
|
|
|
|
// b4: state=7
|
|
|
|
// b5: state=8
|
|
|
|
// b6: card lock
|
|
|
|
// when 0x40 trying to read the card
|
|
|
|
u32 status = 1;
|
|
|
|
if (cardInserted)
|
|
|
|
status &= ~1;
|
|
|
|
if (cardLocked)
|
|
|
|
status |= 0x40;
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Surprisingly recipient and sender aren't swapped in the response so we override RawDma for this reason
|
|
|
|
// vf4tuned and mushiking do care
|
|
|
|
u32 RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out) override
|
|
|
|
{
|
|
|
|
u32 command=buffer_in[0] &0xFF;
|
|
|
|
//Recipient address
|
|
|
|
u32 reci = (buffer_in[0] >> 8) & 0xFF;
|
|
|
|
//Sender address
|
|
|
|
u32 send = (buffer_in[0] >> 16) & 0xFF;
|
|
|
|
u32 outlen = 0;
|
|
|
|
u32 resp = Dma(command, &buffer_in[1], buffer_in_len - 4, &buffer_out[1], outlen);
|
|
|
|
|
|
|
|
if (reci & 0x20)
|
|
|
|
reci |= maple_GetAttachedDevices(maple_GetBusId(reci));
|
|
|
|
|
|
|
|
verify(u8(outlen / 4) * 4 == outlen);
|
|
|
|
buffer_out[0] = (resp << 0 ) | (reci << 8) | (send << 16) | ((outlen / 4) << 24);
|
|
|
|
|
|
|
|
return outlen + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 dma(u32 cmd) override
|
|
|
|
{
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
case MDC_DeviceRequest:
|
|
|
|
case MDC_AllStatusReq:
|
|
|
|
// custom function
|
|
|
|
w32(0x00100000);
|
|
|
|
// function flags
|
|
|
|
w32(0);
|
|
|
|
w32(0);
|
|
|
|
w32(0);
|
|
|
|
//1 area code
|
|
|
|
w8(0xff); // FF: Worldwide, 01: North America
|
|
|
|
//1 direction
|
|
|
|
w8(0);
|
|
|
|
// Product name (totally made up)
|
|
|
|
wstr("MAPLE/232C CONVERT BD", 30);
|
|
|
|
|
|
|
|
// License (60)
|
|
|
|
wstr(maple_sega_brand, 60);
|
|
|
|
|
|
|
|
// Low-consumption standby current (2)
|
|
|
|
w16(0x0069); // 10.5 mA
|
|
|
|
|
|
|
|
// Maximum current consumption (2)
|
|
|
|
w16(0x0120); // 28.8 mA
|
|
|
|
|
|
|
|
return cmd == MDC_DeviceRequest ? MDRS_DeviceStatus : MDRS_DeviceStatusAll;
|
|
|
|
|
|
|
|
case MDCF_GetCondition:
|
|
|
|
w32(0x00100000); // custom function
|
|
|
|
|
|
|
|
return MDRS_DataTransfer;
|
|
|
|
|
|
|
|
case MDC_DeviceReset:
|
|
|
|
case MDC_DeviceKill:
|
|
|
|
return MDRS_DeviceReply;
|
|
|
|
|
|
|
|
// 90 get status
|
|
|
|
//
|
|
|
|
// read test:
|
|
|
|
// d0 ?
|
|
|
|
// 91 get last cmd status?
|
|
|
|
// a0 ?
|
|
|
|
// 91
|
|
|
|
// a1 read md5 in data
|
|
|
|
// or data itself if after D4 xx xx xx xx
|
|
|
|
// d4 in=d2 03 aa db
|
|
|
|
// 91
|
|
|
|
//
|
|
|
|
// d9 lock
|
|
|
|
// da unlock
|
|
|
|
//
|
|
|
|
// write test:
|
|
|
|
// D0
|
|
|
|
// 91
|
|
|
|
// B1 05 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (28 bytes)
|
|
|
|
// 91
|
|
|
|
// B1 0b 06 00 00 00 00 c6 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (28 bytes)
|
|
|
|
// 91
|
|
|
|
// B1 11 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (28 bytes)
|
|
|
|
// 91
|
|
|
|
// B1 17 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (28 bytes)
|
|
|
|
// 91
|
|
|
|
// B1 1d 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (16 bytes)
|
|
|
|
// 91
|
|
|
|
// C1: 00 00 00 00
|
|
|
|
// 91
|
|
|
|
|
|
|
|
case 0xD0:
|
|
|
|
d4Seen = false;
|
|
|
|
[[fallthrough]];
|
|
|
|
case 0x90:
|
|
|
|
case 0x91:
|
|
|
|
case 0xA0:
|
|
|
|
case 0xD4:
|
|
|
|
case 0xC1:
|
|
|
|
w32(getStatus());
|
|
|
|
if (cmd == 0xd4)
|
|
|
|
d4Seen = true;
|
|
|
|
return (MapleDeviceRV)0xfe;
|
|
|
|
|
|
|
|
case 0xA1: // read card data
|
|
|
|
DEBUG_LOG(MAPLE, "RFID card read (data? %d)", d4Seen);
|
|
|
|
w32(getStatus());
|
|
|
|
if (!d4Seen)
|
|
|
|
w32(0x12345678); // arbitrary value (unknown)
|
|
|
|
else
|
|
|
|
wptr(cardData, sizeof(cardData));
|
|
|
|
return (MapleDeviceRV)0xfe;
|
|
|
|
|
|
|
|
case 0xD9: // lock card
|
|
|
|
w32(getStatus());
|
|
|
|
cardLocked = true;
|
|
|
|
INFO_LOG(MAPLE, "RFID card %d locked", player_num);
|
|
|
|
return (MapleDeviceRV)0xfe;
|
|
|
|
|
|
|
|
case 0xDA: // unlock card
|
|
|
|
w32(getStatus());
|
|
|
|
cardLocked = false;
|
|
|
|
cardInserted = false;
|
|
|
|
INFO_LOG(MAPLE, "RFID card %d unlocked", player_num);
|
|
|
|
return (MapleDeviceRV)0xfe;
|
|
|
|
|
|
|
|
case 0xB1: // write to card
|
|
|
|
{
|
|
|
|
w32(getStatus());
|
|
|
|
u32 offset = r8() * 4;
|
|
|
|
size_t size = r8() * 4;
|
|
|
|
skip(2);
|
|
|
|
DEBUG_LOG(MAPLE, "RFID card write: offset 0x%x len %d", offset, (int)size);
|
|
|
|
rptr(cardData + offset, std::min(size, sizeof(cardData) - offset));
|
|
|
|
saveCard();
|
|
|
|
return (MapleDeviceRV)0xfe;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
INFO_LOG(MAPLE, "RFIDReaderWriter: unknown MAPLE COMMAND %d", cmd);
|
|
|
|
return MDRE_UnknownCmd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MapleDeviceType get_device_type() override {
|
|
|
|
return MDT_RFIDReaderWriter;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnSetup() override
|
|
|
|
{
|
2023-05-06 18:54:45 +00:00
|
|
|
memset(cardData, 0, sizeof(cardData));
|
2023-05-16 07:43:21 +00:00
|
|
|
transientData = false;
|
2023-04-29 15:50:56 +00:00
|
|
|
}
|
|
|
|
|
2023-05-16 07:43:21 +00:00
|
|
|
std::string getCardPath() const
|
|
|
|
{
|
|
|
|
int playerNum;
|
|
|
|
if (config::GGPOEnable && !config::ActAsServer)
|
|
|
|
// Always load P1 card with GGPO to be consistent with P1 inputs being used
|
|
|
|
playerNum = 1;
|
|
|
|
else
|
|
|
|
playerNum = player_num + 1;
|
|
|
|
return hostfs::getArcadeFlashPath() + "-p" + std::to_string(playerNum) + ".card";
|
2023-04-29 15:50:56 +00:00
|
|
|
}
|
|
|
|
|
2023-05-16 07:43:21 +00:00
|
|
|
void loadCard()
|
2023-04-29 15:50:56 +00:00
|
|
|
{
|
2023-05-16 07:43:21 +00:00
|
|
|
if (transientData)
|
|
|
|
return;
|
2023-04-29 15:50:56 +00:00
|
|
|
std::string path = getCardPath();
|
|
|
|
FILE *fp = nowide::fopen(path.c_str(), "rb");
|
|
|
|
if (fp == nullptr)
|
|
|
|
{
|
|
|
|
static u8 blankCard[128] = {
|
|
|
|
0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,0x6c, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0xff
|
|
|
|
};
|
|
|
|
// Generate random bytes used by vf4 vanilla to make the card id
|
|
|
|
srand(time(0));
|
|
|
|
blankCard[2] = rand() & 0xff;
|
|
|
|
blankCard[4] = rand() & 0xff;
|
|
|
|
blankCard[5] = rand() & 0xff;
|
|
|
|
blankCard[6] = rand() & 0xff;
|
|
|
|
blankCard[7] = rand() & 0xff;
|
|
|
|
memcpy(cardData, blankCard, sizeof(blankCard));
|
|
|
|
INFO_LOG(NAOMI, "Card P%d initialized", player_num + 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
INFO_LOG(NAOMI, "Loading card file from %s", path.c_str());
|
|
|
|
if (fread(cardData, 1, sizeof(cardData), fp) != sizeof(cardData))
|
|
|
|
WARN_LOG(NAOMI, "Truncated or empty card file: %s" ,path.c_str());
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void saveCard() const
|
|
|
|
{
|
2023-05-16 07:43:21 +00:00
|
|
|
if (transientData)
|
|
|
|
return;
|
2023-04-29 15:50:56 +00:00
|
|
|
std::string path = getCardPath();
|
|
|
|
FILE *fp = nowide::fopen(path.c_str(), "wb");
|
|
|
|
if (fp == nullptr)
|
|
|
|
{
|
|
|
|
WARN_LOG(NAOMI, "Can't create card file %s: errno %d", path.c_str(), errno);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
INFO_LOG(NAOMI, "Saving card file to %s", path.c_str());
|
|
|
|
if (fwrite(cardData, 1, sizeof(cardData), fp) != sizeof(cardData))
|
|
|
|
WARN_LOG(NAOMI, "Truncated write to file: %s", path.c_str());
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void serialize(Serializer& ser) const override
|
|
|
|
{
|
|
|
|
maple_device::serialize(ser);
|
|
|
|
ser << cardData;
|
|
|
|
ser << d4Seen;
|
|
|
|
ser << cardInserted;
|
|
|
|
ser << cardLocked;
|
|
|
|
}
|
|
|
|
void deserialize(Deserializer& deser) override
|
|
|
|
{
|
|
|
|
maple_device::deserialize(deser);
|
|
|
|
deser >> cardData;
|
|
|
|
deser >> d4Seen;
|
|
|
|
deser >> cardInserted;
|
|
|
|
deser >> cardLocked;
|
|
|
|
}
|
|
|
|
|
2023-05-19 15:45:18 +00:00
|
|
|
void insertCard()
|
|
|
|
{
|
|
|
|
if (!cardInserted) {
|
|
|
|
cardInserted = true;
|
|
|
|
loadCard();
|
|
|
|
}
|
|
|
|
else if (!cardLocked) {
|
|
|
|
cardInserted = false;
|
|
|
|
if (!transientData)
|
|
|
|
memset(cardData, 0, sizeof(cardData));
|
|
|
|
}
|
2023-04-29 15:50:56 +00:00
|
|
|
}
|
|
|
|
|
2023-05-16 07:43:21 +00:00
|
|
|
const u8 *getCardData() {
|
|
|
|
loadCard();
|
|
|
|
return cardData;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setCardData(u8 *data) {
|
|
|
|
memcpy(cardData, data, sizeof(cardData));
|
|
|
|
transientData = true;
|
|
|
|
}
|
|
|
|
|
2023-04-29 15:50:56 +00:00
|
|
|
u8 cardData[128];
|
|
|
|
bool d4Seen = false;
|
|
|
|
bool cardInserted = false;
|
|
|
|
bool cardLocked = false;
|
2023-05-16 07:43:21 +00:00
|
|
|
bool transientData = false;
|
2023-04-29 15:50:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void insertRfidCard(int playerNum)
|
|
|
|
{
|
|
|
|
maple_device *mapleDev = MapleDevices[1 + playerNum][5];
|
|
|
|
if (mapleDev != nullptr && mapleDev->get_device_type() == MDT_RFIDReaderWriter)
|
|
|
|
((RFIDReaderWriter *)mapleDev)->insertCard();
|
|
|
|
}
|
|
|
|
|
2023-05-16 07:43:21 +00:00
|
|
|
void setRfidCardData(int playerNum, u8 *data)
|
|
|
|
{
|
|
|
|
maple_device *mapleDev = MapleDevices[1 + playerNum][5];
|
|
|
|
if (mapleDev != nullptr && mapleDev->get_device_type() == MDT_RFIDReaderWriter)
|
|
|
|
((RFIDReaderWriter *)mapleDev)->setCardData(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
const u8 *getRfidCardData(int playerNum)
|
|
|
|
{
|
|
|
|
maple_device *mapleDev = MapleDevices[1 + playerNum][5];
|
|
|
|
if (mapleDev != nullptr && mapleDev->get_device_type() == MDT_RFIDReaderWriter)
|
|
|
|
return ((RFIDReaderWriter *)mapleDev)->getCardData();
|
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
maple_device* maple_Create(MapleDeviceType type)
|
|
|
|
{
|
|
|
|
maple_device* rv=0;
|
|
|
|
switch(type)
|
|
|
|
{
|
|
|
|
case MDT_SegaController:
|
2022-01-27 16:52:19 +00:00
|
|
|
if (!settings.platform.isAtomiswave())
|
2019-07-09 21:52:19 +00:00
|
|
|
rv = new maple_sega_controller();
|
|
|
|
else
|
|
|
|
rv = new maple_atomiswave_controller();
|
2013-12-19 17:10:14 +00:00
|
|
|
break;
|
2015-08-09 04:34:02 +00:00
|
|
|
|
2014-01-13 17:12:26 +00:00
|
|
|
case MDT_Microphone:
|
|
|
|
rv=new maple_microphone();
|
|
|
|
break;
|
2015-08-09 04:34:02 +00:00
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
case MDT_SegaVMU:
|
|
|
|
rv = new maple_sega_vmu();
|
|
|
|
break;
|
2015-08-09 04:34:02 +00:00
|
|
|
|
2015-12-08 00:21:03 +00:00
|
|
|
case MDT_PurupuruPack:
|
|
|
|
rv = new maple_sega_purupuru();
|
|
|
|
break;
|
|
|
|
|
2018-09-06 18:17:28 +00:00
|
|
|
case MDT_Keyboard:
|
|
|
|
rv = new maple_keyboard();
|
|
|
|
break;
|
2015-08-09 04:34:02 +00:00
|
|
|
|
2018-09-18 07:27:16 +00:00
|
|
|
case MDT_Mouse:
|
|
|
|
rv = new maple_mouse();
|
|
|
|
break;
|
|
|
|
|
2018-10-01 18:34:35 +00:00
|
|
|
case MDT_LightGun:
|
2022-01-27 16:52:19 +00:00
|
|
|
if (!settings.platform.isAtomiswave())
|
2019-07-09 21:52:19 +00:00
|
|
|
rv = new maple_lightgun();
|
|
|
|
else
|
|
|
|
rv = new atomiswave_lightgun();
|
2018-10-01 18:34:35 +00:00
|
|
|
break;
|
|
|
|
|
2015-08-09 04:34:02 +00:00
|
|
|
case MDT_NaomiJamma:
|
|
|
|
rv = new maple_naomi_jamma();
|
|
|
|
break;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-07-12 17:18:39 +00:00
|
|
|
case MDT_TwinStick:
|
|
|
|
rv = new maple_sega_twinstick();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MDT_AsciiStick:
|
|
|
|
rv = new maple_ascii_stick();
|
|
|
|
break;
|
|
|
|
|
2023-05-29 10:45:34 +00:00
|
|
|
case MDT_MaracasController:
|
|
|
|
rv = new maple_maracas_controller();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MDT_FishingController:
|
|
|
|
rv = new maple_fishing_controller();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MDT_PopnMusicController:
|
|
|
|
rv = new maple_popnmusic_controller();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MDT_RacingController:
|
|
|
|
rv = new maple_racing_controller();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MDT_DenshaDeGoController:
|
|
|
|
rv = new maple_densha_controller();
|
|
|
|
break;
|
|
|
|
|
2023-04-29 15:50:56 +00:00
|
|
|
case MDT_RFIDReaderWriter:
|
|
|
|
rv = new RFIDReaderWriter();
|
|
|
|
break;
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
default:
|
2019-07-12 15:20:43 +00:00
|
|
|
ERROR_LOG(MAPLE, "Invalid device type %d", type);
|
|
|
|
die("Invalid maple device type");
|
|
|
|
break;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
2014-01-13 17:12:26 +00:00
|
|
|
}
|