mirror of
https://github.com/libretro/parallel-dc.git
synced 2026-11-13 04:41:41 -10:00
sb, gdromv3 and midiffb become C. The bus's registers that keep only some bits of a write take their mask and set bits from a table rather than from a template each. The drive's registers that were a struct around a union are the union; its CD audio states are CDDA_*; its aggregate resets are memsets, and _vmem_write_ptr() gives C what _vmem_write_const() gives through a reference. The headers they need compile as both: gdromv3.h, emulator.h, netservice.h, modem.h, bba.h, and naomi_cart.h's C part (the game's inputs and name). The pads the frontend gives are declared in libretro/pad_state.h, where four files declared them themselves. tools/c89/linkage_check.py also checks the other way: a variable a C file uses and a C++ file defines has been declared in extern "C" there first. The drive's state and the bus's FIFO count are declared in their headers for serialize.cpp.
653 lines
19 KiB
C++
653 lines
19 KiB
C++
#include "types.h"
|
|
#include <new>
|
|
#include "maple_if.h"
|
|
#include "maple_helper.h"
|
|
#include "maple_devs.h"
|
|
#include "maple_cfg.h"
|
|
#include "libretro/input_latch.h"
|
|
#include "hw/naomi/naomi_periph.h"
|
|
#include "hw/naomi/naomi_cart.h"
|
|
|
|
#include "lockfree.h"
|
|
|
|
static cTripleBuffer vmu_lcd_exchange[4];
|
|
static u8 vmu_lcd_buffers[4][3][VMU_SCREEN_WIDTH * VMU_SCREEN_HEIGHT];
|
|
/* 1 once a picture has been published, 2 once the renderer has one */
|
|
static retro_atomic_int_t vmu_lcd_state[4];
|
|
|
|
void vmu_lcd_publish(int screen, const u8 *pixels)
|
|
{
|
|
if (screen < 0 || screen >= 4 || pixels == NULL)
|
|
return;
|
|
memcpy(vmu_lcd_buffers[screen][vmu_lcd_exchange[screen].Back()], pixels,
|
|
VMU_SCREEN_WIDTH * VMU_SCREEN_HEIGHT);
|
|
vmu_lcd_exchange[screen].Publish();
|
|
if (retro_atomic_load_acquire_int(&vmu_lcd_state[screen]) == 0)
|
|
retro_atomic_store_release_int(&vmu_lcd_state[screen], 1);
|
|
}
|
|
|
|
bool vmu_lcd_refresh(int screen)
|
|
{
|
|
if (screen < 0 || screen >= 4 || retro_atomic_load_acquire_int(&vmu_lcd_state[screen]) == 0)
|
|
return false;
|
|
if (!vmu_lcd_exchange[screen].Take())
|
|
return false;
|
|
retro_atomic_store_release_int(&vmu_lcd_state[screen], 2);
|
|
return true;
|
|
}
|
|
|
|
const u8 *vmu_lcd_pixels(int screen)
|
|
{
|
|
if (screen < 0 || screen >= 4 || retro_atomic_load_acquire_int(&vmu_lcd_state[screen]) != 2)
|
|
return NULL;
|
|
return vmu_lcd_buffers[screen][vmu_lcd_exchange[screen].Front()];
|
|
}
|
|
|
|
void vmu_lcd_reset(void)
|
|
{
|
|
for (int screen = 0; screen < 4; screen++)
|
|
{
|
|
retro_atomic_store_release_int(&vmu_lcd_state[screen], 0);
|
|
vmu_lcd_exchange[screen].Reset();
|
|
}
|
|
}
|
|
|
|
#define HAS_VMU
|
|
/*
|
|
bus_x=0{p0=1{config};p1=2{config};config;}
|
|
Plugins:
|
|
Input Source
|
|
EventMap -- 'Raw' interface, source_name[seid]:mode
|
|
KeyMap -- translated chars ( no re-mapping possible)
|
|
Output
|
|
Image
|
|
|
|
*/
|
|
/*
|
|
MapleConfig:
|
|
InputUpdate(&fmt);
|
|
ImageUpdate(data);
|
|
*/
|
|
void UpdateInputState(u32 port);
|
|
void UpdateInputStateArcade(void);
|
|
void UpdateInputStateMouse(u32 port);
|
|
extern InputLatch input_latch;
|
|
void UpdateVibration(u32 port, u32 value, u32 max_duration);
|
|
|
|
#include "libretro/pad_state.h"
|
|
extern bool enable_purupuru;
|
|
extern f32 mo_x_abs[4];
|
|
extern f32 mo_y_abs[4];
|
|
extern u32 mo_buttons[4];
|
|
extern f32 mo_x_delta[4];
|
|
extern f32 mo_y_delta[4];
|
|
extern f32 mo_wheel_delta[4];
|
|
extern bool EEPROM_loaded;
|
|
|
|
const char* VMU_SCREEN_COLOR_NAMES[VMU_NUM_COLORS] = {
|
|
"DEFAULT_ON",
|
|
"DEFAULT_OFF",
|
|
"BLACK",
|
|
"BLUE",
|
|
"LIGHT_BLUE",
|
|
"GREEN",
|
|
"GREEN_BLUE",
|
|
"GREEN_LIGHT_BLUE",
|
|
"LIGHT_GREEN",
|
|
"LIGHT_GREEN_BLUE",
|
|
"LIGHT_GREEN_LIGHT_BLUE",
|
|
"RED",
|
|
"RED_BLUE",
|
|
"RED_LIGHT_BLUE",
|
|
"RED_GREEN",
|
|
"RED_GREEN_BLUE",
|
|
"RED_GREEN_LIGHT_BLUE",
|
|
"RED_LIGHT_GREEN",
|
|
"RED_LIGHT_GREEN_BLUE",
|
|
"RED_LIGHT_GREEN_LIGHT_BLUE",
|
|
"LIGHT_RED",
|
|
"LIGHT_RED_BLUE",
|
|
"LIGHT_RED_LIGHT_BLUE",
|
|
"LIGHT_RED_GREEN",
|
|
"LIGHT_RED_GREEN_BLUE",
|
|
"LIGHT_RED_GREEN_LIGHT_BLUE",
|
|
"LIGHT_RED_LIGHT_GREEN",
|
|
"LIGHT_RED_LIGHT_GREEN_BLUE",
|
|
"WHITE"
|
|
};
|
|
const rgb_t VMU_SCREEN_COLOR_MAP[VMU_NUM_COLORS] = {
|
|
{ DEFAULT_VMU_PIXEL_ON_R, DEFAULT_VMU_PIXEL_ON_G , DEFAULT_VMU_PIXEL_ON_B },
|
|
{ DEFAULT_VMU_PIXEL_OFF_R, DEFAULT_VMU_PIXEL_OFF_G , DEFAULT_VMU_PIXEL_OFF_B },
|
|
{ 0x00, 0x00, 0x00 },
|
|
{ 0x00, 0x00, 0x7F },
|
|
{ 0x00, 0x00, 0xFF },
|
|
{ 0x00, 0x7F, 0x00 },
|
|
{ 0x00, 0x7F, 0x7F },
|
|
{ 0x00, 0x7F, 0xFF },
|
|
{ 0x00, 0xFF, 0x00 },
|
|
{ 0x00, 0xFF, 0x7F },
|
|
{ 0x00, 0xFF, 0xFF },
|
|
{ 0x7F, 0x00, 0x00 },
|
|
{ 0x7F, 0x00, 0x7F },
|
|
{ 0x7F, 0x00, 0xFF },
|
|
{ 0x7F, 0x7F, 0x00 },
|
|
{ 0x7F, 0x7F, 0x7F },
|
|
{ 0x7F, 0x7F, 0xFF },
|
|
{ 0x7F, 0xFF, 0x00 },
|
|
{ 0x7F, 0xFF, 0x7F },
|
|
{ 0x7F, 0xFF, 0xFF },
|
|
{ 0xFF, 0x00, 0x00 },
|
|
{ 0xFF, 0x00, 0x7F },
|
|
{ 0xFF, 0x00, 0xFF },
|
|
{ 0xFF, 0x7F, 0x00 },
|
|
{ 0xFF, 0x7F, 0x7F },
|
|
{ 0xFF, 0x7F, 0xFF },
|
|
{ 0xFF, 0xFF, 0x00 },
|
|
{ 0xFF, 0xFF, 0x7F },
|
|
{ 0xFF, 0xFF, 0xFF }
|
|
};
|
|
|
|
vmu_screen_params_t vmu_screen_params[4] = { VMU_SCREEN_PARAMS_INIT, VMU_SCREEN_PARAMS_INIT, VMU_SCREEN_PARAMS_INIT, VMU_SCREEN_PARAMS_INIT };
|
|
|
|
u8 lightgun_img_crosshair[LIGHTGUN_CROSSHAIR_SIZE*LIGHTGUN_CROSSHAIR_SIZE] =
|
|
{
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
|
|
};
|
|
|
|
u8 lightgun_palette[LIGHTGUN_COLORS_COUNT*3] =
|
|
{
|
|
0xff,0xff,0xff, // LIGHTGUN_COLOR_OFF
|
|
0xff,0xff,0xff, // LIGHTGUN_COLOR_WHITE
|
|
0xff,0x10,0x10, // LIGHTGUN_COLOR_RED
|
|
0x10,0xff,0x10, // LIGHTGUN_COLOR_GREEN
|
|
0x10,0x10,0xff, // LIGHTGUN_COLOR_BLUE
|
|
};
|
|
|
|
lightgun_params_t lightgun_params[4] ;
|
|
|
|
MapleDeviceType maple_devices[MAPLE_PORTS] =
|
|
{ MDT_SegaController, MDT_SegaController, MDT_SegaController, MDT_SegaController };
|
|
|
|
u8 GetBtFromSgn(s8 val)
|
|
{
|
|
return val+128;
|
|
}
|
|
|
|
struct MapleConfigMap : IMapleConfigMap
|
|
{
|
|
maple_device* dev;
|
|
int player_num;
|
|
|
|
MapleConfigMap(maple_device* dev, int player_num = -1)
|
|
{
|
|
this->dev=dev;
|
|
this->player_num = player_num;
|
|
}
|
|
|
|
void SetVibration(u32 value, u32 max_duration)
|
|
{
|
|
UpdateVibration(player_num == -1 ? dev->bus_id : player_num, value, max_duration);
|
|
}
|
|
|
|
void GetInput(PlainJoystickState* pjs)
|
|
{
|
|
int pnum = player_num == -1 ? dev->bus_id : player_num;
|
|
s8 x1, y1;
|
|
|
|
if (settings.rend.ThreadedRendering)
|
|
{
|
|
/* Sampled on the libretro thread by CaptureInput(). */
|
|
const InputLatch::Port& in = input_latch.Read(pnum);
|
|
|
|
pjs->kcode=in.kcode;
|
|
x1=in.joyx;
|
|
y1=in.joyy;
|
|
pjs->joy[PJAI_X2]=GetBtFromSgn(in.joyrx);
|
|
pjs->joy[PJAI_Y2]=GetBtFromSgn(in.joyry);
|
|
pjs->trigger[PJTI_R]=in.rt;
|
|
pjs->trigger[PJTI_L]=in.lt;
|
|
}
|
|
else
|
|
{
|
|
/* A NAOMI's I/O board is one device, on the first port, and
|
|
* answers for every player; an Atomiswave's coins are read for
|
|
* all four. An arcade machine's ports are read all together,
|
|
* once a frame, whichever device asks first. (A NAOMI's were all
|
|
* read each time a board was asked for its inputs, twice a frame
|
|
* where there are two boards.) */
|
|
if (settings.System == DC_PLATFORM_DREAMCAST)
|
|
UpdateInputState(pnum);
|
|
else
|
|
UpdateInputStateArcade();
|
|
|
|
pjs->kcode=kcode[pnum];
|
|
x1=joyx[pnum];
|
|
y1=joyy[pnum];
|
|
pjs->joy[PJAI_X2]=GetBtFromSgn(joyrx[pnum]);
|
|
pjs->joy[PJAI_Y2]=GetBtFromSgn(joyry[pnum]);
|
|
pjs->trigger[PJTI_R]=rt[pnum];
|
|
pjs->trigger[PJTI_L]=lt[pnum];
|
|
}
|
|
pjs->joy[PJAI_X1]=GetBtFromSgn(x1);
|
|
pjs->joy[PJAI_Y1]=GetBtFromSgn(y1);
|
|
// I think the only Atomiswave game that uses an analog joystick is Block Pong-Pong but only the x axis.
|
|
// Driving games use the first two axes for totally separate things.
|
|
// So only do this for dreamcast
|
|
if (settings.System == DC_PLATFORM_DREAMCAST)
|
|
limit_joystick_magnitude<128>((s8&)pjs->joy[PJAI_X1], (s8&)pjs->joy[PJAI_Y1]);
|
|
}
|
|
void SetImage(void* img)
|
|
{
|
|
vmu_lcd_publish(player_num == -1 ? dev->bus_id : player_num, (const u8 *)img);
|
|
}
|
|
void GetAbsolutePosition(f32 *px, f32 *py)
|
|
{
|
|
int pnum = player_num == -1 ? dev->bus_id : player_num;
|
|
*px = mo_x_abs[pnum];
|
|
*py = mo_y_abs[pnum];
|
|
}
|
|
void GetMouse(u32 *buttons, f32 *delta_x, f32 *delta_y, f32 *delta_wheel)
|
|
{
|
|
int pnum = player_num == -1 ? dev->bus_id : player_num;
|
|
|
|
if (settings.rend.ThreadedRendering)
|
|
{
|
|
input_latch.ReadMouse(pnum, buttons, delta_x, delta_y, delta_wheel);
|
|
return;
|
|
}
|
|
if (settings.System == DC_PLATFORM_DREAMCAST)
|
|
UpdateInputStateMouse(pnum);
|
|
else
|
|
UpdateInputStateArcade();
|
|
*buttons = mo_buttons[pnum];
|
|
*delta_x = mo_x_delta[pnum];
|
|
*delta_y = mo_y_delta[pnum];
|
|
*delta_wheel = mo_wheel_delta[pnum];
|
|
|
|
mo_x_delta[pnum] = 0;
|
|
mo_y_delta[pnum] = 0;
|
|
mo_wheel_delta[pnum] = 0;
|
|
}
|
|
};
|
|
|
|
/* An arcade game's player's buttons (active low), read as the I/O board
|
|
* reads them, for the devices that are not on the maple bus: a card
|
|
* reader's button, a hopper's coin switch, a touch panel. */
|
|
u32 mcfg_ArcadeKcode(int port)
|
|
{
|
|
if (settings.rend.ThreadedRendering)
|
|
return input_latch.Read(port).kcode;
|
|
UpdateInputStateArcade();
|
|
return kcode[port];
|
|
}
|
|
|
|
// Where a player points, on the 640x480 screen
|
|
void mcfg_ArcadeAbsPos(int port, f32 *x, f32 *y)
|
|
{
|
|
*x = mo_x_abs[port];
|
|
*y = mo_y_abs[port];
|
|
}
|
|
|
|
/* The device at @bus.@port, of @type, made anew in its own memory, and its
|
|
* map with it: as gone and made again (mcfg_DestroyDevice(), mcfg_Create()),
|
|
* nothing allocated */
|
|
static void mcfg_Remake(u32 bus, u32 port, MapleDeviceType type, int player_num)
|
|
{
|
|
maple_device *dev = MapleDevices[bus][port];
|
|
IMapleConfigMap *map = dev->config;
|
|
const bool eeprom_loaded = EEPROM_loaded;
|
|
|
|
dev->config = NULL;
|
|
dev->~maple_device();
|
|
if (map != NULL)
|
|
map->~IMapleConfigMap();
|
|
/* (the board, as it went, forgot the EEPROM: the one made remembers) */
|
|
EEPROM_loaded = eeprom_loaded;
|
|
dev = maple_CreateAt(type, dev);
|
|
dev->Setup(maple_GetAddress(bus, port));
|
|
dev->config = map != NULL ? new (map) MapleConfigMap(dev, player_num) : new MapleConfigMap(dev, player_num);
|
|
dev->OnSetup();
|
|
MapleDevices[bus][port] = dev;
|
|
}
|
|
|
|
void mcfg_Create(MapleDeviceType type,u32 bus,u32 port, int player_num /* = -1 */)
|
|
{
|
|
maple_device* dev=maple_Create(type);
|
|
dev->Setup(maple_GetAddress(bus,port));
|
|
dev->config = new MapleConfigMap(dev, player_num);
|
|
dev->OnSetup();
|
|
MapleDevices[bus][port]=dev;
|
|
}
|
|
|
|
/* What a Dreamcast is to have on a bus, as the settings say: at port 5 the
|
|
* device plugged in, at 0 and 1 what is in its slots. */
|
|
static MapleDeviceType mcfg_DreamcastDevice(int bus, int port)
|
|
{
|
|
switch (maple_devices[bus])
|
|
{
|
|
case MDT_SegaController:
|
|
if (port == 5)
|
|
return MDT_SegaController;
|
|
if (port == 0)
|
|
return MDT_SegaVMU;
|
|
if (port == 1)
|
|
return enable_purupuru ? MDT_PurupuruPack : MDT_SegaVMU;
|
|
break;
|
|
|
|
case MDT_Keyboard:
|
|
case MDT_Mouse:
|
|
if (port == 5)
|
|
return maple_devices[bus];
|
|
break;
|
|
|
|
case MDT_LightGun:
|
|
if (port == 5)
|
|
return MDT_LightGun;
|
|
if (port == 0)
|
|
return enable_purupuru ? MDT_PurupuruPack : MDT_SegaVMU;
|
|
break;
|
|
|
|
case MDT_TwinStick:
|
|
case MDT_AsciiStick:
|
|
if (port == 5)
|
|
return maple_devices[bus];
|
|
if (port == 0)
|
|
return MDT_SegaVMU;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return MDT_None;
|
|
}
|
|
|
|
void mcfg_CreateDevices()
|
|
{
|
|
int bus ;
|
|
|
|
for ( bus = 0 ; bus < MAPLE_PORTS; bus++)
|
|
vmu_screen_params[bus].vmu_lcd_screen = NULL ;
|
|
vmu_lcd_reset();
|
|
|
|
|
|
if (settings.System == DC_PLATFORM_DREAMCAST)
|
|
{
|
|
for (bus = 0; bus < MAPLE_PORTS; ++bus)
|
|
{
|
|
if (maple_devices[bus] != MDT_None && mcfg_DreamcastDevice(bus, 5) == MDT_None)
|
|
WARN_LOG(MAPLE, "Invalid device type %d for port %d", maple_devices[bus], bus);
|
|
if (mcfg_DreamcastDevice(bus, 5) == MDT_None)
|
|
continue;
|
|
// the device, then what is in its slots
|
|
mcfg_Create(mcfg_DreamcastDevice(bus, 5), bus, 5);
|
|
for (int port = 0; port < 5; port++)
|
|
{
|
|
const MapleDeviceType type = mcfg_DreamcastDevice(bus, port);
|
|
if (type != MDT_None)
|
|
mcfg_Create(type, bus, port);
|
|
}
|
|
}
|
|
}
|
|
else if (SYSTEM_IS_NAOMI() && naomi_cart_systemsp)
|
|
{
|
|
/* A System SP has no I/O board on its maple bus, nor anything else:
|
|
* its ROM board has the inputs (systemsp.cpp). */
|
|
}
|
|
else if (SYSTEM_IS_NAOMI())
|
|
{
|
|
bus = 0;
|
|
mcfg_Create(MDT_NaomiJamma, bus++, 5);
|
|
maple_naomi_eeprom_load();
|
|
if (naomi_periph_rfid_game())
|
|
{
|
|
// an IC card reader for each player, and nothing else
|
|
mcfg_Create(MDT_RFIDReaderWriter, 1, 5, 0);
|
|
mcfg_Create(MDT_RFIDReaderWriter, 2, 5, 1);
|
|
return;
|
|
}
|
|
for (int i = 0; i < MAPLE_PORTS; i++)
|
|
{
|
|
switch (maple_devices[i])
|
|
{
|
|
case MDT_Keyboard:
|
|
mcfg_Create(MDT_Keyboard, bus++, 5);
|
|
break;
|
|
default:
|
|
if (i == 0)
|
|
{
|
|
// Connect VMU B1
|
|
mcfg_Create(MDT_SegaController, bus, 5);
|
|
mcfg_Create(MDT_SegaVMU, bus++, 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (settings.System == DC_PLATFORM_ATOMISWAVE)
|
|
{
|
|
mcfg_Create(MDT_SegaController, 0, 5);
|
|
mcfg_Create(MDT_SegaController, 1, 5);
|
|
switch (settings.mapping.JammaSetup)
|
|
{
|
|
case JVS_Analog:
|
|
// 2 players with analog axes
|
|
mcfg_Create(MDT_SegaController, 2, 5, 0);
|
|
mcfg_Create(MDT_SegaController, 3, 5, 1);
|
|
break;
|
|
case JVS_LightGun:
|
|
// Light guns
|
|
mcfg_Create(MDT_LightGun, 2, 5, 0);
|
|
mcfg_Create(MDT_LightGun, 3, 5, 1);
|
|
break;
|
|
case JVS_RotaryEncoders:
|
|
case JVS_SegaMarineFishing:
|
|
// Track-ball
|
|
mcfg_Create(MDT_Mouse, 2, 5, 0);
|
|
break;
|
|
default:
|
|
// 4 players
|
|
mcfg_Create(MDT_SegaController, 2, 5);
|
|
mcfg_Create(MDT_SegaController, 3, 5);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void mcfg_DestroyDevices()
|
|
{
|
|
for (int i = 0; i < MAPLE_PORTS; i++)
|
|
for (int j=0;j<=5;j++)
|
|
{
|
|
delete MapleDevices[i][j];
|
|
MapleDevices[i][j] = NULL;
|
|
}
|
|
}
|
|
|
|
void mcfg_DestroyDevice(int i, int j)
|
|
{
|
|
delete MapleDevices[i][j];
|
|
MapleDevices[i][j] = NULL;
|
|
}
|
|
|
|
bool maple_vmu_file_changed(maple_device *dev);
|
|
|
|
/* A Dreamcast's devices made to be what the settings say, touching nothing
|
|
* that already is: on a bus whose device is not the one wanted, the device
|
|
* and all in its slots are unplugged; otherwise each slot whose device is
|
|
* not the one wanted is, or whose card would now be read from another file.
|
|
* With @plug_in, what is missing then is plugged in.
|
|
*
|
|
* Unplugging is what a person changing a controller does, and the game sees
|
|
* the port empty and then the new device. The other ports go on as they
|
|
* were: a game writing to a card in one of them while another port's device
|
|
* is changed is not cut off half way through a save. */
|
|
void mcfg_ReconcileDevices(bool plug_in)
|
|
{
|
|
for (int bus = 0; bus < MAPLE_PORTS; bus++)
|
|
{
|
|
maple_device *device = MapleDevices[bus][5];
|
|
if (device == NULL || device->get_device_type() != mcfg_DreamcastDevice(bus, 5))
|
|
{
|
|
for (int port = 0; port <= 5; port++)
|
|
if (MapleDevices[bus][port] != NULL)
|
|
mcfg_DestroyDevice(bus, port);
|
|
}
|
|
else
|
|
{
|
|
for (int port = 0; port < 5; port++)
|
|
{
|
|
maple_device *dev = MapleDevices[bus][port];
|
|
if (dev != NULL && (dev->get_device_type() != mcfg_DreamcastDevice(bus, port)
|
|
|| maple_vmu_file_changed(dev)))
|
|
mcfg_DestroyDevice(bus, port);
|
|
}
|
|
}
|
|
if (!plug_in || mcfg_DreamcastDevice(bus, 5) == MDT_None)
|
|
continue;
|
|
if (MapleDevices[bus][5] == NULL)
|
|
mcfg_Create(mcfg_DreamcastDevice(bus, 5), bus, 5);
|
|
for (int port = 0; port < 5; port++)
|
|
{
|
|
const MapleDeviceType type = mcfg_DreamcastDevice(bus, port);
|
|
if (type != MDT_None && MapleDevices[bus][port] == NULL)
|
|
mcfg_Create(type, bus, port);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Whether a Dreamcast has the devices the settings say. (Not whether a card
|
|
* would be read from another file: a card no longer the settings' is
|
|
* unplugged at once when they change, and one a state puts back is made
|
|
* from them.) */
|
|
bool mcfg_DevicesAsSettingsSay(void)
|
|
{
|
|
for (int bus = 0; bus < MAPLE_PORTS; bus++)
|
|
for (int port = 0; port <= 5; port++)
|
|
{
|
|
maple_device *dev = MapleDevices[bus][port];
|
|
const MapleDeviceType type = dev == NULL ? MDT_None : dev->get_device_type();
|
|
if (type != mcfg_DreamcastDevice(bus, port))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void mcfg_SerializeDevices(void **data, unsigned int *total_size)
|
|
{
|
|
for (int i = 0; i < MAPLE_PORTS; i++)
|
|
for (int j = 0; j < 6; j++)
|
|
{
|
|
u8 **p = (u8 **)data;
|
|
if (MapleDevices[i][j] != NULL)
|
|
{
|
|
if (*p != NULL)
|
|
{
|
|
**p = MapleDevices[i][j]->get_device_type();
|
|
*p = *p + 1;
|
|
}
|
|
MapleDevices[i][j]->maple_serialize(data, total_size);
|
|
}
|
|
else if (*p != NULL)
|
|
{
|
|
**p = MDT_None;
|
|
*p = *p + 1;
|
|
}
|
|
*total_size = *total_size + 1;
|
|
}
|
|
}
|
|
|
|
/* Whose inputs an Atomiswave's device on the third or fourth bus reads: the
|
|
* first and second player's, where mcfg_CreateDevices() puts their analog
|
|
* axes, guns or trackball there. A state does not say, and a device made
|
|
* from one read the third and fourth player's. */
|
|
static int mcfg_PlayerNum(MapleDeviceType type, int bus)
|
|
{
|
|
switch (settings.mapping.JammaSetup)
|
|
{
|
|
case JVS_Analog:
|
|
return type == MDT_SegaController ? bus - 2 : -1;
|
|
case JVS_LightGun:
|
|
return type == MDT_LightGun ? bus - 2 : -1;
|
|
case JVS_RotaryEncoders:
|
|
case JVS_SegaMarineFishing:
|
|
return type == MDT_Mouse && bus == 2 ? 0 : -1;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
void mcfg_UnserializeDevices(void **data, unsigned int *total_size)
|
|
{
|
|
/* A NAOMI's I/O board, when it goes, forgets that the EEPROM has been
|
|
* read from its file. The board made below is the same one, and the
|
|
* EEPROM is what the state says: it has been read from the state by
|
|
* now, and with it whether it had been read from the file. */
|
|
const bool eeprom_loaded = EEPROM_loaded;
|
|
const bool atomiswave = settings.System == DC_PLATFORM_ATOMISWAVE;
|
|
|
|
/* The devices are made anew, as what the state says is there. Not the
|
|
* memory cards: one that the state has too stays, and is given the
|
|
* state's contents. Making a card is reading its file, after waiting
|
|
* for everything still on its way there to be written - four or eight
|
|
* times for every state loaded, which with run-ahead is every frame -
|
|
* and all of what it then is comes from the state. A device of the
|
|
* type the state has is made anew where it is (mcfg_Remake()): nothing
|
|
* allocated, with run-ahead, every frame. */
|
|
for (int i = 0; i < MAPLE_PORTS; i++)
|
|
for (int j = 0; j < 6; j++)
|
|
{
|
|
/* Which device is there is a byte of the state's: past the end
|
|
* of a state that stops short there is none, and one that is no
|
|
* device at all is none either. (It was made all the same, which
|
|
* maple_Create() cannot do.) */
|
|
u8 type = MDT_None;
|
|
if (!LIBRETRO_US(type))
|
|
type = MDT_None;
|
|
if (type >= MDT_Count)
|
|
{
|
|
WARN_LOG(MAPLE, "Invalid device type %d at %d.%d in the state", type, i, j);
|
|
type = MDT_None;
|
|
// (what such a device has in the state comes next, and is read as something else: the load has failed)
|
|
ra_unserialize_fail();
|
|
}
|
|
MapleDeviceType device_type = (MapleDeviceType)type;
|
|
const int player = atomiswave && i >= 2 && j == 5 ? mcfg_PlayerNum(device_type, i) : -1;
|
|
maple_device *was = MapleDevices[i][j];
|
|
const MapleDeviceType wasType = was != NULL ? was->get_device_type() : MDT_None;
|
|
if (wasType == MDT_SegaVMU && device_type == MDT_SegaVMU)
|
|
; // (the card stays: given the state's contents below)
|
|
else if (wasType != MDT_None && wasType == device_type)
|
|
mcfg_Remake(i, j, device_type, player);
|
|
else
|
|
{
|
|
// (none there, another, or a card where the state has something else)
|
|
if (was != NULL)
|
|
mcfg_DestroyDevice(i, j);
|
|
EEPROM_loaded = eeprom_loaded;
|
|
if (device_type != MDT_None)
|
|
mcfg_Create(device_type, i, j, player);
|
|
}
|
|
/* (a NAOMI's I/O board, when it goes, forgets that the EEPROM was
|
|
* read: it has been, from the state) */
|
|
EEPROM_loaded = eeprom_loaded;
|
|
if (device_type != MDT_None)
|
|
MapleDevices[i][j]->maple_unserialize(data, total_size);
|
|
}
|
|
}
|