mirror of
https://github.com/holub/mame
synced 2025-08-04 15:40:48 +03:00
521 lines
14 KiB
C
521 lines
14 KiB
C
/***************************************************************************
|
|
|
|
driver.c
|
|
|
|
Driver construction helpers.
|
|
|
|
Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
|
|
Visit http://mamedev.org for licensing and usage restrictions.
|
|
|
|
***************************************************************************/
|
|
|
|
#include "driver.h"
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
CONSTANTS
|
|
***************************************************************************/
|
|
|
|
#define DRIVER_LRU_SIZE 10
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
GLOBAL VARIABLES
|
|
***************************************************************************/
|
|
|
|
static int driver_lru[DRIVER_LRU_SIZE];
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
FUNCTION PROTOTYPES
|
|
***************************************************************************/
|
|
|
|
static int penalty_compare(const char *source, const char *target);
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
MISCELLANEOUS BITS & PIECES
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
expand_machine_driver - construct a machine
|
|
driver from the macroized state
|
|
-------------------------------------------------*/
|
|
|
|
void expand_machine_driver(void (*constructor)(machine_config *), machine_config *output)
|
|
{
|
|
/* initialize the tag on the first screen */
|
|
memset(output, 0, sizeof(*output));
|
|
output->watchdog_time = attotime_zero;
|
|
|
|
/* keeping this function allows us to pre-init the driver before constructing it */
|
|
(*constructor)(output);
|
|
|
|
/* if no screen tagged, tag screen 0 as main */
|
|
if (output->screen[0].tag == NULL && output->screen[0].defstate.format != BITMAP_FORMAT_INVALID)
|
|
output->screen[0].tag = "main";
|
|
|
|
/* if no screens, set a dummy refresh for the main screen */
|
|
if (output->screen[0].tag == NULL)
|
|
output->screen[0].defstate.refresh = HZ_TO_ATTOSECONDS(60);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_add_cpu - add a CPU during machine
|
|
driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
cpu_config *driver_add_cpu(machine_config *machine, const char *tag, cpu_type type, int cpuclock)
|
|
{
|
|
int cpunum;
|
|
|
|
for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
|
|
if (machine->cpu[cpunum].type == CPU_DUMMY)
|
|
{
|
|
machine->cpu[cpunum].tag = tag;
|
|
machine->cpu[cpunum].type = type;
|
|
machine->cpu[cpunum].clock = cpuclock;
|
|
return &machine->cpu[cpunum];
|
|
}
|
|
|
|
fatalerror("Out of CPU's!\n");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_find_cpu - find a tagged CPU during
|
|
machine driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
cpu_config *driver_find_cpu(machine_config *machine, const char *tag)
|
|
{
|
|
int cpunum;
|
|
|
|
for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
|
|
if (machine->cpu[cpunum].tag && strcmp(machine->cpu[cpunum].tag, tag) == 0)
|
|
return &machine->cpu[cpunum];
|
|
|
|
fatalerror("Can't find CPU '%s'!\n", tag);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_remove_cpu - remove a tagged CPU
|
|
during machine driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
void driver_remove_cpu(machine_config *machine, const char *tag)
|
|
{
|
|
int cpunum;
|
|
|
|
for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
|
|
if (machine->cpu[cpunum].tag && strcmp(machine->cpu[cpunum].tag, tag) == 0)
|
|
{
|
|
memmove(&machine->cpu[cpunum], &machine->cpu[cpunum + 1], sizeof(machine->cpu[0]) * (MAX_CPU - cpunum - 1));
|
|
memset(&machine->cpu[MAX_CPU - 1], 0, sizeof(machine->cpu[0]));
|
|
return;
|
|
}
|
|
|
|
fatalerror("Can't find CPU '%s'!\n", tag);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_add_speaker - add a speaker during
|
|
machine driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
speaker_config *driver_add_speaker(machine_config *machine, const char *tag, float x, float y, float z)
|
|
{
|
|
int speakernum;
|
|
|
|
for (speakernum = 0; speakernum < MAX_SPEAKER; speakernum++)
|
|
if (machine->speaker[speakernum].tag == NULL)
|
|
{
|
|
machine->speaker[speakernum].tag = tag;
|
|
machine->speaker[speakernum].x = x;
|
|
machine->speaker[speakernum].y = y;
|
|
machine->speaker[speakernum].z = z;
|
|
return &machine->speaker[speakernum];
|
|
}
|
|
|
|
fatalerror("Out of speakers!\n");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_find_speaker - find a tagged speaker
|
|
system during machine driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
speaker_config *driver_find_speaker(machine_config *machine, const char *tag)
|
|
{
|
|
int speakernum;
|
|
|
|
for (speakernum = 0; speakernum < MAX_SPEAKER; speakernum++)
|
|
if (machine->speaker[speakernum].tag && strcmp(machine->speaker[speakernum].tag, tag) == 0)
|
|
return &machine->speaker[speakernum];
|
|
|
|
fatalerror("Can't find speaker '%s'!\n", tag);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_remove_speaker - remove a tagged speaker
|
|
system during machine driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
void driver_remove_speaker(machine_config *machine, const char *tag)
|
|
{
|
|
int speakernum;
|
|
|
|
for (speakernum = 0; speakernum < MAX_SPEAKER; speakernum++)
|
|
if (machine->speaker[speakernum].tag && strcmp(machine->speaker[speakernum].tag, tag) == 0)
|
|
{
|
|
memmove(&machine->speaker[speakernum], &machine->speaker[speakernum + 1], sizeof(machine->speaker[0]) * (MAX_SPEAKER - speakernum - 1));
|
|
memset(&machine->speaker[MAX_SPEAKER - 1], 0, sizeof(machine->speaker[0]));
|
|
return;
|
|
}
|
|
|
|
fatalerror("Can't find speaker '%s'!\n", tag);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_add_sound - add a sound system during
|
|
machine driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
sound_config *driver_add_sound(machine_config *machine, const char *tag, sound_type type, int clock)
|
|
{
|
|
int soundnum;
|
|
|
|
for (soundnum = 0; soundnum < MAX_SOUND; soundnum++)
|
|
if (machine->sound[soundnum].type == SOUND_DUMMY)
|
|
{
|
|
machine->sound[soundnum].tag = tag;
|
|
machine->sound[soundnum].type = type;
|
|
machine->sound[soundnum].clock = clock;
|
|
machine->sound[soundnum].config = NULL;
|
|
machine->sound[soundnum].routes = 0;
|
|
return &machine->sound[soundnum];
|
|
}
|
|
|
|
fatalerror("Out of sounds!\n");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_find_sound - find a tagged sound
|
|
system during machine driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
sound_config *driver_find_sound(machine_config *machine, const char *tag)
|
|
{
|
|
int soundnum;
|
|
|
|
for (soundnum = 0; soundnum < MAX_SOUND; soundnum++)
|
|
if (machine->sound[soundnum].tag && strcmp(machine->sound[soundnum].tag, tag) == 0)
|
|
return &machine->sound[soundnum];
|
|
|
|
fatalerror("Can't find sound '%s'!\n", tag);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_remove_sound - remove a tagged sound
|
|
system during machine driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
void driver_remove_sound(machine_config *machine, const char *tag)
|
|
{
|
|
int soundnum;
|
|
|
|
for (soundnum = 0; soundnum < MAX_SOUND; soundnum++)
|
|
if (machine->sound[soundnum].tag && strcmp(machine->sound[soundnum].tag, tag) == 0)
|
|
{
|
|
memmove(&machine->sound[soundnum], &machine->sound[soundnum + 1], sizeof(machine->sound[0]) * (MAX_SOUND - soundnum - 1));
|
|
memset(&machine->sound[MAX_SOUND - 1], 0, sizeof(machine->sound[0]));
|
|
return;
|
|
}
|
|
|
|
fatalerror("Can't find sound '%s'!\n", tag);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_add_screen - add a screen during
|
|
machine driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
screen_config *driver_add_screen(machine_config *machine, const char *tag, int palbase)
|
|
{
|
|
int screennum;
|
|
|
|
for (screennum = 0; screennum < MAX_SCREENS; screennum++)
|
|
if (machine->screen[screennum].tag == NULL)
|
|
{
|
|
machine->screen[screennum].tag = tag;
|
|
machine->screen[screennum].palette_base = palbase;
|
|
return &machine->screen[screennum];
|
|
}
|
|
|
|
fatalerror("Out of screens!\n");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_find_screen - find a tagged screen
|
|
during machine driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
screen_config *driver_find_screen(machine_config *machine, const char *tag)
|
|
{
|
|
int screennum;
|
|
|
|
for (screennum = 0; screennum < MAX_SCREENS; screennum++)
|
|
if (machine->screen[screennum].tag && strcmp(machine->screen[screennum].tag, tag) == 0)
|
|
return &machine->screen[screennum];
|
|
|
|
fatalerror("Can't find screen '%s'!\n", tag);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_remove_screen - remove a tagged screen
|
|
during machine driver expansion
|
|
-------------------------------------------------*/
|
|
|
|
void driver_remove_screen(machine_config *machine, const char *tag)
|
|
{
|
|
int screennum;
|
|
|
|
for (screennum = 0; screennum < MAX_SCREENS; screennum++)
|
|
if (machine->screen[screennum].tag && strcmp(machine->screen[screennum].tag, tag) == 0)
|
|
{
|
|
memmove(&machine->screen[screennum], &machine->screen[screennum + 1], sizeof(machine->screen[0]) * (MAX_SCREENS - screennum - 1));
|
|
memset(&machine->screen[MAX_SCREENS - 1], 0, sizeof(machine->screen[0]));
|
|
return;
|
|
}
|
|
|
|
fatalerror("Can't find screen '%s'!\n", tag);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_get_name - return a pointer to a
|
|
driver given its name
|
|
-------------------------------------------------*/
|
|
|
|
const game_driver *driver_get_name(const char *name)
|
|
{
|
|
int lurnum, drvnum;
|
|
|
|
/* scan the LRU list first */
|
|
for (lurnum = 0; lurnum < DRIVER_LRU_SIZE; lurnum++)
|
|
if (mame_stricmp(drivers[driver_lru[lurnum]]->name, name) == 0)
|
|
{
|
|
/* if not first, swap with the head */
|
|
if (lurnum != 0)
|
|
{
|
|
int temp = driver_lru[0];
|
|
driver_lru[0] = driver_lru[lurnum];
|
|
driver_lru[lurnum] = temp;
|
|
}
|
|
return drivers[driver_lru[0]];
|
|
}
|
|
|
|
/* scan for a match in the drivers -- slow! */
|
|
for (drvnum = 0; drivers[drvnum] != NULL; drvnum++)
|
|
if (mame_stricmp(drivers[drvnum]->name, name) == 0)
|
|
{
|
|
memmove((void *)&driver_lru[1], (void *)&driver_lru[0], sizeof(driver_lru[0]) * (DRIVER_LRU_SIZE - 1));
|
|
driver_lru[0] = drvnum;
|
|
return drivers[drvnum];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_get_clone - return a pointer to the
|
|
clone of a game driver.
|
|
-------------------------------------------------*/
|
|
|
|
const game_driver *driver_get_clone(const game_driver *driver)
|
|
{
|
|
/* if no clone, easy out */
|
|
if (driver->parent == NULL || (driver->parent[0] == '0' && driver->parent[1] == 0))
|
|
return NULL;
|
|
|
|
/* convert the name to a game_driver */
|
|
return driver_get_name(driver->parent);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_list_get_approx_matches - find the best
|
|
n matches to a driver name.
|
|
-------------------------------------------------*/
|
|
|
|
void driver_list_get_approx_matches(const game_driver * const driverlist[], const char *name, int matches, const game_driver **list)
|
|
{
|
|
#undef rand
|
|
|
|
int matchnum, drvnum;
|
|
int *penalty;
|
|
|
|
/* if no name, pick random entries */
|
|
if (name == NULL || name[0] == 0)
|
|
{
|
|
const game_driver **templist;
|
|
int driver_count;
|
|
int shufnum;
|
|
|
|
/* allocate a temporary list */
|
|
templist = malloc_or_die(driver_list_get_count(driverlist) * sizeof(*templist));
|
|
|
|
/* build up a list of valid entries */
|
|
for (drvnum = driver_count = 0; driverlist[drvnum] != NULL; drvnum++)
|
|
if ((driverlist[drvnum]->flags & GAME_NO_STANDALONE) == 0)
|
|
templist[driver_count++] = driverlist[drvnum];
|
|
|
|
/* seed the RNG first */
|
|
srand(osd_ticks());
|
|
|
|
/* shuffle */
|
|
for (shufnum = 0; shufnum < 4 * driver_count; shufnum++)
|
|
{
|
|
int item1 = rand() % driver_count;
|
|
int item2 = rand() % driver_count;
|
|
const game_driver *temp;
|
|
|
|
temp = templist[item1];
|
|
templist[item1] = templist[item2];
|
|
templist[item2] = temp;
|
|
}
|
|
|
|
/* copy out the first few entries */
|
|
for (matchnum = 0; matchnum < matches; matchnum++)
|
|
list[matchnum] = templist[matchnum % driver_count];
|
|
|
|
free((void *)templist);
|
|
return;
|
|
}
|
|
|
|
/* allocate some temp memory */
|
|
penalty = malloc_or_die(matches * sizeof(*penalty));
|
|
|
|
/* initialize everyone's states */
|
|
for (matchnum = 0; matchnum < matches; matchnum++)
|
|
{
|
|
penalty[matchnum] = 9999;
|
|
list[matchnum] = NULL;
|
|
}
|
|
|
|
/* scan the entire drivers array */
|
|
for (drvnum = 0; driverlist[drvnum] != NULL; drvnum++)
|
|
{
|
|
int curpenalty, tmp;
|
|
|
|
/* skip things that can't run */
|
|
if ((driverlist[drvnum]->flags & GAME_NO_STANDALONE) != 0)
|
|
continue;
|
|
|
|
/* pick the best match between driver name and description */
|
|
curpenalty = penalty_compare(name, driverlist[drvnum]->description);
|
|
tmp = penalty_compare(name, driverlist[drvnum]->name);
|
|
curpenalty = MIN(curpenalty, tmp);
|
|
|
|
/* insert into the sorted table of matches */
|
|
for (matchnum = matches - 1; matchnum >= 0; matchnum--)
|
|
{
|
|
/* stop if we're worse than the current entry */
|
|
if (curpenalty >= penalty[matchnum])
|
|
break;
|
|
|
|
/* as lng as this isn't the last entry, bump this one down */
|
|
if (matchnum < matches - 1)
|
|
{
|
|
penalty[matchnum + 1] = penalty[matchnum];
|
|
list[matchnum + 1] = list[matchnum];
|
|
}
|
|
list[matchnum] = driverlist[drvnum];
|
|
penalty[matchnum] = curpenalty;
|
|
}
|
|
}
|
|
|
|
/* free our temp memory */
|
|
free(penalty);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
penalty_compare - compare two strings for
|
|
closeness and assign a score.
|
|
-------------------------------------------------*/
|
|
|
|
static int penalty_compare(const char *source, const char *target)
|
|
{
|
|
int gaps = 1;
|
|
int last = TRUE;
|
|
|
|
/* scan the strings */
|
|
for ( ; *source && *target; target++)
|
|
{
|
|
/* do a case insensitive match */
|
|
int match = (tolower(*source) == tolower(*target));
|
|
|
|
/* if we matched, advance the source */
|
|
if (match)
|
|
source++;
|
|
|
|
/* if the match state changed, count gaps */
|
|
if (match != last)
|
|
{
|
|
last = match;
|
|
if (!match)
|
|
gaps++;
|
|
}
|
|
}
|
|
|
|
/* penalty if short string does not completely fit in */
|
|
for ( ; *source; source++)
|
|
gaps++;
|
|
|
|
/* if we matched perfectly, gaps == 0 */
|
|
if (gaps == 1 && *source == 0 && *target == 0)
|
|
gaps = 0;
|
|
|
|
return gaps;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
driver_list_get_count - returns the amount of
|
|
drivers
|
|
-------------------------------------------------*/
|
|
|
|
int driver_list_get_count(const game_driver * const driverlist[])
|
|
{
|
|
int count;
|
|
|
|
for (count = 0; driverlist[count] != NULL; count++) ;
|
|
return count;
|
|
}
|