mame/src/emu/audit.c
Aaron Giles ad4910a8a8 Bulk change alert.
This update changes the way we handle memory allocation. Rather
than allocating in terms of bytes, allocations are now done in
terms of objects. This is done via new set of macros that replace
the malloc_or_die() macro:

  alloc_or_die(t) - allocate memory for an object of type 't'
  alloc_array_or_die(t,c) - allocate memory for an array of 'c' objects of type 't'
  alloc_clear_or_die(t) - same as alloc_or_die but memset's the memory to 0
  alloc_array_clear_or_die(t,c) - same as alloc_array_or_die but memset's the memory to 0

All original callers of malloc_or_die have been updated to call these
new macros. If you just need an array of bytes, you can use
alloc_array_or_die(UINT8, numbytes).

Made a similar change to the auto_* allocation macros. In addition,
added 'machine' as a required parameter to the auto-allocation macros,
as the resource pools will eventually be owned by the machine object.
The new macros are:

  auto_alloc(m,t) - allocate memory for an object of type 't'
  auto_alloc_array(m,t,c) - allocate memory for an array of 'c' objects of type 't'
  auto_alloc_clear(m,t) - allocate and memset
  auto_alloc_array_clear(m,t,c) - allocate and memset

All original calls or auto_malloc have been updated to use the new
macros. In addition, auto_realloc(), auto_strdup(), auto_astring_alloc(),
and auto_bitmap_alloc() have been updated to take a machine parameter.

Changed validity check allocations to not rely on auto_alloc* anymore
because they are not done in the context of a machine.

One final change that is included is the removal of SMH_BANKn macros.
Just use SMH_BANK(n) instead, which is what the previous macros mapped
to anyhow.
2009-04-26 23:54:37 +00:00

551 lines
17 KiB
C

/***************************************************************************
audit.c
ROM set auditing functions.
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
***************************************************************************/
#include <stdarg.h>
#include "driver.h"
#include "hash.h"
#include "audit.h"
#include "harddisk.h"
#include "sound/samples.h"
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
static void audit_one_rom(core_options *options, const rom_entry *rom, const char *regiontag, const game_driver *gamedrv, UINT32 validation, audit_record *record);
static void audit_one_disk(core_options *options, const rom_entry *rom, const game_driver *gamedrv, UINT32 validation, audit_record *record);
static int rom_used_by_parent(const game_driver *gamedrv, const rom_entry *romentry, const game_driver **parent);
/***************************************************************************
INLINE FUNCTIONS
***************************************************************************/
/*-------------------------------------------------
set_status - shortcut for setting status and
substatus values
-------------------------------------------------*/
INLINE void set_status(audit_record *record, UINT8 status, UINT8 substatus)
{
record->status = status;
record->substatus = substatus;
}
/***************************************************************************
CORE FUNCTIONS
***************************************************************************/
/*-------------------------------------------------
audit_images - validate the ROM and disk
images for a game
-------------------------------------------------*/
int audit_images(core_options *options, const game_driver *gamedrv, UINT32 validation, audit_record **audit)
{
machine_config *config = machine_config_alloc(gamedrv->machine_config);
const rom_entry *region, *rom;
const rom_source *source;
audit_record *record;
int anyfound = FALSE;
int anyrequired = FALSE;
int allshared = TRUE;
int records;
/* determine the number of records we will generate */
records = 0;
for (source = rom_first_source(gamedrv, config); source != NULL; source = rom_next_source(gamedrv, config, source))
{
int source_is_gamedrv = rom_source_is_gamedrv(gamedrv, source);
for (region = rom_first_region(gamedrv, source); region != NULL; region = rom_next_region(region))
for (rom = rom_first_file(region); rom != NULL; rom = rom_next_file(rom))
if (ROMREGION_ISROMDATA(region) || ROMREGION_ISDISKDATA(region))
{
if (source_is_gamedrv && !ROM_ISOPTIONAL(rom) && !ROM_NOGOODDUMP(rom))
{
anyrequired = TRUE;
if (allshared && !rom_used_by_parent(gamedrv, rom, NULL))
allshared = FALSE;
}
records++;
}
}
if (records > 0)
{
/* allocate memory for the records */
*audit = alloc_array_clear_or_die(audit_record, records);
record = *audit;
/* iterate over ROM sources and regions */
for (source = rom_first_source(gamedrv, config); source != NULL; source = rom_next_source(gamedrv, config, source))
{
int source_is_gamedrv = rom_source_is_gamedrv(gamedrv, source);
for (region = rom_first_region(gamedrv, source); region != NULL; region = rom_next_region(region))
{
const char *regiontag = ROMREGION_ISLOADBYNAME(region) ? ROM_GETNAME(region) : NULL;
for (rom = rom_first_file(region); rom; rom = rom_next_file(rom))
{
/* audit a file */
if (ROMREGION_ISROMDATA(region))
{
audit_one_rom(options, rom, regiontag, gamedrv, validation, record);
}
/* audit a disk */
else if (ROMREGION_ISDISKDATA(region))
{
audit_one_disk(options, rom, gamedrv, validation, record);
}
else
{
continue;
}
if (source_is_gamedrv && record->status != AUDIT_STATUS_NOT_FOUND && (allshared || !rom_used_by_parent(gamedrv, rom, NULL)))
anyfound = TRUE;
record++;
}
}
}
}
/* if we found nothing, we don't have the set at all */
if (!anyfound && anyrequired)
{
free(*audit);
*audit = NULL;
records = 0;
}
machine_config_free(config);
return records;
}
/*-------------------------------------------------
audit_samples - validate the samples for a
game
-------------------------------------------------*/
int audit_samples(core_options *options, const game_driver *gamedrv, audit_record **audit)
{
int records = 0;
#if HAS_SAMPLES
machine_config *config = machine_config_alloc(gamedrv->machine_config);
const device_config *device;
audit_record *record;
int sampnum;
/* count the number of sample records attached to this driver */
for (device = sound_first(config); device != NULL; device = sound_next(device))
if (sound_get_type(device) == SOUND_SAMPLES)
{
const samples_interface *intf = (const samples_interface *)device->static_config;
if (intf->samplenames != NULL)
{
/* iterate over samples in this entry */
for (sampnum = 0; intf->samplenames[sampnum] != NULL; sampnum++)
if (intf->samplenames[sampnum][0] != '*')
records++;
}
}
/* if no records, just quit now */
if (records == 0)
goto skip;
/* allocate memory for the records */
*audit = alloc_array_clear_or_die(audit_record, records);
record = *audit;
/* now iterate over sample entries */
for (device = sound_first(config); device != NULL; device = sound_next(device))
if (sound_get_type(device) == SOUND_SAMPLES)
{
const samples_interface *intf = (const samples_interface *)device->static_config;
const char *sharedname = NULL;
if (intf->samplenames != NULL)
{
/* iterate over samples in this entry */
for (sampnum = 0; intf->samplenames[sampnum] != NULL; sampnum++)
if (intf->samplenames[sampnum][0] == '*')
sharedname = &intf->samplenames[sampnum][1];
else
{
file_error filerr;
mame_file *file;
astring *fname;
/* attempt to access the file from the game driver name */
fname = astring_assemble_3(astring_alloc(), gamedrv->name, PATH_SEPARATOR, intf->samplenames[sampnum]);
filerr = mame_fopen_options(options, SEARCHPATH_SAMPLE, astring_c(fname), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD, &file);
/* attempt to access the file from the shared driver name */
if (filerr != FILERR_NONE && sharedname != NULL)
{
astring_assemble_3(fname, sharedname, PATH_SEPARATOR, intf->samplenames[sampnum]);
filerr = mame_fopen_options(options, SEARCHPATH_SAMPLE, astring_c(fname), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD, &file);
}
astring_free(fname);
/* fill in the record */
record->type = AUDIT_FILE_SAMPLE;
record->name = intf->samplenames[sampnum];
if (filerr == FILERR_NONE)
{
set_status(record++, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD);
mame_fclose(file);
}
else
set_status(record++, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND);
}
}
}
skip:
machine_config_free(config);
#endif /* HAS_SAMPLES */
return records;
}
/*-------------------------------------------------
audit_summary - output a summary given a
list of audit records
-------------------------------------------------*/
int audit_summary(const game_driver *gamedrv, int count, const audit_record *records, int output)
{
int overall_status = CORRECT;
int recnum;
/* no count AND no records means not found */
if (count == 0 && records == NULL)
return NOTFOUND;
/* loop over records */
for (recnum = 0; recnum < count; recnum++)
{
const audit_record *record = &records[recnum];
int best_new_status = INCORRECT;
/* skip anything that's fine */
if (record->substatus == SUBSTATUS_GOOD)
continue;
/* output the game name, file name, and length (if applicable) */
if (output)
{
mame_printf_info("%-8s: %s", gamedrv->name, record->name);
if (record->explength > 0)
mame_printf_info(" (%d bytes)", record->explength);
mame_printf_info(" - ");
}
/* use the substatus for finer details */
switch (record->substatus)
{
case SUBSTATUS_GOOD_NEEDS_REDUMP:
if (output) mame_printf_info("NEEDS REDUMP\n");
best_new_status = BEST_AVAILABLE;
break;
case SUBSTATUS_FOUND_NODUMP:
if (output) mame_printf_info("NO GOOD DUMP KNOWN\n");
best_new_status = BEST_AVAILABLE;
break;
case SUBSTATUS_FOUND_BAD_CHECKSUM:
if (output)
{
char hashbuf[512];
mame_printf_info("INCORRECT CHECKSUM:\n");
hash_data_print(record->exphash, 0, hashbuf);
mame_printf_info("EXPECTED: %s\n", hashbuf);
hash_data_print(record->hash, 0, hashbuf);
mame_printf_info(" FOUND: %s\n", hashbuf);
}
break;
case SUBSTATUS_FOUND_WRONG_LENGTH:
if (output) mame_printf_info("INCORRECT LENGTH: %d bytes\n", record->length);
break;
case SUBSTATUS_NOT_FOUND:
if (output) mame_printf_info("NOT FOUND\n");
break;
case SUBSTATUS_NOT_FOUND_NODUMP:
if (output) mame_printf_info("NOT FOUND - NO GOOD DUMP KNOWN\n");
best_new_status = BEST_AVAILABLE;
break;
case SUBSTATUS_NOT_FOUND_OPTIONAL:
if (output) mame_printf_info("NOT FOUND BUT OPTIONAL\n");
best_new_status = BEST_AVAILABLE;
break;
case SUBSTATUS_NOT_FOUND_PARENT:
if (output) mame_printf_info("NOT FOUND (shared with parent)\n");
break;
case SUBSTATUS_NOT_FOUND_BIOS:
if (output) mame_printf_info("NOT FOUND (BIOS)\n");
break;
}
/* downgrade the overall status if necessary */
overall_status = MAX(overall_status, best_new_status);
}
return overall_status;
}
/***************************************************************************
UTILITIES
***************************************************************************/
/*-------------------------------------------------
audit_one_rom - validate a single ROM entry
-------------------------------------------------*/
static void audit_one_rom(core_options *options, const rom_entry *rom, const char *regiontag, const game_driver *gamedrv, UINT32 validation, audit_record *record)
{
const game_driver *drv;
UINT32 crc = 0;
UINT8 crcs[4];
int has_crc;
/* fill in the record basics */
record->type = AUDIT_FILE_ROM;
record->name = ROM_GETNAME(rom);
record->exphash = ROM_GETHASHDATA(rom);
record->length = 0;
record->explength = rom_file_size(rom);
/* see if we have a CRC and extract it if so */
has_crc = hash_data_extract_binary_checksum(record->exphash, HASH_CRC, crcs);
if (has_crc)
crc = (crcs[0] << 24) | (crcs[1] << 16) | (crcs[2] << 8) | crcs[3];
/* find the file and checksum it, getting the file length along the way */
for (drv = gamedrv; drv != NULL; drv = driver_get_clone(drv))
{
file_error filerr;
mame_file *file;
astring *fname;
/* open the file if we can */
fname = astring_assemble_3(astring_alloc(), drv->name, PATH_SEPARATOR, ROM_GETNAME(rom));
if (has_crc)
filerr = mame_fopen_crc_options(options, SEARCHPATH_ROM, astring_c(fname), crc, OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD, &file);
else
filerr = mame_fopen_options(options, SEARCHPATH_ROM, astring_c(fname), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD, &file);
astring_free(fname);
/* if we got it, extract the hash and length */
if (filerr == FILERR_NONE)
{
hash_data_copy(record->hash, mame_fhash(file, validation));
record->length = (UINT32)mame_fsize(file);
mame_fclose(file);
break;
}
}
/* if not found, check the region as a backup */
if (record->length == 0 && regiontag != NULL)
{
file_error filerr;
mame_file *file;
astring *fname;
/* open the file if we can */
fname = astring_assemble_3(astring_alloc(), regiontag, PATH_SEPARATOR, ROM_GETNAME(rom));
if (has_crc)
filerr = mame_fopen_crc_options(options, SEARCHPATH_ROM, astring_c(fname), crc, OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD, &file);
else
filerr = mame_fopen_options(options, SEARCHPATH_ROM, astring_c(fname), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD, &file);
astring_free(fname);
/* if we got it, extract the hash and length */
if (filerr == FILERR_NONE)
{
hash_data_copy(record->hash, mame_fhash(file, validation));
record->length = (UINT32)mame_fsize(file);
mame_fclose(file);
}
}
/* if we failed to find the file, set the appropriate status */
if (record->length == 0)
{
const game_driver *parent;
/* no good dump */
if (hash_data_has_info(record->exphash, HASH_INFO_NO_DUMP))
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND_NODUMP);
/* optional ROM */
else if (ROM_ISOPTIONAL(rom))
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND_OPTIONAL);
/* not found and used by parent */
else if (rom_used_by_parent(gamedrv, rom, &parent))
set_status(record, AUDIT_STATUS_NOT_FOUND, (parent->flags & GAME_IS_BIOS_ROOT) ? SUBSTATUS_NOT_FOUND_BIOS : SUBSTATUS_NOT_FOUND_PARENT);
/* just plain old not found */
else
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND);
}
/* if we did find the file, do additional verification */
else
{
/* length mismatch */
if (record->explength != record->length)
set_status(record, AUDIT_STATUS_FOUND_INVALID, SUBSTATUS_FOUND_WRONG_LENGTH);
/* found but needs a dump */
else if (hash_data_has_info(record->exphash, HASH_INFO_NO_DUMP))
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_FOUND_NODUMP);
/* incorrect hash */
else if (!hash_data_is_equal(record->exphash, record->hash, 0))
set_status(record, AUDIT_STATUS_FOUND_INVALID, SUBSTATUS_FOUND_BAD_CHECKSUM);
/* correct hash but needs a redump */
else if (hash_data_has_info(record->exphash, HASH_INFO_BAD_DUMP))
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD_NEEDS_REDUMP);
/* just plain old good */
else
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD);
}
}
/*-------------------------------------------------
audit_one_disk - validate a single disk entry
-------------------------------------------------*/
static void audit_one_disk(core_options *options, const rom_entry *rom, const game_driver *gamedrv, UINT32 validation, audit_record *record)
{
mame_file *source_file;
chd_file *source;
chd_error err;
/* fill in the record basics */
record->type = AUDIT_FILE_DISK;
record->name = ROM_GETNAME(rom);
record->exphash = ROM_GETHASHDATA(rom);
/* open the disk */
err = open_disk_image_options(options, gamedrv, rom, &source_file, &source);
/* if we failed, report the error */
if (err != CHDERR_NONE)
{
/* out of memory */
if (err == CHDERR_OUT_OF_MEMORY)
set_status(record, AUDIT_STATUS_ERROR, SUBSTATUS_ERROR);
/* not found but it's not good anyway */
else if (hash_data_has_info(record->exphash, HASH_INFO_NO_DUMP))
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND_NODUMP);
/* not found but optional */
else if (DISK_ISOPTIONAL(rom))
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND_OPTIONAL);
/* not found at all */
else
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND);
}
/* if we succeeded, validate it */
else
{
static const UINT8 nullhash[HASH_BUF_SIZE] = { 0 };
chd_header header = *chd_get_header(source);
/* if there's an MD5 or SHA1 hash, add them to the output hash */
if (memcmp(nullhash, header.md5, sizeof(header.md5)) != 0)
hash_data_insert_binary_checksum(record->hash, HASH_MD5, header.md5);
if (memcmp(nullhash, header.sha1, sizeof(header.sha1)) != 0)
hash_data_insert_binary_checksum(record->hash, HASH_SHA1, header.sha1);
/* found but needs a dump */
if (hash_data_has_info(record->exphash, HASH_INFO_NO_DUMP))
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_FOUND_NODUMP);
/* incorrect hash */
else if (!hash_data_is_equal(record->exphash, record->hash, 0))
set_status(record, AUDIT_STATUS_FOUND_INVALID, SUBSTATUS_FOUND_BAD_CHECKSUM);
/* correct hash but needs a redump */
else if (hash_data_has_info(record->exphash, HASH_INFO_BAD_DUMP))
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD_NEEDS_REDUMP);
/* just plain good */
else
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD);
chd_close(source);
mame_fclose(source_file);
}
}
/*-------------------------------------------------
rom_used_by_parent - determine if a given
ROM is also used by the parent
-------------------------------------------------*/
static int rom_used_by_parent(const game_driver *gamedrv, const rom_entry *romentry, const game_driver **parent)
{
const char *hash = ROM_GETHASHDATA(romentry);
const game_driver *drv;
/* iterate up the parent chain */
for (drv = driver_get_clone(gamedrv); drv != NULL; drv = driver_get_clone(drv))
{
const rom_entry *region;
const rom_entry *rom;
/* see if the parent has the same ROM or not */
for (region = rom_first_region(drv, NULL); region; region = rom_next_region(region))
for (rom = rom_first_file(region); rom; rom = rom_next_file(rom))
if (hash_data_is_equal(ROM_GETHASHDATA(rom), hash, 0))
{
if (parent != NULL)
*parent = drv;
return TRUE;
}
}
return FALSE;
}