summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/audit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/audit.c')
-rw-r--r--src/emu/audit.c505
1 files changed, 505 insertions, 0 deletions
diff --git a/src/emu/audit.c b/src/emu/audit.c
new file mode 100644
index 00000000000..e72cf7604b5
--- /dev/null
+++ b/src/emu/audit.c
@@ -0,0 +1,505 @@
+/***************************************************************************
+
+ audit.c
+
+ ROM set auditing functions.
+
+ Copyright (c) 1996-2007, 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 int audit_one_rom(core_options *options, const rom_entry *rom, const game_driver *gamedrv, UINT32 validation, audit_record *record);
+static int 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)
+{
+ const rom_entry *region, *rom;
+ audit_record *record;
+ int foundany = FALSE;
+ int allshared = TRUE;
+ int records;
+
+ /* determine the number of records we will generate */
+ records = 0;
+ for (region = rom_first_region(gamedrv); 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 (allshared && !rom_used_by_parent(gamedrv, rom, NULL))
+ allshared = FALSE;
+ records++;
+ }
+
+ if (records > 0)
+ {
+ /* allocate memory for the records */
+ *audit = malloc_or_die(sizeof(**audit) * records);
+ memset(*audit, 0, sizeof(**audit) * records);
+ record = *audit;
+
+ /* iterate over regions and ROMs */
+ for (region = rom_first_region(gamedrv); region; region = rom_next_region(region))
+ for (rom = rom_first_file(region); rom; rom = rom_next_file(rom))
+ {
+ int shared = rom_used_by_parent(gamedrv, rom, NULL);
+
+ /* audit a file */
+ if (ROMREGION_ISROMDATA(region))
+ {
+ if (audit_one_rom(options, rom, gamedrv, validation, record++) && (!shared || allshared))
+ foundany = TRUE;
+ }
+
+ /* audit a disk */
+ else if (ROMREGION_ISDISKDATA(region))
+ {
+ if (audit_one_disk(options, rom, gamedrv, validation, record++) && (!shared || allshared))
+ foundany = TRUE;
+ }
+ }
+
+ /* if we found nothing, we don't have the set at all */
+ if (!foundany)
+ {
+ free(*audit);
+ *audit = NULL;
+ records = 0;
+ }
+ }
+ return records;
+}
+
+
+/*-------------------------------------------------
+ audit_samples - validate the samples for a
+ game
+-------------------------------------------------*/
+
+int audit_samples(core_options *options, const game_driver *gamedrv, audit_record **audit)
+{
+ machine_config config;
+ audit_record *record;
+ int sndnum, sampnum;
+ int records = 0;
+
+ /* count the number of sample records attached to this driver */
+ expand_machine_driver(gamedrv->drv, &config);
+#if HAS_SAMPLES
+ for (sndnum = 0; sndnum < ARRAY_LENGTH(config.sound); sndnum++)
+ if (config.sound[sndnum].type == SOUND_SAMPLES)
+ {
+ struct Samplesinterface *intf = (struct Samplesinterface *)config.sound[sndnum].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++;
+ }
+ }
+#endif
+
+ /* if no records, just quit now */
+ if (records == 0)
+ return records;
+
+ /* allocate memory for the records */
+ *audit = malloc_or_die(sizeof(**audit) * records);
+ memset(*audit, 0, sizeof(**audit) * records);
+ record = *audit;
+
+ /* now iterate over sample entries */
+ for (sndnum = 0; sndnum < ARRAY_LENGTH(config.sound); sndnum++)
+ if (config.sound[sndnum].type == SOUND_SAMPLES)
+ {
+ struct Samplesinterface *intf = (struct Samplesinterface *)config.sound[sndnum].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, &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, &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);
+ }
+ }
+ }
+
+ 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 notfound = 0;
+ int recnum;
+
+ /* no count or 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;
+
+ /* count the number of missing items */
+ if (record->status == AUDIT_STATUS_NOT_FOUND)
+ notfound++;
+
+ /* 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 (notfound == count) ? NOTFOUND : overall_status;
+}
+
+
+
+/***************************************************************************
+ UTILITIES
+***************************************************************************/
+
+/*-------------------------------------------------
+ audit_one_rom - validate a single ROM entry
+-------------------------------------------------*/
+
+static int audit_one_rom(core_options *options, const rom_entry *rom, const game_driver *gamedrv, UINT32 validation, audit_record *record)
+{
+ const game_driver *drv;
+ const rom_entry *chunk;
+ 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);
+
+ /* compute the expected length by summing the chunks */
+ for (chunk = rom_first_chunk(rom); chunk; chunk = rom_next_chunk(chunk))
+ record->explength += ROM_GETLENGTH(chunk);
+
+ /* 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, &file);
+ else
+ filerr = mame_fopen_options(options, SEARCHPATH_ROM, astring_c(fname), OPEN_FLAG_READ, &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 we failed to find the file, set the appropriate status */
+ if (drv == NULL)
+ {
+ 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);
+ }
+
+ /* return TRUE if we found anything at all */
+ return (drv != NULL);
+}
+
+
+/*-------------------------------------------------
+ audit_one_disk - validate a single disk entry
+-------------------------------------------------*/
+
+static int 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 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_GOOD_NEEDS_REDUMP);
+
+ /* incorrect hash */
+ else if (!hash_data_is_equal(record->exphash, record->hash, 0))
+ set_status(record, AUDIT_STATUS_FOUND_INVALID, SUBSTATUS_FOUND_BAD_CHECKSUM);
+
+ /* just plain good */
+ else
+ set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD);
+
+ chd_close(source);
+ mame_fclose(source_file);
+ }
+
+ /* return TRUE if we found anything at all */
+ return (source != NULL);
+}
+
+
+/*-------------------------------------------------
+ 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); 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;
+}