diff --git a/src/emu/audit.c b/src/emu/audit.c index 781ca3009a0..6b4fd058089 100644 --- a/src/emu/audit.c +++ b/src/emu/audit.c @@ -22,9 +22,9 @@ 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); +static void audit_one_rom(core_options *options, const rom_entry *rom, const char *regiontag, const game_driver *gamedrv, const char *validation, audit_record *record); +static void audit_one_disk(core_options *options, const rom_entry *rom, const game_driver *gamedrv, const char *validation, audit_record *record); +static int rom_used_by_parent(const game_driver *gamedrv, const hash_collection &romhashes, const game_driver **parent); @@ -54,7 +54,7 @@ INLINE void set_status(audit_record *record, UINT8 status, UINT8 substatus) images for a game -------------------------------------------------*/ -int audit_images(core_options *options, const game_driver *gamedrv, UINT32 validation, audit_record **audit) +int audit_images(core_options *options, const game_driver *gamedrv, const char *validation, audit_record **audit) { machine_config config(*gamedrv); const rom_entry *region, *rom; @@ -74,12 +74,16 @@ int audit_images(core_options *options, const game_driver *gamedrv, UINT32 valid 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)) + if (source_is_gamedrv && !ROM_ISOPTIONAL(rom)) { - anyrequired = TRUE; + hash_collection hashes(ROM_GETHASHDATA(rom)); + if (!hashes.flag(hash_collection::FLAG_NO_DUMP)) + { + anyrequired = TRUE; - if (allshared && !rom_used_by_parent(gamedrv, rom, NULL)) - allshared = FALSE; + if (allshared && !rom_used_by_parent(gamedrv, hashes, NULL)) + allshared = FALSE; + } } records++; } @@ -119,7 +123,7 @@ int audit_images(core_options *options, const game_driver *gamedrv, UINT32 valid continue; } - if (source_is_gamedrv && record->status != AUDIT_STATUS_NOT_FOUND && (allshared || !rom_used_by_parent(gamedrv, rom, NULL))) + if (source_is_gamedrv && record->status != AUDIT_STATUS_NOT_FOUND && (allshared || !rom_used_by_parent(gamedrv, record->exphashes, NULL))) anyfound = TRUE; record++; @@ -265,13 +269,10 @@ int audit_summary(const game_driver *gamedrv, int count, const audit_record *rec case SUBSTATUS_FOUND_BAD_CHECKSUM: if (output) { - char hashbuf[512]; - + astring tempstr; 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); + mame_printf_info("EXPECTED: %s\n", record->exphashes.macro_string(tempstr)); + mame_printf_info(" FOUND: %s\n", record->hashes.macro_string(tempstr)); } break; @@ -319,24 +320,20 @@ int audit_summary(const game_driver *gamedrv, int count, const audit_record *rec 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) +static void audit_one_rom(core_options *options, const rom_entry *rom, const char *regiontag, const game_driver *gamedrv, const char *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->exphashes.from_internal_string(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]; + bool has_crc = record->exphashes.crc(crc); /* find the file and checksum it, getting the file length along the way */ for (drv = gamedrv; drv != NULL; drv = driver_get_clone(drv)) @@ -351,7 +348,7 @@ static void audit_one_rom(core_options *options, const rom_entry *rom, const cha filerr = file.open(drv->name, PATH_SEPARATOR, ROM_GETNAME(rom)); if (filerr == FILERR_NONE) { - hash_data_copy(record->hash, file.hash_string(validation)); + record->hashes = file.hashes(validation); record->length = (UINT32)file.size(); break; } @@ -370,7 +367,7 @@ static void audit_one_rom(core_options *options, const rom_entry *rom, const cha filerr = file.open(regiontag, PATH_SEPARATOR, ROM_GETNAME(rom)); if (filerr == FILERR_NONE) { - hash_data_copy(record->hash, file.hash_string(validation)); + record->hashes = file.hashes(validation); record->length = (UINT32)file.size(); } } @@ -381,7 +378,7 @@ static void audit_one_rom(core_options *options, const rom_entry *rom, const cha const game_driver *parent; /* no good dump */ - if (hash_data_has_info(record->exphash, HASH_INFO_NO_DUMP)) + if (record->exphashes.flag(hash_collection::FLAG_NO_DUMP)) set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND_NODUMP); /* optional ROM */ @@ -389,7 +386,7 @@ static void audit_one_rom(core_options *options, const rom_entry *rom, const cha 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)) + else if (rom_used_by_parent(gamedrv, record->exphashes, &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 */ @@ -405,15 +402,15 @@ static void audit_one_rom(core_options *options, const rom_entry *rom, const cha 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)) + else if (record->exphashes.flag(hash_collection::FLAG_NO_DUMP)) set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_FOUND_NODUMP); /* incorrect hash */ - else if (!hash_data_is_equal(record->exphash, record->hash, 0)) + else if (record->exphashes != record->hashes) 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)) + else if (record->exphashes.flag(hash_collection::FLAG_BAD_DUMP)) set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD_NEEDS_REDUMP); /* just plain old good */ @@ -427,7 +424,7 @@ static void audit_one_rom(core_options *options, const rom_entry *rom, const cha 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) +static void audit_one_disk(core_options *options, const rom_entry *rom, const game_driver *gamedrv, const char *validation, audit_record *record) { emu_file *source_file; chd_file *source; @@ -436,7 +433,7 @@ static void audit_one_disk(core_options *options, const rom_entry *rom, const ga /* fill in the record basics */ record->type = AUDIT_FILE_DISK; record->name = ROM_GETNAME(rom); - record->exphash = ROM_GETHASHDATA(rom); + record->exphashes.from_internal_string(ROM_GETHASHDATA(rom)); /* open the disk */ err = open_disk_image(*options, gamedrv, rom, &source_file, &source, NULL); @@ -449,7 +446,7 @@ static void audit_one_disk(core_options *options, const rom_entry *rom, const ga 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)) + else if (record->exphashes.flag(hash_collection::FLAG_NO_DUMP)) set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND_NODUMP); /* not found but optional */ @@ -464,25 +461,25 @@ static void audit_one_disk(core_options *options, const rom_entry *rom, const ga /* if we succeeded, validate it */ else { - static const UINT8 nullhash[HASH_BUF_SIZE] = { 0 }; + static const UINT8 nullhash[20] = { 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); + record->hashes.add_from_buffer(hash_collection::HASH_MD5, header.md5, sizeof(header.md5)); if (memcmp(nullhash, header.sha1, sizeof(header.sha1)) != 0) - hash_data_insert_binary_checksum(record->hash, HASH_SHA1, header.sha1); + record->hashes.add_from_buffer(hash_collection::HASH_SHA1, header.sha1, sizeof(header.sha1)); /* found but needs a dump */ - if (hash_data_has_info(record->exphash, HASH_INFO_NO_DUMP)) + if (record->exphashes.flag(hash_collection::FLAG_NO_DUMP)) set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_FOUND_NODUMP); /* incorrect hash */ - else if (!hash_data_is_equal(record->exphash, record->hash, 0)) + else if (record->exphashes != record->hashes) 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)) + else if (record->exphashes.flag(hash_collection::FLAG_BAD_DUMP)) set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD_NEEDS_REDUMP); /* just plain good */ @@ -500,9 +497,8 @@ static void audit_one_disk(core_options *options, const rom_entry *rom, const ga 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) +static int rom_used_by_parent(const game_driver *gamedrv, const hash_collection &romhashes, const game_driver **parent) { - const char *hash = ROM_GETHASHDATA(romentry); const game_driver *drv; /* iterate up the parent chain */ @@ -516,7 +512,7 @@ static int rom_used_by_parent(const game_driver *gamedrv, const rom_entry *romen for (const rom_source *source = rom_first_source(config); source != NULL; source = rom_next_source(*source)) for (region = rom_first_region(*source); 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 (hash_collection(ROM_GETHASHDATA(rom)) == romhashes) { if (parent != NULL) *parent = drv; diff --git a/src/emu/audit.h b/src/emu/audit.h index 10639f1dcc2..098737513f0 100644 --- a/src/emu/audit.h +++ b/src/emu/audit.h @@ -23,8 +23,8 @@ ***************************************************************************/ /* hashes to use for validation */ -#define AUDIT_VALIDATE_FAST (HASH_CRC) -#define AUDIT_VALIDATE_FULL (HASH_CRC | HASH_SHA) +#define AUDIT_VALIDATE_FAST "R" /* CRC only */ +#define AUDIT_VALIDATE_FULL "RS" /* CRC + SHA1 */ /* return values from audit_verify_roms and audit_verify_samples */ enum @@ -83,8 +83,8 @@ struct _audit_record const char * name; /* name of item */ UINT32 explength; /* expected length of item */ UINT32 length; /* actual length of item */ - const char * exphash; /* expected hash data */ - char hash[HASH_BUF_SIZE]; /* actual hash information */ + hash_collection exphashes; /* expected hash data */ + hash_collection hashes; /* actual hash information */ }; @@ -93,7 +93,7 @@ struct _audit_record FUNCTION PROTOTYPES ***************************************************************************/ -int audit_images(core_options *options, const game_driver *gamedrv, UINT32 validation, audit_record **audit); +int audit_images(core_options *options, const game_driver *gamedrv, const char *validation, audit_record **audit); int audit_samples(core_options *options, const game_driver *gamedrv, audit_record **audit); int audit_summary(const game_driver *gamedrv, int count, const audit_record *records, int output); diff --git a/src/emu/clifront.c b/src/emu/clifront.c index 60e8c53b27d..b6be267a958 100644 --- a/src/emu/clifront.c +++ b/src/emu/clifront.c @@ -61,7 +61,7 @@ static int info_listsoftware(core_options *options, const char *gamename); static void romident(core_options *options, const char *filename, romident_status *status); static void identify_file(core_options *options, const char *name, romident_status *status); static void identify_data(core_options *options, const char *name, const UINT8 *data, int length, romident_status *status); -static void match_roms(core_options *options, const char *hash, int length, int *found); +static void match_roms(core_options *options, const hash_collection &hashes, int length, int *found); static void display_suggestions(const char *gamename); @@ -535,11 +535,10 @@ int cli_info_listcrc(core_options *options, const char *gamename) for (region = rom_first_region(*source); region; region = rom_next_region(region)) for (rom = rom_first_file(region); rom; rom = rom_next_file(rom)) { - char hashbuf[HASH_BUF_SIZE]; - /* if we have a CRC, display it */ - if (hash_data_extract_printable_checksum(ROM_GETHASHDATA(rom), HASH_CRC, hashbuf)) - mame_printf_info("%s %-12s %s\n", hashbuf, ROM_GETNAME(rom), drivers[drvindex]->description); + UINT32 crc; + if (hash_collection(ROM_GETHASHDATA(rom)).crc(crc)) + mame_printf_info("%08x %-12s %s\n", crc, ROM_GETNAME(rom), drivers[drvindex]->description); } count++; @@ -558,14 +557,13 @@ int cli_info_listcrc(core_options *options, const char *gamename) int cli_info_listroms(core_options *options, const char *gamename) { int drvindex, count = 0; + astring tempstr; /* iterate over drivers */ for (drvindex = 0; drivers[drvindex] != NULL; drvindex++) if (mame_strwildcmp(gamename, drivers[drvindex]->name) == 0) { machine_config config(*drivers[drvindex]); - const rom_entry *region, *rom; - const rom_source *source; /* print the header */ if (count > 0) @@ -574,20 +572,17 @@ int cli_info_listroms(core_options *options, const char *gamename) "Name Size Checksum\n", drivers[drvindex]->name); /* iterate over sources, regions and then ROMs within the region */ - for (source = rom_first_source(config); source != NULL; source = rom_next_source(*source)) - for (region = rom_first_region(*source); region != NULL; region = rom_next_region(region)) - for (rom = rom_first_file(region); rom != NULL; rom = rom_next_file(rom)) + for (const rom_source *source = rom_first_source(config); source != NULL; source = rom_next_source(*source)) + for (const rom_entry *region = rom_first_region(*source); region != NULL; region = rom_next_region(region)) + for (const rom_entry *rom = rom_first_file(region); rom != NULL; rom = rom_next_file(rom)) { - const char *name = ROM_GETNAME(rom); - const char *hash = ROM_GETHASHDATA(rom); - char hashbuf[HASH_BUF_SIZE]; - int length = -1; - /* accumulate the total length of all chunks */ + int length = -1; if (ROMREGION_ISROMDATA(region)) length = rom_file_size(rom); /* start with the name */ + const char *name = ROM_GETNAME(rom); mame_printf_info("%-12s ", name); /* output the length next */ @@ -597,13 +592,12 @@ int cli_info_listroms(core_options *options, const char *gamename) mame_printf_info(" "); /* output the hash data */ - if (!hash_data_has_info(hash, HASH_INFO_NO_DUMP)) + hash_collection hashes(ROM_GETHASHDATA(rom)); + if (!hashes.flag(hash_collection::FLAG_NO_DUMP)) { - if (hash_data_has_info(hash, HASH_INFO_BAD_DUMP)) + if (hashes.flag(hash_collection::FLAG_BAD_DUMP)) mame_printf_info(" BAD"); - - hash_data_print(hash, 0, hashbuf); - mame_printf_info(" %s", hashbuf); + mame_printf_info(" %s", hashes.macro_string(tempstr)); } else mame_printf_info(" NO GOOD DUMP KNOWN"); @@ -975,23 +969,20 @@ static int info_listsoftware(core_options *options, const char *gamename) fprintf( out, "\t\t\t\t\tnext()) + fprintf(out, " %s=\"%s\"", hash->name(), hash->string(tempstr)); } if (!is_disk) fprintf( out, " offset=\"0x%x\"", ROM_GETOFFSET(rom) ); - if ( hash_data_has_info(ROM_GETHASHDATA(rom), HASH_INFO_BAD_DUMP) ) + if ( hashes.flag(hash_collection::FLAG_BAD_DUMP) ) fprintf( out, " status=\"baddump\"" ); - if ( hash_data_has_info(ROM_GETHASHDATA(rom), HASH_INFO_NO_DUMP) ) + if ( hashes.flag(hash_collection::FLAG_NO_DUMP) ) fprintf( out, " status=\"nodump\"" ); if (is_disk) @@ -1081,7 +1072,7 @@ static int info_listsoftware(core_options *options, const char *gamename) softlist_match_roms - scan for a matching software ROM by hash -------------------------------------------------*/ -static void softlist_match_roms(core_options *options, const char *hash, int length, int *found) +static void softlist_match_roms(core_options *options, const hash_collection &hashes, int length, int *found) { int drvindex; @@ -1108,9 +1099,10 @@ static void softlist_match_roms(core_options *options, const char *hash, int len { for ( const rom_entry *rom = rom_first_file(region); rom != NULL; rom = rom_next_file(rom) ) { - if ( hash_data_is_equal(hash, ROM_GETHASHDATA(rom), 0) ) + hash_collection romhashes(ROM_GETHASHDATA(rom)); + if ( hashes == romhashes ) { - int baddump = hash_data_has_info(ROM_GETHASHDATA(rom), HASH_INFO_BAD_DUMP); + bool baddump = romhashes.flag(hash_collection::FLAG_BAD_DUMP); /* output information about the match */ if (*found != 0) @@ -1416,20 +1408,18 @@ static void identify_file(core_options *options, const char *name, romident_stat } else { - static const UINT8 nullhash[HASH_BUF_SIZE] = { 0 }; - char hash[HASH_BUF_SIZE]; /* actual hash information */ - - hash_data_clear(hash); + static const UINT8 nullhash[20] = { 0 }; + hash_collection hashes; /* 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(hash, HASH_MD5, header.md5); + hashes.add_from_buffer(hash_collection::HASH_MD5, header.md5, sizeof(header.md5)); if (memcmp(nullhash, header.sha1, sizeof(header.sha1)) != 0) - hash_data_insert_binary_checksum(hash, HASH_SHA1, header.sha1); + hashes.add_from_buffer(hash_collection::HASH_SHA1, header.sha1, sizeof(header.sha1)); length = header.logicalbytes; - match_roms(options, hash, length, &found); + match_roms(options, hashes, length, &found); if (found == 0) { @@ -1475,7 +1465,6 @@ static void identify_file(core_options *options, const char *name, romident_stat static void identify_data(core_options *options, const char *name, const UINT8 *data, int length, romident_status *status) { - char hash[HASH_BUF_SIZE]; UINT8 *tempjed = NULL; astring basename; int found = 0; @@ -1496,8 +1485,8 @@ static void identify_data(core_options *options, const char *name, const UINT8 * } /* compute the hash of the data */ - hash_data_clear(hash); - hash_compute(hash, data, length, HASH_SHA1 | HASH_CRC); + hash_collection hashes; + hashes.compute(data, length, hash_collection::HASH_TYPES_CRC_SHA1); /* output the name */ status->total++; @@ -1505,7 +1494,7 @@ static void identify_data(core_options *options, const char *name, const UINT8 * mame_printf_info("%-20s", basename.cstr()); /* see if we can find a match in the ROMs */ - match_roms(options, hash, length, &found); + match_roms(options, hashes, length, &found); /* if we didn't find it, try to guess what it might be */ if (found == 0) @@ -1536,7 +1525,7 @@ static void identify_data(core_options *options, const char *name, const UINT8 * match_roms - scan for a matching ROM by hash -------------------------------------------------*/ -static void match_roms(core_options *options, const char *hash, int length, int *found) +static void match_roms(core_options *options, const hash_collection &hashes, int length, int *found) { int drvindex; @@ -1551,9 +1540,11 @@ static void match_roms(core_options *options, const char *hash, int length, int for (source = rom_first_source(config); source != NULL; source = rom_next_source(*source)) for (region = rom_first_region(*source); region; region = rom_next_region(region)) for (rom = rom_first_file(region); rom; rom = rom_next_file(rom)) - if (hash_data_is_equal(hash, ROM_GETHASHDATA(rom), 0)) + { + hash_collection romhashes(ROM_GETHASHDATA(rom)); + if (hashes == romhashes) { - int baddump = hash_data_has_info(ROM_GETHASHDATA(rom), HASH_INFO_BAD_DUMP); + bool baddump = romhashes.flag(hash_collection::FLAG_NO_DUMP); /* output information about the match */ if (*found != 0) @@ -1561,7 +1552,8 @@ static void match_roms(core_options *options, const char *hash, int length, int mame_printf_info("= %s%-20s %-10s %s\n", baddump ? "(BAD) " : "", ROM_GETNAME(rom), drivers[drvindex]->name, drivers[drvindex]->description); (*found)++; } + } } - softlist_match_roms( options, hash, length, found ); + softlist_match_roms( options, hashes, length, found ); } diff --git a/src/emu/devimage.c b/src/emu/devimage.c index 7161849506f..4dcfe535645 100644 --- a/src/emu/devimage.c +++ b/src/emu/devimage.c @@ -313,13 +313,10 @@ bool legacy_image_device_base::load_software(char *swlist, char *swname, rom_ent /* handle files */ if (ROMENTRY_ISFILE(romp)) { - UINT32 crc = 0; - UINT8 crcbytes[4]; file_error filerr = FILERR_NOT_FOUND; - bool has_crc = hash_data_extract_binary_checksum(ROM_GETHASHDATA(romp), HASH_CRC, crcbytes); - if (has_crc) - crc = (crcbytes[0] << 24) | (crcbytes[1] << 16) | (crcbytes[2] << 8) | crcbytes[3]; + UINT32 crc = 0; + bool has_crc = hash_collection(ROM_GETHASHDATA(romp)).crc(crc); // attempt reading up the chain through the parents and create a locationtag astring in the format // " swlist % clonename % parentname " diff --git a/src/emu/diimage.c b/src/emu/diimage.c index af385d482b1..8658c60f9b7 100644 --- a/src/emu/diimage.c +++ b/src/emu/diimage.c @@ -142,16 +142,16 @@ iodevice_t device_config_image_interface::device_typeid(const char *name) using this device's partial hash if appropriate -------------------------------------------------*/ -void device_config_image_interface::device_compute_hash(char *dest, const void *data, size_t length, unsigned int functions) const +void device_config_image_interface::device_compute_hash(hash_collection &hashes, const void *data, size_t length, const char *types) const { /* retrieve the partial hash func */ device_image_partialhash_func partialhash = get_partial_hash(); /* compute the hash */ if (partialhash) - partialhash(dest, (const unsigned char*)data, length, functions); + partialhash(hashes, (const unsigned char*)data, length, types); else - hash_compute(dest, (const unsigned char*)data, length, functions); + hashes.compute(reinterpret_cast(data), length, types); } @@ -505,26 +505,6 @@ void device_image_interface::image_freeptr(void *ptr) to be loaded ****************************************************************************/ -/*------------------------------------------------- - hash_data_extract_crc32 - extract crc32 value - from hash string --------------------------------------------------*/ - -static UINT32 hash_data_extract_crc32(const char *d) -{ - UINT32 crc = 0; - UINT8 crc_bytes[4]; - - if (hash_data_extract_binary_checksum(d, HASH_CRC, crc_bytes) == 1) - { - crc = (((UINT32) crc_bytes[0]) << 24) - | (((UINT32) crc_bytes[1]) << 16) - | (((UINT32) crc_bytes[2]) << 8) - | (((UINT32) crc_bytes[3]) << 0); - } - return crc; -} - int device_image_interface::read_hash_config(const char *sysname) { hash_file *hashfile = NULL; @@ -536,7 +516,7 @@ int device_image_interface::read_hash_config(const char *sysname) goto done; /* look up this entry in the hash file */ - info = hashfile_lookup(hashfile, m_hash.cstr()); + info = hashfile_lookup(hashfile, m_hash); if (!info) goto done; @@ -557,13 +537,13 @@ done: -void device_image_interface::run_hash(void (*partialhash)(char *, const unsigned char *, unsigned long, unsigned int), - char *dest, unsigned int hash_functions) +void device_image_interface::run_hash(void (*partialhash)(hash_collection &, const unsigned char *, unsigned long, const char *), + hash_collection &hashes, const char *types) { UINT32 size; UINT8 *buf = NULL; - *dest = '\0'; + hashes.reset(); size = (UINT32) length(); buf = (UINT8*)malloc(size); @@ -574,9 +554,9 @@ void device_image_interface::run_hash(void (*partialhash)(char *, const unsigned fread(buf, size); if (partialhash) - partialhash(dest, buf, size, hash_functions); + partialhash(hashes, buf, size, types); else - hash_compute(dest, buf, size, hash_functions); + hashes.compute(buf, size, types); /* cleanup */ free(buf); @@ -588,12 +568,11 @@ void device_image_interface::run_hash(void (*partialhash)(char *, const unsigned void device_image_interface::image_checkhash() { const game_driver *drv; - char hash_string[HASH_BUF_SIZE]; device_image_partialhash_func partialhash; int rc; /* only calculate CRC if it hasn't been calculated, and the open_mode is read only */ - if (!m_hash && !m_writeable && !m_created) + if (m_hash.first() == NULL && !m_writeable && !m_created) { /* do not cause a linear read of 600 megs please */ /* TODO: use SHA/MD5 in the CHD header as the hash */ @@ -607,9 +586,7 @@ void device_image_interface::image_checkhash() /* retrieve the partial hash func */ partialhash = get_partial_hash(); - run_hash(partialhash, hash_string, HASH_CRC | HASH_MD5 | HASH_SHA1); - - m_hash = hash_string; + run_hash(partialhash, m_hash, hash_collection::HASH_TYPES_ALL); /* now read the hash file */ drv = device().machine->gamedrv; @@ -628,8 +605,7 @@ UINT32 device_image_interface::crc() UINT32 crc = 0; image_checkhash(); - if (m_hash) - crc = hash_data_extract_crc32(m_hash.cstr()); + m_hash.crc(crc); return crc; } diff --git a/src/emu/diimage.h b/src/emu/diimage.h index 66cce47ced9..99d1f221a78 100644 --- a/src/emu/diimage.h +++ b/src/emu/diimage.h @@ -113,7 +113,7 @@ typedef int (*device_image_load_func)(device_image_interface &image); typedef int (*device_image_create_func)(device_image_interface &image, int format_type, option_resolution *format_options); typedef void (*device_image_unload_func)(device_image_interface &image); typedef void (*device_image_display_func)(device_image_interface &image); -typedef void (*device_image_partialhash_func)(char *, const unsigned char *, unsigned long, unsigned int); +typedef void (*device_image_partialhash_func)(hash_collection &, const unsigned char *, unsigned long, const char *); typedef void (*device_image_get_devices_func)(device_image_interface &device); typedef bool (*device_image_softlist_load_func)(device_image_interface &image, char *swlist, char *swname, rom_entry *start_entry); @@ -178,7 +178,7 @@ public: static iodevice_t device_typeid(const char *name); virtual device_image_partialhash_func get_partial_hash() const = 0; - virtual void device_compute_hash(char *dest, const void *data, size_t length, unsigned int functions) const; + virtual void device_compute_hash(hash_collection &hashes, const void *data, size_t length, const char *types) const; protected: static const image_device_type_info *find_device_type(iodevice_t type); static const image_device_type_info m_device_info_array[]; @@ -282,7 +282,7 @@ protected: bool try_change_working_directory(const char *subdir); int read_hash_config(const char *sysname); - void run_hash(void (*partialhash)(char *, const unsigned char *, unsigned long, unsigned int), char *dest, unsigned int hash_functions); + void run_hash(void (*partialhash)(hash_collection &, const unsigned char *, unsigned long, const char *), hash_collection &hashes, const char *types); void image_checkhash(); // derived class overrides @@ -328,7 +328,7 @@ protected: object_pool *m_mempool; - astring m_hash; + hash_collection m_hash; }; diff --git a/src/emu/emu.h b/src/emu/emu.h index 9cb34fbe1cf..81fa4ce94da 100644 --- a/src/emu/emu.h +++ b/src/emu/emu.h @@ -62,6 +62,7 @@ // emulator-specific utilities #include "attotime.h" +#include "hash.h" #include "fileio.h" // remove me once NVRAM is implemented as device #include "tokenize.h" #include "delegate.h" diff --git a/src/emu/fileio.c b/src/emu/fileio.c index 975346f2c90..3b7750787ae 100644 --- a/src/emu/fileio.c +++ b/src/emu/fileio.c @@ -206,27 +206,39 @@ emu_file::operator core_file *() // hash - returns the hash for a file //------------------------------------------------- -const char *emu_file::hash_string(UINT32 functions) +hash_collection &emu_file::hashes(const char *types) { - // if we already have the functions we need, just return - UINT32 wehave = hash_data_used_functions(m_hash); - if ((wehave & functions) == functions) - return m_hash; - - // load the ZIP file now if we haven't yet + // determine which hashes we need + astring needed; + for (const char *scan = types; *scan != 0; scan++) + if (m_hashes.hash(*scan) == NULL) + needed.cat(*scan); + + // if we need nothing, skip it + if (!needed) + return m_hashes; + + // load the ZIP file if needed if (m_zipfile != NULL && load_zipped_file() != FILERR_NONE) - return m_hash; + return m_hashes; if (m_file == NULL) - return m_hash; + return m_hashes; + + // if we have ZIP data, just hash that directly + if (m_zipdata != NULL) + { + m_hashes.compute(m_zipdata, m_ziplength, needed); + return m_hashes; + } // read the data if we can const UINT8 *filedata = (const UINT8 *)core_fbuffer(m_file); if (filedata == NULL) - return m_hash; + return m_hashes; // compute the hash - hash_compute(m_hash, filedata, core_fsize(m_file), wehave | functions); - return m_hash; + m_hashes.compute(filedata, core_fsize(m_file), needed); + return m_hashes; } @@ -377,6 +389,10 @@ void emu_file::close() if (m_remove_on_close) osd_rmfile(m_fullpath); m_remove_on_close = false; + + // reset our hashes and path as well + m_hashes.reset(); + m_fullpath.reset(); } @@ -652,14 +668,8 @@ file_error emu_file::attempt_zipped() m_ziplength = header->uncompressed_length; // build a hash with just the CRC - hash_data_clear(m_hash); - UINT8 crcs[4]; - crcs[0] = header->crc >> 24; - crcs[1] = header->crc >> 16; - crcs[2] = header->crc >> 8; - crcs[3] = header->crc >> 0; - hash_data_insert_binary_checksum(m_hash, HASH_CRC, crcs); - + m_hashes.reset(); + m_hashes.add_crc(header->crc); return (m_openflags & OPEN_FLAG_NO_PRELOAD) ? FILERR_NONE : load_zipped_file(); } diff --git a/src/emu/fileio.h b/src/emu/fileio.h index b4a441c4b39..af19a57b56c 100644 --- a/src/emu/fileio.h +++ b/src/emu/fileio.h @@ -151,7 +151,7 @@ public: const char *filename() const { return m_filename; } const char *fullpath() const { return m_fullpath; } UINT32 openflags() const { return m_openflags; } - const char *hash_string(UINT32 functions); + hash_collection &hashes(const char *types); // setters void remove_on_close() { m_remove_on_close = true; } @@ -203,7 +203,7 @@ private: path_iterator m_iterator; // iterator for paths UINT32 m_crc; // iterator for paths UINT32 m_openflags; // flags we used for the open - char m_hash[HASH_BUF_SIZE]; // hash data for the file + hash_collection m_hashes; // collection of hashes zip_file * m_zipfile; // ZIP file pointer UINT8 * m_zipdata; // ZIP file data UINT64 m_ziplength; // ZIP file length diff --git a/src/emu/hash.c b/src/emu/hash.c index ae2079acf90..ad2322cf1c2 100644 --- a/src/emu/hash.c +++ b/src/emu/hash.c @@ -1,712 +1,796 @@ -/********************************************************************* +/*************************************************************************** hash.c Function to handle hash functions (checksums) - Copyright Nicola Salmoria and the MAME Team. - Visit http://mamedev.org for licensing and usage restrictions. + Based on original idea by Farfetch'd - Started by Farfetch'd +**************************************************************************** -*********************************************************************/ + Copyright Aaron Giles + All rights reserved. -/* - * Changelog: - * - * 20030314: Farfetch'd - * First release - * - */ + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: -/* - * DONE: - * - * hash.c/h: New files, implement the new hashing engine with flexible - * support for more functions (for now, CRC, SHA1 and MD5). - * - * common.h: transparently support the new RomModule structure through - * ROM_* macros, so that old the legacy code still work - * - * common.c: updated ROM loading engine to support the new hash engine, - * using it to verify ROM integrity at load-time. Updated printromlist() - * (-listroms) to dump all the available checksums, and if a ROM is - * known to be bad or not. - * - * info.c: -listinfo now supports any hashing function correctly - * (both text and XML mode). Notice that XML header should be - * rewritten to automatically define the new tags when new - * functions are added, but I couldn't be bothered for now. - * It also displays informations about baddump/nodump - * - * audit.c/h: Updated audit engine (-verifyroms) to use the new - * hash functions. - * - * fileio.c/h: Updated file engine to use the new hash functions. - * It is now possible to load by any specified checksum (in case - * later we support other archivers with SHA1 signatures or - * equivalent). If the file is open with flag VERIFY_ONLY and - * the file is within an archive (zip), only the checksums - * available in the archive header are used. - * - * windows/fronthlp.c: Updated -identrom to the new hash engine, now - * support any hash function available. - * Added -listsha1 and -listmd5. It would be possible to add - * also a -listbad now, to list bad dumps (ROMS we need a - * redump for) - * Updated -listdupcrc to check for all the available checksums. - * The output is also a bit more useful and readable, even if it - * is still not optimal. - * Update -listwrongmerge to check for all the available checksums. - * - * windows/fileio.c: Removed check for FILE_TYPE_NOCRC (does not exist - * anymore). - * - * - * - * Technical details: - * - * Checksum informations are now stored inside a string. They are - * stored in "printable hex format", which means that they use - * more memory than before (since a CRC needs 8 characters to - * be printed, instead of 4 bytes of raw information). In the - * driver, they are defined with handy macros which rely on - * automatic string pasting. - * - * Additional flags can also be stored in the string: for now we - * support NO_DUMP and BAD_DUMP, which replace, respectively, - * a CRC of 0 and a bit-inverted CRC. - * - * All the code that handles hash data is in hash.c. The rest of - * the core treats the data as an 'opaque type', so that the - * pointers are just passed along through functions but no - * operation is performed on the data outside hash.c. This - * is important in case we want to change the string - * representation later in the future. - * - * When loading a ROM, MAME will calculate and compare the - * checksum using any function for which the driver has declared - * an expected checksum. This happens because it would be useless - * to calculate a checksum if we cannot verify its correctness. - * For developers, it also means that MAME will not compute the - * SHA1 for you unless you specify a bogus one in the driver - * (like SHA1(0)). - * - * When verifying a ROM, MAME will use only the checksums available - * in the archive header (if zip, CRC). This is by design because - * -verifyroms has always been very fast. It is feasible to add - * a -fullverifyroms at a later moment, which will decompress the - * files and compute every checksum that has been declared in the - * driver. - * - * I have also prepared a little tool (SHA1Merger) which takes care - * of the following tasks: - * - * - Given an existing driver in old syntax (0.66 compatible), it will - * convert all the existing ROM_LOAD entries in the new format, and - * it will automatically compute and add SHA1 checksum for you if - * it can find the romset. - * - * - Given a romset (ZIP file), it will prepare a ROM definition - * skeleton for a driver, containing already rom names, lengths, and - * checksums (both CRC and SHA1). - * - * The tool is available on www.mame.net as platform-independent source code - * (in Python), or win32 standalone executable. - * - */ + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ #include "emu.h" -#include "hash.h" +#include "zlib.h" #include "md5.h" #include "sha1.h" - #include -#include -#define ASSERT(x) -#ifndef TRUE -#define TRUE 1 -#endif +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** -#ifndef FALSE -#define FALSE 0 -#endif +// ======================> hash_crc -struct _hash_function_desc +// CRC-32 hash implementation +class hash_crc : public hash_base { - const char* name; // human-readable name - char code; // single-char code used within the hash string - unsigned int size; // checksum size in bytes +public: + // construction/destruction + hash_crc(); - // Functions used to calculate the hash of a memory block - void (*calculate_begin)(void); - void (*calculate_buffer)(const void* mem, unsigned long len); - void (*calculate_end)(UINT8* bin_chksum); + // operators + operator UINT32() const { return (m_buffer[0] << 24) | (m_buffer[1] << 16) | (m_buffer[2] << 8) | m_buffer[3]; } -}; -typedef struct _hash_function_desc hash_function_desc; + // creation + virtual void begin(); + virtual void buffer(const UINT8 *data, UINT32 length); + virtual void end(); -static void h_crc_begin(void); -static void h_crc_buffer(const void* mem, unsigned long len); -static void h_crc_end(UINT8* chksum); - -static void h_sha1_begin(void); -static void h_sha1_buffer(const void* mem, unsigned long len); -static void h_sha1_end(UINT8* chksum); - -static void h_md5_begin(void); -static void h_md5_buffer(const void* mem, unsigned long len); -static void h_md5_end(UINT8* chksum); - -static const hash_function_desc hash_descs[HASH_NUM_FUNCTIONS] = -{ - { - "crc", 'c', 4, - h_crc_begin, - h_crc_buffer, - h_crc_end - }, - - { - "sha1", 's', 20, - h_sha1_begin, - h_sha1_buffer, - h_sha1_end - }, - - { - "md5", 'm', 16, - h_md5_begin, - h_md5_buffer, - h_md5_end - }, +private: + // internal state + UINT8 m_buffer[4]; }; -static const char *const info_strings[] = + +// ======================> hash_md5 + +// MD5 hash implementation +class hash_md5 : public hash_base { - "$ND$", // No dump - "$BD$" // Bad dump +public: + // construction/destruction + hash_md5(); + + // creation + virtual void begin(); + virtual void buffer(const UINT8 *data, UINT32 length); + virtual void end(); + +private: + // internal state + UINT8 m_buffer[16]; + struct MD5Context m_context; }; -static const char binToStr[] = "0123456789abcdef"; +// ======================> hash_sha1 -static const hash_function_desc* hash_get_function_desc(unsigned int function) +// SHA1 hash implementation +class hash_sha1 : public hash_base { - unsigned int idx = 0; +public: + // construction/destruction + hash_sha1(); - // Calling with zero in here is mostly an internal error - ASSERT(function != 0); + // creation + virtual void begin(); + virtual void buffer(const UINT8 *data, UINT32 length); + virtual void end(); - // Compute the index of only one function - while (!(function & 1)) +private: + // internal state + UINT8 m_buffer[20]; + struct sha1_ctx m_context; +}; + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const char *hash_collection::HASH_TYPES_CRC = "R"; +const char *hash_collection::HASH_TYPES_CRC_SHA1 = "RS"; +const char *hash_collection::HASH_TYPES_ALL = "RSM"; + + + +//************************************************************************** +// HASH BASE +//************************************************************************** + +//------------------------------------------------- +// hash_base - constructor +//------------------------------------------------- + +hash_base::hash_base(char id, const char *name, UINT8 length, UINT8 *bufptr) + : m_next(NULL), + m_name(name), + m_in_progress(false), + m_parse_error(false), + m_id(id), + m_length(length), + m_bufptr(bufptr) +{ + memset(m_bufptr, 0, length); +} + + +//------------------------------------------------- +// fromhex - convert a character to a hex value +//------------------------------------------------- + +int hash_base::fromhex(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else + return -1; +} + + +//------------------------------------------------- +// from_buffer - copy a raw buffer into the +// current hash +//------------------------------------------------- + +bool hash_base::from_buffer(const UINT8 *buffer, int buflen) +{ + // fail if we're too small + if (buflen < m_length) + return false; + memcpy(m_bufptr, buffer, m_length); + return true; +} + + +//------------------------------------------------- +// string - output a string for the current hash +//------------------------------------------------- + +const char *hash_base::string(astring &buffer) +{ + buffer.reset(); + for (int index = 0; index < m_length; index++) + buffer.catprintf("%02x", m_bufptr[index]); + return buffer; +} + + +//------------------------------------------------- +// from_string - parse a string into the current +// hash +//------------------------------------------------- + +bool hash_base::from_string(const char *&string, int length) +{ + // fail if we're too short + if (length < 2 * m_length) + return false; + + // loop over bytes + m_parse_error = false; + for (int index = 0; index < m_length; index++) { - idx++; - function >>= 1; - } - - // Specify only one bit or die - ASSERT(function == 1); - - return &hash_descs[idx]; -} - -const char* hash_function_name(unsigned int function) -{ - const hash_function_desc* info = hash_get_function_desc(function); - - return info->name; -} - -int hash_data_has_checksum(const char* data, unsigned int function) -{ - const hash_function_desc* info = hash_get_function_desc(function); - char str[3]; - const char* res; - - str[0] = info->code; - str[1] = ':'; - str[2] = '\0'; - - // Check if the specified hash function is used within this data - res = strstr(data, str); - - if (!res) - return 0; - - // Return the offset within the string where the checksum begins - return (res - data + 2); -} - -static int hash_data_add_binary_checksum(char* d, unsigned int function, const UINT8* checksum) -{ - const hash_function_desc* desc = hash_get_function_desc(function); - char* start = d; - unsigned i; - - *d++ = desc->code; - *d++ = ':'; - - for (i=0;isize;i++) - { - UINT8 c = *checksum++; - - *d++ = binToStr[(c >> 4) & 0xF]; - *d++ = binToStr[(c >> 0) & 0xF]; - } - - *d++ = '#'; - - // Return the number of written bytes - return (d - start); -} - - -static int hash_compare_checksum(const char* chk1, const char* chk2, int length) -{ - char c1, c2; - - // The printable format is twice as longer - length *= 2; - - // This is basically a case-insensitive string compare - while (length--) - { - c1 = *chk1++; - c2 = *chk2++; - - if (tolower((UINT8)c1) != tolower((UINT8)c2)) - return 0; - if (!c1) - return 0; - } - - return 1; -} - - -// Compare two hashdata -int hash_data_is_equal(const char* d1, const char* d2, unsigned int functions) -{ - int i; - char incomplete = 0; - char ok = 0; - - // If no function is specified, it means we need to check for all - // of them - if (!functions) - functions = ~functions; - - for (i=1; i != (1<> 24; + m_buffer[1] = crc >> 16; + m_buffer[2] = crc >> 8; + m_buffer[3] = crc >> 0; +} + + +//------------------------------------------------- +// end - finish hash computation +//------------------------------------------------- + +void hash_crc::end() +{ + m_in_progress = false; +} + + + +//************************************************************************** +// HASH MD5 +//************************************************************************** + +//------------------------------------------------- +// hash_md5 - constructor +//------------------------------------------------- + +hash_md5::hash_md5() + : hash_base(hash_collection::HASH_MD5, "md5", sizeof(m_buffer), m_buffer) +{ +} + + +//------------------------------------------------- +// begin - initialize state for hash computation +//------------------------------------------------- + +void hash_md5::begin() +{ + m_in_progress = true; + MD5Init(&m_context); +} + + +//------------------------------------------------- +// buffer - hash a buffer's worth of data +//------------------------------------------------- + +void hash_md5::buffer(const UINT8 *data, UINT32 length) +{ + MD5Update(&m_context, (md5byte *)data, length); +} + + +//------------------------------------------------- +// end - finish hash computation +//------------------------------------------------- + +void hash_md5::end() +{ + MD5Final(m_buffer, &m_context); + m_in_progress = false; +} + + + +//************************************************************************** +// HASH SHA1 +//************************************************************************** + +//------------------------------------------------- +// hash_sha1 - constructor +//------------------------------------------------- + +hash_sha1::hash_sha1() + : hash_base(hash_collection::HASH_SHA1, "sha1", sizeof(m_buffer), m_buffer) +{ +} + + +//------------------------------------------------- +// begin - initialize state for hash computation +//------------------------------------------------- + +void hash_sha1::begin() +{ + m_in_progress = true; + sha1_init(&m_context); +} + + +//------------------------------------------------- +// buffer - hash a buffer's worth of data +//------------------------------------------------- + +void hash_sha1::buffer(const UINT8 *data, UINT32 length) +{ + sha1_update(&m_context, length, data); +} + + +//------------------------------------------------- +// end - finish hash computation +//------------------------------------------------- + +void hash_sha1::end() +{ + sha1_final(&m_context); + sha1_digest(&m_context, sizeof(m_buffer), m_buffer); + m_in_progress = false; +} + + + +//************************************************************************** +// HASH COLLECTION +//************************************************************************** + +//------------------------------------------------- +// hash_collection - constructor +//------------------------------------------------- + +hash_collection::hash_collection() +{ +} + + +hash_collection::hash_collection(const char *string) +{ + from_internal_string(string); +} + + +hash_collection::hash_collection(const hash_collection &src) +{ + copyfrom(src); +} + + +//------------------------------------------------- +// ~hash_collection - destructor +//------------------------------------------------- + +hash_collection::~hash_collection() +{ +} + + +//------------------------------------------------- +// operator= - assignment operator +//------------------------------------------------- + +hash_collection &hash_collection::operator=(const hash_collection &src) +{ + // ignore self-assignment + if (this != &src) + copyfrom(src); + return *this; +} + + +//------------------------------------------------- +// operator== - test for equality +//------------------------------------------------- + +bool hash_collection::operator==(const hash_collection &rhs) const +{ + // look for a mismatch in any hash; do not fail if one is missing + for (hash_base *hash = m_hashlist.first(); hash != NULL; hash = hash->next()) + { + hash_base *rhs_hash = rhs.hash(hash->id()); + if (hash != NULL && rhs_hash != NULL && *hash != *rhs_hash) + return false; + } + + // if all shared hashes match, return true + return true; +} + + +//------------------------------------------------- +// hash - return a hash of the given type +//------------------------------------------------- + +hash_base *hash_collection::hash(char type) const +{ + // look for a mismatch in any hash; do not fail if one is missing + for (hash_base *hash = m_hashlist.first(); hash != NULL; hash = hash->next()) + if (hash->id() == type) + return hash; + return NULL; +} + + +//------------------------------------------------- +// hash_types - return a list of hash types as +// a string +//------------------------------------------------- + +const char *hash_collection::hash_types(astring &buffer) const +{ + buffer.reset(); + for (hash_base *hash = m_hashlist.first(); hash != NULL; hash = hash->next()) + buffer.cat(hash->id()); + return buffer; +} + + +//------------------------------------------------- +// reset - reset the hash collection to an empty +// set of hashes and flags +//------------------------------------------------- + +bool hash_collection::parse_errors() const +{ + for (hash_base *hash = m_hashlist.first(); hash != NULL; hash = hash->next()) + if (hash->parse_error()) + return true; + return false; +} + + +//------------------------------------------------- +// reset - reset the hash collection to an empty +// set of hashes and flags +//------------------------------------------------- + +void hash_collection::reset() +{ + m_hashlist.reset(); + m_flags.reset(); +} + + +//------------------------------------------------- +// add_from_buffer - add a new hash, importing +// from a buffer +//------------------------------------------------- + +hash_base *hash_collection::add_from_buffer(char type, const UINT8 *buffer, int bufflen) +{ + // nuke any existing hash with the same ID + hash_base *existing = hash(type); + if (existing != NULL) + m_hashlist.remove(*existing); + + // first allocate by ID + hash_base *newhash = alloc_by_id(type); + if (newhash == NULL) + return NULL; + + // then import + if (!newhash->from_buffer(buffer, bufflen)) + { + global_free(newhash); + return NULL; + } + + // and append to our list + return &m_hashlist.append(*newhash); +} + + +//------------------------------------------------- +// add_from_string - add a new hash, importing +// from a string +//------------------------------------------------- + +hash_base *hash_collection::add_from_string(char type, const char *buffer, int length) +{ + // nuke any existing hash with the same ID + hash_base *existing = hash(type); + if (existing != NULL) + m_hashlist.remove(*existing); + + // first allocate by ID + hash_base *newhash = alloc_by_id(type); + if (newhash == NULL) + return NULL; + + // then import + if (!newhash->from_string(buffer, length)) + { + global_free(newhash); + return NULL; + } + + // and append to our list + return &m_hashlist.append(*newhash); +} + + +//------------------------------------------------- +// remove - remove a hash of the given type +//------------------------------------------------- + +bool hash_collection::remove(char type) +{ + // scan the list of hashes for a match + for (hash_base *hash = m_hashlist.first(); hash != NULL; hash = hash->next()) + if (hash->id() == type) + { + m_hashlist.remove(*hash); + return true; + } + + // didn't find it + return false; +} + + +//------------------------------------------------- +// crc - return the CRC hash if present +//------------------------------------------------- + +bool hash_collection::crc(UINT32 &result) +{ + // attempt to find the CRC hash; if we fail, return false + hash_base *crchash = hash(HASH_CRC); + if (crchash == NULL) + return false; + + // downcast to a hash_crc and convert to a UINT32 + result = *downcast(crchash); + return true; +} + + +//------------------------------------------------- +// add_crc - add a CRC hash +//------------------------------------------------- + +hash_base *hash_collection::add_crc(UINT32 crc) +{ + // expand to a buffer + UINT8 buffer[4]; + buffer[0] = crc >> 24; + buffer[1] = crc >> 16; + buffer[2] = crc >> 8; + buffer[3] = crc >> 0; + + // add it the standard way + return add_from_buffer(HASH_CRC, buffer, sizeof(buffer)); +} + + +//------------------------------------------------- +// internal_string - convert set of hashes and +// flags to a string in our internal compact +// format +//------------------------------------------------- + +const char *hash_collection::internal_string(astring &buffer) const +{ + astring temp; + + // output hashes first + buffer.reset(); + for (hash_base *hash = m_hashlist.first(); hash != NULL; hash = hash->next()) + buffer.cat(hash->id()).cat(hash->string(temp)); + + // append flags + buffer.cat(m_flags); + return buffer; +} + + +//------------------------------------------------- +// macro_string - convert set of hashes and +// flags to a string in the macroized format +//------------------------------------------------- + +const char *hash_collection::macro_string(astring &buffer) const +{ + astring temp; + + // output hashes first + buffer.reset(); + for (hash_base *hash = m_hashlist.first(); hash != NULL; hash = hash->next()) + { + buffer.cat(temp.cpy(hash->name()).toupper()); + buffer.cat("(").cat(hash->string(temp)).cat(") "); + } + + // append flags + if (flag(FLAG_NO_DUMP)) + buffer.cat("NO_DUMP "); + if (flag(FLAG_BAD_DUMP)) + buffer.cat("BAD_DUMP "); + return buffer.trimspace(); +} + + +//------------------------------------------------- +// from_internal_string - convert an internal +// compact string to set of hashes and flags +//------------------------------------------------- + +bool hash_collection::from_internal_string(const char *string) +{ + // start fresh + reset(); + + // determine the end of the string + const char *stringend = string + strlen(string); + const char *ptr = string; + + // loop until we hit it + bool errors = false; + while (ptr < stringend) + { + char c = *ptr++; + char lc = tolower(c); + + // non-hex alpha values specify a hash type + if (lc >= 'g' && lc <= 'z') + { + hash_base *hash = alloc_by_id(c); + if (hash != NULL) { - const hash_function_desc* info = hash_get_function_desc(i); - - if (!hash_compare_checksum(d1+offs1, d2+offs2, info->size)) - return 0; - - ok = 1; - } - // If the function was contained only in one, remember that our comparison - // is incomplete - else if (offs1 || offs2) - { - incomplete = 1; + if (!hash->from_string(ptr, stringend - ptr)) + errors = true; + m_hashlist.append(*hash); } + else + errors = true; } - - // If we could not compare any function, return error - if (!ok) - return 0; - - // Return success code - return (incomplete ? 2 : 1); -} - - -int hash_data_extract_printable_checksum(const char* data, unsigned int function, char* checksum) -{ - unsigned int i; - const hash_function_desc* info; - int offs; - - // Check if the hashdata contains the requested function - offs = hash_data_has_checksum(data, function); - - if (!offs) - return 0; - - // Move to the beginning of the checksum - data += offs; - - info = hash_get_function_desc(function); - - // Return the number of required bytes - if (!checksum) - return info->size*2+1; - - // If the terminator is not found at the right position, - // return a full-zero checksum and warn about it. This is mainly - // for developers putting checksums of '0' or '1' to ask MAME - // to compute the correct values for them. - if (data[info->size*2] != '#') - { - memset(checksum, '0', info->size*2); - checksum[info->size*2] = '\0'; - return 2; - } - - // If it contains invalid hexadecimal characters, - // treat the checksum as zero and return warning - for (i=0;isize*2;i++) - if (!(data[i]>='0' && data[i]<='9') && - !(data[i]>='a' && data[i]<='f') && - !(data[i]>='A' && data[i]<='F')) - { - memset(checksum, '0', info->size*2); - checksum[info->size*2] = '\0'; - return 2; - } - - // Copy the checksum (and make it lowercase) - for (i=0;isize*2;i++) - checksum[i] = tolower((UINT8)data[i]); - - checksum[info->size*2] = '\0'; - - return 1; -} - -static int hex_string_to_binary(unsigned char* binary, const char* data, int size) -{ - unsigned int i; - char c; - - for (i = 0; i < size * 2; i++) - { - c = tolower((UINT8)*data++); - - if (c >= '0' && c <= '9') - c -= '0'; - else if (c >= 'a' && c <= 'f') - c -= 'a' - 10; + + // hex values are ignored, though unexpected + else if ((lc >= '0' && lc <= '9') || (lc >= 'a' && lc <= 'f')) + errors = true; + + // anything else is a flag else - return 1; - - if (i % 2 == 0) - binary[i / 2] = c * 16; - else - binary[i / 2] += c; + m_flags.cat(c); } - return 0; + return !errors; } -int hash_data_extract_binary_checksum(const char* data, unsigned int function, unsigned char* checksum) + +//------------------------------------------------- +// begin - begin hashing +//------------------------------------------------- + +void hash_collection::begin(const char *types) { - const hash_function_desc* info; - int offs; - - // Check if the hashdata contains the requested function - offs = hash_data_has_checksum(data, function); - - if (!offs) - return 0; - - // Move to the beginning of the checksum - data += offs; - - info = hash_get_function_desc(function); - - // Return the number of required bytes - if (!checksum) - return info->size; - - // Clear the checksum array - memset(checksum, 0, info->size); - - // If the terminator is not found at the right position, - // return a full-zero checksum and warn about it. This is mainly - // for developers putting checksums of '0' or '1' to ask MAME - // to compute the correct values for them. - if (data[info->size*2] != '#') + // by default use all types + if (types == NULL) { - memset(checksum, '\0', info->size); - return 2; - } - - // Convert hex string into binary - if (hex_string_to_binary(checksum, data, info->size)) - { - // Invalid character: the checksum is treated as zero, - // and a warning is returned - memset(checksum, '\0', info->size); - return 2; - } - - return 1; -} - -int hash_data_has_info(const char* data, unsigned int info) -{ - char* res = (char*)strstr(data, info_strings[info]); - - if (!res) - return 0; - - return 1; -} - -void hash_data_copy(char* dst, const char* src) -{ - // Copying string is enough - strcpy(dst, src); -} - -void hash_data_clear(char* dst) -{ - // Clear the buffer - memset(dst, 0, HASH_BUF_SIZE); -} - -unsigned int hash_data_used_functions(const char* data) -{ - int i; - unsigned int res = 0; - - if (!data) - return 0; - - for (i=0;isize <= sizeof(binary_checksum)); - - if (hex_string_to_binary(binary_checksum, checksum, desc->size)) - return 2; - - return hash_data_insert_binary_checksum(d, function, binary_checksum); -} - -int hash_data_insert_binary_checksum(char* d, unsigned int function, const UINT8* checksum) -{ - int offset; - - offset = hash_data_has_checksum(d, function); - - if (!offset) - { - d += strlen(d); - d += hash_data_add_binary_checksum(d, function, checksum); - *d = '\0'; - - return 1; + m_hashlist.append(*alloc_by_id(HASH_CRC)).begin(); + m_hashlist.append(*alloc_by_id(HASH_MD5)).begin(); + m_hashlist.append(*alloc_by_id(HASH_SHA1)).begin(); } + + // otherwise, just allocate the ones that are specified else { - // Move to the start of the whole checksum signature, not only to the checksum - // itself - d += offset - 2; - - // Overwrite previous checksum with new one - hash_data_add_binary_checksum(d, function, checksum); - - return 2; + for (const char *scan = types; *scan != 0; scan++) + { + // nuke any existing hash of this type + hash_base *existing = hash(*scan); + if (existing != NULL) + m_hashlist.remove(*existing); + + // append a new one + m_hashlist.append(*alloc_by_id(*scan)).begin(); + } } } -void hash_compute(char* dst, const unsigned char* data, unsigned long length, unsigned int functions) + +//------------------------------------------------- +// buffer - add the given buffer to the hash +//------------------------------------------------- + +void hash_collection::buffer(const UINT8 *data, UINT32 length) { - int i; + // buffer each hash appropriately + for (hash_base *hash = m_hashlist.first(); hash != NULL; hash = hash->next()) + if (hash->in_progress()) + hash->buffer(data, length); +} - hash_data_clear(dst); - // Zero means use all the functions - if (functions == 0) - functions = ~functions; +//------------------------------------------------- +// end - stop hashing +//------------------------------------------------- - for (i=0;inext()) + if (hash->in_progress()) + hash->end(); +} + + +//------------------------------------------------- +// alloc_by_id - based on the ID character, +// allocate a new hash +//------------------------------------------------- + +hash_base *hash_collection::alloc_by_id(char id) +{ + switch (id) { - unsigned func = 1 << i; - - if (functions & func) - { - const hash_function_desc* desc = hash_get_function_desc(func); - UINT8 chksum[256]; - - desc->calculate_begin(); - desc->calculate_buffer(data, length); - desc->calculate_end(chksum); - - dst += hash_data_add_binary_checksum(dst, func, chksum); - } - } - - *dst = '\0'; -} - -void hash_data_print(const char* data, unsigned int functions, char* buffer) -{ - int i, j; - char first = 1; - - if (functions == 0) - functions = ~functions; - - buffer[0] = '\0'; - - for (i=0;i= sizeof(hash_descs) / sizeof(hash_descs[0])) - return FALSE; - - /* we have a proper code */ - len = hash_descs[i].size * 2; - hash += 2; - - for (i = 0; (hash[i] != '#') && (i < len); i++) - { - if (!isxdigit((UINT8)hash[i])) - return FALSE; - } - if (hash[i] != '#') - return FALSE; - - hash += i+1; - } - } - return TRUE; -} - - - -/********************************************************************* - Hash functions - Wrappers - *********************************************************************/ - -static UINT32 crc; - -static void h_crc_begin(void) -{ - crc = 0; -} - -static void h_crc_buffer(const void* mem, unsigned long len) -{ - crc = crc32(crc, (UINT8*)mem, len); -} - -static void h_crc_end(UINT8* bin_chksum) -{ - bin_chksum[0] = (UINT8)(crc >> 24); - bin_chksum[1] = (UINT8)(crc >> 16); - bin_chksum[2] = (UINT8)(crc >> 8); - bin_chksum[3] = (UINT8)(crc >> 0); -} - - -static struct sha1_ctx sha1ctx; - -static void h_sha1_begin(void) -{ - sha1_init(&sha1ctx); -} - -static void h_sha1_buffer(const void* mem, unsigned long len) -{ - sha1_update(&sha1ctx, len, (UINT8*)mem); -} - -static void h_sha1_end(UINT8* bin_chksum) -{ - sha1_final(&sha1ctx); - sha1_digest(&sha1ctx, 20, bin_chksum); -} - - -static struct MD5Context md5_ctx; - -static void h_md5_begin(void) -{ - MD5Init(&md5_ctx); -} - -static void h_md5_buffer(const void* mem, unsigned long len) -{ - MD5Update(&md5_ctx, (md5byte*)mem, len); -} - -static void h_md5_end(UINT8* bin_chksum) -{ - MD5Final(bin_chksum, &md5_ctx); + // copy flags directly + m_flags = src.m_flags; + + // rebuild the hashlist by copying from the source + m_hashlist.reset(); + for (hash_base *hash = src.first(); hash != NULL; hash = hash->next()) + add_from_buffer(hash->id(), hash->buffer(), hash->length()); } diff --git a/src/emu/hash.h b/src/emu/hash.h index 9f57d356a55..38b0633b217 100644 --- a/src/emu/hash.h +++ b/src/emu/hash.h @@ -1,79 +1,188 @@ -/********************************************************************* +/*************************************************************************** hash.h Function to handle hash functions (checksums) - Copyright Nicola Salmoria and the MAME Team. - Visit http://mamedev.org for licensing and usage restrictions. + Based on original idea by Farfetch'd -*********************************************************************/ +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ #pragma once #ifndef __HASH_H__ #define __HASH_H__ -#define HASH_INFO_NO_DUMP 0 -#define HASH_INFO_BAD_DUMP 1 -#define HASH_CRC (1 << 0) -#define HASH_SHA1 (1 << 1) -#define HASH_MD5 (1 << 2) +//************************************************************************** +// MACROS +//************************************************************************** -#define HASH_NUM_FUNCTIONS 3 +// use these to define compile-time internal-format hash strings +#define CRC(x) "R" #x +#define MD5(x) "M" #x +#define SHA1(x) "S" #x +#define NO_DUMP "!" +#define BAD_DUMP "^" -// Standard size of a hash data buffer, all the manipulated buffers -// must respect this size -#define HASH_BUF_SIZE 256 -// Get function name of the specified function -const char* hash_function_name(unsigned int function); -// Check if const char* contains the checksum for a specific function -int hash_data_has_checksum(const char* d, unsigned int function); +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** -// Extract the binary or printable checksum of a specific function from a hash data. If the checksum information -// is not available, the functions return 0. If the pointer to the output buffer is NULL, the function will -// return the minimum size of the output buffer required to store the informations. Otherwise, the buffer -// will be filled and the function will return 1 as success code. -int hash_data_extract_binary_checksum(const char* d, unsigned int function, unsigned char* checksum); -int hash_data_extract_printable_checksum(const char* d, unsigned int function, char* checksum); -// Insert an already computed binary checksum inside a hash data. This is useful when we already have -// checksum informations (e.g, from archive headers) and we want to prepare a hash data to compare -// with another const char* (e.g. the expected checksums). Returns 0 in case of error, 1 if the checksum -// was added correctly, 2 if the checksum was added overwriting a previously existing checksum for the -// the same function -int hash_data_insert_binary_checksum(char* d, unsigned int function, const unsigned char* checksum); -int hash_data_insert_printable_checksum(char* d, unsigned int function, const char* checksum); +// ======================> hash_base -// Check if the hash data contains the requested info -int hash_data_has_info(const char* d, unsigned int info); +// base class for all hash types, which does most of the heavy lifting +class hash_base +{ + friend class simple_list; -// Compare two hash data to check if they are the same. 'functions' can be either a combination of the -// hash function bits (HASH_CRC, etc) or zero to ask to check for all the available checksums -int hash_data_is_equal(const char* d1, const char* d2, unsigned int functions); +public: + // construction/destruction + hash_base(char id, const char *name, UINT8 length, UINT8 *bufptr); + + // operators + bool operator==(const hash_base &rhs) const { return (m_length == rhs.m_length && memcmp(m_bufptr, rhs.m_bufptr, m_length) == 0); } + bool operator!=(const hash_base &rhs) const { return (m_length != rhs.m_length || memcmp(m_bufptr, rhs.m_bufptr, m_length) != 0); } + + // getters + hash_base *next() const { return m_next; } + char id() const { return m_id; } + const char *name() const { return m_name; } + int length() const { return m_length; } + bool in_progress() const { return m_in_progress; } + bool parse_error() const { return m_parse_error; } + UINT8 byte(int index) const { return (index >= 0 && index < m_length) ? m_bufptr[index] : 0; } + + // buffer conversion + const UINT8 *buffer() { return m_bufptr; } + bool from_buffer(const UINT8 *buffer, int buflen); + + // string conversion + const char *string(astring &buffer); + bool from_string(const char *&string, int length); + + // creation + virtual void begin() = 0; + virtual void buffer(const UINT8 *data, UINT32 length) = 0; + virtual void end() = 0; + +protected: + // internal helpers + int fromhex(char c); + + // internal state + hash_base * m_next; + const char * m_name; + bool m_in_progress; + bool m_parse_error; + char m_id; + UINT8 m_length; + UINT8 * m_bufptr; +}; -// Print hash data informations in a standard format. 'functions' can be either a combination of the -// hash function bits (HASH_CRC, etc) or zero to ask to print all the available checksums -void hash_data_print(const char* d, unsigned int functions, char* buffer); -// Copy hash data informations -void hash_data_copy(char* dst, const char* src); +// ======================> hash_collection -// Clear hash data informations -void hash_data_clear(char* dst); +// a collection of the various supported hashes and flags +class hash_collection +{ +public: + // hash types are identified by non-hex alpha values (G-Z) + static const char HASH_CRC = 'R'; + static const char HASH_MD5 = 'M'; + static const char HASH_SHA1 = 'S'; + + // common combinations for requests + static const char *HASH_TYPES_CRC; + static const char *HASH_TYPES_CRC_SHA1; + static const char *HASH_TYPES_ALL; + + // flags are identified by punctuation marks + static const char FLAG_NO_DUMP = '!'; + static const char FLAG_BAD_DUMP = '^'; -// Check which functions we have a checksum of inside the data -unsigned int hash_data_used_functions(const char* d); + // construction/destruction + hash_collection(); + hash_collection(const char *string); + hash_collection(const hash_collection &src); + ~hash_collection(); + + // operators + hash_collection &operator=(const hash_collection &src); + bool operator==(const hash_collection &rhs) const; + bool operator!=(const hash_collection &rhs) const { return !(*this == rhs); } -// Compute hash of a data chunk in memory. Parameter 'functions' specifies which hashing functions -// we want the checksum of. -void hash_compute(char* dst, const unsigned char* data, unsigned long length, unsigned int functions); + // getters + bool flag(char flag) const { return (m_flags.chr(0, flag) != -1); } + hash_base *hash(char type) const; + hash_base *first() const { return m_hashlist.first(); } + const char *hash_types(astring &buffer) const; + bool parse_errors() const; -// Verifies that a hash string is valid -int hash_verify_string(const char *hash); + // hash manipulators + void reset(); + hash_base *add_from_buffer(char type, const UINT8 *buffer, int bufflen); + hash_base *add_from_string(char type, const char *buffer, int length); + bool remove(char type); + + // CRC-specific helpers + bool crc(UINT32 &result); + hash_base *add_crc(UINT32 crc); + + // string conversion + const char *internal_string(astring &buffer) const; + const char *macro_string(astring &buffer) const; + bool from_internal_string(const char *string); + + // creation + void begin(const char *types = NULL); + void buffer(const UINT8 *data, UINT32 length); + void end(); + void compute(const UINT8 *data, UINT32 length, const char *types = NULL) { begin(types); buffer(data, length); end(); } + +private: + // internal helpers + static hash_base *alloc_by_id(char id); + void copyfrom(const hash_collection &src); + + // internal state + astring m_flags; + simple_list m_hashlist; +}; #endif /* __HASH_H__ */ diff --git a/src/emu/hashfile.c b/src/emu/hashfile.c index e8bb9d8b76c..64e04ce67c7 100644 --- a/src/emu/hashfile.c +++ b/src/emu/hashfile.c @@ -18,7 +18,7 @@ struct _hash_file { emu_file *file; object_pool *pool; - unsigned int functions[IO_COUNT]; + astring functions[IO_COUNT]; hash_info **preloaded_hashes; int preloaded_hash_count; @@ -43,7 +43,7 @@ struct hash_parse_state hash_file *hashfile; int done; - int (*selector_proc)(hash_file *hashfile, void *param, const char *name, const char *hash); + int (*selector_proc)(hash_file *hashfile, void *param, const char *name, const hash_collection &hashes); void (*use_proc)(hash_file *hashfile, void *param, hash_info *hi); void (*error_proc)(const char *message); void *param; @@ -142,8 +142,9 @@ static void start_handler(void *data, const char *tagname, const char **attribut const char *name; hash_info *hi; char **text_dest; - char hash_string[HASH_BUF_SIZE]; - unsigned int functions, all_functions; + hash_collection hashes; + astring all_functions; + char functions; iodevice_t device; int i; @@ -164,8 +165,6 @@ static void start_handler(void *data, const char *tagname, const char **attribut { // we are now examining a hash tag name = NULL; - memset(hash_string, 0, sizeof(hash_string)); - all_functions = 0; device = IO_COUNT; while(attributes[0]) @@ -179,17 +178,17 @@ static void start_handler(void *data, const char *tagname, const char **attribut else if (!strcmp(attributes[0], "crc32")) { /* crc32 attribute */ - functions = HASH_CRC; + functions = hash_collection::HASH_CRC; } else if (!strcmp(attributes[0], "md5")) { /* md5 attribute */ - functions = HASH_MD5; + functions = hash_collection::HASH_MD5; } else if (!strcmp(attributes[0], "sha1")) { /* sha1 attribute */ - functions = HASH_SHA1; + functions = hash_collection::HASH_SHA1; } else if (!strcmp(attributes[0], "type")) { @@ -208,23 +207,19 @@ static void start_handler(void *data, const char *tagname, const char **attribut if (functions) { - hash_data_insert_printable_checksum(hash_string, functions, attributes[1]); - all_functions |= functions; + hashes.add_from_string(functions, attributes[1], strlen(attributes[1])); + all_functions.cat(functions); } attributes += 2; } - if (device == IO_COUNT) - { - for (i = 0; i < IO_COUNT; i++) - state->hashfile->functions[i] |= all_functions; - } - else - state->hashfile->functions[device] |= all_functions; + for (i = 0; i < IO_COUNT; i++) + if (i == device || device == IO_COUNT) + state->hashfile->functions[i] = all_functions; /* do we use this hash? */ - if (!state->selector_proc || state->selector_proc(state->hashfile, state->param, name, hash_string)) + if (!state->selector_proc || state->selector_proc(state->hashfile, state->param, name, hashes)) { hi = (hash_info*)pool_malloc_lib(state->hashfile->pool, sizeof(hash_info)); if (!hi) @@ -235,7 +230,7 @@ static void start_handler(void *data, const char *tagname, const char **attribut if (!hi->longname) return; - strcpy(hi->hash, hash_string); + hi->hashes = hashes; state->hi = hi; } } @@ -331,7 +326,7 @@ static void data_handler(void *data, const XML_Char *s, int len) -------------------------------------------------*/ static void hashfile_parse(hash_file *hashfile, - int (*selector_proc)(hash_file *hashfile, void *param, const char *name, const char *hash), + int (*selector_proc)(hash_file *hashfile, void *param, const char *name, const hash_collection &hashes), void (*use_proc)(hash_file *hashfile, void *param, hash_info *hi), void (*error_proc)(const char *message), void *param) @@ -469,15 +464,14 @@ void hashfile_close(hash_file *hashfile) struct hashlookup_params { - const char *hash; + hash_collection hashes; hash_info *hi; }; -static int singular_selector_proc(hash_file *hashfile, void *param, const char *name, const char *hash) +static int singular_selector_proc(hash_file *hashfile, void *param, const char *name, const hash_collection &hashes) { struct hashlookup_params *hlparams = (struct hashlookup_params *) param; - return hash_data_is_equal(hash, hlparams->hash, - hash_data_used_functions(hash)) == 1; + return (hashes == hlparams->hashes); } @@ -498,17 +492,17 @@ static void singular_use_proc(hash_file *hashfile, void *param, hash_info *hi) hashfile_lookup -------------------------------------------------*/ -const hash_info *hashfile_lookup(hash_file *hashfile, const char *hash) +const hash_info *hashfile_lookup(hash_file *hashfile, const hash_collection &hashes) { struct hashlookup_params param; int i; - param.hash = hash; + param.hashes = hashes; param.hi = NULL; for (i = 0; i < hashfile->preloaded_hash_count; i++) { - if (singular_selector_proc(hashfile, ¶m, NULL, hashfile->preloaded_hashes[i]->hash)) + if (singular_selector_proc(hashfile, ¶m, NULL, hashfile->preloaded_hashes[i]->hashes)) return hashfile->preloaded_hashes[i]; } @@ -523,7 +517,7 @@ const hash_info *hashfile_lookup(hash_file *hashfile, const char *hash) hashfile_functions_used -------------------------------------------------*/ -unsigned int hashfile_functions_used(hash_file *hashfile, iodevice_t devtype) +const char *hashfile_functions_used(hash_file *hashfile, iodevice_t devtype) { assert(devtype >= 0); assert(devtype < IO_COUNT); diff --git a/src/emu/hashfile.h b/src/emu/hashfile.h index 31f203455fe..3ce24ddd9a9 100644 --- a/src/emu/hashfile.h +++ b/src/emu/hashfile.h @@ -20,7 +20,7 @@ typedef struct _hash_info hash_info; struct _hash_info { - char hash[HASH_BUF_SIZE]; + hash_collection hashes; const char *longname; const char *manufacturer; const char *year; @@ -46,13 +46,13 @@ hash_file *hashfile_open(core_options &options, const char *sysname, int is_prel void hashfile_close(hash_file *hashfile); /* looks up information in a hash file */ -const hash_info *hashfile_lookup(hash_file *hashfile, const char *hash); +const hash_info *hashfile_lookup(hash_file *hashfile, const hash_collection &hashes); /* performs a syntax check on a hash file */ int hashfile_verify(const char *sysname, void (*error_proc)(const char *message)); /* returns the functions used in this hash file */ -unsigned int hashfile_functions_used(hash_file *hashfile, iodevice_t devtype); +const char *hashfile_functions_used(hash_file *hashfile, iodevice_t devtype); #endif /* __HASHFILE_H__ */ diff --git a/src/emu/imagedev/multcart.c b/src/emu/imagedev/multcart.c index 1b7643fb4bd..093a1e2efff 100644 --- a/src/emu/imagedev/multcart.c +++ b/src/emu/imagedev/multcart.c @@ -193,12 +193,13 @@ static multicart_open_error load_rom_resource(multicart_load_state *state, xml_d /* check SHA1 now */ if ((sha1 = xml_get_attribute_string(resource_node, "sha1", NULL))) { - char calc_sha[256]; + hash_collection actual_hashes; + actual_hashes.compute((const UINT8 *)resource->ptr, resource->length, hash_collection::HASH_TYPES_CRC_SHA1); + + hash_collection expected_hashes; + expected_hashes.add_from_string(hash_collection::HASH_SHA1, sha1, strlen(sha1)); - memset(calc_sha, 0, sizeof(calc_sha)); - hash_compute(&calc_sha[0], (const unsigned char*)resource->ptr, resource->length, HASH_SHA1); - - if ((strncmp(sha1, &calc_sha[2], 20))) + if (actual_hashes != expected_hashes) { return MCERR_INVALID_FILE_REF; } diff --git a/src/emu/info.c b/src/emu/info.c index 283b2db05b2..c11f2d17a13 100644 --- a/src/emu/info.c +++ b/src/emu/info.c @@ -452,11 +452,11 @@ static void print_game_bios(FILE *out, const game_driver *game) parent set -------------------------------------------------*/ -static const char *get_merge_name(const rom_entry *rom, int parents, const parent_info **pinfoarray) +static const char *get_merge_name(const hash_collection &romhashes, int parents, const parent_info **pinfoarray) { int parent; const char *merge_name = NULL; - + for (parent = 0; parent < parents; ++parent) { const machine_config *pconfig = &pinfoarray[parent]->mconfig; @@ -467,7 +467,7 @@ static const char *get_merge_name(const rom_entry *rom, int parents, const paren for (psource = rom_first_source(*pconfig); psource != NULL; psource = rom_next_source(*psource)) for (pregion = rom_first_region(*psource); pregion != NULL; pregion = rom_next_region(pregion)) for (prom = rom_first_file(pregion); prom != NULL; prom = rom_next_file(prom)) - if (hash_data_is_equal(ROM_GETHASHDATA(rom), ROM_GETHASHDATA(prom), 0)) + if (romhashes == hash_collection(ROM_GETHASHDATA(prom))) { merge_name = ROM_GETNAME(prom); break; @@ -528,9 +528,10 @@ static void print_game_rom(FILE *out, const game_driver *game, const machine_con continue; /* if we have a valid ROM and we are a clone, see if we can find the parent ROM */ - if (!ROM_NOGOODDUMP(rom) && parents > 0) + hash_collection hashes(ROM_GETHASHDATA(rom)); + if (!hashes.flag(hash_collection::FLAG_NO_DUMP) && parents > 0) { - merge_name = get_merge_name(rom, parents, pinfoarray); + merge_name = get_merge_name(hashes, parents, pinfoarray); } /* scan for a BIOS name */ @@ -565,24 +566,21 @@ static void print_game_rom(FILE *out, const game_driver *game, const machine_con fprintf(out, " size=\"%d\"", rom_file_size(rom)); /* dump checksum information only if there is a known dump */ - if (!hash_data_has_info(ROM_GETHASHDATA(rom), HASH_INFO_NO_DUMP)) + if (!hashes.flag(hash_collection::FLAG_NO_DUMP)) { - char checksum[HASH_BUF_SIZE]; - int hashtype; - /* iterate over hash function types and print out their values */ - for (hashtype = 0; hashtype < HASH_NUM_FUNCTIONS; hashtype++) - if (hash_data_extract_printable_checksum(ROM_GETHASHDATA(rom), 1 << hashtype, checksum)) - fprintf(out, " %s=\"%s\"", hash_function_name(1 << hashtype), checksum); + astring tempstr; + for (hash_base *hash = hashes.first(); hash != NULL; hash = hash->next()) + fprintf(out, " %s=\"%s\"", hash->name(), hash->string(tempstr)); } /* append a region name */ fprintf(out, " region=\"%s\"", ROMREGION_GETTAG(region)); /* add nodump/baddump flags */ - if (hash_data_has_info(ROM_GETHASHDATA(rom), HASH_INFO_NO_DUMP)) + if (hashes.flag(hash_collection::FLAG_NO_DUMP)) fprintf(out, " status=\"nodump\""); - if (hash_data_has_info(ROM_GETHASHDATA(rom), HASH_INFO_BAD_DUMP)) + if (hashes.flag(hash_collection::FLAG_BAD_DUMP)) fprintf(out, " status=\"baddump\""); /* for non-disk entries, print offset */ diff --git a/src/emu/romload.c b/src/emu/romload.c index 95fa97d6c6d..f0fe51681e6 100644 --- a/src/emu/romload.c +++ b/src/emu/romload.c @@ -434,7 +434,7 @@ static void handle_missing_file(rom_load_data *romdata, const rom_entry *romp) } /* no good dumps are okay */ - else if (ROM_NOGOODDUMP(romp)) + else if (hash_collection(ROM_GETHASHDATA(romp)).flag(hash_collection::FLAG_NO_DUMP)) { romdata->errorstring.catprintf("%s NOT FOUND (NO GOOD DUMP KNOWN)\n", ROM_GETNAME(romp)); romdata->knownbad++; @@ -455,46 +455,19 @@ static void handle_missing_file(rom_load_data *romdata, const rom_entry *romp) correct checksums for a given ROM -------------------------------------------------*/ -static void dump_wrong_and_correct_checksums(rom_load_data *romdata, const char *hash, const char *acthash) +static void dump_wrong_and_correct_checksums(rom_load_data *romdata, const hash_collection &hashes, const hash_collection &acthashes) { - unsigned i; - char chksum[256]; - unsigned found_functions; - unsigned wrong_functions; + astring tempstr; + romdata->errorstring.catprintf(" EXPECTED: %s\n", hashes.macro_string(tempstr)); + romdata->errorstring.catprintf(" FOUND: %s\n", acthashes.macro_string(tempstr)); - found_functions = hash_data_used_functions(hash) & hash_data_used_functions(acthash); - - hash_data_print(hash, found_functions, chksum); - romdata->errorstring.catprintf(" EXPECTED: %s\n", chksum); - - /* We dump informations only of the functions for which MAME provided - a correct checksum. Other functions we might have calculated are - useless here */ - hash_data_print(acthash, found_functions, chksum); - romdata->errorstring.catprintf(" FOUND: %s\n", chksum); - - /* For debugging purposes, we check if the checksums available in the - driver are correctly specified or not. This can be done by checking - the return value of one of the extract functions. Maybe we want to - activate this only in debug buils, but many developers only use - release builds, so I keep it as is for now. */ - wrong_functions = 0; - for (i = 0; i < HASH_NUM_FUNCTIONS; i++) - if (hash_data_extract_printable_checksum(hash, 1 << i, chksum) == 2) - wrong_functions |= 1 << i; - - if (wrong_functions) - { - for (i = 0; i < HASH_NUM_FUNCTIONS; i++) - if (wrong_functions & (1 << i)) - { - romdata->errorstring.catprintf( - "\tInvalid %s checksum treated as 0 (check leading zeros)\n", - hash_function_name(1 << i)); - - romdata->warnings++; - } - } + // warn about any ill-formed hashes + for (hash_base *hash = hashes.first(); hash != NULL; hash = hash->next()) + if (hash->parse_error()) + { + romdata->errorstring.catprintf("\tInvalid %s checksum treated as 0 (check leading zeros)\n", hash->name()); + romdata->warnings++; + } } @@ -503,20 +476,14 @@ static void dump_wrong_and_correct_checksums(rom_load_data *romdata, const char and hash signatures of a file -------------------------------------------------*/ -static void verify_length_and_hash(rom_load_data *romdata, const char *name, UINT32 explength, const char *hash) +static void verify_length_and_hash(rom_load_data *romdata, const char *name, UINT32 explength, const hash_collection &hashes) { - UINT32 actlength; - const char* acthash; - /* we've already complained if there is no file */ if (romdata->file == NULL) return; - /* get the length and CRC from the file */ - actlength = romdata->file->size(); - acthash = romdata->file->hash_string(hash_data_used_functions(hash)); - /* verify length */ + UINT32 actlength = romdata->file->size(); if (explength != actlength) { romdata->errorstring.catprintf("%s WRONG LENGTH (expected: %08x found: %08x)\n", name, explength, actlength); @@ -524,23 +491,23 @@ static void verify_length_and_hash(rom_load_data *romdata, const char *name, UIN } /* If there is no good dump known, write it */ - if (hash_data_has_info(hash, HASH_INFO_NO_DUMP)) + astring tempstr; + hash_collection &acthashes = romdata->file->hashes(hashes.hash_types(tempstr)); + if (hashes.flag(hash_collection::FLAG_NO_DUMP)) { romdata->errorstring.catprintf("%s NO GOOD DUMP KNOWN\n", name); romdata->knownbad++; } /* verify checksums */ - else if (!hash_data_is_equal(hash, acthash, 0)) + else if (hashes != acthashes) { /* otherwise, it's just bad */ romdata->errorstring.catprintf("%s WRONG CHECKSUMS:\n", name); - - dump_wrong_and_correct_checksums(romdata, hash, acthash); - + dump_wrong_and_correct_checksums(romdata, hashes, acthashes); romdata->warnings++; } /* If it matches, but it is actually a bad dump, write it */ - else if (hash_data_has_info(hash, HASH_INFO_BAD_DUMP)) + else if (hashes.flag(hash_collection::FLAG_BAD_DUMP)) { romdata->errorstring.catprintf("%s ROM NEEDS REDUMP\n",name); romdata->knownbad++; @@ -646,17 +613,13 @@ static int open_rom_file(rom_load_data *romdata, const char *regiontag, const ro file_error filerr = FILERR_NOT_FOUND; UINT32 romsize = rom_file_size(romp); const game_driver *drv; - int has_crc = FALSE; - UINT8 crcbytes[4]; - UINT32 crc = 0; /* update status display */ display_loading_rom_message(romdata, ROM_GETNAME(romp)); /* extract CRC to use for searching */ - has_crc = hash_data_extract_binary_checksum(ROM_GETHASHDATA(romp), HASH_CRC, crcbytes); - if (has_crc) - crc = (crcbytes[0] << 24) | (crcbytes[1] << 16) | (crcbytes[2] << 8) | crcbytes[3]; + UINT32 crc = 0; + bool has_crc = hash_collection(ROM_GETHASHDATA(romp)).crc(crc); /* attempt reading up the chain through the parents. It automatically also attempts any kind of load by checksum supported by the archives. */ @@ -1006,7 +969,7 @@ static void process_rom_entries(rom_load_data *romdata, const char *regiontag, c if (baserom) { LOG(("Verifying length (%X) and checksums\n", explength)); - verify_length_and_hash(romdata, ROM_GETNAME(baserom), explength, ROM_GETHASHDATA(baserom)); + verify_length_and_hash(romdata, ROM_GETNAME(baserom), explength, hash_collection(ROM_GETHASHDATA(baserom))); LOG(("Verify finished\n")); } @@ -1148,6 +1111,7 @@ chd_error open_disk_image(core_options &options, const game_driver *gamedrv, con /* otherwise, look at our parents for a CHD with an identical checksum */ /* and try to open that */ + hash_collection romphashes(ROM_GETHASHDATA(romp)); for (drv = gamedrv; drv != NULL; drv = driver_get_clone(drv)) { machine_config config(*drv); @@ -1158,7 +1122,7 @@ chd_error open_disk_image(core_options &options, const game_driver *gamedrv, con /* look for a differing name but with the same hash data */ if (strcmp(ROM_GETNAME(romp), ROM_GETNAME(rom)) != 0 && - hash_data_is_equal(ROM_GETHASHDATA(romp), ROM_GETHASHDATA(rom), 0)) + romphashes == hash_collection(ROM_GETHASHDATA(rom))) { /* attempt to open the properly named file, scanning up through parent directories */ filerr = FILERR_NOT_FOUND; @@ -1249,7 +1213,7 @@ static void process_disk_entries(rom_load_data *romdata, const char *regiontag, /* handle files */ if (ROMENTRY_ISFILE(romp)) { - char acthash[HASH_BUF_SIZE]; + hash_collection hashes(ROM_GETHASHDATA(romp)); open_chd chd = { 0 }; chd_header header; chd_error err; @@ -1271,7 +1235,7 @@ static void process_disk_entries(rom_load_data *romdata, const char *regiontag, romdata->errorstring.catprintf("%s CHD ERROR: %s\n", filename.cstr(), chd_error_string(err)); /* if this is NO_DUMP, keep going, though the system may not be able to handle it */ - if (hash_data_has_info(ROM_GETHASHDATA(romp), HASH_INFO_NO_DUMP)) + if (hashes.flag(hash_collection::FLAG_NO_DUMP)) romdata->knownbad++; else if (DISK_ISOPTIONAL(romp)) romdata->warnings++; @@ -1282,17 +1246,17 @@ static void process_disk_entries(rom_load_data *romdata, const char *regiontag, /* get the header and extract the MD5/SHA1 */ header = *chd_get_header(chd.origchd); - hash_data_clear(acthash); - hash_data_insert_binary_checksum(acthash, HASH_SHA1, header.sha1); + hash_collection acthashes; + acthashes.add_from_buffer(hash_collection::HASH_SHA1, header.sha1, sizeof(header.sha1)); /* verify the hash */ - if (!hash_data_is_equal(ROM_GETHASHDATA(romp), acthash, 0)) + if (hashes != acthashes) { romdata->errorstring.catprintf("%s WRONG CHECKSUMS:\n", filename.cstr()); - dump_wrong_and_correct_checksums(romdata, ROM_GETHASHDATA(romp), acthash); + dump_wrong_and_correct_checksums(romdata, hashes, acthashes); romdata->warnings++; } - else if (hash_data_has_info(ROM_GETHASHDATA(romp), HASH_INFO_BAD_DUMP)) + else if (hashes.flag(hash_collection::FLAG_BAD_DUMP)) { romdata->errorstring.catprintf("%s CHD NEEDS REDUMP\n", filename.cstr()); romdata->knownbad++; diff --git a/src/emu/romload.h b/src/emu/romload.h index cab3a18cf95..9177b18b9c0 100644 --- a/src/emu/romload.h +++ b/src/emu/romload.h @@ -189,7 +189,6 @@ struct rom_entry #define ROM_GETBITSHIFT(r) ((ROM_GETFLAGS(r) & ROM_BITSHIFTMASK) >> 20) #define ROM_INHERITSFLAGS(r) ((ROM_GETFLAGS(r) & ROM_INHERITFLAGSMASK) == ROM_INHERITFLAGS) #define ROM_GETBIOSFLAGS(r) ((ROM_GETFLAGS(r) & ROM_BIOSFLAGSMASK) >> 24) -#define ROM_NOGOODDUMP(r) (hash_data_has_info((r)->_hashdata, HASH_INFO_NO_DUMP)) /* ----- per-disk macros ----- */ @@ -257,14 +256,6 @@ struct rom_entry #define DISK_IMAGE_READONLY_OPTIONAL(name,idx,hash) ROMX_LOAD(name, idx, 0, hash, DISK_READONLY | ROM_OPTIONAL) -/* ----- hash macros ----- */ -#define CRC(x) "c:" #x "#" -#define SHA1(x) "s:" #x "#" -#define MD5(x) "m:" #x "#" -#define NO_DUMP "$ND$" -#define BAD_DUMP "$BD$" - - /*************************************************************************** FUNCTION PROTOTYPES diff --git a/src/emu/softlist.c b/src/emu/softlist.c index dbe04b01ad2..25c59ffc2d6 100644 --- a/src/emu/softlist.c +++ b/src/emu/softlist.c @@ -670,7 +670,7 @@ static void start_handler(void *data, const char *tagname, const char **attribut return; strcpy( s_name, str_name ); - sprintf( hashdata, "c:%s#s:%s#%s", str_crc, str_sha1, ( nodump ? NO_DUMP : ( baddump ? BAD_DUMP : "" ) ) ); + sprintf( hashdata, "%c%s%c%s%s", hash_collection::HASH_CRC, str_crc, hash_collection::HASH_SHA1, str_sha1, ( nodump ? NO_DUMP : ( baddump ? BAD_DUMP : "" ) ) ); /* Handle loadflag attribute */ if ( str_loadflag && !strcmp(str_loadflag, "load16_word_swap") ) @@ -1689,7 +1689,8 @@ static DEVICE_VALIDITY_CHECK( software_list ) } /* make sure the hash is valid */ - if (!hash_verify_string(data->_hashdata)) + hash_collection hashes; + if (!hashes.from_internal_string(data->_hashdata)) { mame_printf_error("%s: %s has rom '%s' with an invalid hash string '%s'\n", swlist->list_name[i], swinfo->shortname, data->_name, data->_hashdata); error = TRUE; diff --git a/src/emu/validity.c b/src/emu/validity.c index 543a5455ac1..5291cd9329e 100644 --- a/src/emu/validity.c +++ b/src/emu/validity.c @@ -523,7 +523,6 @@ static bool validate_roms(const machine_config &config, region_array *rgninfo, g /* if this is a file, make sure it is properly formatted */ else if (ROMENTRY_ISFILE(romp)) { - const char *hash; const char *s; items_since_region++; @@ -541,10 +540,10 @@ static bool validate_roms(const machine_config &config, region_array *rgninfo, g } /* make sure the hash is valid */ - hash = ROM_GETHASHDATA(romp); - if (!hash_verify_string(hash)) + hash_collection hashes; + if (!hashes.from_internal_string(ROM_GETHASHDATA(romp))) { - mame_printf_error("%s: rom '%s' has an invalid hash string '%s'\n", driver.name, last_name, hash); + mame_printf_error("%s: rom '%s' has an invalid hash string '%s'\n", driver.name, last_name, ROM_GETHASHDATA(romp)); error = true; } }