flycast/core/hw/maple/maple_cfg.cpp

498 lines
14 KiB
C++

#include "maple_cfg.h"
#include "maple_helper.h"
#include "maple_if.h"
#include "hw/naomi/naomi_cart.h"
#include "hw/naomi/card_reader.h"
#include "cfg/option.h"
#include "stdclass.h"
#include "serialize.h"
MapleInputState mapleInputState[4];
extern bool maple_ddt_pending_reset;
extern std::vector<std::pair<u32, std::vector<u32>>> mapleDmaOut;
extern bool SDCKBOccupied;
void (*MapleConfigMap::UpdateVibration)(u32 port, float power, float inclination, u32 duration_ms);
static u8 GetBtFromSgn(s16 val)
{
return (val + 32768) >> 8;
}
u32 awave_button_mapping[32] = {
AWAVE_BTN2_KEY, // DC_BTN_C
AWAVE_BTN1_KEY, // DC_BTN_B
AWAVE_BTN0_KEY, // DC_BTN_A
AWAVE_START_KEY, // DC_BTN_START
AWAVE_UP_KEY, // DC_DPAD_UP
AWAVE_DOWN_KEY, // DC_DPAD_DOWN
AWAVE_LEFT_KEY, // DC_DPAD_LEFT
AWAVE_RIGHT_KEY, // DC_DPAD_RIGHT
AWAVE_BTN4_KEY, // DC_BTN_Z (duplicated)
AWAVE_BTN4_KEY, // DC_BTN_Y
AWAVE_BTN3_KEY, // DC_BTN_X
AWAVE_COIN_KEY, // DC_BTN_D
AWAVE_SERVICE_KEY, // DC_DPAD2_UP
AWAVE_TEST_KEY, // DC_DPAD2_DOWN
0, // DC_DPAD2_LEFT
0, // DC_DPAD2_RIGHT
};
u32 awavelg_button_mapping[32] = {
AWAVE_BTN1_KEY, // DC_BTN_C
AWAVE_BTN0_KEY, // DC_BTN_B
AWAVE_TRIGGER_KEY, // DC_BTN_A
AWAVE_START_KEY, // DC_BTN_START
AWAVE_UP_KEY, // DC_DPAD_UP
AWAVE_DOWN_KEY, // DC_DPAD_DOWN
AWAVE_LEFT_KEY, // DC_DPAD_LEFT
AWAVE_RIGHT_KEY, // DC_DPAD_RIGHT
AWAVE_BTN4_KEY, // DC_BTN_Z
AWAVE_BTN3_KEY, // DC_BTN_Y
AWAVE_BTN2_KEY, // DC_BTN_X
AWAVE_COIN_KEY, // DC_BTN_D
AWAVE_SERVICE_KEY, // DC_DPAD2_UP
AWAVE_TEST_KEY, // DC_DPAD2_DOWN
0, // DC_DPAD2_LEFT
0, // DC_DPAD2_RIGHT
AWAVE_BTN0_KEY // DC_BTN_RELOAD (not needed for AW, mapped to BTN0 = pump)
};
inline u32 MapleConfigMap::playerNum()
{
return dev->player_num;
}
void MapleConfigMap::SetVibration(float power, float inclination, u32 duration_ms)
{
UpdateVibration(playerNum(), power, inclination, duration_ms);
}
void MapleConfigMap::GetInput(PlainJoystickState* pjs)
{
const MapleInputState& inputState = mapleInputState[playerNum()];
if (settings.platform.isConsole())
{
pjs->kcode = inputState.kcode;
pjs->joy[PJAI_X1] = GetBtFromSgn(inputState.fullAxes[PJAI_X1]);
pjs->joy[PJAI_Y1] = GetBtFromSgn(inputState.fullAxes[PJAI_Y1]);
pjs->joy[PJAI_X2] = GetBtFromSgn(inputState.fullAxes[PJAI_X2]);
pjs->joy[PJAI_Y2] = GetBtFromSgn(inputState.fullAxes[PJAI_Y2]);
pjs->joy[PJAI_X3] = GetBtFromSgn(inputState.fullAxes[PJAI_X3]);
pjs->joy[PJAI_Y3] = GetBtFromSgn(inputState.fullAxes[PJAI_Y3]);
pjs->trigger[PJTI_R] = inputState.halfAxes[PJTI_R] >> 8;
pjs->trigger[PJTI_L] = inputState.halfAxes[PJTI_L] >> 8;
pjs->trigger[PJTI_L2] = inputState.halfAxes[PJTI_L2] >> 8;
pjs->trigger[PJTI_R2] = inputState.halfAxes[PJTI_R2] >> 8;
}
else if (settings.platform.isAtomiswave())
{
#ifdef LIBRETRO
pjs->kcode = inputState.kcode;
#else
const u32* mapping = settings.input.lightgunGame ? awavelg_button_mapping : awave_button_mapping;
pjs->kcode = ~0;
for (u32 i = 0; i < std::size(awave_button_mapping); i++)
{
if ((inputState.kcode & (1 << i)) == 0)
pjs->kcode &= ~mapping[i];
}
#endif
if (NaomiGameInputs != NULL)
{
for (u32 axis = 0; axis < PJAI_Count; axis++)
{
if (NaomiGameInputs->axes[axis].name != NULL)
{
if (NaomiGameInputs->axes[axis].type == Full)
{
switch (NaomiGameInputs->axes[axis].axis)
{
case 0:
pjs->joy[axis] = GetBtFromSgn(inputState.fullAxes[PJAI_X1]);
break;
case 1:
pjs->joy[axis] = GetBtFromSgn(inputState.fullAxes[PJAI_Y1]);
break;
case 2:
pjs->joy[axis] = GetBtFromSgn(inputState.fullAxes[PJAI_X2]);
break;
case 3:
pjs->joy[axis] = GetBtFromSgn(inputState.fullAxes[PJAI_Y2]);
break;
default:
pjs->joy[axis] = 0x80;
break;
}
}
else
{
switch (NaomiGameInputs->axes[axis].axis)
{
case 4:
pjs->joy[axis] = inputState.halfAxes[PJTI_R] >> 8;
break;
case 5:
pjs->joy[axis] = inputState.halfAxes[PJTI_L] >> 8;
break;
case 6:
pjs->joy[axis] = inputState.halfAxes[PJTI_R2] >> 8;
break;
case 7:
pjs->joy[axis] = inputState.halfAxes[PJTI_L2] >> 8;
break;
default:
pjs->joy[axis] = 0;
break;
}
}
if (NaomiGameInputs->axes[axis].inverted)
pjs->joy[axis] = pjs->joy[axis] == 0 ? 0xff : 0x100 - pjs->joy[axis];
}
else
{
pjs->joy[axis] = 0x80;
}
}
}
else
{
pjs->joy[PJAI_X1] = GetBtFromSgn(inputState.fullAxes[PJAI_X1]);
pjs->joy[PJAI_Y1] = GetBtFromSgn(inputState.fullAxes[PJAI_Y1]);
pjs->joy[PJAI_X2] = inputState.halfAxes[PJTI_R] >> 8;
pjs->joy[PJAI_Y2] = inputState.halfAxes[PJTI_L] >> 8;
}
}
}
void MapleConfigMap::SetImage(u8 *img)
{
push_vmu_screen(dev->bus_id, dev->bus_port, img);
}
void MapleConfigMap::GetAbsCoordinates(int& x, int& y)
{
const MapleInputState& inputState = mapleInputState[playerNum()];
x = inputState.absPos.x;
y = inputState.absPos.y;
}
void MapleConfigMap::GetMouseInput(u8& buttons, int& x, int& y, int& wheel)
{
MapleInputState& inputState = mapleInputState[playerNum()];
buttons = inputState.mouseButtons;
x = inputState.relPos.x;
y = inputState.relPos.y * (invertMouseY ? -1 : 1);
wheel = inputState.relPos.wheel;
inputState.relPos.x = 0;
inputState.relPos.y = 0;
inputState.relPos.wheel = 0;
}
void MapleConfigMap::GetKeyboardInput(u8& shift, u8 keys[6])
{
const MapleInputState& inputState = mapleInputState[playerNum()];
shift = inputState.keyboard.shift;
memcpy(keys, inputState.keyboard.key, sizeof(inputState.keyboard.key));
}
bool maple_atomiswave_coin_chute(int slot)
{
#ifdef LIBRETRO
return mapleInputState[slot].kcode & AWAVE_COIN_KEY;
#else
for (size_t i = 0; i < std::size(awave_button_mapping); i++)
{
if ((mapleInputState[slot].kcode & (1 << i)) == 0 && awave_button_mapping[i] == AWAVE_COIN_KEY)
return true;
}
return false;
#endif
}
static void mcfg_Create(MapleDeviceType type, u32 bus, u32 port, s32 player_num = -1)
{
MapleDevices[bus][port].reset();
std::shared_ptr<maple_device> dev = maple_Create(type);
dev->Setup(bus, port, player_num);
}
static void createNaomiDevices()
{
const std::string& gameId = settings.content.gameId;
mcfg_Create(MDT_NaomiJamma, 0, 5);
if (gameId == "THE TYPING OF THE DEAD"
|| gameId == " LUPIN THE THIRD -THE TYPING-"
|| gameId == "------La Keyboardxyu------")
{
INFO_LOG(MAPLE, "Enabling keyboard for game %s", gameId.c_str());
mcfg_Create(MDT_Keyboard, 1, 5, 0);
mcfg_Create(MDT_Keyboard, 2, 5, 1);
settings.input.keyboardGame = true;
}
else if (gameId.substr(0, 8) == "MKG TKOB"
|| gameId == "VIRTUA FIGHTER 4 JAPAN"
|| gameId == "VF4 EVOLUTION JAPAN"
|| gameId == "VF4 FINAL TUNED JAPAN")
{
mcfg_Create(MDT_RFIDReaderWriter, 1, 5, 0);
mcfg_Create(MDT_RFIDReaderWriter, 2, 5, 1);
if (gameId.substr(0, 8) == "MKG TKOB") {
insertRfidCard(0);
insertRfidCard(1);
}
}
else
{
// Connect VMU B1
mcfg_Create(MDT_SegaController, 1, 5);
mcfg_Create(MDT_SegaVMU, 1, 0);
// Connect VMU C1
mcfg_Create(MDT_SegaController, 2, 5);
mcfg_Create(MDT_SegaVMU, 2, 0);
}
if (gameId == " DERBY OWNERS CLUB WE ---------"
|| gameId == " DERBY OWNERS CLUB ------------"
|| gameId == " DERBY OWNERS CLUB II-----------")
card_reader::derbyInit();
}
static void createAtomiswaveDevices()
{
const std::string& gameId = settings.content.gameId;
// Looks like two controllers needs to be on bus 0 and 1 for digital inputs
// Then other devices on port 2 and 3 for analog axes, light guns, ...
mcfg_Create(MDT_SegaController, 0, 5);
mcfg_Create(MDT_SegaController, 1, 5);
if (NaomiGameInputs != NULL && NaomiGameInputs->axes[0].name != NULL)
{
// Game needs analog axes
mcfg_Create(MDT_SegaController, 2, 5, 0);
mcfg_Create(MDT_SegaController, 3, 5, 1);
// Faster Than Speed needs 1 std controller on port 0 (digital inputs) and one on port 2 (analog axes)
// Maximum Speed same
}
else if (gameId == "GUILTY GEAR isuka"
|| gameId == "Dirty Pigskin Football")
{
// 4 players
INFO_LOG(MAPLE, "Enabling 4-player setup for game %s", gameId.c_str());
mcfg_Create(MDT_SegaController, 2, 5);
mcfg_Create(MDT_SegaController, 3, 5);
settings.input.fourPlayerGames = true;
}
else if (gameId == "Sports Shooting USA"
|| gameId == "SEGA CLAY CHALLENGE"
|| gameId == "RANGER MISSION"
|| gameId == "EXTREME HUNTING"
|| gameId == "Fixed BOOT strapper") // Extreme hunting 2
{
// needs 2 std controllers on port 0 & 1 (digital in) and light guns on port 2 & 3
INFO_LOG(MAPLE, "Enabling lightgun setup for game %s", gameId.c_str());
mcfg_Create(MDT_LightGun, 2, 5, 0);
mcfg_Create(MDT_LightGun, 3, 5, 1);
settings.input.lightgunGame = true;
}
else if (gameId == "BASS FISHING SIMULATOR VER.A" || gameId == "DRIVE")
{
// Sega Bass Fishing Challenge needs a mouse (track-ball) on port 2
// Waiwai drive needs two track-balls
mcfg_Create(MDT_Mouse, 2, 5, 0);
mcfg_Create(MDT_Mouse, 3, 5, 1);
if (gameId == "DRIVE")
{
MapleDevices[2][5]->config->invertMouseY = true;
MapleDevices[3][5]->config->invertMouseY = true;
}
settings.input.mouseGame = true;
}
}
static void createDreamcastDevices()
{
for (int bus = 0; bus < MAPLE_PORTS; ++bus)
{
switch (config::MapleMainDevices[bus])
{
case MDT_SegaController:
case MDT_SegaControllerXL:
mcfg_Create(config::MapleMainDevices[bus], bus, 5);
if (config::MapleExpansionDevices[bus][0] != MDT_None)
mcfg_Create(config::MapleExpansionDevices[bus][0], bus, 0);
if (config::MapleExpansionDevices[bus][1] != MDT_None)
mcfg_Create(config::MapleExpansionDevices[bus][1], bus, 1);
break;
case MDT_Keyboard:
case MDT_Mouse:
case MDT_MaracasController:
case MDT_FishingController:
case MDT_PopnMusicController:
case MDT_DenshaDeGoController:
case MDT_Dreameye:
mcfg_Create(config::MapleMainDevices[bus], bus, 5);
if (config::MapleMainDevices[bus] == MDT_FishingController)
// integrated vibration pack
mcfg_Create(MDT_PurupuruPack, bus, 4);
break;
case MDT_LightGun:
case MDT_TwinStick:
case MDT_AsciiStick:
case MDT_RacingController:
mcfg_Create(config::MapleMainDevices[bus], bus, 5);
if (config::MapleExpansionDevices[bus][0] != MDT_None)
mcfg_Create(config::MapleExpansionDevices[bus][0], bus, 0);
break;
case MDT_None:
break;
default:
WARN_LOG(MAPLE, "Invalid device type %d for port %d", (MapleDeviceType)config::MapleMainDevices[bus], bus);
break;
}
}
}
static void vmuDigest()
{
if (!config::GGPOEnable)
return;
MD5Sum md5;
for (int i = 0; i < MAPLE_PORTS; i++)
for (int j = 0; j < 6; j++)
{
std::shared_ptr<const maple_device> device = MapleDevices[i][j];
if (device != nullptr)
{
size_t size;
const void *data = device->getData(size);
if (data != nullptr)
md5.add(data, size);
}
}
md5.getDigest(settings.network.md5.vmu);
}
void mcfg_CreateDevices()
{
settings.input.lightgunGame = false;
settings.input.keyboardGame = false;
settings.input.mouseGame = false;
settings.input.fourPlayerGames = false;
switch (settings.platform.system)
{
case DC_PLATFORM_DREAMCAST:
createDreamcastDevices();
break;
case DC_PLATFORM_NAOMI:
case DC_PLATFORM_NAOMI2:
createNaomiDevices();
break;
case DC_PLATFORM_ATOMISWAVE:
createAtomiswaveDevices();
break;
case DC_PLATFORM_SYSTEMSP:
if (settings.content.gameId == "INW PUPPY 2008 VER1.001")
settings.input.lightgunGame = true;
return;
default:
die("Unknown system");
break;
}
vmuDigest();
}
// Don't destroy the JVS MIE if full is false
void mcfg_DestroyDevices(bool full)
{
for (int i = 0; i < MAPLE_PORTS; i++)
for (int j = 0; j <= 5; j++)
{
if (MapleDevices[i][j] != nullptr
&& (full || MapleDevices[i][j]->get_device_type() != MDT_NaomiJamma))
{
MapleDevices[i][j].reset();
}
}
}
void mcfg_SerializeDevices(Serializer& ser)
{
ser << maple_ddt_pending_reset;
ser << SDCKBOccupied;
ser << (u32)mapleDmaOut.size();
for (const auto& pair : mapleDmaOut)
{
ser << pair.first; // u32 address
ser << (u32)pair.second.size();
ser.serialize(pair.second.data(), pair.second.size());
}
for (int i = 0; i < MAPLE_PORTS; i++)
for (int j = 0; j < 6; j++)
{
u8 deviceType = MDT_None;
std::shared_ptr<maple_device> device = MapleDevices[i][j];
if (device != nullptr)
deviceType = device->get_device_type();
ser << deviceType;
if (device != nullptr)
device->serialize(ser);
}
}
void mcfg_DeserializeDevices(Deserializer& deser)
{
if (!deser.rollback())
mcfg_DestroyDevices(false);
u8 eeprom[sizeof(maple_naomi_jamma::eeprom)];
if (deser.version() < Deserializer::V23)
{
deser >> eeprom;
deser.skip(128); // Unused eeprom space
deser.skip<bool>(); // EEPROM_loaded
}
deser >> maple_ddt_pending_reset;
if (deser.version() >= Deserializer::V47)
deser >> SDCKBOccupied;
mapleDmaOut.clear();
if (deser.version() >= Deserializer::V23)
{
u32 size;
deser >> size;
for (u32 i = 0; i < size; i++)
{
u32 address;
deser >> address;
u32 dataSize;
deser >> dataSize;
mapleDmaOut.emplace_back(address, std::vector<u32>(dataSize));
deser.deserialize(mapleDmaOut.back().second.data(), dataSize);
}
}
for (int i = 0; i < MAPLE_PORTS; i++)
for (int j = 0; j < 6; j++)
{
u8 deviceType;
deser >> deviceType;
if (deviceType != MDT_None)
{
if (!deser.rollback() && deviceType != MDT_NaomiJamma)
mcfg_Create((MapleDeviceType)deviceType, i, j);
MapleDevices[i][j]->deserialize(deser);
}
}
if (deser.version() < Deserializer::V23 && EEPROM != nullptr)
memcpy(EEPROM, eeprom, sizeof(eeprom));
}
std::shared_ptr<maple_naomi_jamma> getMieDevice()
{
if (MapleDevices[0][5] == nullptr || MapleDevices[0][5]->get_device_type() != MDT_NaomiJamma)
return nullptr;
return std::static_pointer_cast<maple_naomi_jamma>(MapleDevices[0][5]);
}