summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/romload.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/romload.c')
-rw-r--r--src/emu/romload.c1169
1 files changed, 1169 insertions, 0 deletions
diff --git a/src/emu/romload.c b/src/emu/romload.c
new file mode 100644
index 00000000000..344ae0a4901
--- /dev/null
+++ b/src/emu/romload.c
@@ -0,0 +1,1169 @@
+/*********************************************************************
+
+ romload.c
+
+ ROM loading functions.
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+*********************************************************************/
+
+#include "osdepend.h"
+#include "driver.h"
+#include "hash.h"
+#include "png.h"
+#include "harddisk.h"
+#include "config.h"
+#include "ui.h"
+#include <stdarg.h>
+#include <ctype.h>
+
+
+//#define LOG_LOAD
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _open_chd open_chd;
+struct _open_chd
+{
+ open_chd * next; /* pointer to next in the list */
+ chd_file * origchd; /* handle to the original CHD */
+ mame_file * origfile; /* file handle to the original CHD file */
+ chd_file * diffchd; /* handle to the diff CHD */
+ mame_file * difffile; /* file handle to the diff CHD file */
+};
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+/* disks */
+static open_chd *chd_list;
+static open_chd **chd_list_tailptr;
+
+/* system BIOS */
+int system_bios;
+
+static int total_rom_load_warnings;
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static void rom_exit(running_machine *machine);
+
+
+
+/***************************************************************************
+ HARD DISK HANDLING
+***************************************************************************/
+
+chd_file *get_disk_handle(int diskindex)
+{
+ open_chd *curdisk;
+ for (curdisk = chd_list; curdisk != NULL && diskindex-- != 0; curdisk = curdisk->next) ;
+ if (curdisk != NULL)
+ return (curdisk->diffchd != NULL) ? curdisk->diffchd : curdisk->origchd;
+ return NULL;
+}
+
+
+
+/***************************************************************************
+ ROM LOADING
+***************************************************************************/
+
+/*-------------------------------------------------
+ rom_first_region - return pointer to first ROM
+ region
+-------------------------------------------------*/
+
+const rom_entry *rom_first_region(const game_driver *drv)
+{
+ return (drv->rom != NULL && !ROMENTRY_ISEND(drv->rom)) ? drv->rom : NULL;
+}
+
+
+/*-------------------------------------------------
+ rom_next_region - return pointer to next ROM
+ region
+-------------------------------------------------*/
+
+const rom_entry *rom_next_region(const rom_entry *romp)
+{
+ romp++;
+ while (!ROMENTRY_ISREGIONEND(romp))
+ romp++;
+ return ROMENTRY_ISEND(romp) ? NULL : romp;
+}
+
+
+/*-------------------------------------------------
+ rom_first_file - return pointer to first ROM
+ file
+-------------------------------------------------*/
+
+const rom_entry *rom_first_file(const rom_entry *romp)
+{
+ romp++;
+ while (!ROMENTRY_ISFILE(romp) && !ROMENTRY_ISREGIONEND(romp))
+ romp++;
+ return ROMENTRY_ISREGIONEND(romp) ? NULL : romp;
+}
+
+
+/*-------------------------------------------------
+ rom_next_file - return pointer to next ROM
+ file
+-------------------------------------------------*/
+
+const rom_entry *rom_next_file(const rom_entry *romp)
+{
+ romp++;
+ while (!ROMENTRY_ISFILE(romp) && !ROMENTRY_ISREGIONEND(romp))
+ romp++;
+ return ROMENTRY_ISREGIONEND(romp) ? NULL : romp;
+}
+
+
+/*-------------------------------------------------
+ rom_first_chunk - return pointer to first ROM
+ chunk
+-------------------------------------------------*/
+
+const rom_entry *rom_first_chunk(const rom_entry *romp)
+{
+ return (ROMENTRY_ISFILE(romp)) ? romp : NULL;
+}
+
+
+/*-------------------------------------------------
+ rom_next_chunk - return pointer to next ROM
+ chunk
+-------------------------------------------------*/
+
+const rom_entry *rom_next_chunk(const rom_entry *romp)
+{
+ romp++;
+ return (ROMENTRY_ISCONTINUE(romp) || ROMENTRY_ISIGNORE(romp)) ? romp : NULL;
+}
+
+
+/*-------------------------------------------------
+ debugload - log data to a file
+-------------------------------------------------*/
+
+static void CLIB_DECL debugload(const char *string, ...)
+{
+#ifdef LOG_LOAD
+ static int opened;
+ va_list arg;
+ FILE *f;
+
+ f = fopen("romload.log", opened++ ? "a" : "w");
+ if (f)
+ {
+ va_start(arg, string);
+ vfprintf(f, string, arg);
+ va_end(arg);
+ fclose(f);
+ }
+#endif
+}
+
+
+/*-------------------------------------------------
+ determine_bios_rom - determine system_bios
+ from SystemBios structure and OPTION_BIOS
+-------------------------------------------------*/
+
+static int determine_bios_rom(const rom_entry *romp)
+{
+ const char *specbios = options_get_string(mame_options(), OPTION_BIOS);
+ const rom_entry *rom;
+ int bios_count = 0;
+
+ /* set to default */
+ int bios_no = 1;
+
+ for (rom = romp;!ROMENTRY_ISEND(rom);rom++)
+ if (ROMENTRY_ISSYSTEM_BIOS(rom))
+ {
+ const char *biosname = ROM_GETHASHDATA(rom);
+ int bios_flags = ROM_GETBIOSFLAGS(rom);
+ char bios_number[3];
+
+ /* Allow '-bios n' to still be used */
+ sprintf(bios_number, "%d", bios_flags-1);
+ if (strcmp(bios_number, specbios) == 0 || strcmp(biosname, specbios) == 0)
+ bios_no = bios_flags;
+ bios_count++;
+ }
+
+ if (bios_count == 0)
+ bios_no = 0;
+
+ debugload("Using System BIOS: %d\n", bios_no);
+
+ return bios_no;
+}
+
+
+/*-------------------------------------------------
+ count_roms - counts the total number of ROMs
+ that will need to be loaded
+-------------------------------------------------*/
+
+static int count_roms(const rom_entry *romp)
+{
+ const rom_entry *region, *rom;
+ int count = 0;
+
+ /* loop over regions, then over files */
+ for (region = romp; region; region = rom_next_region(region))
+ for (rom = rom_first_file(region); rom; rom = rom_next_file(rom))
+ {
+ int bios_flags = ROM_GETBIOSFLAGS(rom);
+ if (!bios_flags || (bios_flags == system_bios)) /* alternate bios sets */
+ count++;
+ }
+
+ /* return the total count */
+ return count;
+}
+
+
+/*-------------------------------------------------
+ fill_random - fills an area of memory with
+ random data
+-------------------------------------------------*/
+
+static void fill_random(UINT8 *base, UINT32 length)
+{
+ while (length--)
+ *base++ = mame_rand(Machine);
+}
+
+
+/*-------------------------------------------------
+ handle_missing_file - handles error generation
+ for missing files
+-------------------------------------------------*/
+
+static void handle_missing_file(rom_load_data *romdata, const rom_entry *romp)
+{
+ /* optional files are okay */
+ if (ROM_ISOPTIONAL(romp))
+ {
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "OPTIONAL %s NOT FOUND\n", ROM_GETNAME(romp));
+ romdata->warnings++;
+ }
+
+ /* no good dumps are okay */
+ else if (ROM_NOGOODDUMP(romp))
+ {
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s NOT FOUND (NO GOOD DUMP KNOWN)\n", ROM_GETNAME(romp));
+ romdata->warnings++;
+ }
+
+ /* anything else is bad */
+ else
+ {
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s NOT FOUND\n", ROM_GETNAME(romp));
+ romdata->errors++;
+ }
+}
+
+
+/*-------------------------------------------------
+ dump_wrong_and_correct_checksums - dump an
+ error message containing the wrong and the
+ correct checksums for a given ROM
+-------------------------------------------------*/
+
+static void dump_wrong_and_correct_checksums(rom_load_data* romdata, const char* hash, const char* acthash)
+{
+ unsigned i;
+ char chksum[256];
+ unsigned found_functions;
+ unsigned wrong_functions;
+
+ found_functions = hash_data_used_functions(hash) & hash_data_used_functions(acthash);
+
+ hash_data_print(hash, found_functions, chksum);
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], " 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);
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], " 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))
+ {
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)],
+ "\tInvalid %s checksum treated as 0 (check leading zeros)\n",
+ hash_function_name(1<<i));
+
+ romdata->warnings++;
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ verify_length_and_hash - verify the length
+ and hash signatures of a file
+-------------------------------------------------*/
+
+static void verify_length_and_hash(rom_load_data *romdata, const char *name, UINT32 explength, const char* hash)
+{
+ UINT32 actlength;
+ const char* acthash;
+
+ /* we've already complained if there is no file */
+ if (!romdata->file)
+ return;
+
+ /* get the length and CRC from the file */
+ actlength = mame_fsize(romdata->file);
+ acthash = mame_fhash(romdata->file, hash_data_used_functions(hash));
+
+ /* verify length */
+ if (explength != actlength)
+ {
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s WRONG LENGTH (expected: %08x found: %08x)\n", name, explength, actlength);
+ romdata->warnings++;
+ }
+
+ /* If there is no good dump known, write it */
+ if (hash_data_has_info(hash, HASH_INFO_NO_DUMP))
+ {
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s NO GOOD DUMP KNOWN\n", name);
+ romdata->warnings++;
+ }
+ /* verify checksums */
+ else if (!hash_data_is_equal(hash, acthash, 0))
+ {
+ /* otherwise, it's just bad */
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s WRONG CHECKSUMS:\n", name);
+
+ dump_wrong_and_correct_checksums(romdata, hash, acthash);
+
+ 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))
+ {
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s ROM NEEDS REDUMP\n",name);
+ romdata->warnings++;
+ }
+}
+
+
+/*-------------------------------------------------
+ display_loading_rom_message - display
+ messages about ROM loading to the user
+-------------------------------------------------*/
+
+static void display_loading_rom_message(const char *name, rom_load_data *romdata)
+{
+ char buffer[200];
+
+ if (name != NULL)
+ sprintf(buffer, "Loading (%d%%)", 100 * romdata->romsloaded / romdata->romstotal);
+ else
+ sprintf(buffer, "Loading Complete");
+
+ ui_set_startup_text(buffer, FALSE);
+}
+
+
+/*-------------------------------------------------
+ display_rom_load_results - display the final
+ results of ROM loading
+-------------------------------------------------*/
+
+static void display_rom_load_results(rom_load_data *romdata)
+{
+ int region;
+
+ /* final status display */
+ display_loading_rom_message(NULL, romdata);
+
+ /* if we had errors, they are fatal */
+ if (romdata->errors != 0)
+ {
+ /* clean up any regions */
+ for (region = 0; region < MAX_MEMORY_REGIONS; region++)
+ free_memory_region(Machine, region);
+
+ /* create the error message and exit fatally */
+ strcat(romdata->errorbuf, "ERROR: required files are missing, the game cannot be run.");
+ fatalerror_exitcode(MAMERR_MISSING_FILES, "%s", romdata->errorbuf);
+ }
+
+ /* if we had warnings, output them, but continue */
+ if (romdata->warnings)
+ {
+ strcat(romdata->errorbuf, "WARNING: the game might not run correctly.");
+ mame_printf_warning("%s\n", romdata->errorbuf);
+ }
+}
+
+
+/*-------------------------------------------------
+ region_post_process - post-process a region,
+ byte swapping and inverting data as necessary
+-------------------------------------------------*/
+
+static void region_post_process(rom_load_data *romdata, const rom_entry *regiondata)
+{
+ int type = ROMREGION_GETTYPE(regiondata);
+ int datawidth = ROMREGION_GETWIDTH(regiondata) / 8;
+ int littleendian = ROMREGION_ISLITTLEENDIAN(regiondata);
+ UINT8 *base;
+ int i, j;
+
+ debugload("+ datawidth=%d little=%d\n", datawidth, littleendian);
+
+ /* if this is a CPU region, override with the CPU width and endianness */
+ if (type >= REGION_CPU1 && type < REGION_CPU1 + MAX_CPU)
+ {
+ cpu_type cputype = Machine->drv->cpu[type - REGION_CPU1].type;
+ if (cputype != CPU_DUMMY)
+ {
+ datawidth = cputype_databus_width(cputype, ADDRESS_SPACE_PROGRAM) / 8;
+ littleendian = (cputype_endianness(cputype) == CPU_IS_LE);
+ debugload("+ CPU region #%d: datawidth=%d little=%d\n", type - REGION_CPU1, datawidth, littleendian);
+ }
+ }
+
+ /* if the region is inverted, do that now */
+ if (ROMREGION_ISINVERTED(regiondata))
+ {
+ debugload("+ Inverting region\n");
+ for (i = 0, base = romdata->regionbase; i < romdata->regionlength; i++)
+ *base++ ^= 0xff;
+ }
+
+ /* swap the endianness if we need to */
+#ifdef LSB_FIRST
+ if (datawidth > 1 && !littleendian)
+#else
+ if (datawidth > 1 && littleendian)
+#endif
+ {
+ debugload("+ Byte swapping region\n");
+ for (i = 0, base = romdata->regionbase; i < romdata->regionlength; i += datawidth)
+ {
+ UINT8 temp[8];
+ memcpy(temp, base, datawidth);
+ for (j = datawidth - 1; j >= 0; j--)
+ *base++ = temp[j];
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ open_rom_file - open a ROM file, searching
+ up the parent and loading by checksum
+-------------------------------------------------*/
+
+static int open_rom_file(rom_load_data *romdata, const rom_entry *romp)
+{
+ file_error filerr = FILERR_NOT_FOUND;
+ const game_driver *drv;
+
+ ++romdata->romsloaded;
+
+ /* update status display */
+ display_loading_rom_message(ROM_GETNAME(romp), romdata);
+
+ /* Attempt reading up the chain through the parents. It automatically also
+ attempts any kind of load by checksum supported by the archives. */
+ romdata->file = NULL;
+ for (drv = Machine->gamedrv; !romdata->file && drv; drv = driver_get_clone(drv))
+ if (drv->name && *drv->name)
+ {
+ UINT8 crcs[4];
+ astring *fname;
+
+ fname = astring_assemble_3(astring_alloc(), drv->name, PATH_SEPARATOR, ROM_GETNAME(romp));
+ if (hash_data_extract_binary_checksum(ROM_GETHASHDATA(romp), HASH_CRC, crcs))
+ {
+ UINT32 crc = (crcs[0] << 24) | (crcs[1] << 16) | (crcs[2] << 8) | crcs[3];
+ filerr = mame_fopen_crc(SEARCHPATH_ROM, astring_c(fname), crc, OPEN_FLAG_READ, &romdata->file);
+ }
+ else
+ filerr = mame_fopen(SEARCHPATH_ROM, astring_c(fname), OPEN_FLAG_READ, &romdata->file);
+ astring_free(fname);
+ }
+
+ /* return the result */
+ return (filerr == FILERR_NONE);
+}
+
+
+/*-------------------------------------------------
+ rom_fread - cheesy fread that fills with
+ random data for a NULL file
+-------------------------------------------------*/
+
+static int rom_fread(rom_load_data *romdata, UINT8 *buffer, int length)
+{
+ /* files just pass through */
+ if (romdata->file)
+ return mame_fread(romdata->file, buffer, length);
+
+ /* otherwise, fill with randomness */
+ else
+ fill_random(buffer, length);
+
+ return length;
+}
+
+
+/*-------------------------------------------------
+ read_rom_data - read ROM data for a single
+ entry
+-------------------------------------------------*/
+
+static int read_rom_data(rom_load_data *romdata, const rom_entry *romp)
+{
+ int datashift = ROM_GETBITSHIFT(romp);
+ int datamask = ((1 << ROM_GETBITWIDTH(romp)) - 1) << datashift;
+ int numbytes = ROM_GETLENGTH(romp);
+ int groupsize = ROM_GETGROUPSIZE(romp);
+ int skip = ROM_GETSKIPCOUNT(romp);
+ int reversed = ROM_ISREVERSED(romp);
+ int numgroups = (numbytes + groupsize - 1) / groupsize;
+ UINT8 *base = romdata->regionbase + ROM_GETOFFSET(romp);
+ int i;
+
+ debugload("Loading ROM data: offs=%X len=%X mask=%02X group=%d skip=%d reverse=%d\n", ROM_GETOFFSET(romp), numbytes, datamask, groupsize, skip, reversed);
+
+ /* make sure the length was an even multiple of the group size */
+ if (numbytes % groupsize != 0)
+ fatalerror("Error in RomModule definition: %s length not an even multiple of group size\n", ROM_GETNAME(romp));
+
+ /* make sure we only fill within the region space */
+ if (ROM_GETOFFSET(romp) + numgroups * groupsize + (numgroups - 1) * skip > romdata->regionlength)
+ fatalerror("Error in RomModule definition: %s out of memory region space\n", ROM_GETNAME(romp));
+
+ /* make sure the length was valid */
+ if (numbytes == 0)
+ fatalerror("Error in RomModule definition: %s has an invalid length\n", ROM_GETNAME(romp));
+
+ /* special case for simple loads */
+ if (datamask == 0xff && (groupsize == 1 || !reversed) && skip == 0)
+ return rom_fread(romdata, base, numbytes);
+
+ /* chunky reads for complex loads */
+ skip += groupsize;
+ while (numbytes)
+ {
+ int evengroupcount = (sizeof(romdata->tempbuf) / groupsize) * groupsize;
+ int bytesleft = (numbytes > evengroupcount) ? evengroupcount : numbytes;
+ UINT8 *bufptr = romdata->tempbuf;
+
+ /* read as much as we can */
+ debugload(" Reading %X bytes into buffer\n", bytesleft);
+ if (rom_fread(romdata, romdata->tempbuf, bytesleft) != bytesleft)
+ return 0;
+ numbytes -= bytesleft;
+
+ debugload(" Copying to %p\n", base);
+
+ /* unmasked cases */
+ if (datamask == 0xff)
+ {
+ /* non-grouped data */
+ if (groupsize == 1)
+ for (i = 0; i < bytesleft; i++, base += skip)
+ *base = *bufptr++;
+
+ /* grouped data -- non-reversed case */
+ else if (!reversed)
+ while (bytesleft)
+ {
+ for (i = 0; i < groupsize && bytesleft; i++, bytesleft--)
+ base[i] = *bufptr++;
+ base += skip;
+ }
+
+ /* grouped data -- reversed case */
+ else
+ while (bytesleft)
+ {
+ for (i = groupsize - 1; i >= 0 && bytesleft; i--, bytesleft--)
+ base[i] = *bufptr++;
+ base += skip;
+ }
+ }
+
+ /* masked cases */
+ else
+ {
+ /* non-grouped data */
+ if (groupsize == 1)
+ for (i = 0; i < bytesleft; i++, base += skip)
+ *base = (*base & ~datamask) | ((*bufptr++ << datashift) & datamask);
+
+ /* grouped data -- non-reversed case */
+ else if (!reversed)
+ while (bytesleft)
+ {
+ for (i = 0; i < groupsize && bytesleft; i++, bytesleft--)
+ base[i] = (base[i] & ~datamask) | ((*bufptr++ << datashift) & datamask);
+ base += skip;
+ }
+
+ /* grouped data -- reversed case */
+ else
+ while (bytesleft)
+ {
+ for (i = groupsize - 1; i >= 0 && bytesleft; i--, bytesleft--)
+ base[i] = (base[i] & ~datamask) | ((*bufptr++ << datashift) & datamask);
+ base += skip;
+ }
+ }
+ }
+ debugload(" All done\n");
+ return ROM_GETLENGTH(romp);
+}
+
+
+/*-------------------------------------------------
+ fill_rom_data - fill a region of ROM space
+-------------------------------------------------*/
+
+static void fill_rom_data(rom_load_data *romdata, const rom_entry *romp)
+{
+ UINT32 numbytes = ROM_GETLENGTH(romp);
+ UINT8 *base = romdata->regionbase + ROM_GETOFFSET(romp);
+
+ /* make sure we fill within the region space */
+ if (ROM_GETOFFSET(romp) + numbytes > romdata->regionlength)
+ fatalerror("Error in RomModule definition: FILL out of memory region space\n");
+
+ /* make sure the length was valid */
+ if (numbytes == 0)
+ fatalerror("Error in RomModule definition: FILL has an invalid length\n");
+
+ /* fill the data (filling value is stored in place of the hashdata) */
+ memset(base, (FPTR)ROM_GETHASHDATA(romp) & 0xff, numbytes);
+}
+
+
+/*-------------------------------------------------
+ copy_rom_data - copy a region of ROM space
+-------------------------------------------------*/
+
+static void copy_rom_data(rom_load_data *romdata, const rom_entry *romp)
+{
+ UINT8 *base = romdata->regionbase + ROM_GETOFFSET(romp);
+ int srcregion = ROM_GETFLAGS(romp) >> 24;
+ UINT32 numbytes = ROM_GETLENGTH(romp);
+ UINT32 srcoffs = (FPTR)ROM_GETHASHDATA(romp); /* srcoffset in place of hashdata */
+ UINT8 *srcbase;
+
+ /* make sure we copy within the region space */
+ if (ROM_GETOFFSET(romp) + numbytes > romdata->regionlength)
+ fatalerror("Error in RomModule definition: COPY out of target memory region space\n");
+
+ /* make sure the length was valid */
+ if (numbytes == 0)
+ fatalerror("Error in RomModule definition: COPY has an invalid length\n");
+
+ /* make sure the source was valid */
+ srcbase = memory_region(srcregion);
+ if (!srcbase)
+ fatalerror("Error in RomModule definition: COPY from an invalid region\n");
+
+ /* make sure we find within the region space */
+ if (srcoffs + numbytes > memory_region_length(srcregion))
+ fatalerror("Error in RomModule definition: COPY out of source memory region space\n");
+
+ /* fill the data */
+ memcpy(base, srcbase + srcoffs, numbytes);
+}
+
+
+/*-------------------------------------------------
+ process_rom_entries - process all ROM entries
+ for a region
+-------------------------------------------------*/
+
+static void process_rom_entries(rom_load_data *romdata, const rom_entry *romp)
+{
+ UINT32 lastflags = 0;
+
+ /* loop until we hit the end of this region */
+ while (!ROMENTRY_ISREGIONEND(romp))
+ {
+ /* if this is a continue entry, it's invalid */
+ if (ROMENTRY_ISCONTINUE(romp))
+ fatalerror("Error in RomModule definition: ROM_CONTINUE not preceded by ROM_LOAD\n");
+
+ /* if this is an ignore entry, it's invalid */
+ if (ROMENTRY_ISIGNORE(romp))
+ fatalerror("Error in RomModule definition: ROM_IGNORE not preceded by ROM_LOAD\n");
+
+ /* if this is a reload entry, it's invalid */
+ if (ROMENTRY_ISRELOAD(romp))
+ fatalerror("Error in RomModule definition: ROM_RELOAD not preceded by ROM_LOAD\n");
+
+ /* handle fills */
+ if (ROMENTRY_ISFILL(romp))
+ fill_rom_data(romdata, romp++);
+
+ /* handle copies */
+ else if (ROMENTRY_ISCOPY(romp))
+ copy_rom_data(romdata, romp++);
+
+ /* handle files */
+ else if (ROMENTRY_ISFILE(romp))
+ {
+ int bios_flags = ROM_GETBIOSFLAGS(romp);
+ if (!bios_flags || (bios_flags == system_bios)) /* alternate bios sets */
+ {
+ const rom_entry *baserom = romp;
+ int explength = 0;
+
+ /* open the file */
+ debugload("Opening ROM file: %s\n", ROM_GETNAME(romp));
+ if (!open_rom_file(romdata, romp))
+ handle_missing_file(romdata, romp);
+
+ /* loop until we run out of reloads */
+ do
+ {
+ /* loop until we run out of continues/ignores */
+ do
+ {
+ rom_entry modified_romp = *romp++;
+ int readresult;
+
+ /* handle flag inheritance */
+ if (!ROM_INHERITSFLAGS(&modified_romp))
+ lastflags = modified_romp._flags;
+ else
+ modified_romp._flags = (modified_romp._flags & ~ROM_INHERITEDFLAGS) | lastflags;
+
+ explength += ROM_GETLENGTH(&modified_romp);
+
+ /* attempt to read using the modified entry */
+ if (!ROMENTRY_ISIGNORE(&modified_romp))
+ readresult = read_rom_data(romdata, &modified_romp);
+ }
+ while (ROMENTRY_ISCONTINUE(romp) || ROMENTRY_ISIGNORE(romp));
+
+ /* if this was the first use of this file, verify the length and CRC */
+ if (baserom)
+ {
+ debugload("Verifying length (%X) and checksums\n", explength);
+ verify_length_and_hash(romdata, ROM_GETNAME(baserom), explength, ROM_GETHASHDATA(baserom));
+ debugload("Verify finished\n");
+ }
+
+ /* reseek to the start and clear the baserom so we don't reverify */
+ if (romdata->file)
+ mame_fseek(romdata->file, 0, SEEK_SET);
+ baserom = NULL;
+ explength = 0;
+ }
+ while (ROMENTRY_ISRELOAD(romp));
+
+ /* close the file */
+ if (romdata->file)
+ {
+ debugload("Closing ROM file\n");
+ mame_fclose(romdata->file);
+ romdata->file = NULL;
+ }
+ }
+ else
+ {
+ romp++; /* skip over file */
+ }
+ }
+ else
+ {
+ romp++; /* something else; skip */
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ open_disk_image - open a DISK image, searching
+ up the parent and loading by checksum
+-------------------------------------------------*/
+
+chd_error open_disk_image(const game_driver *gamedrv, const rom_entry *romp, mame_file **image_file, chd_file **image_chd)
+{
+ return open_disk_image_options(mame_options(), gamedrv, romp, image_file, image_chd);
+}
+
+
+/*-------------------------------------------------
+ open_disk_image_options - open a DISK image, searching
+ up the parent and loading by checksum
+-------------------------------------------------*/
+
+chd_error open_disk_image_options(core_options *options, const game_driver *gamedrv, const rom_entry *romp, mame_file **image_file, chd_file **image_chd)
+{
+ const game_driver *drv, *searchdrv;
+ const rom_entry *region, *rom;
+ file_error filerr;
+ chd_error err;
+
+ *image_file = NULL;
+ *image_chd = NULL;
+
+ /* attempt to open the properly named file, scanning up through parent directories */
+ filerr = FILERR_NOT_FOUND;
+ for (searchdrv = gamedrv; searchdrv != NULL && filerr != FILERR_NONE; searchdrv = driver_get_clone(searchdrv))
+ {
+ astring *fname = astring_assemble_4(astring_alloc(), searchdrv->name, PATH_SEPARATOR, ROM_GETNAME(romp), ".chd");
+ filerr = mame_fopen_options(options, SEARCHPATH_IMAGE, astring_c(fname), OPEN_FLAG_READ, image_file);
+ astring_free(fname);
+ }
+
+ /* did the file open succeed? */
+ if (filerr == FILERR_NONE)
+ {
+ /* try to open the CHD */
+ err = chd_open_file(mame_core_file(*image_file), CHD_OPEN_READ, NULL, image_chd);
+ if (err == CHDERR_NONE)
+ return err;
+
+ /* close the file on failure */
+ mame_fclose(*image_file);
+ *image_file = NULL;
+ }
+ else
+ err = CHDERR_FILE_NOT_FOUND;
+
+ /* otherwise, look at our parents for a CHD with an identical checksum */
+ /* and try to open that */
+ for (drv = gamedrv; drv != NULL; drv = driver_get_clone(drv))
+ for (region = rom_first_region(drv); region != NULL; region = rom_next_region(region))
+ if (ROMREGION_ISDISKDATA(region))
+ for (rom = rom_first_file(region); rom != NULL; rom = rom_next_file(rom))
+
+ /* 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))
+ {
+ /* attempt to open the properly named file, scanning up through parent directories */
+ filerr = FILERR_NOT_FOUND;
+ for (searchdrv = drv; searchdrv != NULL && filerr != FILERR_NONE; searchdrv = driver_get_clone(searchdrv))
+ {
+ astring *fname = astring_assemble_4(astring_alloc(), searchdrv->name, PATH_SEPARATOR, ROM_GETNAME(rom), ".chd");
+ filerr = mame_fopen_options(options, SEARCHPATH_IMAGE, astring_c(fname), OPEN_FLAG_READ, image_file);
+ astring_free(fname);
+ }
+
+ /* did the file open succeed? */
+ if (filerr == FILERR_NONE)
+ {
+ /* try to open the CHD */
+ err = chd_open_file(mame_core_file(*image_file), CHD_OPEN_READ, NULL, image_chd);
+ if (err == CHDERR_NONE)
+ return err;
+
+ /* close the file on failure */
+ mame_fclose(*image_file);
+ *image_file = NULL;
+ }
+ }
+
+ return err;
+}
+
+
+/*-------------------------------------------------
+ open_disk_diff - open a DISK diff file
+-------------------------------------------------*/
+
+static chd_error open_disk_diff(const game_driver *drv, const rom_entry *romp, chd_file *source, mame_file **diff_file, chd_file **diff_chd)
+{
+ astring *fname = astring_assemble_2(astring_alloc(), ROM_GETNAME(romp), ".dif");
+ file_error filerr;
+ chd_error err;
+
+ *diff_file = NULL;
+ *diff_chd = NULL;
+
+ /* try to open the diff */
+ debugload("Opening differencing image file: %s\n", astring_c(fname));
+ filerr = mame_fopen(SEARCHPATH_IMAGE_DIFF, astring_c(fname), OPEN_FLAG_READ | OPEN_FLAG_WRITE, diff_file);
+ if (filerr != FILERR_NONE)
+ {
+ /* didn't work; try creating it instead */
+ debugload("Creating differencing image: %s\n", astring_c(fname));
+ filerr = mame_fopen(SEARCHPATH_IMAGE_DIFF, astring_c(fname), OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, diff_file);
+ if (filerr != FILERR_NONE)
+ {
+ err = CHDERR_FILE_NOT_FOUND;
+ goto done;
+ }
+
+ /* create the CHD */
+ err = chd_create_file(mame_core_file(*diff_file), 0, 0, CHDCOMPRESSION_NONE, source);
+ if (err != CHDERR_NONE)
+ goto done;
+ }
+
+ debugload("Opening differencing image file: %s\n", astring_c(fname));
+ err = chd_open_file(mame_core_file(*diff_file), CHD_OPEN_READWRITE, source, diff_chd);
+ if (err != CHDERR_NONE)
+ goto done;
+
+done:
+ astring_free(fname);
+ if ((err != CHDERR_NONE) && (*diff_file != NULL))
+ {
+ mame_fclose(*diff_file);
+ *diff_file = NULL;
+ }
+ return err;
+}
+
+
+/*-------------------------------------------------
+ process_disk_entries - process all disk entries
+ for a region
+-------------------------------------------------*/
+
+static void process_disk_entries(rom_load_data *romdata, const rom_entry *romp)
+{
+ /* loop until we hit the end of this region */
+ while (!ROMENTRY_ISREGIONEND(romp))
+ {
+ /* handle files */
+ if (ROMENTRY_ISFILE(romp))
+ {
+ char acthash[HASH_BUF_SIZE];
+ open_chd chd = { 0 };
+ chd_header header;
+ astring *filename;
+ chd_error err;
+
+ /* make the filename of the source */
+ filename = astring_assemble_2(astring_alloc(), ROM_GETNAME(romp), ".chd");
+
+ /* first open the source drive */
+ debugload("Opening disk image: %s\n", astring_c(filename));
+ err = open_disk_image(Machine->gamedrv, romp, &chd.origfile, &chd.origchd);
+ if (err != CHDERR_NONE)
+ {
+ if (err == CHDERR_UNSUPPORTED_VERSION)
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s UNSUPPORTED CHD VERSION\n", astring_c(filename));
+ else
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s NOT FOUND\n", astring_c(filename));
+
+ /* 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))
+ romdata->warnings++;
+ else
+ romdata->errors++;
+ goto next;
+ }
+
+ /* 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_MD5, header.md5);
+ hash_data_insert_binary_checksum(acthash, HASH_SHA1, header.sha1);
+
+ /* verify the MD5 */
+ if (!hash_data_is_equal(ROM_GETHASHDATA(romp), acthash, 0))
+ {
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s WRONG CHECKSUMS:\n", astring_c(filename));
+ dump_wrong_and_correct_checksums(romdata, ROM_GETHASHDATA(romp), acthash);
+ romdata->warnings++;
+ }
+ else if (hash_data_has_info(ROM_GETHASHDATA(romp), HASH_INFO_BAD_DUMP))
+ {
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s CHD NEEDS REDUMP\n", astring_c(filename));
+ romdata->warnings++;
+ }
+
+ /* if not read-only, make the diff file */
+ if (!DISK_ISREADONLY(romp))
+ {
+ /* try to open or create the diff */
+ err = open_disk_diff(Machine->gamedrv, romp, chd.origchd, &chd.difffile, &chd.diffchd);
+ if (err != CHDERR_NONE)
+ {
+ if (err == CHDERR_UNSUPPORTED_VERSION)
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s UNSUPPORTED CHD VERSION\n", astring_c(filename));
+ else
+ sprintf(&romdata->errorbuf[strlen(romdata->errorbuf)], "%s: CAN'T OPEN DIFF FILE\n", astring_c(filename));
+ romdata->errors++;
+ goto next;
+ }
+ }
+
+ /* we're okay, add to the list of disks */
+ debugload("Assigning to handle %d\n", DISK_GETINDEX(romp));
+ *chd_list_tailptr = auto_malloc(sizeof(**chd_list_tailptr));
+ **chd_list_tailptr = chd;
+ chd_list_tailptr = &(*chd_list_tailptr)->next;
+next:
+ romp++;
+ astring_free(filename);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ rom_init - new, more flexible ROM
+ loading system
+-------------------------------------------------*/
+
+void rom_init(running_machine *machine, const rom_entry *romp)
+{
+ const rom_entry *regionlist[REGION_MAX];
+ const rom_entry *region;
+ static rom_load_data romdata;
+ int regnum;
+
+ /* if no roms, bail */
+ if (romp == NULL)
+ return;
+
+ /* make sure we get called back on the way out */
+ add_exit_callback(machine, rom_exit);
+
+ /* reset the region list */
+ memset((void *)regionlist, 0, sizeof(regionlist));
+
+ /* reset the romdata struct */
+ memset(&romdata, 0, sizeof(romdata));
+
+ /* determine the correct biosset to load based on OPTION_BIOS string */
+ system_bios = determine_bios_rom(romp);
+
+ romdata.romstotal = count_roms(romp);
+
+ /* reset the disk list */
+ chd_list = NULL;
+ chd_list_tailptr = &chd_list;
+
+ /* loop until we hit the end */
+ for (region = romp, regnum = 0; region; region = rom_next_region(region), regnum++)
+ {
+ int regiontype = ROMREGION_GETTYPE(region);
+
+ debugload("Processing region %02X (length=%X)\n", regiontype, ROMREGION_GETLENGTH(region));
+
+ /* the first entry must be a region */
+ assert(ROMENTRY_ISREGION(region));
+
+ /* remember the base and length */
+ romdata.regionbase = new_memory_region(machine, regiontype, ROMREGION_GETLENGTH(region), ROMREGION_GETFLAGS(region));
+ romdata.regionlength = ROMREGION_GETLENGTH(region);
+ debugload("Allocated %X bytes @ %p\n", romdata.regionlength, romdata.regionbase);
+
+ /* clear the region if it's requested */
+ if (ROMREGION_ISERASE(region))
+ memset(romdata.regionbase, ROMREGION_GETERASEVAL(region), romdata.regionlength);
+
+ /* or if it's sufficiently small (<= 4MB) */
+ else if (romdata.regionlength <= 0x400000)
+ memset(romdata.regionbase, 0, romdata.regionlength);
+
+#ifdef MAME_DEBUG
+ /* if we're debugging, fill region with random data to catch errors */
+ else
+ fill_random(romdata.regionbase, romdata.regionlength);
+#endif
+
+ /* now process the entries in the region */
+ if (ROMREGION_ISROMDATA(region))
+ process_rom_entries(&romdata, region + 1);
+ else if (ROMREGION_ISDISKDATA(region))
+ process_disk_entries(&romdata, region + 1);
+
+ /* add this region to the list */
+ if (regiontype < REGION_MAX)
+ regionlist[regiontype] = region;
+ }
+
+ /* post-process the regions */
+ for (regnum = 0; regnum < REGION_MAX; regnum++)
+ if (regionlist[regnum])
+ {
+ debugload("Post-processing region %02X\n", regnum);
+ romdata.regionlength = memory_region_length(regnum);
+ romdata.regionbase = memory_region(regnum);
+ region_post_process(&romdata, regionlist[regnum]);
+ }
+
+ /* display the results and exit */
+ total_rom_load_warnings = romdata.warnings;
+
+ display_rom_load_results(&romdata);
+}
+
+
+/*-------------------------------------------------
+ rom_exit - clean up after ourselves
+-------------------------------------------------*/
+
+static void rom_exit(running_machine *machine)
+{
+ open_chd *curchd;
+ int i;
+
+ /* free the memory allocated for various regions */
+ for (i = 0; i < MAX_MEMORY_REGIONS; i++)
+ free_memory_region(machine, i);
+
+ /* close all hard drives */
+ for (curchd = chd_list; curchd != NULL; curchd = curchd->next)
+ {
+ if (curchd->diffchd != NULL)
+ chd_close(curchd->diffchd);
+ if (curchd->difffile != NULL)
+ mame_fclose(curchd->difffile);
+ if (curchd->origchd != NULL)
+ chd_close(curchd->origchd);
+ if (curchd->origfile != NULL)
+ mame_fclose(curchd->origfile);
+ }
+}
+
+
+int rom_load_warnings(void)
+{
+ return total_rom_load_warnings;
+}